use core::f32::consts::TAU;
use num_complex::Complex32 as C32;
#[derive(Debug, Clone)]
pub struct Nco {
fs: f32,
freq_hz: f32,
z: C32, w: C32, renorm_ctr: u32,
}
impl Nco {
pub fn new(freq_hz: f32, fs: f32) -> Self {
let dphi = TAU * freq_hz / fs;
let (s, c) = dphi.sin_cos();
Self {
fs,
freq_hz,
z: C32::new(1.0, 0.0),
w: C32::new(c, s),
renorm_ctr: 0,
}
}
#[inline]
pub fn set_freq(&mut self, freq_hz: f32) {
self.freq_hz = freq_hz;
let dphi = TAU * freq_hz / self.fs;
let (s, c) = dphi.sin_cos();
self.w = C32::new(c, s);
}
#[inline(always)]
pub fn next_cs(&mut self) -> (f32, f32) {
let zr = self.z.re.mul_add(self.w.re, -self.z.im * self.w.im);
let zi = self.z.im.mul_add(self.w.re, self.z.re * self.w.im);
self.z = C32::new(zr, zi);
self.renorm_ctr = self.renorm_ctr.wrapping_add(1);
if (self.renorm_ctr & 0x3FF) == 0 {
let inv = (self.z.re * self.z.re + self.z.im * self.z.im)
.sqrt()
.recip();
self.z.re *= inv;
self.z.im *= inv;
}
(self.z.re, self.z.im)
}
}
#[inline]
pub fn mix_with_nco(x: C32, nco: &mut Nco) -> C32 {
let (c, s) = nco.next_cs();
C32::new(x.re * c - x.im * s, x.re * s + x.im * c)
}