Getting Tilt Data on an Android Phone

I haven't posted in a while, so I thought I'd post a little bit of code that I wrote for an android phone app that I'm working on. You see, Google recently deprecated one of the older ways of getting tilt data from the Android OS, but they didn't really document the new way of doing it. Plus, most resources I found online still use the old method, so I wrote a class to get the tilt data in an easy-to-use manner. (And without using any deprecated functions.)

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

public class TiltCalc {

private boolean needsRecalc = false;
private float[] tilt_data = {0, 0, 0}, gravity = {0, 0, 0}, magnet = {0, 0, 0};

// Change this to make the sensors respond quicker, or slower:
private static final int delay = SensorManager.SENSOR_DELAY_GAME;


// Special class used to handle sensor events:
private final SensorEventListener listen = new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
final float[] vals = e.values, target;

// Just capture the Gyroscope data, if it exists:
//if(e.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
// System.arraycopy(vals, 0, tilt_data, 0, 3);
// return;
//}

// Else, we'll capture the data, and mark the class for a re-calc:
target = (e.sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? gravity : magnet;
needsRecalc = true;
System.arraycopy(vals, 0, target, 0, 3);
}

public void onAccuracyChanged(Sensor event, int res) {}
};

// The constructor will use a context object to register itself for various inputs:
public TiltCalc(Context c) {
SensorManager man = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE);

Sensor mag_sensor = man.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Sensor acc_sensor = man.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//Sensor gyr_sensor = man.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

// Turns out Android's gyroscope doesn't work this way, so it's been disabled for now...
//if(man.registerListener(listen, gyr_sensor, delay)) {
// Log.d("TiltCalc", "Gyroscope detected, and successfully connected.");

// Use an accelerometer+compass approach:
//} else
if( man.registerListener(listen, mag_sensor, delay) &&
man.registerListener(listen, acc_sensor, delay) ) {
Log.d("TiltCalc", "No gyroscope, falling back on accelerometer+compass.");

} else {
Log.d("TiltCalc", "No acceptable hardware found.");

// We will remove the listener, just in case one of the accelerometer sensors
// registered, just not the other one:
man.unregisterListener(listen);
}
}

// Will return the most up-to-date tilt data in the vals[] array
public void getTilt(float[] vals) {

// If some of the data has been changed, then we need to recalculate some things...
if(needsRecalc) {
float[] R={0,0,0,0,0,0,0,0,0};

// Calculate the rotation matrix, and use that to get the orientation:
if(SensorManager.getRotationMatrix(R, null, gravity, magnet))
SensorManager.getOrientation(R, tilt_data);

needsRecalc = false;
}

System.arraycopy(tilt_data, 0, vals, 0, 3);
}
}

The constructor needs a context object, so that it can register itself for sensor updates. The easiest way to do this is to call the function in the main activity, using 'this' as the context. (Activity is a subclass of Context...)

Then, you would just call getTilt(float[] vals) to get the 3 tilt values. That function will return the 3 tilt values into the float[] that is passed into the class. The tilt values will be setup like:

Diagram showing the tilt axes

So vals[0] is rotating the phone around like a compass, vals[1] is tilting the phone up and down, and vals[2] is tilting the phone left and right.

This class is coded so that if you have an accurate gyroscope in the phone, the class will use that to get the tilt data. However, in most cases, you won't have a gyroscope, so the class will fall back on a less accurate (but still perfectly usable) accelerometer + compass method of calculating tilt.

Note: I do not have a phone with a gyroscope, so that code is ENTIRELY untested. Plus, since Google didn't document the output format of gyroscope updates, this code is based on the very bold assumption that Google wouldn't give tilt data in differing formats based on the data's source. (EDIT: Turns out this *WAS* a very bold assumption... :P)

Anyway, this code is public domain, so have fun. :)

EDIT (19 Jan 2011): Now that there are Android phones with gyroscopes, we know that they don't work in the way I was expecting, so the gyroscope code has been commented out. I'll update the code again once I know for sure how they work...


5 Responses to Getting Tilt Data on an Android Phone

  1. 730 bongju,kim 2010-11-05 01:33:20

    i'm always wondering how to use gyroscope api in SensorManager.
    your blog is very cool.
    thanks.

  2. 1013 sprocket2cog 2011-01-12 13:24:08

    Hi im trying to find someone who can code a tilt sensor logger that outputs a csv file (comma seperated values) with the ability to set the time between samples , ie 500ms to 2 secs
    so it can be reformatted into a subtitle file for video overlay.
    features:
    calibrate
    log data
    set time frame for sensor reading
    only needs to show pitch and roll and a time stamp

    any ideas would be great.
    thx

  3. 1014 Adam Nyb├Ąck 2011-01-18 03:30:38

    Now that Gingerbread is out we know that this code doesn't work for the TYPE_GYROSCOPE data. TYPE_GYROSCOPE gives the rotational speed in radians/s.

  4. 1015 ray 2011-01-19 16:38:35

    @Adam: Ah, crap. Well, I'll figure out how to tie that data into this class later. For now, the gyroscope data is commented out. :/

  5. 1016 desentizised 2011-01-31 15:57:31

    I used this code and unfortunately I can't get to the code where accelerometer and compass are used because I have no device for it and I cant add a compass to a virtual device

    The gyroscope code which you commented out on the other hand seems to work just fine. The Gyroscope of my Galaxy Tab is detected as expected and it gives me 3 axis positive or negative values according to the devices orientation, the values stay the same if it is held still so I don't think this is supposed to display rotational speed at all.

    I'm using Android SDK 2.2 on a Galaxy Tab with Android 2.2.
    Thanks a lot for the code. Works great for me.

Leave a Reply



About

User