use crate::error::SignalError;
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MovingAverage<const WINDOW: usize, T: Numeric = f64> {
samples: [T; WINDOW],
next: usize,
initialized: bool,
}
impl<const WINDOW: usize, T: Numeric> MovingAverage<WINDOW, T> {
pub fn new() -> Result<Self, SignalError> {
if WINDOW == 0 {
return Err(SignalError::WindowTooShort);
}
Ok(Self {
samples: [T::ZERO; WINDOW],
next: 0,
initialized: false,
})
}
#[inline]
#[must_use]
pub fn filter(&mut self, input: T) -> T {
push(
&mut self.samples,
&mut self.next,
&mut self.initialized,
input,
);
self.value()
}
#[inline]
pub fn reset(&mut self) {
self.samples = [T::ZERO; WINDOW];
self.next = 0;
self.initialized = false;
}
#[inline]
#[must_use]
pub fn value(&self) -> T {
let mut total = T::ZERO;
for sample in self.samples {
total += sample;
}
total / T::from_usize(WINDOW)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RunningMedian<const WINDOW: usize, T: Numeric = f64> {
samples: [T; WINDOW],
next: usize,
initialized: bool,
}
impl<const WINDOW: usize, T: Numeric> RunningMedian<WINDOW, T> {
pub fn new() -> Result<Self, SignalError> {
if WINDOW == 0 {
return Err(SignalError::WindowTooShort);
}
if WINDOW % 2 == 0 {
return Err(SignalError::WindowEvenLength);
}
Ok(Self {
samples: [T::ZERO; WINDOW],
next: 0,
initialized: false,
})
}
#[inline]
#[must_use]
pub fn filter(&mut self, input: T) -> T {
push(
&mut self.samples,
&mut self.next,
&mut self.initialized,
input,
);
self.value()
}
#[inline]
pub fn reset(&mut self) {
self.samples = [T::ZERO; WINDOW];
self.next = 0;
self.initialized = false;
}
#[inline]
#[must_use]
pub fn value(&self) -> T {
let mut sorted = self.samples;
for placed in 1..WINDOW {
let moving = sorted[placed];
let mut slot = placed;
while slot > 0 && sorted[slot - 1] > moving {
sorted[slot] = sorted[slot - 1];
slot -= 1;
}
sorted[slot] = moving;
}
sorted[WINDOW / 2]
}
}
#[inline]
fn push<const WINDOW: usize, T: Numeric>(
samples: &mut [T; WINDOW],
next: &mut usize,
initialized: &mut bool,
input: T,
) {
if *initialized {
samples[*next] = input;
*next = (*next + 1) % WINDOW;
} else {
*samples = [input; WINDOW];
*next = 0;
*initialized = true;
}
}