extern crate alloc;
use alloc::vec::Vec;
use core::ops::AddAssign;
use core::ops::Mul;
use num_traits::Zero;
use crate::ComputationStatus;
use crate::StatefulFilter;
use crate::Taps;
pub struct IirFilter<InputType, OutputType, TapsType: Taps> {
a_taps: TapsType,
b_taps: TapsType,
memory: Vec<InputType>,
_input_type: core::marker::PhantomData<InputType>,
_output_type: core::marker::PhantomData<OutputType>,
}
impl<InputType, OutputType, TapType, TapsType: Taps<TapType = TapType>>
IirFilter<InputType, OutputType, TapsType>
{
pub fn new(a_taps: TapsType, b_taps: TapsType) -> Self {
Self {
a_taps,
b_taps,
memory: Vec::new(),
_input_type: core::marker::PhantomData,
_output_type: core::marker::PhantomData,
}
}
}
impl<TapsType: Taps<TapType = f32>> StatefulFilter<f32, f32, f32>
for IirFilter<f32, f32, TapsType>
{
fn filter(&mut self, input: &[f32], output: &mut [f32]) -> (usize, usize, ComputationStatus) {
taps_accessor_work(&mut self.memory, &self.a_taps, &self.b_taps, input, output)
}
fn length(&self) -> usize {
self.b_taps.num_taps()
}
}
impl<TapsType: Taps<TapType = f64>> StatefulFilter<f64, f64, f64>
for IirFilter<f64, f64, TapsType>
{
fn filter(&mut self, input: &[f64], output: &mut [f64]) -> (usize, usize, ComputationStatus) {
taps_accessor_work(&mut self.memory, &self.a_taps, &self.b_taps, input, output)
}
fn length(&self) -> usize {
self.b_taps.num_taps()
}
}
#[inline(always)]
fn taps_accessor_work<TT, T>(
memory: &mut Vec<T>,
a_taps: &TT,
b_taps: &TT,
i: &[T],
o: &mut [T],
) -> (usize, usize, ComputationStatus)
where
TT: Taps<TapType = T>,
T: Copy + AddAssign + Zero + Mul<Output = T>,
{
if i.is_empty() {
return (
0,
0,
if o.is_empty() {
ComputationStatus::BothSufficient
} else {
ComputationStatus::InsufficientInput
},
);
}
let mut num_filled = 0;
while memory.len() < a_taps.num_taps() {
if i.len() <= memory.len() {
return (
0,
0,
if o.is_empty() {
ComputationStatus::BothSufficient
} else {
ComputationStatus::InsufficientInput
},
);
}
memory.push(i[memory.len()]);
num_filled += 1;
}
if num_filled == i.len() {
return (
0,
0,
if o.is_empty() {
ComputationStatus::BothSufficient
} else {
ComputationStatus::InsufficientInput
},
);
}
assert_eq!(a_taps.num_taps(), memory.len());
assert!(b_taps.num_taps() > 0);
let mut n_consumed = 0;
let mut n_produced = 0;
while n_consumed + b_taps.num_taps() - 1 < i.len() && n_produced < o.len() {
let o: &mut T = &mut o[n_produced];
*o = T::zero();
for b_tap in 0..b_taps.num_taps() {
*o += b_taps.get(b_tap) * i[n_consumed + b_taps.num_taps() - b_tap - 1];
}
#[allow(clippy::needless_range_loop)]
for a_tap in 0..a_taps.num_taps() {
*o += a_taps.get(a_tap) * memory[a_tap];
}
for idx in (1..memory.len()).rev() {
memory[idx] = memory[idx - 1];
}
if !memory.is_empty() {
memory[0] = *o;
}
n_produced += 1;
n_consumed += 1;
}
(
n_consumed,
n_produced,
if n_consumed == i.len() && n_produced == o.len() {
ComputationStatus::BothSufficient
} else if n_consumed < i.len() {
ComputationStatus::InsufficientOutput
} else {
assert!(n_produced < o.len());
ComputationStatus::InsufficientInput
},
)
}
#[cfg(test)]
mod test {
use super::*;
use alloc::vec;
struct Feeder {
filter: IirFilter<f32, f32, Vec<f32>>,
input: Vec<f32>,
}
impl Feeder {
fn feed(&mut self, input: f32) -> Option<f32> {
self.input.push(input);
let mut out = [0.0];
let (n_consumed, n_produced, _status) = self.filter.filter(&self.input[..], &mut out);
assert_eq!(n_consumed, n_produced); if n_consumed > 0 {
self.input.drain(0..n_consumed);
}
if n_produced > 0 { Some(out[0]) } else { None }
}
}
fn make_filter(a_taps: Vec<f32>, b_taps: Vec<f32>) -> Feeder {
Feeder {
filter: IirFilter {
a_taps,
b_taps,
memory: vec![],
_input_type: core::marker::PhantomData,
_output_type: core::marker::PhantomData,
},
input: vec![],
}
}
#[test]
fn test_iir_b_taps_algorithm() {
let mut iir = make_filter(vec![], vec![1.0, 2.0, 3.0]);
assert_eq!(iir.feed(10.0), None);
assert_eq!(iir.feed(20.0), None);
assert_eq!(iir.feed(30.0), Some(30.0 + 40.0 + 30.0));
assert_eq!(iir.feed(40.0), Some(40.0 + 60.0 + 60.0));
}
#[test]
fn test_iir_single_a_tap_algorithm() {
let mut iir = make_filter(vec![0.5], vec![1.0]);
assert_eq!(iir.feed(10.0), None);
assert_eq!(iir.feed(10.0), Some(15.0));
assert_eq!(iir.feed(10.0), Some(17.5));
assert_eq!(iir.feed(10.0), Some(18.75));
}
}