Mobile Signal Processing & Hardware Sensors in Compose
Modern smartphones contain remarkable scientific instruments: micro-electro-mechanical accelerometers, high-frequency acoustic microphones, magnetometers, and optical sensors. Here is how we built a unified signal processing engine and reactive Jetpack Compose library.
Key Architectural Takeaways
1. Optical Camera Photoplethysmography (PPG)
When a user places their fingertip over the camera lens with the torch enabled, capillary blood vessel volume changes with every heartbeat. Because oxygenated hemoglobin absorbs specific wavelengths of light, we analyze the mean red/luminance channel variance across incoming frames:
1. Frame Sampling
Calculates center-frame red channel intensity at 30+ frames per second.
2. DC Bias Removal
Subtracts rolling moving average to isolate the pulsatile AC waveform.
3. Peak Detection
Measures inter-beat intervals (IBI) with refractory lockout to derive BPM.
2. Fast Fourier Transform (FFT) & Acoustic Metering
Raw PCM audio buffers from the microphone are passed through a Cooley-Tukey Radix-2 FFT algorithm with Hann windowing to compute frequency magnitudes from 20 Hz to 20,000 Hz:
// Cooley-Tukey FFT execution with Hann window preconditioning
val fftEngine = AudioFftEngine(fftSize = 1024)
val (magnitudes, peakFrequency) = fftEngine.process(pcmShortBuffer, sampleRate = 44100)
val decibelsDba = DecibelMeterSensor.calculateDecibels(pcmShortBuffer, readSize)3. Declarative Jetpack Compose Integration
Hardware sensor listeners traditionally require cumbersome boilerplate across Activity lifecycles. compose-sensors provides automatic lifecycle registration with reactive Compose state hooks:
@Composable
fun SensorDashboardScreen() {
val seismic by rememberSeismicReading()
val lux by rememberLuxReading()
val emf by rememberEmfReading()
val clinometer by rememberClinometerReading()
Column(modifier = Modifier.padding(16.dp)) {
Text("Vibration: ${seismic.magnitude} m/s² (Dominant: ${seismic.dominantFrequencyHz} Hz)")
Text("Ambient Light: ${lux.lux} Lux (${lux.lightCondition})")
Text("EMF Field: ${emf.totalMicroTesla} µT (${emf.radiationLevel.name})")
Text("Clinometer: Pitch ${clinometer.pitchDegrees}°, Roll ${clinometer.rollDegrees}°")
}
}Open Source on GitHub
This library is open-source and maintained by Epheos LTD under the Apache 2.0 license. Star the repository, inspect the algorithms, and build your next measurement app!
View compose-sensors on GitHub