Chrony GPS NTP Server¶
The ntppool namespace runs a stratum-1 NTP server backed by a GPS receiver with PPS (Pulse Per Second) output, connected via USB to one of the cluster nodes.
Architecture¶
flowchart LR
gps["GPS receiver\nTTL UART 115200\nmulti-constellation\n(GP/GL/GA/BD)"]
ft232["FT232R USB-UART\n(ttyUSB0 in pod)"]
gpsd["gpsd\nSHM 0: NMEA\nSHM 1: PPS (TIOCMIWAIT)"]
chrony["chronyd\nSHM 0: NMEA (falseticker)\nSHM 1: PPS (primary)"]
clients["NTP pool clients\nstratum 2"]
gps -->|"NMEA + PPS on DCD pin"| ft232 --> gpsd
gpsd -->|"SHM 0 + SHM 1"| chrony
chrony -->|"NTP port 123"| clients
Key Configuration¶
refclock SHM 0 refid NMEA precision 1e-1
refclock SHM 1 refid PPS precision 1e-7 offset -0.100 lock NMEA
The offset -0.100 corrects for the FT232R's 100 ms early-fire characteristic on the PPS signal.
Current Status¶
| Source | Offset | Role |
|---|---|---|
| NMEA (SHM 0) | ~+118ms | Falseticker — used only for second identification |
| PPS (SHM 1) | ~-7µs ±9µs | Primary stratum-1 source |
Key Findings¶
- gpsd SHM timestamp inversion — gpsd 3.26.1 writes
clockTimestamp=system_timeandreceiveTimestamp=GPS_time, opposite of the NTP SHM spec. This is intentional and well-known. - Do not use ldattach / N_PPS ldisc — it drops all NMEA serial data on the same port. gpsd's TIOCMIWAIT-based PPS (SHM 1) is the correct approach.
- Startup order matters — gpsd must start first (
gpsd & sleep 3 && exec chronyd). Reversed order causes zombie accumulation and stale SHM slots. - gpsd SHM 2/3 not written — only SHM 0 (NMEA) and SHM 1 (PPS) are populated by gpsd 3.26.1.
Deployment Notes¶
The pod runs in the ntppool namespace on node qui (which has the USB GPS receiver). It participates in the public NTP Pool Project, serving stratum-2 time to pool clients via PureLB VIP 192.168.1.48.