pll: add note on dithering
@@ -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);
}
}
