I wonder how to caluclate the vertical movement of a mobile device regardless of its orientation in space. That is, up and down movement in the real world. Below are three examples to demonstrate my purpose:
Example 1: A mobile phone lies flat on a table and is moved towards the ceiling for 100ms.
Example 2: A mobile phone is in landscape mode and is then moved towards the ceiling for 100ms.
Example 3: A mobile phone is in landscape mode but is tilted 45° on the horizontal axis (earth's surface) and is moved towards the floor for 100ms.
The algorithm I am looking for should output for all examples above and in general for every possible phone orientation that at least the device was moved upwards respectively downwards during the time period in the real world coordinate system.
I am not asking how this works on a particular system or OS. I only want to be given instructions how something like this would be accomplished in general.
What I do have right now is the code example below (which is from this topic on stackoverflow) but a) I don't really understand how it works and b) it doesn't work so well at all. Especially upwards movement detection is quite bad. The alpha value seems to be chosen quite arbitrarily (as many threshholds in answers to related questions do) which leaves me a little confused what it actually means.
As an alternative, I though about reading values out of the gyroscope and the accelerometer. With gyroscope data I could calculate a rotation matrix. By multiplying the matrix with the accelerometer data (a 1D vector of the device's x, y and z acceleration in its own coordinate system) I should be able to obtain real world movement, shouldn't I?
I am developing for Android so I am able to make use of accelerometer, gyroskop, magnetometer etc.
Thanks!
class AccelerometerReader(context: Context): SensorEventListener {
private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val gravity = FloatArray(3)
private val linearAcc = FloatArray(3)
fun registerListener(){
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL)
}
fun unregisterListener(){
sensorManager.unregisterListener(this)
}
override fun onSensorChanged(event: SensorEvent?) {
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
val 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]
linearAcc[0] = event.values[0] - gravity[0]
linearAcc[1] = event.values[1] - gravity[1]
linearAcc[2] = event.values[2] - gravity[2]
val scalarProduct: Float = gravity[0] * linearAcc[0] + gravity[1] * linearAcc[1] + gravity[2] * linearAcc[2]
val gravityVectorLength = sqrt(gravity[0] * gravity[0] + gravity[1] * gravity[1] + gravity[2] * gravity[2])
val linearAccVectorLength = sqrt(linearAcc[0] * linearAcc[0] + linearAcc[1] * linearAcc[1] + linearAcc[2] * linearAcc[2])
val cosVectorAngle = scalarProduct / (gravityVectorLength * linearAccVectorLength)
if (linearAccVectorLength > 2) { //increase to detect only bigger accelerations, decrease to make detection more sensitive but noisy
if (cosVectorAngle > 0.5) {
println("Down")
} else if (cosVectorAngle < -0.5) {
println("Up")
}
}
}
}