I'm having problem transforming device acceleration values to earth coordinate system in a web app using pure javascript (most questions with solutions are Android or iOS specific).
I'm using the gl-matrix library to perform the calculations and have based my solution so far mostly on the answers I have found for Android native apps. What I have so far for the event handlers for orientation and motion:
readonly deg2rad = Math.PI / 180; // Degree-to-Radian conversion
rotMatrix: mat3;
relRotMatrix: mat3;
earthAcc: mat3;
resetOrientationPending: boolean = false;
handleOrientation(event) {
var absolute = event.absolute;
var alpha = event.alpha;
var beta = event.beta;
var gamma = event.gamma;
if (this.rotMatrix) {
let curRotMat = this.getRotationMatrix(alpha * this.deg2rad, beta * this.deg2rad, gamma * this.deg2rad);
this.relRotMatrix = mat3.multiply(mat3.create(), this.rotMatrix, curRotMat);
}
else if (this.resetOrientationPending) {
this.resetOrientationPending = false;
this.rotMatrix = mat3.transpose(mat3.create(), this.getRotationMatrix(alpha * this.deg2rad, beta * this.deg2rad, gamma * this.deg2rad));
}
}
handleMotion(event) {
let x = event.acceleration.x;
let y = event.acceleration.y;
let z = event.acceleration.z;
let accMatrix = mat3.fromTranslation(mat3.create(), [x, y, z]);
this.earthAcc = mat3.multiply(mat3.create(), this.relRotMatrix, accMatrix);
let ex = this.earthAcc[6];
let ey = this.earthAcc[7];
let ez = this.earthAcc[8];
}
getRotationMatrix(alpha: number, beta: number, gamma: number): mat3 {
let _x, _y, _z;
let cX, cY, cZ, sX, sY, sZ;
_z = alpha;
_x = beta;
_y = gamma;
cX = Math.cos(_x);
cY = Math.cos(_y);
cZ = Math.cos(_z);
sX = Math.sin(_x);
sY = Math.sin(_y);
sZ = Math.sin(_z);
let res = mat3.create();
res[0] = cZ * cY + sZ * sX * sY; // row 1, col 1
res[1] = cX * sZ; // row 2, col 1
res[2] = -cZ * sY + sZ * sX * cY; // row 3, col 1
res[3] = -cY * sZ + cZ * sX * sY; // row 1, col 2
res[4] = cZ * cX; // row 2, col 2
res[5] = sZ * sY + cZ * cY * sX; // row 3, col 2
res[6] = cX * sY; // row 1, col 3
res[7] = -sX; // row 2, col 3
res[8] = cX * cY; // row 3, col 3
return res;
}
If I have my phone laying flat on the table (in portrait mode) with the top pointing away from me, and perform a "resetOrientation", this works for left/right and forward/backward movement. That is, if I rotate the phone 180 degrees around the z-axis (meaning still flat on the table) so I have the top pointing towards me, the acceleration (earthAcc matrix in code) for L/R and F/B movement is still correct. But up/down is not correct, and as soon as I start to rotate the phone around other axis' I get inconsistent results.
There is probably just one line missing or something like that, hopefully someone who is a matrix algebra guru spots the problem :)
UPDATE: By changing how I create the accMatrix in handleMotion to this:
let accMatrix = mat3.create();
accMatrix.set([0, 0, 0, 0, 0, 0, x, y, z]);
...I also have UP/DOWN working, but if I rotate the phone in any other axis than Z, it is no longer giving correct values.
UPDATE 2: I am testing this on iOS Safari (Iphone) so I don't know if this is might be a device type specific problem, but I am using the Gyronorm library that should return normalized values.
I have been able to sort out my mistake. The acceleration multiplication matrix should look like this:
accMatrix.set([1, 0, 0, 0, 1, 0, x, y, z]);
That is, instead of only zeroes, the base should be an identity matrix.