I have implemented the compass reading according to the usual recommendations that I could find on the web. I use the ROTATION_VECTOR
sensor type and I transform it into the (azimuth, pitch, roll)
triple using the standard API calls. Here's my code:
fun Fragment.receiveAzimuthUpdates(
azimuthChanged: (Float) -> Unit,
accuracyChanged: (Int) -> Unit
) {
val sensorManager = activity!!.getSystemService(Context.SENSOR_SERVICE)
as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)!!
sensorManager.registerListener(OrientationListener(azimuthChanged, accuracyChanged),
sensor, 10_000)
}
private class OrientationListener(
private val azimuthChanged: (Float) -> Unit,
private val accuracyChanged: (Int) -> Unit
) : SensorEventListener {
private val rotationMatrix = FloatArray(9)
private val orientation = FloatArray(3)
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
SensorManager.getOrientation(rotationMatrix, orientation)
azimuthChanged(orientation[0])
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
if (sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
accuracyChanged(accuracy)
}
}
}
This results in behavior that's quite good when you hold the phone horizontally, like you would a real compass. However, when you hold it like a camera, upright and in front of you, the reading breaks down. If you tilt it even slightly beyond upright, so it leans towards you, the azimuth turns to the opposite direction (sudden 180 degree rotation).
Apparently this code tracks the orientation of the phone's y-axis, which becomes vertical on an upright phone, and its ground orientation is towards you when the phone leans towards you.
What could I do to improve this behavior so it's not sensitive to the phone's pitch?