use core::num::Wrapping as W;
use dsp_fixedpoint::Q32;
use dsp_process::{Process, SplitProcess};
use crate::ClampWrap;
#[derive(Debug, Clone, Default)]
pub struct PLL {
pub ba: [Q32<32>; 3],
}
impl PLL {
pub fn from_zpk(zero: f32, pole: f32, gain: f32) -> Self {
Self {
ba: [gain, -gain * zero, -(1.0 - pole)].map(Q32::from_f32),
}
}
pub fn from_bandwidth(bw: f32, split: f32) -> Self {
let a = bw * 2.0 * core::f32::consts::PI;
let z = 1.0 - a / split;
let p = 1.0 - a * split;
let k = -a * a * split;
Self::from_zpk(z, p, k)
}
}
#[derive(Debug, Clone, Default)]
pub struct PLLState {
pub clamp: ClampWrap<W<i32>>,
pub z0: i32,
pub y0: i32,
pub f0: i64,
pub f: W<i64>,
pub y: W<i32>,
}
impl PLLState {
pub fn phase(&self) -> W<i32> {
self.y
}
pub fn frequency(&self) -> W<i32> {
W((self.f.0 >> 32) as _)
}
}
impl SplitProcess<W<i32>, W<i32>, PLLState> for PLL {
fn process(&self, state: &mut PLLState, x: W<i32>) -> W<i32> {
state.y += state.frequency();
let z0 = state.clamp.process(x + state.y).0 >> 1;
let y0 = z0 + state.z0;
state.z0 = z0;
state.f0 +=
(self.ba[0] * y0 + self.ba[1] * state.y0 + self.ba[2] * (state.f0 >> 32) as i32)
.into_bits()
+ ((self.ba[2].into_bits() as i64 * state.f0 as u32 as i64) >> 32);
state.y0 = y0;
state.f += W(state.f0);
state.y
}
}
#[cfg(test)]
mod tests {
use crate::Accu;
use super::*;
use core::num::Wrapping as W;
#[test]
fn converge_pll() {
let p = PLL::from_bandwidth(5e-2, 4.0);
println!("{p:?}");
let mut s = PLLState::default();
let a = Accu::<W<i32>>::new(W(0x0), W(0x71f63049));
let n = 1 << 9;
for (i, x) in a.take(n).enumerate() {
let y = p.process(&mut s, x);
println!("x: {x:#010x} y+x: {:#010x}", y + x);
if i > n / 2 {
assert!((a.step + s.frequency()).0.abs() <= 1);
assert!((x + y).0.abs() <= 4);
}
}
}
#[test]
fn converge_narrow() {
let p = PLL::from_bandwidth(8e-5, 4.0);
println!("{p:?}");
let mut s = PLLState::default();
let a = Accu::<W<i32>>::new(W(0x0), W(0x140_1235));
let n = 1 << 18;
for (i, x) in a.take(n).enumerate() {
let y = p.process(&mut s, x);
println!("x: {x:#010x} y+x: {:#010x}", y + x);
if i > n / 2 {
assert!((a.step + s.frequency()).0.abs() <= 1 << 16);
assert!((x + y).0.abs() <= 1 << 16);
}
}
}
}