extern crate rand;
extern crate rand_distr;
use self::rand::thread_rng;
use self::rand_distr::{Distribution, Normal};
pub struct ClockNoise {
dist: Normal<f64>,
span: f64,
}
impl ClockNoise {
fn with_ppm_over(ppm: f64, span: f64) -> ClockNoise {
ClockNoise {
dist: Normal::new(0.0, ppm / span * 1e-6).unwrap(),
span,
}
}
pub fn with_ppm(ppm: f64) -> ClockNoise {
ClockNoise::with_ppm_over(ppm, 1.0)
}
pub fn with_ppm_over_1sec(ppm: f64) -> ClockNoise {
ClockNoise::with_ppm_over(ppm, 1.0)
}
pub fn with_ppm_over_1min(ppm: f64) -> ClockNoise {
ClockNoise::with_ppm_over(ppm, 60.0)
}
pub fn with_ppm_over_15min(ppm: f64) -> ClockNoise {
ClockNoise::with_ppm_over(ppm, 900.0)
}
pub fn noise_up(&self, duration_in_secs: f64) -> f64 {
let mut nl_secs = duration_in_secs;
let mut drift: f64 = 0.0;
while nl_secs > 0.0 {
drift += self.dist.sample(&mut thread_rng());
nl_secs -= self.span;
}
duration_in_secs + drift
}
pub fn sample(&self, value: f64) -> f64 {
value + self.dist.sample(&mut thread_rng())
}
}
#[test]
fn clock_noise_up() {
let clock_1ppm_1s = ClockNoise::with_ppm_over_1sec(1.0);
let clock_1ppm_1m = ClockNoise::with_ppm_over_1min(1.0);
let clock_1ppm_15m = ClockNoise::with_ppm_over_15min(1.0);
let mut err_1s = 0;
let mut err_1m = 0;
let mut err_15m = 0;
for _ in 0..100 {
if (clock_1ppm_1s.noise_up(1.0) - 1.0).abs() > 1.0 {
err_1s += 1;
}
if (clock_1ppm_1m.noise_up(60.0) - 60.0).abs() > 60.0 {
err_1m += 1;
}
if (clock_1ppm_15m.noise_up(900.0) - 900.0).abs() > 900.0 {
err_15m += 1;
}
}
assert!(
err_1s <= 1,
"Clock drift greater than span {:} times over 100 draws (1s)",
err_1s
);
assert!(
err_1m <= 1,
"Clock drift greater than span {:} times over 100 draws (1m)",
err_1m
);
assert!(
err_15m <= 1,
"Clock drift greater than span {:} times over 100 draws (15m)",
err_15m
);
}
#[test]
fn clock_sample() {
let sc_clk = ClockNoise::with_ppm(2.0);
let mut sum = 0.0;
let cnt = 100000;
let freq = 2.3e9;
for _ in 0..cnt {
sum += sc_clk.sample(freq);
}
let variation = freq * 2.0e-6;
let mean = sum / cnt as f64;
println!("mean: {}", mean);
assert!((mean - freq).abs() < variation);
}