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

Reply via email to