dsp: implement Complex as type alias for tuple
This commit is contained in:
parent
d1b7efad48
commit
260206e4f0
|
@ -1,21 +0,0 @@
|
||||||
use core::cmp::PartialEq;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub struct Complex {
|
|
||||||
pub re: f32,
|
|
||||||
pub im: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Complex {
|
|
||||||
pub fn new(re: f32, im: f32) -> Self {
|
|
||||||
Complex { re: re, im: im }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arg(&self) -> f32 {
|
|
||||||
libm::atan2f(self.im, self.re)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn abs(&self) -> f32 {
|
|
||||||
libm::sqrtf([self.re, self.im].iter().map(|i| i * i).sum())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))]
|
#![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))]
|
||||||
|
|
||||||
pub mod complex;
|
pub type Complex<T> = (T, T);
|
||||||
pub mod iir;
|
pub mod iir;
|
||||||
pub mod lockin;
|
pub mod lockin;
|
||||||
pub mod pll;
|
pub mod pll;
|
||||||
|
|
|
@ -52,8 +52,8 @@
|
||||||
//! the demodulation frequency. This does not require any state
|
//! the demodulation frequency. This does not require any state
|
||||||
//! information and is therefore a normal function.
|
//! information and is therefore a normal function.
|
||||||
|
|
||||||
use super::complex::Complex;
|
|
||||||
use super::iir::{IIRState, IIR};
|
use super::iir::{IIRState, IIR};
|
||||||
|
use super::Complex;
|
||||||
use core::f32::consts::PI;
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
/// The number of ADC samples in one batch.
|
/// The number of ADC samples in one batch.
|
||||||
|
@ -155,7 +155,7 @@ impl Lockin {
|
||||||
&mut self,
|
&mut self,
|
||||||
adc_samples: &[i16],
|
adc_samples: &[i16],
|
||||||
timestamps: &[u16],
|
timestamps: &[u16],
|
||||||
) -> Result<[Complex; ADC_SAMPLE_BUFFER_SIZE], &str> {
|
) -> Result<[Complex<f32>; ADC_SAMPLE_BUFFER_SIZE], &str> {
|
||||||
// update old timestamps for new ADC batch
|
// update old timestamps for new ADC batch
|
||||||
let sample_period = self.sample_period as i32;
|
let sample_period = self.sample_period as i32;
|
||||||
self.timestamps.iter_mut().for_each(|t| match *t {
|
self.timestamps.iter_mut().for_each(|t| match *t {
|
||||||
|
@ -187,7 +187,7 @@ impl Lockin {
|
||||||
// compute ADC sample phases, sines/cosines and demodulate
|
// compute ADC sample phases, sines/cosines and demodulate
|
||||||
let reference_period =
|
let reference_period =
|
||||||
self.timestamps[0].unwrap() - self.timestamps[1].unwrap();
|
self.timestamps[0].unwrap() - self.timestamps[1].unwrap();
|
||||||
let mut signal = [Complex::new(0., 0.); ADC_SAMPLE_BUFFER_SIZE];
|
let mut signal = [(0., 0.); ADC_SAMPLE_BUFFER_SIZE];
|
||||||
signal
|
signal
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(adc_samples.iter())
|
.zip(adc_samples.iter())
|
||||||
|
@ -200,8 +200,8 @@ impl Lockin {
|
||||||
+ 2. * PI * integer_phase as f32 / reference_period as f32;
|
+ 2. * PI * integer_phase as f32 / reference_period as f32;
|
||||||
let (sine, cosine) = libm::sincosf(phase);
|
let (sine, cosine) = libm::sincosf(phase);
|
||||||
let sample = *sample as f32;
|
let sample = *sample as f32;
|
||||||
s.re = sine * sample;
|
s.0 = sine * sample;
|
||||||
s.im = cosine * sample;
|
s.1 = cosine * sample;
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(signal)
|
Ok(signal)
|
||||||
|
@ -213,10 +213,10 @@ impl Lockin {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `signal` - Complex signal to filter.
|
/// * `signal` - Complex signal to filter.
|
||||||
pub fn filter(&mut self, signal: &mut [Complex]) {
|
pub fn filter(&mut self, signal: &mut [Complex<f32>]) {
|
||||||
signal.iter_mut().for_each(|s| {
|
signal.iter_mut().for_each(|s| {
|
||||||
s.re = self.iir.update(&mut self.iirstate[0], s.re);
|
s.0 = self.iir.update(&mut self.iirstate[0], s.0);
|
||||||
s.im = self.iir.update(&mut self.iirstate[1], s.im);
|
s.1 = self.iir.update(&mut self.iirstate[1], s.1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,21 +233,21 @@ impl Lockin {
|
||||||
///
|
///
|
||||||
/// The decimated signal.
|
/// The decimated signal.
|
||||||
pub fn decimate(
|
pub fn decimate(
|
||||||
signal: [Complex; ADC_SAMPLE_BUFFER_SIZE],
|
signal: [Complex<f32>; ADC_SAMPLE_BUFFER_SIZE],
|
||||||
) -> [Complex; DECIMATED_BUFFER_SIZE] {
|
) -> [Complex<f32>; DECIMATED_BUFFER_SIZE] {
|
||||||
let n_k = ADC_SAMPLE_BUFFER_SIZE / DECIMATED_BUFFER_SIZE;
|
let n_k = ADC_SAMPLE_BUFFER_SIZE / DECIMATED_BUFFER_SIZE;
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
ADC_SAMPLE_BUFFER_SIZE == DECIMATED_BUFFER_SIZE || n_k % 2 == 0
|
ADC_SAMPLE_BUFFER_SIZE == DECIMATED_BUFFER_SIZE || n_k % 2 == 0
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut signal_decimated = [Complex::new(0., 0.); DECIMATED_BUFFER_SIZE];
|
let mut signal_decimated = [(0_f32, 0_f32); DECIMATED_BUFFER_SIZE];
|
||||||
|
|
||||||
signal_decimated
|
signal_decimated
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(signal.iter().step_by(n_k))
|
.zip(signal.iter().step_by(n_k))
|
||||||
.for_each(|(s_d, s)| {
|
.for_each(|(s_d, s)| {
|
||||||
s_d.re = s.re;
|
s_d.0 = s.0;
|
||||||
s_d.im = s.im;
|
s_d.1 = s.1;
|
||||||
});
|
});
|
||||||
|
|
||||||
signal_decimated
|
signal_decimated
|
||||||
|
@ -259,12 +259,12 @@ pub fn decimate(
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `signal` - Complex signal to decimate.
|
/// * `signal` - Complex signal to decimate.
|
||||||
pub fn magnitude_phase(signal: &mut [Complex]) {
|
pub fn magnitude_phase(signal: &mut [Complex<f32>]) {
|
||||||
signal.iter_mut().for_each(|s| {
|
signal.iter_mut().for_each(|s| {
|
||||||
let new_i = s.abs();
|
let new_i = libm::sqrtf([s.0, s.1].iter().map(|i| i * i).sum());
|
||||||
let new_q = s.arg();
|
let new_q = libm::atan2f(s.1, s.0);
|
||||||
s.re = new_i;
|
s.0 = new_i;
|
||||||
s.im = new_q;
|
s.1 = new_q;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,11 +276,11 @@ mod tests {
|
||||||
(a - b).abs() <= a.abs().max(b.abs()) * f32::EPSILON
|
(a - b).abs() <= a.abs().max(b.abs()) * f32::EPSILON
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_is_close(a: Complex, b: Complex) -> bool {
|
fn complex_is_close(a: Complex<f32>, b: Complex<f32>) -> bool {
|
||||||
f32_is_close(a.re, b.re) && f32_is_close(a.im, b.im)
|
f32_is_close(a.0, b.0) && f32_is_close(a.1, b.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_array_is_close(a: &[Complex], b: &[Complex]) -> bool {
|
fn complex_array_is_close(a: &[Complex<f32>], b: &[Complex<f32>]) -> 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_is_close(*i, *j);
|
result &= complex_is_close(*i, *j);
|
||||||
|
@ -299,18 +299,18 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_within_tolerance(
|
fn complex_within_tolerance(
|
||||||
a: Complex,
|
a: Complex<f32>,
|
||||||
b: Complex,
|
b: Complex<f32>,
|
||||||
relative_tolerance: f32,
|
relative_tolerance: f32,
|
||||||
fixed_tolerance: f32,
|
fixed_tolerance: f32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
within_tolerance(a.re, b.re, relative_tolerance, fixed_tolerance)
|
within_tolerance(a.0, b.0, relative_tolerance, fixed_tolerance)
|
||||||
&& within_tolerance(a.im, b.im, relative_tolerance, fixed_tolerance)
|
&& within_tolerance(a.1, b.1, relative_tolerance, fixed_tolerance)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_array_within_tolerance(
|
fn complex_array_within_tolerance(
|
||||||
a: &[Complex],
|
a: &[Complex<f32>],
|
||||||
b: &[Complex],
|
b: &[Complex<f32>],
|
||||||
relative_tolerance: f32,
|
relative_tolerance: f32,
|
||||||
fixed_tolerance: f32,
|
fixed_tolerance: f32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -339,93 +339,81 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_phase_length_1_quadrant_1() {
|
fn magnitude_phase_length_1_quadrant_1() {
|
||||||
let mut signal: [Complex; 1] = [Complex::new(1., 1.)];
|
let mut signal: [Complex<f32>; 1] = [(1., 1.)];
|
||||||
magnitude_phase(&mut signal);
|
magnitude_phase(&mut signal);
|
||||||
assert!(complex_array_is_close(
|
assert!(complex_array_is_close(&signal, &[(2_f32.sqrt(), PI / 4.)]));
|
||||||
&signal,
|
|
||||||
&[Complex::new(2_f32.sqrt(), PI / 4.)]
|
|
||||||
));
|
|
||||||
|
|
||||||
signal = [Complex::new(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(
|
assert!(complex_array_is_close(&signal, &[(1., PI / 6.)]));
|
||||||
&signal,
|
|
||||||
&[Complex::new(1., PI / 6.)]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_phase_length_1_quadrant_2() {
|
fn magnitude_phase_length_1_quadrant_2() {
|
||||||
let mut signal = [Complex::new(-1., 1.)];
|
let mut signal = [(-1., 1.)];
|
||||||
magnitude_phase(&mut signal);
|
magnitude_phase(&mut signal);
|
||||||
assert!(complex_array_is_close(
|
assert!(complex_array_is_close(
|
||||||
&signal,
|
&signal,
|
||||||
&[Complex::new(2_f32.sqrt(), 3. * PI / 4.)]
|
&[(2_f32.sqrt(), 3. * PI / 4.)]
|
||||||
));
|
));
|
||||||
|
|
||||||
signal = [Complex::new(-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(
|
assert!(complex_array_is_close(&signal, &[(1_f32, 2. * PI / 3.)]));
|
||||||
&signal,
|
|
||||||
&[Complex::new(1_f32, 2. * PI / 3.)]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_phase_length_1_quadrant_3() {
|
fn magnitude_phase_length_1_quadrant_3() {
|
||||||
let mut signal = [Complex::new(-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_array_is_close(
|
||||||
&signal,
|
&signal,
|
||||||
&[Complex::new(1_f32.sqrt(), -3. * PI / 4.)]
|
&[(1_f32.sqrt(), -3. * PI / 4.)]
|
||||||
));
|
));
|
||||||
|
|
||||||
signal = [Complex::new(-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_array_is_close(
|
||||||
&signal,
|
&signal,
|
||||||
&[Complex::new((3. / 2.) as f32, -1.91063323625 as f32)]
|
&[((3. / 2.) as f32, -1.91063323625 as f32)]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn magnitude_phase_length_1_quadrant_4() {
|
fn magnitude_phase_length_1_quadrant_4() {
|
||||||
let mut signal = [Complex::new(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_array_is_close(
|
||||||
&signal,
|
&signal,
|
||||||
&[Complex::new(1_f32.sqrt(), -1. * PI / 4.)]
|
&[(1_f32.sqrt(), -1. * PI / 4.)]
|
||||||
));
|
));
|
||||||
|
|
||||||
signal = [Complex::new(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(
|
assert!(complex_array_is_close(&signal, &[(1_f32, -PI / 6.)]));
|
||||||
&signal,
|
|
||||||
&[Complex::new(1_f32, -PI / 6.)]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decimate_sample_16_decimated_1() {
|
fn decimate_sample_16_decimated_1() {
|
||||||
let signal: [Complex; ADC_SAMPLE_BUFFER_SIZE] = [
|
let signal: [Complex<f32>; ADC_SAMPLE_BUFFER_SIZE] = [
|
||||||
Complex::new(0.0, 1.6),
|
(0.0, 1.6),
|
||||||
Complex::new(0.1, 1.7),
|
(0.1, 1.7),
|
||||||
Complex::new(0.2, 1.8),
|
(0.2, 1.8),
|
||||||
Complex::new(0.3, 1.9),
|
(0.3, 1.9),
|
||||||
Complex::new(0.4, 2.0),
|
(0.4, 2.0),
|
||||||
Complex::new(0.5, 2.1),
|
(0.5, 2.1),
|
||||||
Complex::new(0.6, 2.2),
|
(0.6, 2.2),
|
||||||
Complex::new(0.7, 2.3),
|
(0.7, 2.3),
|
||||||
Complex::new(0.8, 2.4),
|
(0.8, 2.4),
|
||||||
Complex::new(0.9, 2.5),
|
(0.9, 2.5),
|
||||||
Complex::new(1.0, 2.6),
|
(1.0, 2.6),
|
||||||
Complex::new(1.1, 2.7),
|
(1.1, 2.7),
|
||||||
Complex::new(1.2, 2.8),
|
(1.2, 2.8),
|
||||||
Complex::new(1.3, 2.9),
|
(1.3, 2.9),
|
||||||
Complex::new(1.4, 3.0),
|
(1.4, 3.0),
|
||||||
Complex::new(1.5, 3.1),
|
(1.5, 3.1),
|
||||||
];
|
];
|
||||||
assert_eq!(decimate(signal), [Complex::new(0.0, 1.6)]);
|
assert_eq!(decimate(signal), [(0.0, 1.6)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -492,13 +480,13 @@ mod tests {
|
||||||
-(initial_phase_integer as f32) / reference_period as f32 * 2. * PI;
|
-(initial_phase_integer as f32) / reference_period as f32 * 2. * PI;
|
||||||
let phase_increment: f32 =
|
let phase_increment: f32 =
|
||||||
adc_period as f32 / reference_period as f32 * 2. * PI;
|
adc_period as f32 / reference_period as f32 * 2. * PI;
|
||||||
let mut signal = [Complex::new(0., 0.); ADC_SAMPLE_BUFFER_SIZE];
|
let mut signal = [(0., 0.); ADC_SAMPLE_BUFFER_SIZE];
|
||||||
for (n, s) in signal.iter_mut().enumerate() {
|
for (n, s) in signal.iter_mut().enumerate() {
|
||||||
let adc_phase = initial_phase + n as f32 * phase_increment;
|
let adc_phase = initial_phase + n as f32 * phase_increment;
|
||||||
let sine = adc_phase.sin();
|
let sine = adc_phase.sin();
|
||||||
let cosine = adc_phase.cos();
|
let cosine = adc_phase.cos();
|
||||||
s.re = sine * adc_samples[n] as f32;
|
s.0 = sine * adc_samples[n] as f32;
|
||||||
s.im = cosine * adc_samples[n] as f32;
|
s.1 = cosine * adc_samples[n] as f32;
|
||||||
}
|
}
|
||||||
let result = lockin.demodulate(&adc_samples, timestamps).unwrap();
|
let result = lockin.demodulate(&adc_samples, timestamps).unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use dsp::complex::Complex;
|
|
||||||
use dsp::iir::IIR;
|
use dsp::iir::IIR;
|
||||||
use dsp::lockin::{
|
use dsp::lockin::{
|
||||||
decimate, magnitude_phase, Lockin, ADC_SAMPLE_BUFFER_SIZE,
|
decimate, magnitude_phase, Lockin, ADC_SAMPLE_BUFFER_SIZE,
|
||||||
DECIMATED_BUFFER_SIZE,
|
DECIMATED_BUFFER_SIZE,
|
||||||
};
|
};
|
||||||
|
use dsp::Complex;
|
||||||
|
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
@ -598,7 +598,7 @@ fn lowpass_test(
|
||||||
internal_frequency,
|
internal_frequency,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut signal: [Complex; ADC_SAMPLE_BUFFER_SIZE];
|
let mut signal: [Complex<f32>; ADC_SAMPLE_BUFFER_SIZE];
|
||||||
match lockin.demodulate(&adc_signal, timestamps) {
|
match lockin.demodulate(&adc_signal, timestamps) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
signal = s;
|
signal = s;
|
||||||
|
@ -622,7 +622,7 @@ fn lowpass_test(
|
||||||
if n >= samples {
|
if n >= samples {
|
||||||
for k in 0..DECIMATED_BUFFER_SIZE {
|
for k in 0..DECIMATED_BUFFER_SIZE {
|
||||||
let amplitude_normalized: f32 =
|
let amplitude_normalized: f32 =
|
||||||
magnitude_phase_decimated[k].re / ADC_MAX_COUNT as f32;
|
magnitude_phase_decimated[k].0 / ADC_MAX_COUNT as f32;
|
||||||
assert!(
|
assert!(
|
||||||
tolerance_check(linear(desired_input.amplitude_dbfs) as f32, amplitude_normalized, total_magnitude_noise as f32, tolerance),
|
tolerance_check(linear(desired_input.amplitude_dbfs) as f32, amplitude_normalized, total_magnitude_noise as f32, tolerance),
|
||||||
"magnitude actual: {:.4} ({:.2} dBFS), magnitude computed: {:.4} ({:.2} dBFS), tolerance: {:.4}",
|
"magnitude actual: {:.4} ({:.2} dBFS), magnitude computed: {:.4} ({:.2} dBFS), tolerance: {:.4}",
|
||||||
|
@ -635,13 +635,13 @@ fn lowpass_test(
|
||||||
assert!(
|
assert!(
|
||||||
tolerance_check(
|
tolerance_check(
|
||||||
effective_phase_offset as f32,
|
effective_phase_offset as f32,
|
||||||
magnitude_phase_decimated[k].im,
|
magnitude_phase_decimated[k].1,
|
||||||
total_phase_noise as f32,
|
total_phase_noise as f32,
|
||||||
tolerance
|
tolerance
|
||||||
),
|
),
|
||||||
"phase actual: {:.4}, phase computed: {:.4}, tolerance: {:.4}",
|
"phase actual: {:.4}, phase computed: {:.4}, tolerance: {:.4}",
|
||||||
effective_phase_offset as f32,
|
effective_phase_offset as f32,
|
||||||
magnitude_phase_decimated[k].im,
|
magnitude_phase_decimated[k].1,
|
||||||
max_error(
|
max_error(
|
||||||
effective_phase_offset as f32,
|
effective_phase_offset as f32,
|
||||||
total_phase_noise as f32,
|
total_phase_noise as f32,
|
||||||
|
@ -650,9 +650,9 @@ fn lowpass_test(
|
||||||
);
|
);
|
||||||
|
|
||||||
let in_phase_normalized: f32 =
|
let in_phase_normalized: f32 =
|
||||||
signal_decimated[k].re / ADC_MAX_COUNT as f32;
|
signal_decimated[k].0 / ADC_MAX_COUNT as f32;
|
||||||
let quadrature_normalized: f32 =
|
let quadrature_normalized: f32 =
|
||||||
signal_decimated[k].im / ADC_MAX_COUNT as f32;
|
signal_decimated[k].1 / ADC_MAX_COUNT as f32;
|
||||||
assert!(
|
assert!(
|
||||||
tolerance_check(
|
tolerance_check(
|
||||||
in_phase_actual as f32,
|
in_phase_actual as f32,
|
||||||
|
|
Loading…
Reference in New Issue