Skip to content

ntpstats.ptpsim

PTP servos and chains of boundary clocks, simulated with ground truth (#29).

A telecom or datacenter timing chain is a grandmaster followed by N boundary clocks (T-BC), each a PTP slave on its upstream port and a master on its downstream port. This module simulates such a chain sample by sample:

  • every node has its own free-running oscillator (:class:ClockModel);
  • every link has a delay, an asymmetry (master→slave minus slave→master, the source of constant time error that no servo can see), per-timestamp noise, and optionally packet delay variation, either modelled (:class:PathModel, for links through switches without PTP support) or replayed from a real capture (:class:ntpstats.trace.TracePath);
  • every slave runs the E2E delay mechanism with a moving-median delay filter and a servo that steers its clock's frequency.

Two servos are provided:

:class:PIServo The proportional-integral servo of linuxptp (pi.c): the first sample stores the offset, the second estimates the frequency error and steps the clock if the offset exceeds first_step_threshold, then freq = -(kp·offset + Σ ki·offset). The gains follow linuxptp's defaults for hardware time stamping: kp = min(0.7·Ts^-0.3, 0.7/Ts), ki = min(0.3·Ts^0.4, 0.3/Ts) for a sync interval Ts. :class:LinRegServo Adaptive-window linear regression, after linuxptp's linreg.c: a line through the recent free-running phase (offset minus the corrections already applied) predicts the phase at the next sync, and the frequency is set to bring it to zero. The window (4 to 64 samples) is the one with the smallest prediction variance.

The result is the time error of every node relative to the grandmaster, with the time-error metrics of :mod:ntpstats.timeerror per node and per hop, so a chain design can be checked against a time-error budget: per-hop limits (for example the T-BC classes of ITU-T G.8273.2) and an end-to-end limit (for example 1.1 µs, the G.8271.1 network limit for 1.5 µs applications). These are models: they show how servo choice, sync rate, asymmetry and noise accumulate along a chain, not how a particular product behaves.

PIServo dataclass

linuxptp-style PI servo; offsets in seconds (local − master), frequency in s/s.

lock_time(interval)

Rough time for a chain of these loops to settle: 25 time constants of the slowest pole of the continuous-time loop s² + kp·s + ki/Ts.

sample(offset, t)

Return (frequency to set, step the clock by -offset now?).

LinRegServo dataclass

Adaptive-window linear-regression servo (after linuxptp's linreg).

One PTP link (master port to slave port).

asymmetry: master→slave minus slave→master delay (s); it adds asymmetry/2 of constant time error per hop. timestamp_noise: rms of each of the four timestamps (hardware time stamping: a few ns). pdv: a :class:PathModel for the queueing of each direction, for links through switches without PTP support. trace: a :class:ntpstats.trace.TracePath replaying a real capture instead.

ChainScenario dataclass

A grandmaster and hops boundary clocks in series.

ChainResult dataclass

series(node)

Time error of a node as a series (offset = reference − local, the ntpstats convention).

check(limits=None, cls='B', budget=NETWORK_LIMIT)

Per-hop limits (limits or a G.8273.2 cls) and the end-to-end budget on max|TE|.

make_servo(name, **options)

A servo by name; options are its fields (e.g. kp, ki of :class:PIServo).

lock_time(sc)

Estimated settling time of the chain's servos (s).

simulate_chain(sc=None, metrics=True)

Run a chain; te[i] is the time error of node i (0 = grandmaster).

chain_from_dict(d, base_dir='.')

A :class:ChainScenario from a dict (TOML [chain], [link], [oscillator] tables).