use super::filter::Window;
#[derive(Clone, Debug)]
pub struct DCBlocker {
ff: MovingAverage,
fb: MovingAverage,
}
impl DCBlocker {
pub fn new(len: usize) -> Self {
DCBlocker {
ff: MovingAverage::new(len),
fb: MovingAverage::new(len),
}
}
pub fn reset(&mut self) {
self.ff.reset();
self.fb.reset();
}
pub fn filter(&mut self, input: f32) -> f32 {
let (ma0, sig) = self.ff.filter(input);
let (ma1, _) = self.fb.filter(ma0);
sig - ((self.ff.len() > 1) as u8 as f32) * ma1
}
}
#[derive(Clone, Debug)]
struct MovingAverage {
window: Window<f32>,
inv_len: f32,
moving_sum: f32,
}
impl MovingAverage {
pub fn new(len: usize) -> Self {
assert!(len > 0);
Self {
window: Window::new(len),
inv_len: 1.0f32 / (len as f32),
moving_sum: 0.0f32,
}
}
pub fn reset(&mut self) {
self.window.reset();
self.moving_sum = 0.0f32;
}
#[inline]
pub fn len(&self) -> usize {
self.window.len()
}
#[inline]
pub fn filter(&mut self, input: f32) -> (f32, f32) {
let aged = self.window.push_scalar(input);
self.moving_sum += input - aged;
(self.moving_sum * self.inv_len, self.window.front())
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_moving_average_simple() {
let mut mavg = MovingAverage::new(1);
let (oavg, osamp) = mavg.filter(1.0f32);
assert_eq!(1.0f32, osamp);
assert_approx_eq!(1.0f32, oavg);
let (oavg, osamp) = mavg.filter(-10.0f32);
assert_eq!(-10.0f32, osamp);
assert_approx_eq!(-10.0f32, oavg);
let mut mavg = MovingAverage::new(2);
let (oavg, osamp) = mavg.filter(1.0f32);
assert_eq!(0.0f32, osamp);
assert_approx_eq!(0.5f32, oavg);
let (oavg, osamp) = mavg.filter(2.0f32);
assert_eq!(1.0f32, osamp);
assert_approx_eq!(1.5f32, oavg);
}
#[test]
fn test_moving_average_four() {
const INPUT: &[f32] = &[1.0, 2.0, -1.0, 3.0, 8.0];
const EXPECT: &[f32] = &[0.25000, 0.75000, 0.50000, 1.25000, 3.00000];
let mut last = 0.0;
let mut mavg = MovingAverage::new(4);
for (expect, inp) in EXPECT.iter().zip(INPUT.iter()) {
let (ma, dly) = mavg.filter(*inp);
last = dly;
assert_approx_eq!(ma, *expect);
}
assert_eq!(last, 2.0f32);
}
#[test]
fn test_dc_block_trivial() {
let mut uut = DCBlocker::new(1);
assert_eq!(uut.filter(100.0f32), 100.0f32);
assert_eq!(uut.filter(-200.0f32), -200.0f32);
}
#[test]
fn test_dc_block() {
let mut output_history = Window::<f32>::new(2);
let mut uut = DCBlocker::new(31);
let mut clk = 1.0f32;
for _i in 0..256 {
output_history.push_scalar(uut.filter(100.0f32 + clk));
clk = -1.0 * clk;
}
assert_approx_eq!(output_history.inner()[0], 1.0f32, 1.0e-2);
assert_approx_eq!(output_history.inner()[1], -1.0f32, 1.0e-2);
}
}