#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use num_traits::Float;
use super::{AlignedArray, Ring};
#[derive(Clone, Copy, Default)]
pub struct SisoIirFilter<const ORDER: usize> {
y: f32,
d: f32,
x: Ring<f32, ORDER>,
a: AlignedArray<f32, ORDER>,
c: AlignedArray<f32, ORDER>,
}
impl<const ORDER: usize> SisoIirFilter<ORDER> {
#[inline]
pub fn update(&mut self, u: f32) -> f32 {
self.y = self.c.dot(&self.x, self.d * u);
let x0 = self.a.dot(&self.x, u);
self.x.push(x0);
self.y
}
pub fn new(a: &[f32], c: &[f32], d: f32) -> Self {
let mut a_ = [0.0; ORDER];
a_.copy_from_slice(a);
let mut c_ = [0.0; ORDER];
c_.copy_from_slice(c);
Self {
y: 0.0,
x: Ring::new(0.0),
a: AlignedArray(a_),
c: AlignedArray(c_),
d,
}
}
pub fn new_interpolated(
cutoff_ratio: f64,
log10_cutoff_ratio_grid: &[f64],
avals: &[&[f64]],
cvals: &[&[f64]],
dvals: &[f64],
) -> Result<Self, &'static str> {
let log10_cutoff_ratio = cutoff_ratio.log10();
let mut extrapolated = [false; 1];
interpn::multicubic::rectilinear::check_bounds(
&[log10_cutoff_ratio_grid],
&[&[log10_cutoff_ratio]],
1e-6,
&mut extrapolated,
)?;
if extrapolated[0] {
return Err("Selected cutoff ratio is outside the grid");
}
let mut a = [0.0; ORDER];
let mut c = [0.0; ORDER];
for i in 0..ORDER {
a[i] = interpn::MulticubicRectilinear::<'_, _, 1>::new(
&[log10_cutoff_ratio_grid],
avals[i],
true,
)?
.interp_one([log10_cutoff_ratio])? as f32;
}
for i in 0..ORDER {
c[i] = interpn::MulticubicRectilinear::<'_, _, 1>::new(
&[log10_cutoff_ratio_grid],
cvals[i],
true,
)?
.interp_one([log10_cutoff_ratio])? as f32;
}
let d = interpn::MulticubicRectilinear::<'_, _, 1>::new(
&[log10_cutoff_ratio_grid],
dvals,
true,
)?
.interp_one([log10_cutoff_ratio])? as f32;
let asum = a.iter().sum::<f32>() as f64;
let xs = 1.0 / (1.0 - asum);
let csum_desired = (1.0 - d as f64) / xs;
let csum = c.iter().sum::<f32>() as f64;
let scale_factor = (csum_desired / csum) as f32;
c.iter_mut().for_each(|v| *v *= scale_factor);
a.reverse();
c.reverse();
Ok(Self::new(&a, &c, d))
}
pub fn set_steady_state(&mut self, u: f32) {
let asum: f32 = self.a.0.iter().sum();
let xs = u / (1.0 - asum); self.x = Ring::new(xs);
}
pub fn y(&self) -> f32 {
self.y
}
pub fn x(&self) -> &Ring<f32, ORDER> {
&self.x
}
pub fn a(&self) -> &AlignedArray<f32, ORDER> {
&self.a
}
pub fn c(&self) -> &AlignedArray<f32, ORDER> {
&self.c
}
pub fn d(&self) -> f32 {
self.d
}
}