#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DcState {
Disabled,
Locking,
Locked,
LostSync,
}
#[derive(Debug, Clone, Copy)]
pub struct DcConfig {
pub sync0_cycle_ns: u32,
pub sync0_pulse_ns: u32,
pub sync1_cycle_ns: u32,
pub enabled: bool,
}
impl DcConfig {
pub fn at_1khz() -> Self {
Self {
sync0_cycle_ns: 1_000_000,
sync0_pulse_ns: 500_000,
sync1_cycle_ns: 0,
enabled: true,
}
}
pub fn at_4khz() -> Self {
Self {
sync0_cycle_ns: 250_000,
sync0_pulse_ns: 125_000,
sync1_cycle_ns: 0,
enabled: true,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DcSynchronizer {
pub system_time_ns: u64,
pub cycle_ns: u64,
pub max_drift_ns: u64,
pub drift_ns: i64,
drift_integrator: i64,
pub state: DcState,
locked_cycles: u32,
}
impl DcSynchronizer {
pub fn new(cycle_ns: u64, max_drift_ns: u64) -> Self {
Self {
system_time_ns: 0,
cycle_ns,
max_drift_ns,
drift_ns: 0,
drift_integrator: 0,
state: DcState::Disabled,
locked_cycles: 0,
}
}
pub fn enable(&mut self) {
self.state = DcState::Locking;
self.locked_cycles = 0;
}
pub fn update(&mut self, ref_time_ns: u64) -> i64 {
let expected = self.system_time_ns + self.cycle_ns;
self.drift_ns = ref_time_ns as i64 - expected as i64;
self.system_time_ns = ref_time_ns;
let kp = 64i64;
let ki = 1i64;
self.drift_integrator = self.drift_integrator.saturating_add(self.drift_ns * ki);
let correction = (self.drift_ns * kp + self.drift_integrator).saturating_div(1024);
match self.state {
DcState::Disabled => {}
DcState::Locking => {
if self.drift_ns.unsigned_abs() < self.max_drift_ns {
self.locked_cycles += 1;
if self.locked_cycles >= 10 {
self.state = DcState::Locked;
}
} else {
self.locked_cycles = 0;
}
}
DcState::Locked => {
if self.drift_ns.unsigned_abs() > self.max_drift_ns * 4 {
self.state = DcState::LostSync;
self.locked_cycles = 0;
}
}
DcState::LostSync => {
if self.drift_ns.unsigned_abs() < self.max_drift_ns {
self.state = DcState::Locking;
self.locked_cycles = 0;
}
}
}
-correction
}
pub fn is_synchronized(&self) -> bool {
self.state == DcState::Locked
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dc_config_1khz() {
let cfg = DcConfig::at_1khz();
assert_eq!(cfg.sync0_cycle_ns, 1_000_000);
assert!(cfg.enabled);
}
#[test]
fn dc_synchronizer_locks_on_perfect_timing() {
let mut dc = DcSynchronizer::new(1_000_000, 1_000);
dc.enable();
let mut t = 0u64;
for _ in 0..20 {
t += 1_000_000;
dc.update(t);
}
assert_eq!(dc.state, DcState::Locked);
}
#[test]
fn dc_synchronizer_lost_sync_on_large_drift() {
let mut dc = DcSynchronizer::new(1_000_000, 1_000);
dc.enable();
let mut t = 0u64;
for _ in 0..15 {
t += 1_000_000;
dc.update(t);
}
assert_eq!(dc.state, DcState::Locked);
dc.update(t + 1_000_000 + 10_000);
assert_ne!(dc.state, DcState::Locked);
}
}