#[derive(Clone, Copy, Debug)]
pub struct Kalman {
estimate: f32,
error: f32,
process: f32,
measurement: f32,
started: bool,
}
impl Kalman {
pub fn new(process_noise: f32, measurement_noise: f32, initial: f32) -> Self {
Self {
estimate: initial,
error: 1.0,
process: magnitude(process_noise),
measurement: magnitude(measurement_noise),
started: false,
}
}
pub fn update(&mut self, reading: f32) -> f32 {
if !self.started {
self.estimate = reading;
self.started = true;
return self.estimate;
}
let predicted_error = self.error + self.process;
let gain = predicted_error / (predicted_error + self.measurement);
self.estimate += gain * (reading - self.estimate);
self.error = (1.0 - gain) * predicted_error;
self.estimate
}
pub fn estimate(&self) -> f32 {
self.estimate
}
}
fn magnitude(value: f32) -> f32 {
if value < 0.0 {
-value
} else {
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_first_reading_seeds_the_estimate() {
let mut kalman = Kalman::new(0.1, 0.1, 0.0);
assert_eq!(kalman.update(42.0), 42.0);
}
#[test]
fn it_tracks_toward_a_new_level() {
let mut kalman = Kalman::new(0.1, 1.0, 0.0);
kalman.update(0.0); let mut value = 0.0;
for _ in 0..50 {
value = kalman.update(10.0);
}
assert!((value - 10.0).abs() < 0.5);
}
#[test]
fn it_smooths_a_noisy_signal_toward_the_mean() {
let mut kalman = Kalman::new(0.01, 1.0, 0.0);
let mut value = 0.0;
for reading in [10.0, 8.0, 12.0, 9.0, 11.0, 10.0, 10.0, 9.5, 10.5, 10.0] {
value = kalman.update(reading);
}
assert!((value - 10.0).abs() < 1.0);
}
#[test]
fn zero_measurement_noise_follows_the_reading() {
let mut kalman = Kalman::new(1.0, 0.0, 0.0);
kalman.update(5.0); assert_eq!(kalman.update(8.0), 8.0); }
}