dsp testing: improve api for tolerance checking

This commit is contained in:
Matt Huszagh 2020-12-04 09:09:23 -08:00
parent 1b02f558f6
commit 43a760e57a
2 changed files with 52 additions and 53 deletions

View File

@ -315,9 +315,7 @@ pub fn magnitude_phase(signal: &mut [Complex<f32>]) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::testing::{ use crate::testing::complex_allclose;
complex_array_is_close, complex_array_within_tolerance,
};
#[test] #[test]
fn array_push() { fn array_push() {
@ -334,41 +332,62 @@ mod tests {
fn magnitude_phase_length_1_quadrant_1() { fn magnitude_phase_length_1_quadrant_1() {
let mut signal: [Complex<f32>; 1] = [(1., 1.)]; let mut signal: [Complex<f32>; 1] = [(1., 1.)];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close(&signal, &[(2_f32.sqrt(), PI / 4.)])); assert!(complex_allclose(
&signal,
&[(2_f32.sqrt(), PI / 4.)],
f32::EPSILON,
0.
));
signal = [(3_f32.sqrt() / 2., 1. / 2.)]; signal = [(3_f32.sqrt() / 2., 1. / 2.)];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close(&signal, &[(1., PI / 6.)])); assert!(complex_allclose(
&signal,
&[(1., PI / 6.)],
f32::EPSILON,
0.
));
} }
#[test] #[test]
fn magnitude_phase_length_1_quadrant_2() { fn magnitude_phase_length_1_quadrant_2() {
let mut signal = [(-1., 1.)]; let mut signal = [(-1., 1.)];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close( assert!(complex_allclose(
&signal, &signal,
&[(2_f32.sqrt(), 3. * PI / 4.)] &[(2_f32.sqrt(), 3. * PI / 4.)],
f32::EPSILON,
0.
)); ));
signal = [(-1. / 2., 3_f32.sqrt() / 2.)]; signal = [(-1. / 2., 3_f32.sqrt() / 2.)];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close(&signal, &[(1_f32, 2. * PI / 3.)])); assert!(complex_allclose(
&signal,
&[(1_f32, 2. * PI / 3.)],
f32::EPSILON,
0.
));
} }
#[test] #[test]
fn magnitude_phase_length_1_quadrant_3() { fn magnitude_phase_length_1_quadrant_3() {
let mut signal = [(-1. / 2_f32.sqrt(), -1. / 2_f32.sqrt())]; let mut signal = [(-1. / 2_f32.sqrt(), -1. / 2_f32.sqrt())];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close( assert!(complex_allclose(
&signal, &signal,
&[(1_f32.sqrt(), -3. * PI / 4.)] &[(1_f32.sqrt(), -3. * PI / 4.)],
f32::EPSILON,
0.
)); ));
signal = [(-1. / 2., -2_f32.sqrt())]; signal = [(-1. / 2., -2_f32.sqrt())];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close( assert!(complex_allclose(
&signal, &signal,
&[((3. / 2.) as f32, -1.91063323625 as f32)] &[((3. / 2.) as f32, -1.91063323625 as f32)],
f32::EPSILON,
0.
)); ));
} }
@ -376,14 +395,21 @@ mod tests {
fn magnitude_phase_length_1_quadrant_4() { fn magnitude_phase_length_1_quadrant_4() {
let mut signal = [(1. / 2_f32.sqrt(), -1. / 2_f32.sqrt())]; let mut signal = [(1. / 2_f32.sqrt(), -1. / 2_f32.sqrt())];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close( assert!(complex_allclose(
&signal, &signal,
&[(1_f32.sqrt(), -1. * PI / 4.)] &[(1_f32.sqrt(), -1. * PI / 4.)],
f32::EPSILON,
0.
)); ));
signal = [(3_f32.sqrt() / 2., -1. / 2.)]; signal = [(3_f32.sqrt() / 2., -1. / 2.)];
magnitude_phase(&mut signal); magnitude_phase(&mut signal);
assert!(complex_array_is_close(&signal, &[(1_f32, -PI / 6.)])); assert!(complex_allclose(
&signal,
&[(1_f32, -PI / 6.)],
f32::EPSILON,
0.
));
} }
#[test] #[test]
@ -483,7 +509,7 @@ mod tests {
} }
let result = lockin.demodulate(&adc_samples, timestamps).unwrap(); let result = lockin.demodulate(&adc_samples, timestamps).unwrap();
assert!( assert!(
complex_array_within_tolerance(&result, &signal, 0., 1e-5), complex_allclose(&result, &signal, 0., 1e-5),
"\nsignal computed: {:?},\nsignal expected: {:?}", "\nsignal computed: {:?},\nsignal expected: {:?}",
result, result,
signal signal

View File

@ -1,54 +1,27 @@
use super::Complex; use super::Complex;
pub fn f32_is_close(a: f32, b: f32) -> bool { pub fn isclose(a: f32, b: f32, rtol: f32, atol: f32) -> bool {
(a - b).abs() <= a.abs().max(b.abs()) * f32::EPSILON (a - b).abs() <= a.abs().max(b.abs()) * rtol + atol
} }
pub fn complex_is_close(a: Complex<f32>, b: Complex<f32>) -> bool { pub fn complex_isclose(
f32_is_close(a.0, b.0) && f32_is_close(a.1, b.1)
}
pub fn complex_array_is_close(a: &[Complex<f32>], b: &[Complex<f32>]) -> bool {
let mut result: bool = true;
a.iter().zip(b.iter()).for_each(|(i, j)| {
result &= complex_is_close(*i, *j);
});
result
}
pub fn within_tolerance(
a: f32,
b: f32,
relative_tolerance: f32,
fixed_tolerance: f32,
) -> bool {
(a - b).abs() <= a.abs().max(b.abs()) * relative_tolerance + fixed_tolerance
}
pub fn complex_within_tolerance(
a: Complex<f32>, a: Complex<f32>,
b: Complex<f32>, b: Complex<f32>,
relative_tolerance: f32, rtol: f32,
fixed_tolerance: f32, atol: f32,
) -> bool { ) -> bool {
within_tolerance(a.0, b.0, relative_tolerance, fixed_tolerance) isclose(a.0, b.0, rtol, atol) && isclose(a.1, b.1, rtol, atol)
&& within_tolerance(a.1, b.1, relative_tolerance, fixed_tolerance)
} }
pub fn complex_array_within_tolerance( pub fn complex_allclose(
a: &[Complex<f32>], a: &[Complex<f32>],
b: &[Complex<f32>], b: &[Complex<f32>],
relative_tolerance: f32, rtol: f32,
fixed_tolerance: f32, atol: f32,
) -> bool { ) -> bool {
let mut result: bool = true; let mut result: bool = true;
a.iter().zip(b.iter()).for_each(|(i, j)| { a.iter().zip(b.iter()).for_each(|(i, j)| {
result &= complex_within_tolerance( result &= complex_isclose(*i, *j, rtol, atol);
*i,
*j,
relative_tolerance,
fixed_tolerance,
);
}); });
result result
} }