0

I am building an Android application which logs the degrees of the compass of the device into a file. There are two methods that get this degrees:

Method 1:

SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor orientationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(this, orientationSensor, SensorManager.SENSOR_DELAY_NORMAL);


public void onSensorChanged(SensorEvent event) {
    float azimuthInDegrees = event.values[0]
}

Method 2:

SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor magnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_NORMAL);

float[] mGravity;
float[] mGeomagnetic;

public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
        mGravity = event.values.clone();
    }


    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
        mGeomagnetic = event.values.clone();
    }

    if (mGravity != null && mGeomagnetic != null) {
        float R[] = new float[9];
        float I[] = new float[9];
        boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
        if (success) {
            float orientation[] = new float[3];
        SensorManager.getOrientation(R, orientation);   

        float azimuthInDegress = ((float) Math.toDegrees(orientation[0]) + 360) % 360;
        }
    }
}

I tried out both methods by placing my device in the North direction and doing a whole clockwise 360 degrees cycle and end up again in the North direction.

Method 1 returns the following logged JSON data:

[
   {
      "start time":"25-03-2013 20:42:11.071",
      "direction":"N",
      "end time":"25-03-2013 20:42:14.711"
   },
   {
      "start time":"25-03-2013 20:42:14.781",
      "direction":"NE",
      "end time":"25-03-2013 20:42:18.842"
   },
   {
      "start time":"25-03-2013 20:42:18.912",
      "direction":"E",
      "end time":"25-03-2013 20:42:21.643"
   },
   {
      "start time":"25-03-2013 20:42:21.712",
      "direction":"SE",
      "end time":"25-03-2013 20:42:25.072"
   },
   {
      "start time":"25-03-2013 20:42:25.142",
      "direction":"S",
      "end time":"25-03-2013 20:42:27.524"
   },
   {
      "start time":"25-03-2013 20:42:27.593",
      "direction":"SW",
      "end time":"25-03-2013 20:42:30.113"
   },
   {
      "start time":"25-03-2013 20:42:30.184",
      "direction":"W",
      "end time":"25-03-2013 20:42:32.773"
   },
   {
      "start time":"25-03-2013 20:42:32.843",
      "direction":"NW",
      "end time":"25-03-2013 20:42:34.943"
   },
   {
      "start time":"25-03-2013 20:42:35.013",
      "direction":"N",
      "end time":"25-03-2013 20:42:37.394"
   }
]

Method 2 returns logs the following JSON data:

[
   {
      "start time":"25-03-2013 20:36:07.337",
      "direction":"N",
      "end time":"25-03-2013 20:36:09.728"
   },
   {
      "start time":"25-03-2013 20:36:09.741",
      "direction":"NE",
      "end time":"25-03-2013 20:36:13.832"
   },
   {
      "start time":"25-03-2013 20:36:13.832",
      "direction":"E",
      "end time":"25-03-2013 20:36:16.689"
   },
   {
      "start time":"25-03-2013 20:36:16.754",
      "direction":"SE",
      "end time":"25-03-2013 20:36:16.754"
   },
   {
      "start time":"25-03-2013 20:36:16.819",
      "direction":"E",
      "end time":"25-03-2013 20:36:16.819"
   },
   {
      "start time":"25-03-2013 20:36:16.819",
      "direction":"SE",
      "end time":"25-03-2013 20:36:16.819"
   },
   {
      "start time":"25-03-2013 20:36:16.884",
      "direction":"E",
      "end time":"25-03-2013 20:36:17.014"
   },
   {
      "start time":"25-03-2013 20:36:17.014",
      "direction":"SE",
      "end time":"25-03-2013 20:36:19.546"
   },
   {
      "start time":"25-03-2013 20:36:19.546",
      "direction":"S",
      "end time":"25-03-2013 20:36:22.338"
   },
   {
      "start time":"25-03-2013 20:36:22.338",
      "direction":"SW",
      "end time":"25-03-2013 20:36:25.260"
   },
   {
      "start time":"25-03-2013 20:36:25.324",
      "direction":"W",
      "end time":"25-03-2013 20:36:25.324"
   },
   {
      "start time":"25-03-2013 20:36:25.324",
      "direction":"SW",
      "end time":"25-03-2013 20:36:25.390"
   },
   {
      "start time":"25-03-2013 20:36:25.390",
      "direction":"W",
      "end time":"25-03-2013 20:36:27.987"
   },
   {
      "start time":"25-03-2013 20:36:28.051",
      "direction":"NW",
      "end time":"25-03-2013 20:36:28.128"
   },
   {
      "start time":"25-03-2013 20:36:28.181",
      "direction":"W",
      "end time":"25-03-2013 20:36:28.181"
   },
   {
      "start time":"25-03-2013 20:36:28.181",
      "direction":"NW",
      "end time":"25-03-2013 20:36:28.181"
   },
   {
      "start time":"25-03-2013 20:36:28.246",
      "direction":"W",
      "end time":"25-03-2013 20:36:28.246"
   },
   {
      "start time":"25-03-2013 20:36:28.246",
      "direction":"NW",
      "end time":"25-03-2013 20:36:30.974"
   },
   {
      "start time":"25-03-2013 20:36:31.038",
      "direction":"N",
      "end time":"25-03-2013 20:36:36.233"
   }
]

As you can see, the results in the second method aren't smooth as the fist method although I did one straight turn from North to North. I prefer to use the first method but the problem is that it is deprecated. On the other hand, the second method doesn't log smooth data. What shall I do in your opinion guys?

duncanportelli
  • 3,161
  • 8
  • 38
  • 59

1 Answers1

2

To be able to calculate the rotation matrix, the gravity parameter in the getRotationMatrix is assume to be gravitation acceleration only. That is if (w_1, w_2, w_3) are the world basis, w_1 is a unit vector pointing EAST, w_2 is a unit vector pointing NORTH and w_3 is a unit vector pointing toward the SKY, then the gravity parameter is assumed to be a scalar multiple of the w_3 vector.
The reason your result is not smooth because when the device is rotated the acceleration of the device is now include non-gravity acceleration. The accelerometer values now do not accurately approximate gravity anymore. Therefore, the word basis is not accurate represented. The getOrientation method calculates the azimuth by orthogonally projected the device unit y-axis onto the w_1 ,w_2 plane (East-North plane) and then calculate the angle between the resulting vector and the w_2 (North) vector. So if w_1, w_2, w_3 are not accurately represented, then your result will not be accurated.
To improve the accuracy, you need to filter the accelerometer to eliminate other acceleration beside gravitation. For this the most simple method is low pass filter.
For API > 8, android provides TYPE_GRAVITY which I am pretty sure is just filtered accelerometer values, the filtering method is probably some kind of KALMAN filter. Using TYPE_GRAVITY improves the results upto 10 degrees from a lowpass filter.
Also, to remove fluctuation one keeps a history of the results and average them. You can save the rotation matrix in a list and then average or save the azimuths and then average. You have to be careful if you average the azimuths because of their periodic nature. You can use the following method to average them.

public static final float averageAngle(float[] terms, int totalTerm)
{
    float sumSin = 0;
    float sumCos = 0;
    for (int i = 0; i < totalTerm; i++)
    {
        sumSin += Math.sin(terms[i]);
        sumCos += Math.cos(terms[i]);
    }
    return (float) Math.atan2(sumSin / totalTerm, sumCos / totalTerm);
}

Note: I think the reason TYPE_ORIENTATION is depreciated because it only provides the orientation in the normal sense only if the device is flat or near flat. It gives the results return by getOrientation. Now if the device is vertical and rotated, for example half way between POTRAIT and LANDSCAPE, then getRotation return the azimuth as the direction of where the projection of y-axis onto the w_1 ,w_2 plane is pointing. It is not the direction of where the back camera is pointing, which is the direction of the -z-axis. People do not understand this and used in all cases and thus android depreciated. If you still using it you will be surprise because hand phone manufacturers implement it in different way. The HTC and the Motorola will give you totally different results.
You can get more info from my answer at Convert magnetic field X, Y, Z values from device into global reference frame and at Magnetic Fields, Rotation Matrix And global coordinates

Community
  • 1
  • 1
Hoan Nguyen
  • 18,033
  • 3
  • 50
  • 54