//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.google.vrtoolkit.cardboard.sensors.internal;
public class OrientationEKF {
private static final float NS2S = 1.0E-9F;
private static final double MIN_ACCEL_NOISE_SIGMA = 0.75D;
private static final double MAX_ACCEL_NOISE_SIGMA = 7.0D;
private double[] rotationMatrix = new double[16];
private Matrix3x3d so3SensorFromWorld = new Matrix3x3d();
private Matrix3x3d so3LastMotion = new Matrix3x3d();
private Matrix3x3d mP = new Matrix3x3d();
private Matrix3x3d mQ = new Matrix3x3d();
private Matrix3x3d mR = new Matrix3x3d();
private Matrix3x3d mRaccel = new Matrix3x3d();
private Matrix3x3d mS = new Matrix3x3d();
private Matrix3x3d mH = new Matrix3x3d();
private Matrix3x3d mK = new Matrix3x3d();
private Vector3d mNu = new Vector3d();
private Vector3d mz = new Vector3d();
private Vector3d mh = new Vector3d();
private Vector3d mu = new Vector3d();
private Vector3d mx = new Vector3d();
private Vector3d down = new Vector3d();
private Vector3d north = new Vector3d();
private long sensorTimeStampGyro;
private final Vector3d lastGyro = new Vector3d();
private double previousAccelNorm = 0.0D;
private double movingAverageAccelNormChange = 0.0D;
private float filteredGyroTimestep;
private boolean timestepFilterInit = false;
private int numGyroTimestepSamples;
private boolean gyroFilterValid = true;
private Matrix3x3d getPredictedGLMatrixTempM1 = new Matrix3x3d();
private Matrix3x3d getPredictedGLMatrixTempM2 = new Matrix3x3d();
private Vector3d getPredictedGLMatrixTempV1 = new Vector3d();
private Matrix3x3d setHeadingDegreesTempM1 = new Matrix3x3d();
private Matrix3x3d processGyroTempM1 = new Matrix3x3d();
private Matrix3x3d processGyroTempM2 = new Matrix3x3d();
private Matrix3x3d processAccTempM1 = new Matrix3x3d();
private Matrix3x3d processAccTempM2 = new Matrix3x3d();
private Matrix3x3d processAccTempM3 = new Matrix3x3d();
private Matrix3x3d processAccTempM4 = new Matrix3x3d();
private Matrix3x3d processAccTempM5 = new Matrix3x3d();
private Vector3d processAccTempV1 = new Vector3d();
private Vector3d processAccTempV2 = new Vector3d();
private Vector3d processAccVDelta = new Vector3d();
private Vector3d processMagTempV1 = new Vector3d();
private Vector3d processMagTempV2 = new Vector3d();
private Vector3d processMagTempV3 = new Vector3d();
private Vector3d processMagTempV4 = new Vector3d();
private Vector3d processMagTempV5 = new Vector3d();
private Matrix3x3d processMagTempM1 = new Matrix3x3d();
private Matrix3x3d processMagTempM2 = new Matrix3x3d();
private Matrix3x3d processMagTempM4 = new Matrix3x3d();
private Matrix3x3d processMagTempM5 = new Matrix3x3d();
private Matrix3x3d processMagTempM6 = new Matrix3x3d();
private Matrix3x3d updateCovariancesAfterMotionTempM1 = new Matrix3x3d();
private Matrix3x3d updateCovariancesAfterMotionTempM2 = new Matrix3x3d();
private Matrix3x3d accObservationFunctionForNumericalJacobianTempM = new Matrix3x3d();
private Matrix3x3d magObservationFunctionForNumericalJacobianTempM = new Matrix3x3d();
private boolean alignedToGravity;
private boolean alignedToNorth;
public OrientationEKF() {
this.reset();
}
public synchronized void reset() {
this.sensorTimeStampGyro = 0L;
this.so3SensorFromWorld.setIdentity();
this.so3LastMotion.setIdentity();
double initialSigmaP = 5.0D;
this.mP.setZero();
this.mP.setSameDiagonal(25.0D);
double initialSigmaQ = 1.0D;
this.mQ.setZero();
this.mQ.setSameDiagonal(1.0D);
double initialSigmaR = 0.25D;
this.mR.setZero();
this.mR.setSameDiagonal(0.0625D);
this.mRaccel.setZero();
this.mRaccel.setSameDiagonal(0.5625D);
this.mS.setZero();
this.mH.setZero();
this.mK.setZero();
this.mNu.setZero();
this.mz.setZero();
this.mh.setZero();
this.mu.setZero();
this.mx.setZero();
this.down.set(0.0D, 0.0D, 9.81D);
this.north.set(0.0D, 1.0D, 0.0D);
this.alignedToGravity = false;
this.alignedToNorth = false;
}
public boolean isReady() {
return this.alignedToGravity;
}
public double getHeadingDegrees() {
double x = this.so3SensorFromWorld.get(2, 0);
double y = this.so3SensorFromWorld.get(2, 1);
double mag = Math.sqrt(x * x + y * y);
if(mag < 0.1D) {
return 0.0D;
} else {
double heading = -90.0D - Math.atan2(y, x) / 3.141592653589793D * 180.0D;
if(heading < 0.0D) {
heading += 360.0D;
}
if(heading >= 360.0D) {
heading -= 360.0D;
}
return heading;
}
}
public synchronized void setHeadingDegrees(double heading) {
double currentHeading = this.getHeadingDegrees();
double deltaHeading = heading - currentHeading;
double s = Math.sin(deltaHeading / 180.0D * 3.141592653589793D);
double c = Math.cos(deltaHeading / 180.0D * 3.141592653589793D);
double[][] deltaHeadingRotationVals = new double[][]{{c, -s, 0.0D}, {s, c, 0.0D}, {0.0D, 0.0D, 1.0D}};
arrayAssign(deltaHeadingRotationVals, this.setHeadingDegreesTempM1);
Matrix3x3d.mult(this.so3SensorFromWorld, this.setHeadingDegreesTempM1, this.so3SensorFromWorld);
}
public double[] getGLMatrix() {
return this.glMatrixFromSo3(this.so3SensorFromWorld);
}
public double[] getPredictedGLMatrix(double secondsAfterLastGyroEvent) {
Vector3d pmu = this.getPredictedGLMatrixTempV1;
pmu.set(this.lastGyro);
pmu.scale(-secondsAfterLastGyroEvent);
Matrix3x3d so3PredictedMotion = this.getPredictedGLMatrixTempM1;
So3Util.sO3FromMu(pmu, so3PredictedMotion);
Matrix3x3d so3PredictedState = this.getPredictedGLMatrixTempM2;
Matrix3x3d.mult(so3PredictedMotion, this.so3SensorFromWorld, so3PredictedState);
return this.glMatrixFromSo3(so3PredictedState);
}
public Matrix3x3d getRotationMatrix() {
return this.so3SensorFromWorld;
}
public static void arrayAssign(double[][] data, Matrix3x3d m) {
assert 3 == data.length;
assert 3 == data[0].length;
assert 3 == data[1].length;
assert 3 == data[2].length;
m.set(data[0][0], data[0][1], data[0][2], data[1][0], data[1][1], data[1][2], data[2][0], data[2][1], data[2][2]);
}
public boolean isAlignedToGravity() {
return this.alignedToGravity;
}
public boolean isAlignedToNorth() {
return this.alignedToNorth;
}
public synchronized void processGyro(Vector3d gyro, long sensorTimeStamp) {
float kTimeThreshold = 0.04F;
float kdTdefault = 0.01F;
if(this.sensorTimeStampGyro != 0L) {
float dT = (float)(sensorTimeStamp - this.sensorTimeStampGyro) * 1.0E-9F;
if(dT > 0.04F) {
dT = this.gyroFilterValid?this.filteredGyroTimestep:0.01F;
} else {
this.filterGyroTimestep(dT);
}
this.mu.set(gyro);
this.mu.scale((double)(-dT));
So3Util.sO3FromMu(this.mu, this.so3LastMotion);
this.processGyroTempM1.set(this.so3SensorFromWorld);
Matrix3x3d.mult(this.so3LastMotion, this.so3SensorFromWorld, this.processGyroTempM1);
this.so3SensorFromWorld.set(this.processGyroTempM1);
this.updateCovariancesAfterMotion();
this.processGyroTempM2.set(this.mQ);
this.processGyroTempM2.scale((double)(dT * dT));
this.mP.plusEquals(this.processGyroTempM2);
}
this.sensorTimeStampGyro = sensorTimeStamp;
this.lastGyro.set(gyro);
}
private void updateAccelCovariance(double currentAccelNorm) {
double currentAccelNormChange = Math.abs(currentAccelNorm - this.previousAccelNorm);
this.previousAccelNorm = currentAccelNorm;
double kSmoothingFactor = 0.5D;
this.movingAverageAccelNormChange = 0.5D * currentAccelNormChange + 0.5D * this.movingAverageAccelNormChange;
double kMaxAccelNormChange = 0.15D;
double normChangeRatio = this.movingAverageAccelNormChange / 0.15D;
double accelNoiseSigma = Math.min(7.0D, 0.75D + normChangeRatio * 6.25D);
this.mRaccel.setSameDiagonal(accelNoiseSigma * accelNoiseSigma);
}
public synchronized void processAcc(Vector3d acc, long sensorTimeStamp) {
this.mz.set(acc);
this.updateAccelCovariance(this.mz.length());
if(this.alignedToGravity) {
this.accObservationFunctionForNumericalJacobian(this.so3SensorFromWorld, this.mNu);
double eps = 1.0E-7D;
for(int dof = 0; dof < 3; ++dof) {
Vector3d delta = this.processAccVDelta;
delta.setZero();
delta.setComponent(dof, eps);
So3Util.sO3FromMu(delta, this.processAccTempM1);
Matrix3x3d.mult(this.processAccTempM1, this.so3SensorFromWorld, this.processAccTempM2);
this.accObservationFunctionForNumericalJacobian(this.processAccTempM2, this.processAccTempV1);
Vector3d withDelta = this.processAccTempV1;
Vector3d.sub(this.mNu, withDelta, this.processAccTempV2);
this.processAccTempV2.scale(1.0D / eps);
this.mH.setColumn(dof, this.processAccTempV2);
}
this.mH.transpose(this.processAccTempM3);
Matrix3x3d.mult(this.mP, this.processAccTempM3, this.processAccTempM4);
Matrix3x3d.mult(this.mH, this.processAccTempM4, this.processAccTempM5);
Matrix3x3d.add(this.processAccTempM5, this.mRaccel, this.mS);
this.mS.invert(this.processAccTempM3);
this.mH.transpose(this.processAccTempM4);
Matrix3x3d.mult(this.processAccTempM4, this.processAccTempM3, this.processAccTempM5);
Matrix3x3d.mult(this.mP, this.processAccTempM5, this.mK);
Matrix3x3d.mult(this.mK, this.mNu, this.mx);
Matrix3x3d.mult(this.mK, this.mH, this.processAccTempM3);
this.processAccTempM4.setIdentity();
this.processAccTempM4.minusEquals(this.processAccTempM3);
Matrix3x3d.mult(this.processAccTempM4, this.mP, this.processAccTempM3);
this.mP.set(this.processAccTempM3);
So3Util.sO3FromMu(this.mx, this.so3LastMotion);
Matrix3x3d.mult(this.so3LastMotion, this.so3SensorFromWorld, this.so3SensorFromWorld);
this.updateCovariancesAfterMotion();
} else {
So3Util.sO3FromTwoVec(this.down, this.mz, this.so3SensorFromWorld);
this.alignedToGravity = true;
}
}
public synchronized void processMag(float[] mag, long sensorTimeStamp) {
if(this.alignedToGravity) {
this.mz.set((double)mag[0], (double)mag[1], (double)mag[2]);
this.mz.normalize();
Vector3d downInSensorFrame = new Vector3d();
this.so3SensorFromWorld.getColumn(2, downInSensorFrame);
Vector3d.cross(this.mz, downInSensorFrame, this.processMagTempV1);
Vector3d perpToDownAndMag = this.processMagTempV1;
perpToDownAndMag.normalize();
Vector3d.cross(downInSensorFrame, perpToDownAndMag, this.processMagTempV2);
Vector3d magHorizontal = this.processMagTempV2;
magHorizontal.normalize();
this.mz.set(magHorizontal);
if(this.alignedToNorth) {
this.magObservationFunctionForNumericalJacobian(this.so3SensorFromWorld, this.mNu);
double eps = 1.0E-7D;
for(int dof = 0; dof < 3; ++dof) {
Vector3d delta = this.processMagTempV3;
delta.setZero();
delta.setComponent(dof, eps);
So3Util.sO3FromMu(delta, this.processMagTempM1);
Matrix3x3d.mult(this.processMagTempM1, this.so3SensorFromWorld, this.processMagTempM2);
this.magObservationFunctionForNumericalJacobian(this.processMagTempM2, this.processMagTempV4);
Vector3d withDelta = this.processMagTempV4;
Vector3d.sub(this.mNu, withDelta, this.processMagTempV5);
this.processMagTempV5.scale(1.0D / eps);
this.mH.setColumn(dof, this.processMagTempV5);
}
this.mH.transpose(this.processMagTempM4);
Matrix3x3d.mult(this.mP, this.processMagTempM4, this.processMagTempM5);
Matrix3x3d.mult(this.mH, this.processMagTempM5, this.processMagTempM6);
Matrix3x3d.add(this.processMagTempM6, this.mR, this.mS);
this.mS.invert(this.processMagTempM4);
this.mH.transpose(this.processMagTempM5);
Matrix3x3d.mult(this.processMagTempM5, this.processMagTempM4, this.processMagTempM6);
Matrix3x3d.mult(this.mP, this.processMagTempM6, this.mK);
Matrix3x3d.mult(this.mK, this.mNu, this.mx);
Matrix3x3d.mult(this.mK, this.mH, this.processMagTempM4);
this.processMagTempM5.setIdentity();
this.processMagTempM5.minusEquals(this.processMagTempM4);
Matrix3x3d.mult(this.processMagTempM5, this.mP, this.processMagTempM4);
this.mP.set(this.processMagTempM4);
So3Util.sO3FromMu(this.mx, this.so3LastMotion);
Matrix3x3d.mult(this.so3LastMotion, this.so3SensorFromWorld, this.processMagTempM4);
this.so3SensorFromWorld.set(this.processMagTempM4);
this.updateCovariancesAfterMotion();
} else {
this.magObservationFunctionForNumericalJacobian(this.so3SensorFromWorld, this.mNu);
So3Util.sO3FromMu(this.mNu, this.so3LastMotion);
Matrix3x3d.mult(this.so3LastMotion, this.so3SensorFromWorld, this.processMagTempM4);
this.so3SensorFromWorld.set(this.processMagTempM4);
this.updateCovariancesAfterMotion();
this.alignedToNorth = true;
}
}
}
private double[] glMatrixFromSo3(Matrix3x3d so3) {
for(int r = 0; r < 3; ++r) {
for(int c = 0; c < 3; ++c) {
this.rotationMatrix[4 * c + r] = so3.get(r, c);
}
}
this.rotationMatrix[3] = this.rotationMatrix[7] = this.rotationMatrix[11] = 0.0D;
this.rotationMatrix[12] = this.rotationMatrix[13] = this.rotationMatrix[14] = 0.0D;
this.rotationMatrix[15] = 1.0D;
return this.rotationMatrix;
}
private void filterGyroTimestep(float timeStep) {
float kFilterCoeff = 0.95F;
float kMinSamples = 10.0F;
if(!this.timestepFilterInit) {
this.filteredGyroTimestep = timeStep;
this.numGyroTimestepSamples = 1;
this.timestepFilterInit = true;
} else {
this.filteredGyroTimestep = 0.95F * this.filteredGyroTimestep + 0.050000012F * timeStep;
if((float)(++this.numGyroTimestepSamples) > 10.0F) {
this.gyroFilterValid = true;
}
}
}
private void updateCovariancesAfterMotion() {
this.so3LastMotion.transpose(this.updateCovariancesAfterMotionTempM1);
Matrix3x3d.mult(this.mP, this.updateCovariancesAfterMotionTempM1, this.updateCovariancesAfterMotionTempM2);
Matrix3x3d.mult(this.so3LastMotion, this.updateCovariancesAfterMotionTempM2, this.mP);
this.so3LastMotion.setIdentity();
}
private void accObservationFunctionForNumericalJacobian(Matrix3x3d so3SensorFromWorldPred, Vector3d result) {
Matrix3x3d.mult(so3SensorFromWorldPred, this.down, this.mh);
So3Util.sO3FromTwoVec(this.mh, this.mz, this.accObservationFunctionForNumericalJacobianTempM);
So3Util.muFromSO3(this.accObservationFunctionForNumericalJacobianTempM, result);
}
private void magObservationFunctionForNumericalJacobian(Matrix3x3d so3SensorFromWorldPred, Vector3d result) {
Matrix3x3d.mult(so3SensorFromWorldPred, this.north, this.mh);
So3Util.sO3FromTwoVec(this.mh, this.mz, this.magObservationFunctionForNumericalJacobianTempM);
So3Util.muFromSO3(this.magObservationFunctionForNumericalJacobianTempM, result);
}
}