idsp/
lockin.rs

1use super::Complex;
2use core::num::Wrapping;
3use dsp_fixedpoint::Q32;
4use dsp_process::SplitProcess;
5
6/// Lockin filter
7///
8/// Combines two [`SplitProcess`] filters and an NCO to perform demodulation
9#[derive(Copy, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
10pub struct Lockin<C>(
11    // Lowpass configuration
12    pub C,
13);
14
15/// Input and LO
16impl<C: SplitProcess<i32, i32, S>, S> SplitProcess<(i32, Complex<Q32<31>>), Complex<i32>, [S; 2]>
17    for Lockin<C>
18{
19    /// Update the lockin with a sample taken at a local oscillator IQ value.
20    fn process(&self, state: &mut [S; 2], x: (i32, Complex<Q32<31>>)) -> Complex<i32> {
21        Complex::new(
22            self.0.process(&mut state[0], x.0 * x.1.re()),
23            self.0.process(&mut state[1], x.0 * x.1.im()),
24        )
25    }
26}
27
28/// Sample and phase
29impl<C: SplitProcess<i32, i32, S>, S> SplitProcess<(i32, Wrapping<i32>), Complex<i32>, [S; 2]>
30    for Lockin<C>
31{
32    /// Update the lockin with a sample taken at a given phase.
33    fn process(&self, state: &mut [S; 2], x: (i32, Wrapping<i32>)) -> Complex<i32> {
34        // Get the LO signal for demodulation and mix the sample;
35        self.process(state, (x.0, Complex::from_angle(x.1)))
36    }
37}