javascriptdevicemotiongl-matrix

Javascript - transforming device acceleration to earth coordinates


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.


Solution

  • 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.