use heapless::Vec;
#[derive(Debug, Clone)]
pub struct MovingAvgFilter {
buffer: Vec<f32, 32>, window_size: usize,
index: usize,
count: usize,
}
impl MovingAvgFilter {
pub fn new(window_size: usize) -> Self {
debug_assert!(window_size > 0 && window_size <= 32);
let mut buffer = Vec::new();
for _ in 0..window_size.min(32) {
let _ = buffer.push(0.0);
}
Self {
buffer,
window_size,
index: 0,
count: 0,
}
}
pub fn apply(&mut self, input: f32) -> f32 {
self.buffer[self.index] = input;
self.index = (self.index + 1) % self.window_size;
if self.count < self.window_size {
self.count += 1;
}
let sum: f32 = self.buffer.iter().take(self.count).sum();
sum / self.count as f32
}
pub fn reset(&mut self) {
self.index = 0;
self.count = 0;
for val in self.buffer.iter_mut() {
*val = 0.0;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_call_returns_input() {
let mut filter = MovingAvgFilter::new(4);
assert_eq!(filter.apply(0.5), 0.5);
}
#[test]
fn averages_samples() {
let mut filter = MovingAvgFilter::new(3);
filter.apply(1.0);
filter.apply(2.0);
let avg = filter.apply(3.0);
assert!((avg - 2.0).abs() < 1e-6);
}
#[test]
fn circular_buffer_wraps() {
let mut filter = MovingAvgFilter::new(3);
filter.apply(1.0);
filter.apply(2.0);
filter.apply(3.0);
let avg = filter.apply(4.0);
assert!((avg - 3.0).abs() < 1e-6);
}
#[test]
fn smooths_noise() {
let mut filter = MovingAvgFilter::new(4);
let samples = [1.0, 1.1, 0.9, 1.0];
let mut outputs: Vec<f32, 4> = Vec::new();
for &sample in &samples {
let _ = outputs.push(filter.apply(sample));
}
let expected = (1.0 + 1.1 + 0.9 + 1.0) / 4.0;
assert!((outputs[3] - expected).abs() < 1e-6);
}
#[test]
fn reset_clears_buffer() {
let mut filter = MovingAvgFilter::new(3);
filter.apply(5.0);
filter.apply(5.0);
filter.apply(5.0);
filter.reset();
assert_eq!(filter.apply(1.0), 1.0);
}
}