Created
December 3, 2019 09:37
-
-
Save bananaumai/ad3c4d20b56e3eef6d9e78fb972f878b to your computer and use it in GitHub Desktop.
Android Accelerometer to event (with calibration steps)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @ExperimentalCoroutinesApi | |
| private fun accelerationFlow(context: Context): Flow<Acceleration> = channelFlow { | |
| val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager | |
| val listener = object : SensorEventListener { | |
| private val sampleNumber = 20 | |
| private val samples: MutableList<Acceleration.Coordinate> = mutableListOf() | |
| private var offset: Acceleration.Coordinate? = null | |
| private var calibrated: Acceleration.Coordinate? = null | |
| private var isReadyToEmit = false | |
| override fun onSensorChanged(event: SensorEvent?) { | |
| if (event?.sensor?.type != Sensor.TYPE_ACCELEROMETER) { | |
| return | |
| } | |
| // The unit of Android's 3 axes acceleration data is m/s2. | |
| // Convert into gravity acceleration | |
| val raw = Acceleration.Coordinate( | |
| lat = event.values[0].toDouble() / 9.80665, | |
| lon = event.values[2].toDouble() / 9.80665 * -1, | |
| vert = event.values[1].toDouble() / 9.80665 | |
| ) | |
| if (!isReadyToEmit) { | |
| samples.add(raw) | |
| if (samples.count() >= sampleNumber) { | |
| isReadyToEmit = true | |
| Timber.d("Ready to emit acceleration data, offset is $offset") | |
| } | |
| return | |
| } | |
| calibrate(raw) | |
| CoroutineScope(coroutineContext).launch { | |
| send(Acceleration(raw, calibrated!!)) | |
| } | |
| } | |
| override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { | |
| } | |
| private fun calibrate(raw: Acceleration.Coordinate) { | |
| /* | |
| * offset calculation is based on the following logic | |
| * (applying low-pass filter to offset calculation) | |
| * https://developer.android.com/guide/topics/sensors/sensors_motion | |
| */ | |
| val offsetAlpha = 0.9 | |
| offset = if (offset == null) { | |
| Acceleration.Coordinate( | |
| samples.map { it.lat }.average(), | |
| samples.map { it.lon }.average(), | |
| samples.map { it.vert }.average() | |
| ) | |
| } else { | |
| Acceleration.Coordinate( | |
| offsetAlpha * offset!!.lat + (1 - offsetAlpha) * raw.lat, | |
| offsetAlpha * offset!!.lon + (1 - offsetAlpha) * raw.lon, | |
| offsetAlpha * offset!!.vert + (1 - offsetAlpha) * raw.vert | |
| ) | |
| } | |
| /* | |
| * applying low-pass filter in order to ignore noisy acceleration | |
| */ | |
| val calibrationAlpha = 0.8 | |
| calibrated = if (calibrated == null) { | |
| Acceleration.Coordinate( | |
| raw.lat - offset!!.lat, | |
| raw.lon - offset!!.lon, | |
| raw.vert - offset!!.vert | |
| ) | |
| } else { | |
| Acceleration.Coordinate( | |
| calibrationAlpha * calibrated!!.lat + (1 - calibrationAlpha) * (raw.lat - offset!!.lat), | |
| calibrationAlpha * calibrated!!.lon + (1 - calibrationAlpha) * (raw.lon - offset!!.lon), | |
| calibrationAlpha * calibrated!!.vert + (1 - calibrationAlpha) * (raw.vert - offset!!.vert) | |
| ) | |
| } | |
| } | |
| } | |
| val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) | |
| val handlerThread = | |
| HandlerThread("accelerometer-handler-thread", Process.THREAD_PRIORITY_BACKGROUND).apply { start() } | |
| val handler = Handler(handlerThread.looper) | |
| withContext(handler.asCoroutineDispatcher()) { | |
| sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL, null) | |
| } | |
| awaitClose { | |
| sensorManager.unregisterListener(listener, sensor) | |
| handlerThread.quitSafely() | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment