Skip to content

Commit

Permalink
Merge pull request #125 from card-io-ecg/iir
Browse files Browse the repository at this point in the history
Fix iir filters
  • Loading branch information
bugadani committed Sep 30, 2023
2 parents b781be9 + e38c19e commit 31ec78b
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 35 deletions.
6 changes: 6 additions & 0 deletions macros/src/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ fn iir(args: FilterSpec, ty: FilterBandType) -> TokenStream {
// Strip off always-1 coefficient
assert!(a.remove(0) == 1.0);

// we reverse a to avoid having to reverse it during filtering
a.reverse();

// b seems to be returned in the wrong order
b.reverse();

let n = a.len();

quote! {
Expand Down
162 changes: 162 additions & 0 deletions signal-processing/src/filter/dyn_iir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ where
// Strip off always-1 coefficient
assert!(a.remove(0) == 1.0);

// we reverse a to avoid having to reverse it during filtering
a.reverse();

// b seems to be returned in the wrong order
b.reverse();

let denom_coeffs = a.into_boxed_slice();
let num_coeffs = b.into_boxed_slice();

Expand Down Expand Up @@ -97,3 +103,159 @@ impl<T: DynFilterType, const N: usize> IirFilter for DynIir<T, N> {
self.filter.transfer_coeff_at(w)
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_iir_no_input() {
let input = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];
let expectation = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.];

#[rustfmt::skip]
test_filter(
DynIir::<HighPass, 1>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_lowpass_iir_impluse_response_order1() {
let input = [0., 1., 0., 0., 0., 0., 0.];
let expectation = [0.0000, 0.2452, 0.3702, 0.1886, 0.0961, 0.0490, 0.0250];

#[rustfmt::skip]
test_filter(
DynIir::<LowPass, 1>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_lowpass_iir_step_response_order1() {
let input = [0., 1., 1., 1., 1., 1., 1.];
let expectation = [0.0000, 0.2452, 0.6154, 0.8041, 0.9002, 0.9491, 0.9741];

#[rustfmt::skip]
test_filter(
DynIir::<LowPass, 1>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_highpass_iir_impluse_response_order1() {
let input = [0., 1., 0., 0., 0., 0., 0.];
let expectation = [0.0000, 0.7548, -0.3702, -0.1886, -0.0961, -0.0490, -0.0250];

#[rustfmt::skip]
test_filter(
DynIir::<HighPass, 1>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_highpass_iir_step_response_order1() {
let input = [0., 1., 1., 1., 1., 1., 1.];
let expectation = [0.0000, 0.7548, 0.3846, 0.1959, 0.0998, 0.0509, 0.0259];

#[rustfmt::skip]
test_filter(
DynIir::<HighPass, 1>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_lowpass_iir_impluse_response_order2() {
let input = [0., 1., 0., 0., 0., 0., 0., 0.];
let expectation = [0.0000, 0.0675, 0.2120, 0.2819, 0.2347, 0.1519, 0.0767];

#[rustfmt::skip]
test_filter(
DynIir::<LowPass, 2>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_lowpass_iir_step_response_order2() {
let input = [0., 1., 1., 1., 1., 1., 1.];
let expectation = [0.0000, 0.0675, 0.2795, 0.5614, 0.7961, 0.9480, 1.0248];

#[rustfmt::skip]
test_filter(
DynIir::<LowPass, 2>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_highpass_iir_impluse_response_order2() {
let input = [0., 1., 0., 0., 0., 0., 0., 0.];
let expectation = [0.0000, 0.6389, -0.5476, -0.2507, -0.0605, 0.0343, 0.0642];

#[rustfmt::skip]
test_filter(
DynIir::<HighPass, 2>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[test]
fn test_highpass_iir_step_response_order2() {
let input = [0., 1., 1., 1., 1., 1., 1.];
let expectation = [0.0000, 0.6389, 0.0914, -0.1593, -0.2198, -0.1855, -0.1213];

#[rustfmt::skip]
test_filter(
DynIir::<HighPass, 2>::design(10.0, 1.0),
&input,
&expectation,
0.0001
);
}

#[track_caller]
fn test_filter(mut filter: impl Filter, input: &[f32], expectation: &[f32], epsilon: f32) {
let mut output = vec![];
for sample in input.iter().copied() {
if let Some(out_sample) = filter.update(sample) {
output.push(out_sample);
}
}

let pairs = output.iter().copied().zip(expectation.iter().copied());

for (out_sample, expectation) in pairs.clone() {
assert!(
(out_sample - expectation).abs() < epsilon,
"[\n // (output, expectation)\n{}]",
pairs
.map(|(a, b)| format!(
" {} ({a:>7.04}, {b:>7.04})\n",
if (a - b).abs() < epsilon { " " } else { "!" }
))
.collect::<String>()
);
}
}
}
Loading

0 comments on commit 31ec78b

Please sign in to comment.