A Raspberry Pi stratum 1 time server.
Takes in GPS (or potentially other stratum 0 time sources), spits out NTP, PTP, etc.
To run a decent time server (with high accuracy), you need a few things:
- A computer running Linux (all the nice tooling for network time is availabe here)
- A high quality time source (GPS is most commonly used these days)
- A network adapter capable of hardware timestamping (Intel and ASIX make some good NICs for time-related applications)
There are many options for each of the above items—for example, many use Adafruit's Ultimate GPS HAT or its USB equivalent for GPS acquisition, and for PTP, you can use a Compute Module 4 or 5's built-in NIC (with PPS in or out), or add on a compatible NIC on the Pi 5 with something like the uPCIty Lite.
My own hardware configuration—which is the basis for the code in this repository, consists of:
- Computer: Raspberry Pi 5 model B
- Time source: u-blox ZED-F9T-00B-01 (installed on TimeHAT V4)
- NIC: Intel i226-LM (installed on TimeHAT V4)
A precise GPS signal for nanosecond-accurate time requires a decent antenna with as clear a view of the sky as possible. Some GPS receivers are better than others, but even USB receivers will do better than NTP!
I use a small active GPS antenna for testing, but for permanent installation it is best to mount a higher-quality GPS antenna outside, clear of obstructions.
Make sure you have Ansible installed. Copy the following example files and customize them according to your setup:
example.hosts.ini
tohosts.ini
example.config.yml
toconfig.yml
Now run the Ansible playbook:
ansible-playbook main.yml
This playbook will configure:
- GPSd: interface with the u-blox GPS and provide GPS data to other applications
- Chrony: NTP server, which also sync GPS time (with Internet NTP server backup) to the system clock
- Linux PTP: synchronize the system clock to the NIC PHC (Physical Hardware Clock), and set up the Pi as a PTP grandmaster clock
Intel i226 Notes: Currently I can't get the i226 to work with DHCP at all, so I have to manually set an IP address using
nmtui
. It also doesn't work at 2.5 Gbps currently, and it can't be overridden via Linux, so I make sure to plug it into a 1 Gbps port on my network.
Using u-blox GPS modules, you may encounter a baud rate mismatch. Many of the u-blox modules default to 38400
baud, but this project recommends 115200
baud for slightly faster timing updates.
# Get the protocol version ('PROTVER')
ubxtool -p MON-VER
...
UBX-MON-VER:
swVersion EXT CORE 4.04 (7f89f7)
hwVersion 00190000
extension ROM BASE 0x118B2060
extension FWVER=SPG 4.04
extension PROTVER=32.01
...
# Set the version in ubxtool options
export UBXOPTS="-P 32.01"
# Set the baud rate to 115200
ubxtool -S 115200
# Persist the setting
ubxtool -p SAVE
KNOWN ISSUE: The baud setting is currently not persisting across reboots. See this GitHub issue for updates.
ubxtool
is installed as part of the gpsd-clients
package, which is automatically installed by this playbook.
For more on how to set the baud rate (or tweak other GPS module parameters), see millerjs.org's ubxtool page and the ubxtool examples page.
Some handy commands:
# GPS-related debugging
sudo systemctl status gpsd # check gpsd status
gpsmon -n # monitors gpsd output
cgps -s # also monitors gps output
# PTP timestamping debugging
ethtool -T eth0 # or eth1, lists hardware clock info
# Chrony debugging
chronyc sources -v # shows sources with documentation of fields
chronyc tracking # shows detailed timing data
# Check on NTP service from another computer
ntpdate -q [ip of grandmaster]
# Check on PTP clocks and offsets (assuming ptp4l is running)
wget https://tsn.readthedocs.io/_downloads/f329e8dec804247b1dbb5835bd949e6f/check_clocks.c
gcc -o check_clocks check_clocks.c
sudo ./check_clocks -d eth0 # or eth1 (the interface you're using for PTP)
# Check offset between NIC PHY and system clock
sudo phc_ctl eth1 cmp # should be nearly -37000000000ns
Much of the work that went into this project was documented in this thread on the TimeHat v2.
Like cellular modems, GPS modules can be a bit tricky, using arcane syntaxes and custom protocols for communication.
For the NEO-M9N module, the default baud
rate is a little low for my liking, but to get it working, I had to go through a lengthy process learning ubxtool
.
To configure my module for 115200
baud, I did the following:
# Set the baud rate
ubxtool -S 115200
# Save the settings (get the `-P` PROTVER with `ubxtool -p MON-VER`)
$ ubxtool -p SAVE -P 32.01
# Update the rate in your GPSd config and restart `gpsd`
sudo nano /etc/default/gpsd
sudo systemctl restart gpsd
# Test the settings by rebooting the GPS module manually.
$ ubxtool -p COLDBOOT -P 32.01
See this issue for more: Debug NEO-M9N module on TimeHAT V2.
For PTP, you need to install and configure PTP for Linux on slave/client machines, and synchronize them to the master/server node as well.
An example configuration for a slave/client node is set up in ptp-client-node.yml
, and further examples may be provided in the future.
- LeapSecond.com
- Austin's Nerdy Things: Nanosecond accurate PTP Pi server
- Andreas Spiess: NTP Server from GPS Satellites
- [Jeff Geerling: Time Card mini for GPS and OXCO on Pi](Time Card mini adds Pi, GPS, and OCXO to your PC)
- Where does my computer get the time from? (good overview of the sources of modern NTP + GPS time, with the history of each source)
GPLv3 or Later
Jeff Geerling, with assistance from Ahmad Byagowi and Oleg Obleukhov from Meta.