Skip to content

Sendspin/time-filter

Repository files navigation

Sendspin Time Filter

A Kalman filter-based time synchronization library for maintaining accurate client-server timestamp synchronization with microsecond-level precision.

A project from the Open Home Foundation

Overview

This library implements a reference two-dimensional Kalman filter that tracks clock offset and drift between client and server using NTP-style time message exchange. It provides adaptive synchronization with automatic recovery from network disruptions and clock adjustments.

Features

  • NTP-style time synchronization using 4-timestamp message exchange
  • Kalman filter for optimal offset and drift estimation
  • Adaptive forgetting factor for quick recovery from clock adjustments
  • Thread-safe time conversions and time updates
  • Microsecond-level precision using double-precision arithmetic

Core API

class SendspinTimeFilter {
 public:
  // Configuration struct (all fields have defaults)
  struct Config {
    double process_std_dev = 0.0;            // Offset random-walk diffusion (µs / sqrt(µs))
    double drift_process_std_dev = 1e-11;    // Drift random-walk diffusion (1 / sqrt(µs); drift itself is dimensionless)
    double forget_factor = 2.0;              // Forgetting factor (>1) for recovery from disruptions
    double adaptive_cutoff = 3.0;            // Multiple of max_error that triggers forgetting
    uint8_t min_samples = 100;               // Minimum samples before adaptive forgetting
    double drift_significance_threshold = 2.0;  // SNR threshold for drift compensation
    double max_error_scale = 0.5;            // Scale applied to max_error before Kalman update
  };

  // Constructors
  explicit SendspinTimeFilter(const Config &config);
  SendspinTimeFilter();  // Uses default Config
  // ...
};

// Update filter with computed offset and uncertainty from NTP exchange
// measurement: ((T2-T1)+(T3-T4))/2 in microseconds
// max_error: ((T4-T1)-(T3-T2))/2 in microseconds
// time_added: Client timestamp when measurement was taken in microseconds
void update(int64_t measurement, int64_t max_error, int64_t time_added);

// Convert between client and server timestamps
int64_t compute_server_time(int64_t client_time) const;
int64_t compute_client_time(int64_t server_time) const;

// Get Kalman offset covariance as a proxy for synchronization accuracy
int64_t get_error() const;

Recommended Usage

Best results come from a burst strategy at the call site (outside the library). Send a small burst of NTP-style exchanges sequentially; e.g., 8 messages back-to-back, each waiting for its reply, every ~10 seconds, then call update() with the sample that has the lowest max_error. Sendspin typically runs over an ordered TCP/WebSocket transport, so a delayed earlier message will also delay later ones in the burst, making the individual measurement delays not independent.

This approach speeds up convergence and gives better overall synchronization accuracy.

Recommended Values

The default Config values are based on preliminary experiments and provide good synchronization performance for typical network conditions:

SendspinTimeFilter filter;  // Uses default Config

// Or override specific fields:
SendspinTimeFilter::Config config;
config.forget_factor = 1.5;
SendspinTimeFilter filter(config);

The defaults balance tracking responsiveness with stability for typical network conditions.

Documentation

See docs/theory.md for detailed mathematical documentation of the Kalman filter implementation and time synchronization protocol.

About

Reference implementation for the Sendspin time synchronization filter

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages