Working with Acclerometer Data


Where screen-relative results are desired, the accelerometer values must be rotated according to the display orientation returned by the Android API’s getOrientation() or getRotation() functions. Both functions return the same values, but the former is a deprecated usage.

The value returned from these functions corresponds to the integers/constants defined in Android.view.Surface, those prefixed with ROTATION_. Below is an example of the invocation of one of these functions. Here this is of type Activity.

WindowManager windowMgr =
     (WindowManager)this.getSystemService(WINDOW_SERVICE);
int rotationIndex = windowMgr.getDefaultDisplay().getRotation();

The returned constants are:

Constant Name Index/Value
ROTATION_O 0
ROTATION_90 1
ROTATION_180 2
ROTATION_270 3

Applications can use the rotation value to construct a transformation matrix that will convert Android Canonical accelerometer data to other coordinate spaces. In order to transform from Canonical aligned accelerometer values into either screen or world aligned accelerometer values, a canonAccel vector needs to be rotated by 90 degree increments based on the rotationIndex, (where ROTATION_0 means no rotation is necessary).

For the canonToScreen transform, the rotations follow these equations:

screenAccel[0] = canonAccel[0] * cos⁡null - canonAccel[1] * sin⁡null
screenAccel[1] = -canonAccel[0] * sin⁡null - canonAccel[1] * cos⁡null
screenAccel[2] = canonAccel[2]

Where:

canonToScreen formula

A function implementing the canonToScreen transform follows.

static void canonicalToScreen(int     displayRotation,
                              float[] canVec,
                              float[] screenVec)
{
     struct AxisSwap
     {
          signed char negateX;
          signed char negateY;
          signed char xSrc;
          signed char ySrc;
     };
     static const AxisSwap axisSwap[] = {
          { 1, -1, 0, 1 },   // ROTATION_0
          {-1, -1, 1, 0 },   // ROTATION_90
          {-1,  1, 0, 1 },   // ROTATION_180
          { 1,  1, 1, 0 } }; // ROTATION_270

     const AxisSwap& as = axisSwap[displayRotation];
     screenVec[0] = (float)as.negateX * canVec[ as.xSrc ];
     screenVec[1] = (float)as.negateY * canVec[ as.ySrc ];
     screenVec[2] = canVec[2]; 
}

For the canonToWorld transform, the rotations follow these equations instead:

screenAccel[0] = canonAccel[0] * cos⁡null - canonAccel[1] * sin⁡null
screenAccel[1] = canonAccel[0] * sin⁡null + canonAccel[1] * cos⁡null
screenAccel[2] = canonAccel[2]

This axis-aligned transformation can be put into a static array, as shown below in the canonicalToWorld() function that uses a simple integer lookup array to avoid costly trigonometric functions when converting a canonical space accelerometer vector into an OpenGL-style world space vector.

static void canonicalToWorld( int           displayRotation,
                              const float*  canVec,
                              float*        worldVec)
{
     struct AxisSwap
     {
          signed char negateX;
          signed char negateY;
          signed char xSrc;
          signed char ySrc;
     };
     static const AxisSwap axisSwap[] = {
          { 1,  1, 0, 1 },   // ROTATION_0
          {-1,  1, 1, 0 },   // ROTATION_90
          {-1, -1, 0, 1 },   // ROTATION_180
          { 1, -1, 1, 0 } }; // ROTATION_270

     const AxisSwap& as = axisSwap[displayRotation];
     worldVec[0] = (float)as.negateX * canVec[ as.xSrc ];
     worldVec[1] = (float)as.negateY * canVec[ as.ySrc ];
     worldVec[2] = canVec[2];
}

The next function will compute the axis-angle transform necessary to align a model’s localUp vector with that of the accelerometer. The function returns the vector (rotAxis) and angle (ang), which is sufficient to build a transformation matrix or to build a quaternion to orient a model vertically in World space.

void computeAxisAngle(const float* localUp, const float* worldVec,
                      float* rotAxis, float* ang)
{
     const Vec3& lup  =  *(Vec3*)localUp;
     Vec3 nTarget     =  normalize(*(Vec3*)worldVec);
     *rotAxis         =  cross(lup, nTarget);
     *rotAxis         =  normalize(*rotAxis);
     *ang             = -acosf(dot(lup, nTarget));
}

The NVIDIA Android NDK Samples includes library functions for building matrices and quaternions from the axis angle representation. It may be necessary to apply an additional rotation to orient objects in the plane orthogonal to the final world vector.


NVIDIA® GameWorks™ Documentation Rev. 1.0.200601 ©2014-2020. NVIDIA Corporation. All Rights Reserved.