I want to get the current magnetic orientation regardless of the current screen orientation (landscape or portrait).
I found this example, but it's not orientation independant, right? And this didn't help me either. I did also read http://android-developers.blogspot.de/2010/09/one-screen-turn-deserves-another.html.
This is my current approach with the deprecated way I don't want to use (short):
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
private SensorEventListener sensorEventListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
/* Get measured value */
float current_measured_bearing = (float) event.values[0];
/* Compensate device orientation */
switch (((WindowManager) getSystemService(WINDOW_SERVICE))
.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_90:
current_measured_bearing = current_measured_bearing + 90f;
break;
case Surface.ROTATION_180:
current_measured_bearing = current_measured_bearing - 180f;
break;
case Surface.ROTATION_270:
current_measured_bearing = current_measured_bearing - 90f;
break;
}
But the last part is definitely wrong! How do I use the newer method getRotationMatrix()
correctly in this case? (Orientation independent) Or do I simply have to use other values of the event.values[]
array based on the Rotation Matrix? Or will I need to 'remap the coordinates'? So is that the correct way of achieving this?
I'm developing for devices with 360° screen rotation and on API Level 11+.
I know that those questions are asked very often but I could simply not transfer their answers to my question.
OK I finally managed to get the code working!
First, I register a Sensor.TYPE_MAGNETIC_FIELD
and Sensor.TYPE_GRAVITY
: (like Hoan Nguyen said!)
/**
* Initialize the Sensors (Gravity and magnetic field, required as a compass
* sensor)
*/
private void initSensors() {
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor mSensorGravity = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
Sensor mSensorMagneticField = sensorManager
.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
/* Initialize the gravity sensor */
if (mSensorGravity != null) {
Log.i(TAG, "Gravity sensor available. (TYPE_GRAVITY)");
sensorManager.registerListener(mSensorEventListener,
mSensorGravity, SensorManager.SENSOR_DELAY_GAME);
} else {
Log.i(TAG, "Gravity sensor unavailable. (TYPE_GRAVITY)");
}
/* Initialize the magnetic field sensor */
if (mSensorMagneticField != null) {
Log.i(TAG, "Magnetic field sensor available. (TYPE_MAGNETIC_FIELD)");
sensorManager.registerListener(mSensorEventListener,
mSensorMagneticField, SensorManager.SENSOR_DELAY_GAME);
} else {
Log.i(TAG,
"Magnetic field sensor unavailable. (TYPE_MAGNETIC_FIELD)");
}
}
And I use that SensorEventListner
for the computation:
private SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_GRAVITY) {
mGravity = event.values.clone();
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
mMagnetic = event.values.clone();
}
if (mGravity != null && mMagnetic != null) {
/* Create rotation Matrix */
float[] rotationMatrix = new float[9];
if (SensorManager.getRotationMatrix(rotationMatrix, null,
mGravity, mMagnetic)) {
/* Compensate device orientation */
// http://android-developers.blogspot.de/2010/09/one-screen-turn-deserves-another.html
float[] remappedRotationMatrix = new float[9];
switch (getWindowManager().getDefaultDisplay()
.getRotation()) {
case Surface.ROTATION_0:
SensorManager.remapCoordinateSystem(rotationMatrix,
SensorManager.AXIS_X, SensorManager.AXIS_Y,
remappedRotationMatrix);
break;
case Surface.ROTATION_90:
SensorManager.remapCoordinateSystem(rotationMatrix,
SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X,
remappedRotationMatrix);
break;
case Surface.ROTATION_180:
SensorManager.remapCoordinateSystem(rotationMatrix,
SensorManager.AXIS_MINUS_X,
SensorManager.AXIS_MINUS_Y,
remappedRotationMatrix);
break;
case Surface.ROTATION_270:
SensorManager.remapCoordinateSystem(rotationMatrix,
SensorManager.AXIS_MINUS_Y,
SensorManager.AXIS_X, remappedRotationMatrix);
break;
}
/* Calculate Orientation */
float results[] = new float[3];
SensorManager.getOrientation(remappedRotationMatrix,
results);
/* Get measured value */
float current_measured_bearing = (float) (results[0] * 180 / Math.PI);
if (current_measured_bearing < 0) {
current_measured_bearing += 360;
}
/* Smooth values using a 'Low Pass Filter' */
current_measured_bearing = current_measured_bearing
+ SMOOTHING_FACTOR_COMPASS
* (current_measured_bearing - compass_last_measured_bearing);
/* Update normal output */
visual_compass_value.setText(String.valueOf(Math
.round(current_bearing))
+ getString(R.string.degrees));
/*
* Update variables for next use (Required for Low Pass
* Filter)
*/
compass_last_measured_bearing = current_measured_bearing;
}
}
}
};
Sensor.TYPE_ORIENTATION
is depreciated and only good if the device is flat. When using Sensor.TYPE_ORIENTATION
, the bearing (azimuth) is the direction where the device Y-axis
points. So if the device is held vertical, the direction where the Y-axis
points using as the bearing does not make sense. It only make sense to calculate the direction where the back camera points. To find this direction you have to use Sensor.TYPE_MAGNETIC_FIELD
and Sensor.TYPE_GRAVITY
or Sensor.TYPE_ACCELEROMETER
. If using Sensor.TYPE_ACCELEROMETER
, you have to filter the accelerometer values.
Using these sensors, you call getRotationMatrix
and then remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR)
before calling getOrientation
. To get a stable direction you should keep a history of the direction and then calculate the average. For an implementation using TYPE_GRAVITY check Android getOrientation Azimuth gets polluted when phone is tilted
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With