SensorManager.getRotationMatrix()
does what's outlined below, written before I found this out. I'll leave the added explanation because if you want to correct for the difference between magnetic and true north you'll still need it.
The rough algorithm is to get the rotation matrix, multiply vector [0,0,-1]
by it, then adjust this to your coordinate system. Why? Android docs give the coordinate systems for device and world
Note [0,0,-1]
in Android device coords points perpendicular backward away from the screen. If you multiply rotation matrix R by this vector, you'll get [0,0,-1]
in world coords when the device is on the table facing up as you desire. When it's upright facing north, you'll get [0,-1,0]
, which indicates that you've chosen a coordinate system where x
and y
are swapped with respect to the Android system, but that's simply a change of conventions.
Note R * [0,0,-1]^T
is just the third column of R
negated. From this I get the pseudocode:
getRotationMatrix(R);
Let v = first three elements of third column of R.
swap v[0] and v[1]
This ought to get what you want.
Additional information on what getRotationMatrix()
is doing follows.
You need both accerometer data to establish the direction "down" and magnetometer data to determine the direction "north." You'll have to assume the accelerometers are sensing only gravity (the device is stationary or moving at a steady velocity). Then you need to project the magnetometer vector onto the plane perpendicular to the gravity vector (because the magnetic field is not generally tangent to the earth's surface). This gives you two axes. The third is orthogonal, so can be computed by cross product. This gives you the earth coordinate vectors in the device system. It looks like you want the inverse: device coordinates in earth coordinates. For this just construct the matrix of direction cosines and invert.
I will add that the above discussion assumes that the magnetometer vector points north. I think (from high school science!) it's actually toward magnetic south, but have no device at hand so can't try it. Of course magnetic north/south is different from true by zero to 180 degrees depending where you are on the earth. You can retrieve GPS coordinates and compute an actual offset.
If you are not familiar with the math needed to do these, I can explain further, but it will have to be later.