crate::ix!();
#[test]
fn test_coeff_lp() {
let mut filter = BiquadFilter::default();
filter.coeff_lp(0.1, 0.5);
let expected = (
1.1800682076597505,
-1.690035837119334,
0.7436025846804989,
0.12968589617062476,
0.25937179234124953,
0.12968589617062476,
);
let actual = filter.get_coef();
assert_approx_eq!(expected.0, actual.0, 1e-12);
assert_approx_eq!(expected.1, actual.1, 1e-12);
assert_approx_eq!(expected.2, actual.2, 1e-12);
assert_approx_eq!(expected.3, actual.3, 1e-12);
assert_approx_eq!(expected.4, actual.4, 1e-12);
assert_approx_eq!(expected.5, actual.5, 1e-12);
}
#[test]
fn test_lp_filter_frequency_response() {
let mut filter = BiquadFilter::new();
filter.coeff_lp_with_bw(2.0 * PI * 500.0, 0.7);
let num_samples = 10000;
let sample_rate = 44100.0;
let duration = num_samples as f64 / sample_rate;
let f_start = 20.0;
let f_stop = 20000.0;
let num_freqs = 100;
let log_spacing = true;
let mut signals = Vec::new();
let freqs = if log_spacing {
logspace(f_start, f_stop, num_freqs)
} else {
linspace(f_start, f_stop, num_freqs)
};
for f in freqs {
let mut signal = Vec::new();
for n in 0..num_samples {
let t = n as f64 / sample_rate;
signal.push(0.5 * (2.0 * PI * f * t).sin());
}
signals.push(signal);
}
let mut outputs = Vec::new();
let mut freq_responses = Vec::new();
for signal in signals {
let output = filter.filter(&signal);
outputs.push(output.clone());
let freq_response = fourier_transform::magnitude_spectrum(&output, sample_rate);
freq_responses.push(freq_response);
}
let expected_response = if log_spacing {
logspace(f_start, f_stop, num_freqs)
.iter()
.map(|&f| if f <= 500.0 {
1.0
} else {
1.0 / (1.0 + ((f / 500.0) / 0.7).powi(4))
})
.collect()
} else {
linspace(f_start, f_stop, num_freqs)
.iter()
.map(|&f| if f <= 500.0 {
1.0
} else {
1.0 / (1.0 + ((f / 500.0) / 0.7).powi(4))
})
.collect()
};
for i in 0..num_freqs {
let freq = freqs[i];
let freq_response = &freq_responses[i];
let expected = expected_response[i];
let measured = freq_response[0]; let error = (measured - expected).abs() / expected;
assert!(error < 0.1, "Error too large at frequency {:.1} Hz: expected {:.4}, measured {:.4}", freq, expected, measured);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coeff_lp() {
let mut filter = BiquadFilter::new();
filter.coeff_lp(3.5, 0.5);
let expected = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0];
assert_eq!(filter.get_coef(), expected);
let mut filter = BiquadFilter::new();
filter.coeff_lp(1.0, 0.7);
let expected = [
1.4289375376768227,
-0.5486291845786517,
0.4161249249193546,
0.7915624891192163,
1.5831249782384326,
0.7915624891192163,
];
assert_approx_eq!(filter.get_coef(), expected);
}
#[test]
fn test_coeff_lp_with_bw() {
let mut filter = BiquadFilter::new();
filter.coeff_lp_with_bw(1.0, 0.5);
let expected = [
1.2405145438263382,
-0.28010248420823334,
0.24051454382633833,
0.6202572719131692,
1.2405145438263382,
0.6202572719131692,
];
assert_approx_eq!(filter.get_coef(), expected);
let mut filter = BiquadFilter::new();
filter.coeff_lp_with_bw(2.5, 1.0);
let expected = [
1.1037195231547624,
-0.42750521737628485,
0.1869475083210603,
0.09347375416053014,
0.1869475083210603,
0.09347375416053014,
];
assert_approx_eq!(filter.get_coef(), expected);
}
}
#[test]
fn test_filter_sine_wave() {
let mut filter = BiquadFilter::new();
let f0 = 1000.0;
let q = 0.707;
let sample_rate = 44100.0;
let omega = 2.0 * PI * f0 / sample_rate;
filter.coeff_lp(omega, q);
let duration = 1.0;
let amplitude = 0.5;
let sample_count = (duration * sample_rate) as usize;
let mut input_signal = vec![0.0; sample_count];
let t = (0..sample_count).map(|i| i as f64 / sample_rate);
let sine_wave = t.map(|t| amplitude * (2.0 * PI * f0 * t).sin());
input_signal.copy_from_slice(&sine_wave.collect::<Vec<f64>>());
let output_signal = filter.process(&input_signal);
let mut expected_output_signal = vec![0.0; sample_count];
let b0 = 0.5 * (1.0 - (2.0 * f0 / sample_rate * PI).cos());
let b1 = 1.0 - (2.0 * f0 / sample_rate * PI).cos();
let b2 = 0.5 * (1.0 - (2.0 * f0 / sample_rate * PI).cos());
let a0 = 1.0 + (q * omega).sin();
let a1 = -2.0 * (2.0 * f0 / sample_rate * PI).cos();
let a2 = 1.0 - (q * omega).sin();
for i in 0..sample_count {
if i == 0 {
expected_output_signal[i] = b0 * input_signal[i] + b1 * 0.0 + b2 * 0.0;
} else if i == 1 {
expected_output_signal[i] = b0 * input_signal[i] + b1 * input_signal[i-1] + b2 * 0.0
- a1 * expected_output_signal[i-1];
} else {
expected_output_signal[i] = b0 * input_signal[i] + b1 * input_signal[i-1] + b2 * input_signal[i-2]
- a1 * expected_output_signal[i-1] - a2 * expected_output_signal[i-2];
}
}
assert!(output_signal.iter().zip(expected_output_signal.iter())
.all(|(a, b)| (a-b).abs() < 1e-12));
}