I set up a GPS-reference Stratum-1 NTP server at home.

This was a neat project, but rather painful. Not sure I'd recommend it, honestly, but I learned a lot. See thread for some learnings and rantings.

1/x

#ntp #time #homelab #selfhost

First, it's not "GPS" that you're having Chrony (or NTPd) syncing to. It's 1 Pulse-Per-Second (PPS), which is why my screenshot says PPS is my reference clock.

GPS delivers "NMEA stanzas" over a serial connection. Which means we're talking over (usually slow) serial baud rates, over which you receive "the time is now X", along with many other messages and have to guess at how long ago that timestamp was generated, since it takes many milliseconds to receive.

2/x

You need a GPS that can also deliver PPS on a GPIO pin, or separate serial port. PPS uses a voltage rise (that causes a kernel interrupt, providing accuracy) once per second to get a measure of accurate time, in nanoseconds range, which is zero point <7-9 zeros> seconds (compared to milliseconds accuracy for GPS).

That gets the time into GPSd. Getting the time into Chrony (or NTPd) was also quite painful (for me, at least).

3/x

Chrony and GPSd use shared memory to communicate, which requires the daemons to start in a specific order, otherwise they don't talk to eachother.

GPSd is supposed to send GPS on SHM0 (shared memory slot 0) and PPS on SHM1... which didn't work for me. I got GPS fine, but no PPS data. No idea why. GPSd definitely has PPS info, but wasn't sending it.

4/x

I tried having Chrony use SHM0 for GPS, and reading the /dev/pps0 device directly for PPS... which would work fine for several hours, then PPS data would just randomly stop. Weird. Meanwhile, GPSd still has a running PPS feed.

What finally worked for me was getting GPS via SHM0 from GPSd, and PPS via socket from GPSd. That has been stable for a couple days now. But that doesn't work if I "lock" PPS to GPS. Weird.

5/x

The config for sockets is also weird. You need to tell Chrony that its reference clock is /run/chrony.<device>.sock (for me, /run/chrony.pps0.sock), a file that does not exist, which Chrony creates.

GPSd, when it starts, without any config, looks for that socket, and if it exists, communicates with it (connects as root, then sheds privileges).

6/7

These communication methods are all backwards black magic voodo nonsense. The receiver (Chrony) creates sockets (or shared memory) that the sender (GPSd) guesses the names of, and uses root privileges to send to / or write into its memory space. Utterly deranged.

7/7