How to measure the tilt of the phone in XY plane using accelerometer in Android
The accelerometer is sufficient for checking if the phone is flat as Hoan very nicely demonstrated.
For anyone who arrives here looking to not only check if the phone flat, but what the rotation of the phone is, it can be achieved through the Rotation Vector Motion Sensor.
private double pitch, tilt, azimuth;
@Override
public void onSensorChanged(SensorEvent event) {
//Get Rotation Vector Sensor Values
double[] g = convertFloatsToDoubles(event.values.clone());
//Normalise
double norm = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2] + g[3] * g[3]);
g[0] /= norm;
g[1] /= norm;
g[2] /= norm;
g[3] /= norm;
//Set values to commonly known quaternion letter representatives
double x = g[0];
double y = g[1];
double z = g[2];
double w = g[3];
//Calculate Pitch in degrees (-180 to 180)
double sinP = 2.0 * (w * x + y * z);
double cosP = 1.0 - 2.0 * (x * x + y * y);
pitch = Math.atan2(sinP, cosP) * (180 / Math.PI);
//Calculate Tilt in degrees (-90 to 90)
double sinT = 2.0 * (w * y - z * x);
if (Math.abs(sinT) >= 1)
tilt = Math.copySign(Math.PI / 2, sinT) * (180 / Math.PI);
else
tilt = Math.asin(sinT) * (180 / Math.PI);
//Calculate Azimuth in degrees (0 to 360; 0 = North, 90 = East, 180 = South, 270 = West)
double sinA = 2.0 * (w * z + x * y);
double cosA = 1.0 - 2.0 * (y * y + z * z);
azimuth = Math.atan2(sinA, cosA) * (180 / Math.PI);
}
private double[] convertFloatsToDoubles(float[] input)
{
if (input == null)
return null;
double[] output = new double[input.length];
for (int i = 0; i < input.length; i++)
output[i] = input[i];
return output;
}
Then to check if the phone is flat you can simply compare the tilt
and pitch
values with a tolerance values. For example
public boolean flatEnough(double degreeTolerance) {
return tilt <= degreeTolerance && tilt >= -degreeTolerance && pitch <= degreeTolerance && pitch >= -degreeTolerance;
}
The advantage to doing it this way is you can check if the phone is being held in any specific rotation.
It is worth noting that the app's orientation will not affect the values of pitch, tilt, and azimuth.
Working off of the perfect response from @Dan
He missed a very slight bit of information that @davy307 pointed out.
When initializing the mAccelerometer, you must define it as Sensor.TYPE_ROTATION_VECTOR otherwise, it will not have the 3rd rotation vector and throw an ArrayIndexOutOfBounds exception.
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
Otherwise, this is a perfect solution... Appreciated!
Essentially, there is 2 cases here: the device is laying flat and not flat. Flat here means the angle between the surface of the device screen and the world xy plane (I call it the inclination) is less than 25 degree or larger than 155 degree. Think of the phone lying flat or tilt up just a little bit from a table.
First you need to normalize the accelerometer vector.
That is if g is the vector returns by the accelerometer sensor event values. In code
float[] g = new float[3];
g = event.values.clone();
double norm_Of_g = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);
// Normalize the accelerometer vector
g[0] = g[0] / norm_Of_g
g[1] = g[1] / norm_Of_g
g[2] = g[2] / norm_Of_g
Then the inclination can be calculated as
int inclination = (int) Math.round(Math.toDegrees(Math.acos(g[2])));
Thus
if (inclination < 25 || inclination > 155)
{
// device is flat
}
else
{
// device is not flat
}
For the case of laying flat, you have to use a compass to see how much the device is rotating from the starting position.
For the case of not flat, the rotation (tilt) is calculated as follow
int rotation = (int) Math.round(Math.toDegrees(Math.atan2(g[0], g[1])));
Now rotation = 0 means the device is in normal position. That is portrait without any tilt for most phone and probably landscape for tablet. So if you hold a phone as in your picture above and start rotating, the rotation will change and when the phone is in landscape the rotation will be 90 or -90 depends on the direction of rotation.