2021-02-10 01:30:50 +08:00
|
|
|
use generic_array::{ArrayLength, GenericArray};
|
|
|
|
|
|
|
|
/// Arbitrary order, high dynamic range, wide coefficient range,
|
2021-02-11 21:30:05 +08:00
|
|
|
/// lowpass filter implementation. DC gain is 1.
|
2021-02-10 01:30:50 +08:00
|
|
|
///
|
2021-02-11 21:30:05 +08:00
|
|
|
/// Type argument N is the filter order.
|
2021-02-10 01:30:50 +08:00
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub struct Lowpass<N: ArrayLength<i32>> {
|
|
|
|
// IIR state storage
|
2021-02-11 21:30:05 +08:00
|
|
|
y: GenericArray<i32, N>,
|
2021-02-10 01:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<N: ArrayLength<i32>> Lowpass<N> {
|
|
|
|
/// Update the filter with a new sample.
|
|
|
|
///
|
|
|
|
/// # Args
|
2021-02-19 01:50:31 +08:00
|
|
|
/// * `x`: Input data. Needs 1 bit headroom but will saturate cleanly beyond that.
|
|
|
|
/// * `k`: Log2 time constant, 1..=31.
|
2021-02-10 01:30:50 +08:00
|
|
|
///
|
2021-02-10 16:46:49 +08:00
|
|
|
/// # Return
|
2021-02-18 21:06:01 +08:00
|
|
|
/// Filtered output y.
|
2021-02-10 16:46:49 +08:00
|
|
|
pub fn update(&mut self, x: i32, k: u8) -> i32 {
|
2021-02-11 21:30:05 +08:00
|
|
|
debug_assert!(k & 31 == k);
|
2021-02-19 01:50:31 +08:00
|
|
|
debug_assert!((k - 1) & 31 == k - 1);
|
2021-02-10 01:30:50 +08:00
|
|
|
// This is an unrolled and optimized first-order IIR loop
|
|
|
|
// that works for all possible time constants.
|
2021-02-18 21:06:01 +08:00
|
|
|
// Note T-DF-I and the zeros at Nyquist.
|
|
|
|
let mut x = x;
|
2021-02-11 21:30:05 +08:00
|
|
|
for y in self.y.iter_mut() {
|
2021-02-25 23:38:57 +08:00
|
|
|
let dy = x.saturating_sub(*y) >> k;
|
2021-02-11 21:30:05 +08:00
|
|
|
*y += dy;
|
2021-02-12 18:06:59 +08:00
|
|
|
x = *y - (dy >> 1);
|
2021-02-10 01:30:50 +08:00
|
|
|
}
|
2021-02-25 23:38:57 +08:00
|
|
|
x.saturating_add((self.y.len() as i32) << (k - 1))
|
2021-02-10 01:30:50 +08:00
|
|
|
}
|
|
|
|
}
|