pounder_test/dsp/src/lockin.rs

58 lines
1.6 KiB
Rust
Raw Normal View History

2021-02-01 19:37:44 +08:00
use super::{
iir_int::{Vec5, IIR},
Accu, Complex,
};
2021-01-21 21:55:33 +08:00
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
pub struct Lockin {
iir: IIR,
state: [Vec5; 2],
2021-01-21 21:55:33 +08:00
}
impl Lockin {
2021-02-01 03:32:44 +08:00
/// Create a new Lockin with given IIR coefficients.
pub fn new(ba: Vec5) -> Self {
2021-02-01 19:37:44 +08:00
Self {
iir: IIR { ba, ..Default::default() },
state: [Vec5::default(); 2],
2021-01-21 21:55:33 +08:00
}
}
2021-02-01 03:32:44 +08:00
/// Update the lockin with a sample taken at a given phase.
pub fn update(&mut self, sample: i32, phase: i32) -> Complex<i32> {
2021-01-21 21:55:33 +08:00
// Get the LO signal for demodulation.
2021-02-01 03:32:44 +08:00
let lo = Complex::from_angle(phase);
2021-01-21 21:55:33 +08:00
// Mix with the LO signal, filter with the IIR lowpass,
// return IQ (in-phase and quadrature) data.
// Note: 32x32 -> 64 bit multiplications are pretty much free.
Complex(
self.iir.update(
&mut self.state[0],
2021-02-01 03:32:44 +08:00
((sample as i64 * lo.0 as i64) >> 32) as _,
2021-01-21 21:55:33 +08:00
),
self.iir.update(
&mut self.state[1],
2021-02-01 03:32:44 +08:00
((sample as i64 * lo.1 as i64) >> 32) as _,
2021-01-21 21:55:33 +08:00
),
)
}
2021-01-31 01:05:54 +08:00
2021-02-01 03:32:44 +08:00
/// Feed an iterator into the Lockin and return the latest I/Q data.
/// Initial stample phase and frequency (phase increment between samples)
/// are supplied.
2021-01-31 01:05:54 +08:00
pub fn feed<I: IntoIterator<Item = i32>>(
&mut self,
signal: I,
phase: i32,
frequency: i32,
) -> Option<Complex<i32>> {
signal
.into_iter()
.zip(Accu::new(phase, frequency))
.map(|(sample, phase)| self.update(sample, phase))
2021-01-31 01:05:54 +08:00
.last()
}
2021-01-21 21:55:33 +08:00
}