N900 accelerometer

The accelerometer detects orientation and movement of the phone.

Contents

[edit] Hardware

The accelerometer in the phone is a LIS302DL.

It is connected through the I2C bus.

It features:

  • Low power consumption
  • Dynamically selectable 2/8 G full-scale.
  • Programmable multiple interrupt generator
    • Click and double click recognition
    • Zero G detection
    • Interrupt on crossing arbitrarily set threshold on any axis, either absolute or speed of change (via programmable highpass filer). Note: the kernel driver doesn't support any of the advanced trigger configs, just simple threshold on x,y axis.

The click and double click functionality are not implemented in the current software.

The accelerometer is uncalibrated - the sensitivity may vary by around 10% plus or minus per-axis. Ideally this would be able to be calibrated by the user. It is possible to recover the bias and sensitivity by rotating the phone several times, and then finding the best mapping of the resulting ellipsoid to a sphere centered around zero.

The absolute resolution is poor - 18 mG (at 2 G sensitivity) typical. This is fine for detecting the orientation of the phone, but makes most uses that would attempt to integrate the acceleration for detection of position useless.

At the fastest sampling speed, the noise is enough to dither the output, so the resolution is somewhat higher.

[edit] Fundamental limitations

For example - with a 9 mG error - there is an acceleration error of 9 centimeters per meter per second.

This means that after 10 seconds, the velocity may be anywhere within a 1.8 m/s (around 4 MPH) band, and the position inaccurate by 4.5 m. After 100 s, the position inaccuracy is up to 450 m (this assumes the orientation of the phone is known exactly).

[edit] Bugs

Sometimes the LIS302 in N900 gets massive offset on one or more axes, due to unknown reason (possibly I2C errors). Symptoms: Dialer autorotation fails or gets weird. Even rebooting seems not to help to fix this, but removing battery for some seconds resets the chip. Powering down the device should help too (Note: device never really powers down as long as power is on USB, i.e. charging)

[edit] Software

The accelerometer is supported by the dbus/sysfs infrastructure accelerometers, and the application rotation framework. It is used by much software, including N900Fly and a pedometer widget as well as all applications that support the screen rotating automatically to be up.

Fremantle provides an accelerometer API. There are currently two interfaces available:

  • D-Bus
  • sysfs

See also the related thread at talk.maemo.org.

[edit] D-Bus

Thomas Thurman (marnanel) has put together a simple demo of an application using accelerometers using the D-Bus interface. You can find sources and .deb up at http://people.collabora.co.uk/~tthurman/sandcastle/

See also how to handle portrait mode switching.

[edit] sysfs

Another way is to use the sysfs file information:

/sys/class/i2c-adapter/i2c-3/3-001d/coord

When reading that file you get 3 values X, Y and Z (provided on one line, separated by white space). Values are in mG (milli G). 1000 mG= 1 G

Theoretical values from the accelerometers
Position X Y Z
Lying on table (back down) 0 0 -1000
Lying on table (face down) 0 0 1000
Sitting on table (bottom edge down) 0 -1000 0
Sitting on table (right edge down) -1000 0 0
Sitting on table (left edge down) 1000 0 0
Bottom right corner down (approx.) -707 -707 0

These are theoretical values. In real life your mileage will vary.

An example can be found at: [1]

[edit] Using the data

The X and Y values can be used to calculate[1] the pitch (that is, clockwise rotation) using the atan2 function (note the inverted sign of y):

 angle_in_radians = atan2(x, -y)

Similar, Y and Z can be used to calculate the roll:

 angle_in_radians = atan2(y, -z)

[edit] Python

Using the sysfs interface:

  def get_rotation():
    f = open("/sys/class/i2c-adapter/i2c-3/3-001d/coord", 'r' )
    coords = [int(w) for w in f.readline().split()]
    f.close()
    return coords

And using the D-Bus interface:

import dbus
bus = dbus.SystemBus()             
accel = bus.get_object('com.nokia.mce',
        '/com/nokia/mce/request',
        'com.nokia.mce.request') 
orientation , stand , face , x , y , z = accel.get_device_orientation()

x,y,z are the raw accelerometer measurements values, and the first three variables give text information about the device orientation. In particular, the first one informs about the landscape/portrait state.

A python class that gives smoothed accelerometer values is available here.

[edit] Smoothed C interface

This is a C function which returns smooth values from the accelerometer. Designed for applications running at >=25fps (otherwise it lags)

(GPL code extracted from libliqbase: liqaccel.c)

static int ocnt=0;
static int oax=0;
static int oay=0;
static int oaz=0;
 
static const char *accel_filename = "/sys/class/i2c-adapter/i2c-3/3-001d/coord";
 
int liqaccel_read(int *ax,int *ay,int *az)
{
	FILE *fd;
	int rs;
	fd = fopen(accel_filename, "r");
	if(fd==NULL){ liqapp_log("liqaccel, cannot open for reading"); return -1;}	
	rs=fscanf((FILE*) fd,"%i %i %i",ax,ay,az);	
	fclose(fd);	
	if(rs != 3){ liqapp_log("liqaccel, cannot read information"); return -2;}
	int bx=*ax;
	int by=*ay;
	int bz=*az;
	if(ocnt>0)
	{
		*ax=oax+(bx-oax)*0.1;
		*ay=oay+(by-oay)*0.1;
		*az=oaz+(bz-oaz)*0.1;
	}
	oax=*ax;
	oay=*ay;
	oaz=*az;
	ocnt++;
	return 0;
}

[edit] C++ Class

This is a simple C++ class based on the sysfs interface. Placed in the public domain by thp. Feel free to improve:

 
#include <stdio.h>
 
#define ACCELEROMETER_FILE_N900 "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
 
class Accelerometer {
    int x;
    int y;
    int z;
 
public:
    Accelerometer() : x(0), y(0), z(0)
    {
        update();
    }
 
    bool update()
    {
        int tmp[3] = {0, 0, 0};
        FILE *fd;
 
        if (!(fd = fopen(ACCELEROMETER_FILE_N900, "r"))) {
            return false;
        }
 
        if (fscanf(fd, "%i %i %i", tmp, tmp+1, tmp+2) != 3) {
            return false;
        }
 
        if (fclose(fd) == EOF) {
            return false;
        }
 
        x = tmp[0];
        y = tmp[1];
        z = tmp[2];
 
        return true;
    }
 
    int getX() { return x; }
    int getY() { return y; }
    int getZ() { return z; }
 
};

[edit] References

  1. Tom Pycke, Accelerometer to pitch and roll