Battery-efficient motion detection for background location tracking.
GPS tracking apps drain battery by constantly requesting location updates. Worse: indoor GPS is inaccurate, creating noise in your data.
The solution? Only request GPS when the device is actually moving.
- 🔋 Battery-efficient: No GPS drain when stationary
- 🎯 Accurate: Spike filtering ignores phone pickup/handling
- 🔄 Stable: Hysteresis prevents flapping between states
- 📊 Clean data: Indoor stationary periods = no noisy GPS points
- 🧪 Tunable: All parameters exposed for testing and optimization
- 📱 Platform-independent: Pure Kotlin, no Android dependencies
- Low-pass filter estimates gravity from accelerometer
- High-pass filter extracts linear acceleration
- RMS calculation over 2-second rolling window
- Spike rejection filters phone pickup events
- Hysteresis state machine prevents false triggers
Result: Reliable motion detection without GPS overhead.
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.nrob81:motion-detector:1.0.0'
}// 1. Implement Logger interface
class ConsoleLogger : Logger {
override fun d(tag: String, message: String) = println("[$tag] $message")
override fun w(tag: String, message: String, throwable: Throwable?) = println("[$tag] WARN: $message")
override fun e(tag: String, message: String, throwable: Throwable?) = println("[$tag] ERROR: $message")
override fun setContext(key: String, value: Any) {}
}
// 2. Initialize MotionController
MotionController.init(
config = MotionConfig.optimized(),
logger = ConsoleLogger()
)
// 3. Feed accelerometer data
sensorManager.registerListener(object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
MotionController.onAcceleration(
x = event.values[0],
y = event.values[1],
z = event.values[2],
timestamp = System.currentTimeMillis()
)
}
})
// 4. Observe motion state
lifecycleScope.launch {
MotionController.motionFlow.collect { state ->
if (state.isMoving) {
// Request GPS update
}
}
}Use presets or customize:
// Optimized preset (tuned via grid search)
MotionConfig.optimized()
// Default preset
MotionConfig.default()
// Custom configuration
MotionConfig(
motionStartThreshold = 0.7f, // m/s² to trigger motion
motionStopThreshold = 0.3f, // m/s² to trigger still
startDelayMs = 5000, // delay before STILL → MOVING
stopDelayMs = 2500, // delay before MOVING → STILL
spikeThreshold = 1.5f, // reject acceleration spikes
rmsAlpha = 0.4f // RMS smoothing factor
)MIT - use freely, see LICENSE
Built for a family GPS safety tracker to monitor my daughter's location.
Challenge: Balance battery life with reliable tracking.
Solution: Motion-based GPS - only track when actually moving.
This library is the battle-tested core, extracted and optimized via grid search on 50+ minutes of real-world data (walking, driving, phone handling, stationary).
Open-sourced to help others solve similar problems.