1use crate::Accu;
2use core::num::Wrapping as W;
3use dsp_process::SplitProcess;
4
5#[derive(Copy, Clone, Default)]
13pub struct RPLL {
14 x: W<i32>, ff: W<u32>, f: W<u32>, y: W<i32>, }
19
20#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
22pub struct RPLLConfig {
23 pub dt2: u8,
25 pub shift_frequency: u8,
31 pub shift_phase: u8,
35}
36
37impl SplitProcess<Option<W<i32>>, Accu<W<i32>>, RPLL> for RPLLConfig {
38 fn process(&self, state: &mut RPLL, x: Option<W<i32>>) -> Accu<W<i32>> {
47 debug_assert!(self.shift_frequency >= self.dt2);
48 debug_assert!(self.shift_phase >= self.dt2);
49 state.y += W(state.f.0 as i32);
51 if let Some(x) = x {
52 let dx = x - state.x;
54 state.x = x;
56 let p_sig_64 = W(state.ff.0 as u64 * dx.0 as u64);
58 let p_sig = W(((p_sig_64 + W((1u32 << (self.shift_frequency - 1)) as u64))
60 >> self.shift_frequency as usize)
61 .0 as _);
62 let p_ref = W(1u32 << (32 + self.dt2 - self.shift_frequency));
64 state.ff += p_ref - p_sig;
66 let dt = W((-x & W((1 << self.dt2) - 1)).0 as _);
68 let y_ref = W(((state.f >> self.dt2 as usize) * dt).0 as _);
70 let dy = (y_ref - state.y) >> (self.shift_phase - self.dt2) as usize;
72 state.f = state.ff + W(dy.0 as u32);
74 }
75 Accu::new(state.y, W(state.f.0 as _))
76 }
77}
78
79impl RPLL {
80 pub fn phase(&self) -> W<i32> {
82 self.y
83 }
84
85 pub fn frequency(&self) -> W<u32> {
87 self.f
88 }
89}
90
91#[cfg(test)]
92mod test {
93 use super::{RPLL, RPLLConfig};
94 use core::num::Wrapping as W;
95 use dsp_process::SplitProcess;
96 use rand::{prelude::*, rngs::StdRng};
97 use std::vec::Vec;
98
99 #[test]
100 fn make() {
101 let _ = RPLL::default();
102 }
103
104 struct Harness {
105 rpll: RPLL,
106 rpll_config: RPLLConfig,
107 noise: i32,
108 period: i32,
109 next: W<i32>,
110 next_noisy: W<i32>,
111 time: W<i32>,
112 rng: StdRng,
113 }
114
115 impl Harness {
116 fn default() -> Self {
117 Self {
118 rpll: RPLL::default(),
119 rpll_config: RPLLConfig {
120 dt2: 8,
121 shift_frequency: 9,
122 shift_phase: 8,
123 },
124 noise: 0,
125 period: 333,
126 next: W(111),
127 next_noisy: W(111),
128 time: W(0),
129 rng: StdRng::seed_from_u64(42),
130 }
131 }
132
133 fn run(&mut self, n: usize) -> (Vec<f32>, Vec<f32>) {
134 assert!(self.period >= 1 << self.rpll_config.dt2);
135 assert!(self.period < 1 << self.rpll_config.shift_frequency);
136 assert!(self.period < 1 << self.rpll_config.shift_phase + 1);
137
138 let mut y = Vec::<f32>::new();
139 let mut f = Vec::<f32>::new();
140 for _ in 0..n {
141 let timestamp = if self.time - self.next_noisy >= W(0) {
142 assert!(self.time - self.next_noisy < W(1 << self.rpll_config.dt2));
143 self.next += self.period;
144 let timestamp = self.next_noisy;
145 let p_noise = self.rng.random_range(-self.noise..=self.noise);
146 self.next_noisy = self.next + W(p_noise);
147 Some(timestamp)
148 } else {
149 None
150 };
151 let _accu = self.rpll_config.process(&mut self.rpll, timestamp);
152 let (yi, fi) = (self.rpll.phase(), self.rpll.frequency());
153
154 let y_ref = W(
155 ((self.time - self.next).0 as i64 * (1i64 << 32) / self.period as i64) as i32,
156 );
157 y.push((yi - y_ref).0 as f32 / 2f32.powi(32));
159
160 let p_ref = 1 << 32 + self.rpll_config.dt2;
161 let p_sig = fi.0 as u64 * self.period as u64;
162 f.push(
164 p_sig.wrapping_sub(p_ref) as i64 as f32
165 / 2f32.powi(32 + self.rpll_config.dt2 as i32),
166 );
167
168 self.time += W(1 << self.rpll_config.dt2);
170 }
171 (y, f)
172 }
173
174 fn measure(&mut self, n: usize, limits: [f32; 4]) {
175 let t_settle = (1 << self.rpll_config.shift_frequency - self.rpll_config.dt2 + 4)
176 + (1 << self.rpll_config.shift_phase - self.rpll_config.dt2 + 4);
177 self.run(t_settle);
178
179 let (y, f) = self.run(n);
180 let fm = f.iter().copied().sum::<f32>() / f.len() as f32;
183 let fs = f.iter().map(|f| (*f - fm).powi(2)).sum::<f32>().sqrt() / f.len() as f32;
184 let ym = y.iter().copied().sum::<f32>() / y.len() as f32;
185 let ys = y.iter().map(|y| (*y - ym).powi(2)).sum::<f32>().sqrt() / y.len() as f32;
186
187 println!("f: {:.2e}±{:.2e}; y: {:.2e}±{:.2e}", fm, fs, ym, ys);
188
189 let m = [fm, fs, ym, ys];
190
191 print!("relative: ");
192 for i in 0..m.len() {
193 let rel = m[i].abs() / limits[i].abs();
194 print!("{:.2e} ", rel);
195 assert!(
196 rel <= 1.,
197 "idx {}, have |{:.2e}| > limit {:.2e}",
198 i,
199 m[i],
200 limits[i]
201 );
202 }
203 println!();
204 }
205 }
206
207 #[test]
208 fn default() {
209 let mut h = Harness::default();
210
211 h.measure(1 << 16, [1e-11, 4e-8, 2e-8, 2e-8]);
212 }
213
214 #[test]
215 fn noisy() {
216 let mut h = Harness::default();
217 h.noise = 10;
218 h.rpll_config.shift_frequency = 23;
219 h.rpll_config.shift_phase = 22;
220
221 h.measure(1 << 16, [3e-9, 3e-6, 5e-4, 2e-4]);
222 }
223
224 #[test]
225 fn narrow_fast() {
226 let mut h = Harness::default();
227 h.period = 990;
228 h.next = W(351);
229 h.next_noisy = h.next;
230 h.noise = 5;
231 h.rpll_config.shift_frequency = 23;
232 h.rpll_config.shift_phase = 22;
233
234 h.measure(1 << 16, [2e-9, 2e-6, 1e-3, 1e-4]);
235 }
236
237 #[test]
238 fn narrow_slow() {
239 let mut h = Harness::default();
240 h.period = 1818181;
241 h.next = W(35281);
242 h.next_noisy = h.next;
243 h.noise = 1000;
244 h.rpll_config.shift_frequency = 23;
245 h.rpll_config.shift_phase = 22;
246
247 h.measure(1 << 16, [2e-5, 6e-4, 2e-4, 2e-4]);
248 }
249
250 #[test]
251 fn wide_fast() {
252 let mut h = Harness::default();
253 h.period = 990;
254 h.next = W(351);
255 h.next_noisy = h.next;
256 h.noise = 5;
257 h.rpll_config.shift_frequency = 10;
258 h.rpll_config.shift_phase = 9;
259
260 h.measure(1 << 16, [2e-6, 3e-2, 2e-5, 2e-2]);
261 }
262
263 #[test]
264 fn wide_slow() {
265 let mut h = Harness::default();
266 h.period = 1818181;
267 h.next = W(35281);
268 h.next_noisy = h.next;
269 h.noise = 1000;
270 h.rpll_config.shift_frequency = 21;
271 h.rpll_config.shift_phase = 20;
272
273 h.measure(1 << 16, [2e-4, 6e-3, 2e-4, 2e-3]);
274 }
275
276 #[test]
277 fn batch_fast_narrow() {
278 let mut h = Harness::default();
279 h.rpll_config.dt2 = 8 + 3;
280 h.period = 2431;
281 h.next = W(35281);
282 h.next_noisy = h.next;
283 h.noise = 100;
284 h.rpll_config.shift_frequency = 23;
285 h.rpll_config.shift_phase = 23;
286
287 h.measure(1 << 16, [1e-8, 2e-5, 6e-4, 6e-4]);
288 }
289}