## Recipe Getting a Devices Rotational Attitude

Ideally, the accelerometer measures the Earth's gravitational field as G=9.8 meters/sec2, and the magnetometer measures the Earth's magnetic field that ranges from H=30^T to 60^T depending on the location in the world. These two vectors are enough to implement a simple textbook estimation of rotation, as used in the getRotationMatrix() method. This recipe shows how to use this information.

The coordinate system of the device (also known as the body) frame is defined as:

■ x-axis in the direction of the short side of the screen (along the menu keys)

■ y-axis in the direction of the long side of the screen

■ z-axis pointing out of the screen

The coordinate system of the world (also known as inertial) frame is defined as:

■ The x-axis is the cross-product of the y-axis with the z-axis.

■ The y-axis is tangential to the ground and points toward the North Pole.

■ The z-axis points perpendicular to the ground toward the sky.

These two systems are aligned when the device is flat on a table with the screen facing up and pointing north. In this case, the accelerometer measures (0, 0, G) in the x-, y-, and z-directions.At most locations, the magnetic field of the Earth points slightly toward the ground at an angle 8 and even when the device points north is given by (0, H cos(8), -H sm(9)).

As the device tilts and rotates, SensorManager.getRotationMatrix() provides the 3x3 rotation matrix R[] to get from the device coordinate system to the world coordinate system and 3x3 inclination matrix I[] (rotation around the x-axis) to get from the true magnetic field direction to the ideal case (0, H, 0).

Note that if the device has its own acceleration or is near a strong magnetic field, the values measured do not necessarily reflect the proper reference frame of the Earth.

Another way to express the rotation is using SensorManager.getOrientation() .This provides the rotation matrix R[] and the attitude vector attitude[]:

■ attitude[0]—Azimuth (in radians) is the rotation angle around the world-frame z-axis required to have the device facing north. It takes values between -PI and PI, with 0 representing north and PI/2 representing east.

■ attitude[1]—Pitch (in radians) is the rotation angle around the world-frame x-axis required to have the device face straight up along the long dimension of the screen. It takes values between -PI and PI with 0 representing device face up, and PI/2 means it points toward the ground.

■ attitude[2]—Roll (in radians) is the rotation angle around the world-frame y-axis required to have the device face straight up along the short dimension of the screen. It takes values between -PI and PI with 0 representing device face up, and PI/2 means it points toward the right.

This recipe displays the attitude information to the screen. The layout provides a text with ID attitude, as shown in Listing 7.4.

Listing 7.4 res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"

android:layout_height="fill_parent" >

<TextView android:id="@+id/attitude" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Azimuth, Pitch, Roll"

</LinearLayout>

The main activity is shown in Listing 7.5.The accelerometer and magnetometer are registered to return data to the sensor listener.The SensorEventListener then assigns values based on which sensor triggered the callback. The attitude information is determined based on the rotation matrix, converted from radians to degrees, and displayed on the screen. Note the refresh rate of the sensors can take on different values as follows:

■ SENSOR_DELAY_FASTEST—Fastest update rate possible (ranges from 8ms to approximately 30ms depending on device)

■ SENSOR_DELAY_GAME—Update rate suitable for games (approximately 40ms)

■ SENSOR_DELAY_NORMAL—The default; update rate suitable for screen orientation changes (approximately 200ms)

■ SENSOR_DELAY_UI—Update rate suitable for the user interface (approximately 350ms) Listing 7.5 src/com/cookbook/orientation/OrientationMeasurements.java package com.cookbook.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.widget.TextView;

public class OrientationMeasurements extends Activity { private SensorManager myManager = null;

TextView tv;

^Override public void onCreate(Bundle savedlnstanceState) { super.onCreate(savedlnstanceState); setContentView(R.layout.main); tv = (TextView) findViewByld(R.id.attitude); // Set Sensor Manager myManager = (SensorManager)getSystemService(SENSOR_SERVICE); myManager.registerListener(mySensorListener, myManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); myManager.registerListener(mySensorListener, myManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME);

float[] mags = new float[3]; float[] accels = new float[3]; float[] RotationMat = new float[9]; float[] InclinationMat = new float[9]; float[] attitude = new float[3];

final static double RAD2DEG = 180/Math.PI; private final SensorEventListener mySensorListener

= new SensorEventListener() {

@Override public void onSensorChanged(SensorEvent event) {

int type = event.sensor.getType();

if(type == Sensor.TYPE_MAGNETIC_FIELD) { mags = event.values;

if(type == Sensor.TYPE_ACCELEROMETER) { accels = event.values;

SensorManager.getRotationMatrix(RotationMat,

InclinationMat, accels, mags); SensorManager.getOrientation(RotationMat, attitude); tv.setText("Azimuth, Pitch, Roll:\n" + attitude[0]*RAD2DEG + "\n" + attitude[1]*RAD2DEG + "\n" + attitude[2]*RAD2DEG);

public void onAccuracyChanged(Sensor sensor, int accuracy) {}

For consistent data, it is good practice to avoid putting computationally intensive code into the onSensorChanged() method. Also note that the SensorEvent is reused for subsequent sensor data. Therefore, for precise data, it is good practice to use the clone() method on event values, for example: accels = event.values.clone();

This ensures that if the accels data is used elsewhere in the class, it does not keep changing as the sensors continue sampling.

## Character Building Thought Power

Character-Building Thought Power by Ralph Waldo Trine. Ralph draws a distinct line between bad and good habits. In this book, every effort is made by the writer to explain what comprises good habits and why every one needs it early in life. It draws the conclusion that habits nurtured in early life concretize into impulses in future for the good or bad of the subject.

Get My Free Ebook

### Responses

• GISELLA
How to get attitude data from pi to pi to pi/2 to pi/2 iphone?
7 years ago