If you're as excited about precision time keeping as I am, then setting up your own Stratum 1 time source may have occurred to you at some point. If not, then it still makes for a fairly interesting project!
What even is a Stratum?
The Network Time Protocol(NTP) uses a series of layers, or strata, of time sources.
Starting at the top, we have stratum 0, these are high precision time-keeping devices such as atomic clocks, GPS clocks, or radio clocks. They are also known as reference clocks.
These stratum 0 devices are in turn connected to stratum 1 devices, these are usually computers whose system clocks are synchronized to within microseconds of stratum 0. Their job is to provide accurate time signals to other machines over a network. They are also known as primary time servers.
Stratum 2 servers are computers receive time signals from stratum 1 servers over a network. They may query several stratum 1 servers and stratum 2 servers for sanity and stability.
Stratum 3 servers receive their time signal from a stratum 2 servers, stratum 4 servers receive their time signal from a stratum 3 servers, and so on. The stratum of a time server is always 1 higher than the stratum of the time source it is synchronized to.
The highest stratum is stratum 15, and stratum 16 is used to denote an unsynchronized time source.
So, by connecting our Raspberry Pi to a stratum 0 time source (GPS), it becomes a stratum 1 time source.
What do we need?
- A Raspberry Pi (If you're using a Pi 3, make sure to correct the UART baud rate first!)
- A GPS module with PPS output (I will elaborate on this later), such as the Adafruit Ultimate GPS Breakout
- A clear view of the sky, to receive a decent GPS signal (ideally with an extenal antenna)
- Install the required packages with
sudo apt-get install picocom pps-tools ntp
Setting up the hardware
This is the easy bit. Connect up the GPS module to the Pi's GPIO header like so:
- Connect VIN on the module to pin 4 (5V) on the Pi
- Connect GND on the module to pin 6 (GND) on the Pi
- Connect RX on the module to pin 8 (TXD0) on the Pi
- Connect TX on the module to pin 10 (RXD0) on the Pi
- Connect PPS on the module to pin 12 (GPIO18) on the Pi
Make sure the levels coming out of your GPS module are 3.3V safe! If you're using the Adafruit module everything should be fine.
Configuring the Pi
The first thing we need to do is to stop linux putting a serial console on the UART. To do this we need to edit the kernel arguments with
sudo nano /boot/cmdline.txt. For the Pi/Pi 2 remove the section
console=ttyAMA0,115200, for the Pi 3, remove the section
Next, we need to get the Pi to load the
pps-gpio kernel module on boot with
sudo sh -c "echo pps-gpio >> /etc/modules"
And then tell it to use GPIO18 as a PPS input with
sudo sh -c "echo dtoverlay=pps-gpio,gpiopin=18 >> /boot/config.txt"
If we reboot at this point, we can check everything is working as expected. If we open a serial console with
picocom -b 9600 -f n /dev/ttyS0 (replace
ttyAMA0 on Pi/Pi 2), we should see some output from the GPS module, specifically we are looking for lines that start with
If you have a garbled mess, your GPS module may be set at a different baud. If you don't see any
$GPRMC messages, you need to configure your module to output them.
Next, we need to check our PPS has been configured correctly. Make sure you GPS module has a fix (usually indicated by an LED), and execute the command
sudo ppstest /dev/pps0. You should get an output similar to below:
~ $ sudo ppstest /dev/pps0 trying PPS source "/dev/pps0" found PPS source "/dev/pps0" ok, found 1 source(s), now start fetching data... source 0 - assert 1457361736.999999817, sequence: 4986 - clear 0.000000000, sequence: 0 source 0 - assert 1457361737.999997945, sequence: 4987 - clear 0.000000000, sequence: 0
Finally, we need to configure udev to create some symlinks in
/dev/ to make our gps serial and PPS interfaces easier to find. Create a new file with
sudo nano /etc/udev/rules.d/10-pps.rules with the following contents:
KERNEL=="ttyS0", SYMLINK+="gps0" KERNEL=="pps0", OWNER="root", GROUP="tty", MODE="0660", SYMLINK+="gpspps0"
For the Pi/Pi 2, replace
What's the big deal with PPS?
You may have been wondering why I keep going on about PPS.
Well, imagine you ask your friend the time, and they reply "It's 14:53". This is fine for a rough idea, but you have no idea when that minute started. It could be 14:53:00, or 14:53:59. Similarly, it's all very well your GPS module telling you the time is 14:53:42, but you have no way of knowing precisely when that second started.
This is what PPS is for.
The PPS signal specifies EXACTLY when a new UTC second starts. This allows for incredibly accurate timekeeping, down to the microsecond, rather than to the nearest second.
Getting an ntpd with PPS support
The version of ntpd that ships with the Pi doesn't include PPS (or ATOM, as the clock is called) support. So, we must compile our own!
First, we need to enable the source repositories with
sudo nano /etc/apt/sources.list, and then uncomment the line containing
deb-src. Then run
sudo apt-get update to get the package lists.
We're not going to get the source for the
ntp package this way, but we do need the required build tools, so install them with
sudo apt-get build-dep ntp.
Grab the latest (at time of writing) version of
ntp from the project home page, extract it, and change directory into the source directory:
wget http://archive.ntp.org/ntp4/ntp-4.2/ntp-4.2.8p6.tar.gz tar zxvf ntp-4.2.8p6.tar.gz cd ntp-4.2.8p6
If you're using a Pi 2/3, configure with
./configure --enable-linuxcaps --prefix=/usr, else use
./configure --prefix=/usr. Wait for this to complete.
Then compile and install with
make sudo service ntp stop sudo make install
If you're using a Pi 2/3, you can use
make -j5 instead of just
make, but be sure that your Pi is plugged in to a 2A power supply, or it may reset mid-compile!
As we've just overwritten the package version of
ntp with our own compiled version, we need to stop
apt-get upgrade from overwriting it with
echo "ntp hold" | sudo dpkg --set-selections.
Stopping dhcpcd from interfering
Later Raspian versions ship with the
dhcpcd5 package. This has a nasty habit of interfering where it's not wanted. Specifically, in this instance, if your DHCP server specifies an NTP server, it will start
ntpd with it's own version of
ntp.conf. Not what we want at all if we're setting up our own NTP server!
So, to stop this behavior, remove the offending script with
sudo rm /lib/dhcpcd/dhcpcd-hooks/50-ntp.conf, and remove any configuration files it may have created with
sudo rm /var/lib/ntp/ntp.conf.dhcp.
Now we have everything else set up, we can configure
ntpd. Open up the configuration file with
sudo nano /etc/ntp.conf and replace the contents with the following (I'll explain the interesting bits further down):
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help # Local server 127.127.1.0 fudge 127.127.1.0 stratum 10 # GPS with PPS enabled server 127.127.20.0 mode 17 minpoll 4 maxpoll 4 iburst true prefer fudge 127.127.20.0 flag1 1 refid GPS # Internet time servers for sanity server 0.pool.ntp.org iburst prefer server 1.pool.ntp.org iburst server 2.pool.ntp.org iburst server 3.pool.ntp.org iburst # By default, exchange time with everybody, but don't allow configuration. restrict default kod nomodify notrap nopeer noquery restrict -6 default kod nomodify notrap nopeer noquery # Local users may interrogate the ntp server more closely. restrict 127.0.0.1 restrict -6 ::1 # Drift file etc. driftfile /var/lib/ntp/ntp.drift
- Local: This section sets up a local clock that
nptdcan refer to if it can't connect to anything else.
- GPS with PPS enabled: This section specifies the particulars for our GPS setup
server 127.127.20.0tells it that we have a serial GPS reciever on
mode 17tells it to look for
$GPRMCmessages at a baud of 9600bps
fudge 127.127.20.0tells it that we have some further configuration for our GPS
flag1 1tells it that we have a PPS interface from our GPS on
refid GPStells it to label this time source with the reference "GPS"
- Full documentation for the
ntpdNMEA driver (for if you require different baud rates etc) can be found here
- Internet time servers for sanity This section specifies some NTP pool servers to act as a sanity check for our GPS time. They will also keep the time accurate if you lose GPS signal for whatever reason, such as your cat entangling itself within the antenna wire and disconnecting it.
Check everything is working!
Now we just need to start
ntpd back up with
sudo service ntp restart. If we wait a few minutes, and then run
ntpq -pn we should see a line similar to
o127.127.20.0 .GPS. 0 l 1 16 377 0.000 0.005 0.003
o at the beginning of the line shows that
ntpd is receiving a PPS signal from our GPS module!
EDIT: Thanks to Jeremy for correcting a typo!