time.clepore.com

About this server

Hardware, Software, and a Diagram.

Hardware

Host
Raspberry Pi Compute Module 5 (4 GB/16 GB EMMC/WiFi) on an Official CM5 IO Board, running DietPi.
GPS / RTC HAT
Uputronics Raspberry Pi GPS/RTC expansion board. The board features a u-blox MAX-M8Q multi-GNSS receiver attached to an external antenna and a RV-3028 RTC. NMEA arrives over the UART (/dev/ttyAMA0); the HAT's PPS signal on GPIO 18 is directly connected to J2 Pin 6 (the Ethernet PHY's PPS input pin) allowing for the NIC's PTP Hardware Clock to be disciplined directly, instead of the kernel reading the pulse off a GPIO pin.
Antenna
Bingfu GPS Antenna with SMA Male Connector
Network
Wired onboard Gigabit Ethernet. The CM5's PHY (BCM54210PE) exposes a PTP Hardware Clock and a hardware PPS input pin. That is what makes a hardware-timestamped PTP grandmaster possible here.
Storage
16gb eMMC with log2ram (128 MB), shielding the SD card from chrony's measurement logs.

Software

SatPulse
GPS-disciplined daemon (satpulsed) that reads NMEA and the PHY-pin PPS, disciplines the NIC's PTP Hardware Clock, feeds chrony through a SOCK refclock, and drives ptp4l.
PTP Time daemon
ptp4l (linuxptp): PTP grandmaster on eth0, the PHC-disciplined clock is fed by SatPulse over a Unix domain socket and serving IEEE-1588 compliant time to the LAN on UDP ports 319/320 with hardware timestamping.
NTP Time daemon
chrony: time-discipline mechanism on the host, fed by SatPulse and serving NTP to the LAN.
Reference clocks
refclock SOCK /var/run/chrony.satpulse.sock poll 2 filter 4 refid GNSS prefer (SatPulse-disciplined GNSS time), plus a public NTP pool for cold-start / outage fallback and sanity-check.
Kernel
dtparam=uart0=on enables the GPS UART and dtoverlay=i2c-rtc,rv3028 enables the RTC. A hardware watchdog (bcm2835_wdt) restarts the box if discipline ever deteriorates.

Precision Time Protocol

This box's primary function is to act as a PTP (IEEE-1588) grandmaster and server highly accurate time. satpulsed keeps the NIC's PTP Hardware Clock locked to GPS, and ptp4l distributes that hardware-timestamped time to the LAN over UDP 319/320.

# /etc/linuxptp/ptp4l.conf  (grandmaster on eth0)
          [global]
          time_stamping    hardware
          serverOnly       1          # linuxptp 3.x: masterOnly
          domainNumber     0
          uds_address      /var/run/ptp4l   # SatPulse pushes the disciplined clock here

          # /etc/satpulse.toml
          [phc]
          interface        = "eth0"
          [ntp]
          sock.path        = "/var/run/chrony.satpulse.sock"
          [ptp]
          ptp4l.udsAddress = "/var/run/ptp4l"

Reference clock chain

GPS satellites broadcast time tied to a UTC offset table. The MAX-M8Q recovers that time and emits a hardware pulse on the start of every UTC second (the PPS signal). That pulse travels from GPIO 18 to J2 Pin 6 (the Ethernet PHY's PPS input) where satpulsed reads it through the NIC's PHC, using the NMEA stream only to label which second the pulse belongs to. From there the disciplined clock fans out to chrony (NTP) and ptp4l (PTP).

GPS constellation
        │  (radio: time-of-week + sat ephemerides)
        ▼
[ MAX-M8Q receiver ]
   NMEA → /dev/ttyAMA0            ─┐  (which second?)
   PPS  → GPIO18 → J2 Pin6 (PHY)  ─┴─►  satpulsed → disciplines eth0 PHC
                                              │  (when exactly?)
              ┌───────────────────────────────┤
              ▼                                ▼
   chrony SOCK refclock (GNSS)        ptp4l (uds)
              │                                │
              ▼  NTP/123 over LAN              ▼  PTP 319/320 over LAN
        [ LAN clients ]                  [ PTP clients ]

Inspiration & credit

The PPS-via-PHY discipline this server depends on runs on SatPulse (the satpulsed daemon above), built by JClark. And the whole project was inspired by Jeff Geerling and his videos on precision time. Thank you both.