title: Inconsistent orientation sensor values on Android for azimuth/yaw and roll
body: <p>I’m having trouble getting good orientation sensor readings.  The sensor readings seemed unreliable, so I tested my code against two free sensor test apps (<a href="https://play.google.com/store/apps/details?id=com.dicotomica.sensortester&amp;hl=en">Sensor Tester (Dicotomica)</a> and <a href="https://play.google.com/store/apps/details?id=jp.holy.rssoftware.app.sensor">Sensor Monitoring (R's Software)</a>).  I found that while my readings often agreed with the sensor test apps, occasionally the values for azimuth/yaw, and roll differed by up to 40 degrees, although the pitch reading mostly agreed.  The two free apps always seemed to agree with each other.</p>
  
  <p>I put my code into a tiny Android activity and got the same inconsistency.  The code is as follows:</p>
  
  <pre><code>public class MainActivity extends Activity implements  SensorEventListener {
  
      private SensorManager mSensorManager;
      private float[] AccelerometerValues;
      private float[] MagneticFieldValues;
      private float[] RotationMatrix;
      private long nextRefreshTime;           // used to ensure dump to LogCat occurs no more than 4 times a second
      private DecimalFormat df;               // used for dumping sensors to LogCat
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
  
          mSensorManager = (SensorManager)getSystemService(android.content.Context.SENSOR_SERVICE);
          Sensor SensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
          mSensorManager.registerListener(this, SensorAccelerometer, SensorManager.SENSOR_DELAY_UI);  
          Sensor SensorMagField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
          mSensorManager.registerListener(this, SensorMagField, SensorManager.SENSOR_DELAY_UI);
          AccelerometerValues = new float[3];
          MagneticFieldValues = new float[3];
          RotationMatrix = new float[9];
          nextRefreshTime = 0;
          df = new DecimalFormat("#.00");
      }
  
      @Override
      public void onSensorChanged(SensorEvent event) {
  
          if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
              System.arraycopy(event.values, 0, AccelerometerValues, 0, AccelerometerValues.length);
          else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
                  System.arraycopy(event.values, 0, MagneticFieldValues, 0, MagneticFieldValues.length);
  
          if (AccelerometerValues != null &amp;&amp; MagneticFieldValues != null) {
              if(SensorManager.getRotationMatrix(RotationMatrix, null, AccelerometerValues, MagneticFieldValues)) {
                  float[] OrientationValues = new float[3];
                  SensorManager.getOrientation(RotationMatrix, OrientationValues);
  
                  // chance conventions to match sample apps
                  if (OrientationValues[0] &lt; 0) OrientationValues[0] += 2*(float)Math.PI;
                  OrientationValues[2] *= -1;
  
                  // dump to logcat 4 times a second
                  long currentTimeMillis = System.currentTimeMillis();
                  if (currentTimeMillis &gt; nextRefreshTime) {
                      nextRefreshTime = currentTimeMillis+250;
                      Log.i("Sensors",    // arrange output so that numbers line up in columns :-)
                              "(" + AngleToStr(OrientationValues[0]) + "," + AngleToStr(OrientationValues[1]) + "," + AngleToStr(OrientationValues[2])
                              + ") ("+FloatToStr(AccelerometerValues[0]) + "," + FloatToStr(AccelerometerValues[1]) + "," + FloatToStr(AccelerometerValues[2])
                              + ") ("+FloatToStr(MagneticFieldValues[0]) + "," + FloatToStr(MagneticFieldValues[1]) + "," + FloatToStr(MagneticFieldValues[2])+")");
                  }               
              }
          }               
      }
  
      private String AngleToStr(double AngleInRadians) {
          String Str = "   "+Integer.toString((int)Math.toDegrees(AngleInRadians));
          return Str.substring(Str.length() - 3);
      }
      private String FloatToStr(float flt) {
          String Str = "      "+df.format(flt);
          return Str.substring(Str.length() - 6);
      }   
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
          mSensorManager.unregisterListener(this);
      }
  
      @Override
      public void onAccuracyChanged(Sensor arg0, int arg1) { }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          getMenuInflater().inflate(R.menu.activity_main, menu);
          return true;
      }
  
  }
  </code></pre>
  
  <p>I’m using a Galaxy Note 2 running Jelly Bean 4.1.1.  Can anyone tell me what I’m doing wrong?</p>
  
  <p>Update 24-Mar-2013:  More information. (1) I've disabled switches between portrait and landscape in the manifest, so getWindowManager().getDefaultDisplay().getRotation() is always zero.  Hence I don't think remapCoordSystem would help here, because that's for switching axes, whereas the errors that I'm seeing aren't big errors, they're much more subtle.  (2) I've checked the accuracy sensitivity, and the inconsistencies occur when both sensors claim to have high accuracy.</p>
  
  <p>As an example of the inconsistencies that I'm seeing, when the code above give me (azimuth,pitch,roll) = (235,-52,-11) then the two free apps show similar values.  But when I see (278, -58, -52) the apps show (256, -58, -26), so big differences in both Azimuth and roll, although pitch seems OK.</p>