use crate::Accu;
use core::num::Wrapping as W;
use dsp_process::SplitProcess;
#[derive(Copy, Clone, Default)]
pub struct RPLL {
x: W<i32>, ff: W<u32>, f: W<u32>, y: W<i32>, }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
pub struct RPLLConfig {
pub dt2: u8,
pub shift_frequency: u8,
pub shift_phase: u8,
}
impl SplitProcess<Option<W<i32>>, Accu<W<i32>>, RPLL> for RPLLConfig {
fn process(&self, state: &mut RPLL, x: Option<W<i32>>) -> Accu<W<i32>> {
debug_assert!(self.shift_frequency >= self.dt2);
debug_assert!(self.shift_phase >= self.dt2);
state.y += W(state.f.0 as i32);
if let Some(x) = x {
let dx = x - state.x;
state.x = x;
let p_sig_64 = W(state.ff.0 as u64 * dx.0 as u64);
let p_sig = W(((p_sig_64 + W((1u32 << (self.shift_frequency - 1)) as u64))
>> self.shift_frequency as usize)
.0 as _);
let p_ref = W(1u32 << (32 + self.dt2 - self.shift_frequency));
state.ff += p_ref - p_sig;
let dt = W((-x & W((1 << self.dt2) - 1)).0 as _);
let y_ref = W(((state.f >> self.dt2 as usize) * dt).0 as _);
let dy = (y_ref - state.y) >> (self.shift_phase - self.dt2) as usize;
state.f = state.ff + W(dy.0 as u32);
}
Accu::new(state.y, W(state.f.0 as _))
}
}
impl RPLL {
pub fn phase(&self) -> W<i32> {
self.y
}
pub fn frequency(&self) -> W<u32> {
self.f
}
}
#[cfg(test)]
mod test {
use super::{RPLL, RPLLConfig};
use core::num::Wrapping as W;
use dsp_process::SplitProcess;
use rand::{prelude::*, rngs::StdRng};
use std::vec::Vec;
#[test]
fn make() {
let _ = RPLL::default();
}
struct Harness {
rpll: RPLL,
rpll_config: RPLLConfig,
noise: i32,
period: i32,
next: W<i32>,
next_noisy: W<i32>,
time: W<i32>,
rng: StdRng,
}
impl Harness {
fn default() -> Self {
Self {
rpll: RPLL::default(),
rpll_config: RPLLConfig {
dt2: 8,
shift_frequency: 9,
shift_phase: 8,
},
noise: 0,
period: 333,
next: W(111),
next_noisy: W(111),
time: W(0),
rng: StdRng::seed_from_u64(42),
}
}
fn run(&mut self, n: usize) -> (Vec<f32>, Vec<f32>) {
assert!(self.period >= 1 << self.rpll_config.dt2);
assert!(self.period < 1 << self.rpll_config.shift_frequency);
assert!(self.period < 1 << self.rpll_config.shift_phase + 1);
let mut y = Vec::<f32>::new();
let mut f = Vec::<f32>::new();
for _ in 0..n {
let timestamp = if self.time - self.next_noisy >= W(0) {
assert!(self.time - self.next_noisy < W(1 << self.rpll_config.dt2));
self.next += self.period;
let timestamp = self.next_noisy;
let p_noise = self.rng.random_range(-self.noise..=self.noise);
self.next_noisy = self.next + W(p_noise);
Some(timestamp)
} else {
None
};
let _accu = self.rpll_config.process(&mut self.rpll, timestamp);
let (yi, fi) = (self.rpll.phase(), self.rpll.frequency());
let y_ref = W(
((self.time - self.next).0 as i64 * (1i64 << 32) / self.period as i64) as i32,
);
y.push((yi - y_ref).0 as f32 / 2f32.powi(32));
let p_ref = 1 << 32 + self.rpll_config.dt2;
let p_sig = fi.0 as u64 * self.period as u64;
f.push(
p_sig.wrapping_sub(p_ref) as i64 as f32
/ 2f32.powi(32 + self.rpll_config.dt2 as i32),
);
self.time += W(1 << self.rpll_config.dt2);
}
(y, f)
}
fn measure(&mut self, n: usize, limits: [f32; 4]) {
let t_settle = (1 << self.rpll_config.shift_frequency - self.rpll_config.dt2 + 4)
+ (1 << self.rpll_config.shift_phase - self.rpll_config.dt2 + 4);
self.run(t_settle);
let (y, f) = self.run(n);
let fm = f.iter().copied().sum::<f32>() / f.len() as f32;
let fs = f.iter().map(|f| (*f - fm).powi(2)).sum::<f32>().sqrt() / f.len() as f32;
let ym = y.iter().copied().sum::<f32>() / y.len() as f32;
let ys = y.iter().map(|y| (*y - ym).powi(2)).sum::<f32>().sqrt() / y.len() as f32;
println!("f: {:.2e}±{:.2e}; y: {:.2e}±{:.2e}", fm, fs, ym, ys);
let m = [fm, fs, ym, ys];
print!("relative: ");
for i in 0..m.len() {
let rel = m[i].abs() / limits[i].abs();
print!("{:.2e} ", rel);
assert!(
rel <= 1.,
"idx {}, have |{:.2e}| > limit {:.2e}",
i,
m[i],
limits[i]
);
}
println!();
}
}
#[test]
fn default() {
let mut h = Harness::default();
h.measure(1 << 16, [1e-11, 4e-8, 2e-8, 2e-8]);
}
#[test]
fn noisy() {
let mut h = Harness::default();
h.noise = 10;
h.rpll_config.shift_frequency = 23;
h.rpll_config.shift_phase = 22;
h.measure(1 << 16, [3e-9, 3e-6, 5e-4, 2e-4]);
}
#[test]
fn narrow_fast() {
let mut h = Harness::default();
h.period = 990;
h.next = W(351);
h.next_noisy = h.next;
h.noise = 5;
h.rpll_config.shift_frequency = 23;
h.rpll_config.shift_phase = 22;
h.measure(1 << 16, [2e-9, 2e-6, 1e-3, 1e-4]);
}
#[test]
fn narrow_slow() {
let mut h = Harness::default();
h.period = 1818181;
h.next = W(35281);
h.next_noisy = h.next;
h.noise = 1000;
h.rpll_config.shift_frequency = 23;
h.rpll_config.shift_phase = 22;
h.measure(1 << 16, [2e-5, 6e-4, 2e-4, 2e-4]);
}
#[test]
fn wide_fast() {
let mut h = Harness::default();
h.period = 990;
h.next = W(351);
h.next_noisy = h.next;
h.noise = 5;
h.rpll_config.shift_frequency = 10;
h.rpll_config.shift_phase = 9;
h.measure(1 << 16, [2e-6, 3e-2, 2e-5, 2e-2]);
}
#[test]
fn wide_slow() {
let mut h = Harness::default();
h.period = 1818181;
h.next = W(35281);
h.next_noisy = h.next;
h.noise = 1000;
h.rpll_config.shift_frequency = 21;
h.rpll_config.shift_phase = 20;
h.measure(1 << 16, [2e-4, 6e-3, 2e-4, 2e-3]);
}
#[test]
fn batch_fast_narrow() {
let mut h = Harness::default();
h.rpll_config.dt2 = 8 + 3;
h.period = 2431;
h.next = W(35281);
h.next_noisy = h.next;
h.noise = 100;
h.rpll_config.shift_frequency = 23;
h.rpll_config.shift_phase = 23;
h.measure(1 << 16, [1e-8, 2e-5, 6e-4, 6e-4]);
}
}