diff --git a/dsp/src/pll.rs b/dsp/src/pll.rs index 8f060c2..5ee054d 100644 --- a/dsp/src/pll.rs +++ b/dsp/src/pll.rs @@ -10,9 +10,8 @@ use serde::{Deserialize, Serialize}; /// stable for any gain (1 <= shift <= 30). It has a single parameter that determines the loop /// bandwidth in octave steps. The gain can be changed freely between updates. /// -/// The frequency settling time constant for an (any) frequency jump is `1 << shift` update cycles. -/// The phase settling time in response to a frequency jump is about twice that. The loop bandwidth -/// is about `1/(2*pi*(1 << shift))` in units of the sample rate. +/// The frequency and phase settling time constants for an (any) frequency jump are `1 << shift` +/// update cycles. The loop bandwidth is about `1/(2*pi*(1 << shift))` in units of the sample rate. /// /// All math is naturally wrapping 32 bit integer. Phase and frequency are understood modulo that /// overflow in the first Nyquist zone. Expressing the IIR equations in other ways (e.g. single @@ -20,7 +19,8 @@ use serde::{Deserialize, Serialize}; /// /// There are no floating point rounding errors here. But there is integer quantization/truncation /// error of the `shift` lowest bits leading to a phase offset for very low gains. Truncation -/// bias is applied. Rounding is "half up". +/// bias is applied. Rounding is "half up". The phase truncation error can be removed very +/// efficiently by dithering. /// /// This PLL does not unwrap phase slips during lock acquisition. This can and should be /// implemented elsewhere by (down) scaling and then unwrapping the input phase and (up) scaling @@ -89,6 +89,7 @@ mod tests { assert_eq!(f.wrapping_sub(f0).abs() <= 1, true); } if i > n / 2 { + // The remaining error is removed by dithering. assert_eq!(y.wrapping_sub(x).abs() < 1 << 18, true); } }