Hi, While trying to retrieve orientation data I found some pretty straight forward code making use of SensorEventListener for Sensor.TYPE_ORIENTATION and it works fine, but the Sensor.TYPE_ORIENTATION constant has been deprecated recently - I believe it was on 8 (API level) - which I understand to mean that while I can still use it, I shouldn't. The documentation [1] only says "use SensorManager.getOrientation() instead." [2] but as a newbie, I had difficulties following that "detailed" instruction.
So the question is: How to properly get device orientation data? In an attempt to make this discussion constructive, here is what I managed to figure out, hopefully it will attract corrections relevant to my limitations and not just general pointers to material I already read (and misunderstood) and perhaps also benefit other newbies struggling with this task. So please fix any misconceptions or errors you find below. The most important thing to realize is that SensorManager.getOrientation() doesn't get the orientation from the sensors (as with SensorEvent for Sensor.TYPE_ORIENTATION), it merely "Computes the device's orientation based on the rotation matrix" which you must provide as the first parameter. In a similar fashion SensorManager.getRotationMatrix() doesn't read sensors data to produce the rotation matrix, it depends on you to provide the data through the third and forth arguments (gravity, geomagnetic), also note that the values of these arguments must be within expected limits or the method will fail. This holds in particular for the case where these are initialized to zeros, as this will imply free fall for gravity and something even more disturbing for geomagnetic. Don't take my word on it look it up for yourself in the source [3], for the Java end at list. Once that was digested it appears the only way to get sensor data is through registering a sensor event listener (I thought it would be really nice if I could query the sensors's readings at will and that what I naively assumed was done by the get methods in SensorManager, could anyone comment why this is not made available?), then that data may be used to calculate the orientation as outlined below: 1) Retrieve sensor data from accelerometer and magnetic field sensor as required by getRotationMatrix(): This actually involves a number of steps in the Activity class: 1.1) Obtain a sensor manager. NOTE: Context.getSystemService needs a Context, typically called within an Activity's context. mSensMan = (SensorManager) getSystemService(SENSOR_SERVICE); 1.2) Register a sensor event listener for each of the above sensor types. mSensMan.registerListener(this, mSensMan.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_UI); mSensMan.registerListener(this, mSensMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI); 1.3) In the listener's onSensorChanged method copy the data from the SensorEvent.values. NOTE: The data must be copied off the event.values as the system is reusing that array in all SensorEvents, simply assigning won't work. public void onSensorChanged(SensorEvent event) { switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: System.arraycopy(event.values, 0, mGravs, 0, 3); break; case Sensor.TYPE_MAGNETIC_FIELD: System.arraycopy(event.values, 0, mGeoMags, 0, 3); break; default: return; } } 2) Pass the copied sensor data as arrays to SensorManager.getRotationMatrix() to receive the rotation matrix. SensorManager.getRotationMatrix(mRotationM, null, mGravs, mGeoMags) Optionally transform the returned rotation matrix through SensorManager.remapCoordinateSystem() or multiplying by a transformation matrix. 3) Pass the rotation matrix to SensorManager.getOrientation() to receive the orientation as yaw, pitch and roll expressed in radians. SensorManager.getOrientation(mRotationM, mOrientation); Voila. Below the relevant code is wrapped with a complete Activity to allow for testing and to get the whole picture, once again - corrections and remarks are requested - the TODO: tags would also indicate areas where knowledge is lacking. Please handle with care, this newbie is still a little wet and slippery! [1] http://developer.android.com/reference/android/hardware/Sensor.html#TYPE_ORIENTATION [2] http://developer.android.com/reference/android/hardware/SensorManager.html#getOrientation%28float[],%20float[]%29 [3] http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/hardware/SensorManager.java;h=271f973e97e02fa9b6831becd8386a423cb0d9ae;hb=HEAD <<------------------------------------------------------------------------------------------------------------------------ >> package my.odd.demo.orientation; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.view.ViewGroup.LayoutParams; import android.widget.TextView; public class OrientationDemo extends Activity implements SensorEventListener { TextView mOrientationData; private SensorManager mSensMan; private float mAzimuth; private float[] mGravs = new float[3]; private float[] mGeoMags = new float[3]; private float[] mOrientation = new float[3]; private float[] mRotationM = new float[9]; // Use [16] to co-operate with android.opengl.Matrix private float[] mRemapedRotationM = new float[9]; private boolean mFailed; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // I'd like to actually see something so let's have a view: mOrientationData = new TextView(this); setContentView(mOrientationData, new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); // Initiate the Sensor Manager and register this as Listener for the required sensor types: // TODO: Find how to get a SensorManager outside an Activity, to implement as a utility class. mSensMan = (SensorManager) getSystemService(SENSOR_SERVICE); mSensMan.registerListener(this, mSensMan.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), // Anonymous Sensors- no further use for them. SensorManager.SENSOR_DELAY_UI); mSensMan.registerListener(this, mSensMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do nothing } @Override public void onSensorChanged(SensorEvent event) { switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: /* * NOTE: The data must be copied off the event.values * as the system is reusing that array in all SensorEvents. * Simply assigning: * mGravs = event.values won't work. * * I use a member array in an attempt to reduce garbage production. */ System.arraycopy(event.values, 0, mGravs, 0, 3); break; case Sensor.TYPE_MAGNETIC_FIELD: // Here let's try another way: for (int i=0;i<3;i++) mGeoMags[i] = event.values[i]; break; default: return; } if (SensorManager.getRotationMatrix(mRotationM, null, mGravs, mGeoMags)){ // Rotate to the camera's line of view (Y axis along the camera's axis) // TODO: Find how to use android.opengl.Matrix to rotate to an arbitrary coordinate system. SensorManager.remapCoordinateSystem(mRotationM, SensorManager.AXIS_X, SensorManager.AXIS_Z, mRemapedRotationM); SensorManager.getOrientation(mRemapedRotationM, mOrientation); onSuccess(); } else onFailure(); } void onSuccess(){ if (mFailed) mFailed = false; // Convert the azimuth to degrees in 0.5 degree resolution. mAzimuth = (float) Math.round((Math.toDegrees(mOrientation[0])) *2)/ 2; // Adjust the range: 0 < range <= 360 (from: -180 < range <= 180). mAzimuth = (mAzimuth+360)%360; // alternative: mAzimuth = mAzimuth>=0 ? mAzimuth : mAzimuth+360; mOrientationData.setText("Azimuth= " + mAzimuth); } void onFailure() { if (!mFailed) { mFailed = true; mOrientationData.setText("Failed to retrive rotation Matrix"); } } } -- You received this message because you are subscribed to the Google Groups "Android Beginners" group. NEW! Try asking and tagging your question on Stack Overflow at http://stackoverflow.com/questions/tagged/android To unsubscribe from this group, send email to android-beginners+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-beginners?hl=en