To my utmost surprise, in Android 4.0+ they decided to remove LINEAR ACCELERATION sensor from available software sensors and put it inside a hardware sensor, the gyroscope that is rarely available in regular phones. I don't know why they did it, but imagine my surprise when I delivered the software that was tested on a cheap 2.3 phone, and on 2 months old phone it shows Unable to instantiate linear acceleration sensor. Why have they done it?
To save my skin, I checked if an Accelerometer is available in the phone, and it does. Based on this article, I should be able to calculate linear acceleration using the data from accelerometer. I checked the code and I see a variable that has no declaration available.
public void onSensorChanged(SensorEvent event){
// In this example, alpha is calculated as t / (t + dT),
// where t is the low-pass filter's time-constant and
// dT is the event delivery rate.
final float alpha = 0.8;
float[] gravity=new float[]{0.0f,0.0f,0.0f}; //should it be here???
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}
What does the gravity array contain in the first run? Should I add it like
float[] gravity=new float[]{0.0f,0.0f,0.0f};
?
Or should it contain real gravity data that is available via the Gravity sensor (also not available in regular devices)?
Does the calculation result proper linear acceleration data? Is it possible to calculate just by substracting a number from the incoming sensor data?
Actually what is done in that docs provided code snippet is that they take data from accelerometer(which contains both gravity and linear acceleration) and filter it. So how it basically works, we take raw data and assume that gravity vector is something constant and cannot change very quickly, opposite to linear acceleration, which changes really fast. So let's say if we take 80% of past counted gravity vector and add 20% of raw data we filter fast linear acceleration peaks and get gravity vector. If we remove gravity vector from raw data we get linear acceleration without gravity. Google for exponential filter for more information. Here you can play with it http://copterpilot.ru/custom-app/exponential/. So you can easily initialize your global gravity array of floats with zeros and wait a little for filtered data. Here is code snippet
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private float[] gravity = new float[3];
private float[] linear_acceleration = new float[3];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
public void onSensorChanged(SensorEvent event) {
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8f;
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
}
}