The first step is to get your Raspberry Pi networked. For this, the strong recommendation is Ethernet connectivity rather than WiFi. WiFi by its very nature has wildly unpredictable latency that will have an impact on the obtainable accuracy and stability of your NTP service. For the Raspberry Pi, the only option is Ethernet over USB (even the USB ports built in to the larger Pis are in actuality USB devices). This somewhat degrades the available stability if you're going to use the USB bus for other purposes, but it won't be noticeable if you're building a dedicated NTP server with little other traffic and no other USB peripherals.
On the hardware side, you're going to need (in addition to the Pi) a GPS receiver breakout board of some sort. Your breakout board should have a bidirectional serial interface and active-high PPS, with the leading edge synchronized. It should also be powered by either 5v or 3.3v (5v is preferable). The serial I/O and PPS should be 3.3v TTL level serial. If it's 5 volt serial, then you'll need to use diode+pull-up level shifting.
Connect the ground of the GPS module to pin 6 of the Pi GPIO header. Connect either a +5 power input to pin 2 or a +3.3 power input to pin 1. Connect the serial input of the GPS module to pin 8, the serial output to pin 10 and the PPS to pin 12.
Run raspi-config and go into the advanced menu and disable the login on serial. The serial port is being given over to the GPS module instead.
Edit /boot/cmdline.txt Remove the part that says
Edit /boot/config.txt and add this line:
This will configure the Pi for PPS input on GPIO 18, which is pin 12 - where we connected the PPS output of the receiver.
Edit /etc/modules and add this line:
Reboot the pi for this to take effect.
After rebooting, you should see /dev/pps0.
Run this (as root):
apt-get install pps-tools gpsd gpsd-clients
As root, run
You should see
trying PPS source "/dev/pps0" found PPS source "/dev/pps0" ok, found 1 source(s), now start fetching data... source 0 - assert 1474694802.999938248, sequence: 466791 - clear 0.000000000, sequence: 0 source 0 - assert 1474694803.999931101, sequence: 466792 - clear 0.000000000, sequence: 0 source 0 - assert 1474694804.999950952, sequence: 466793 - clear 0.000000000, sequence: 0
The actual output may vary, but you should see one of those "source" lines every second.
As root run
and set the GPSD device to /dev/ttyAMA0.
As not root run
You should see an ASCII-art pseudo-graphical display showing the GPS information being sent by the receiver.
Edit /etc/init.d/ntp. Find the line that looks like this:
# Required-Start: $network $remote_fs $syslog
Add " gpsd" to the end of that line. This will cause ntp to delay its startup until after gpsd, which allows gpsd to create its socket before ntpd goes and looks for it.
At this point, we must install a special customized version of ntpd, as, unfortunately, the standard packaged version lacks the NTP driver support for getting time from GPSD and PPS.
We're going to simply overwrite the NTP daemon installed on the system. To prevent aptitude from upgrading (and therefore wiping out) our customized version, do this (as root):
apt-mark hold ntpd
Next, download the latest NTP daemon from ntp.org.
Untar the distribution somewhere convenient, cd into it and run
apt-get install libcap-dev configure --enable-linuxcaps make
This will take a long time on a Pi Zero. If you have another Pi handy (and why wouldn't you?), then you should do this on a heftier Pi and then copy the result to the zero.
copy the resulting ntpd to /usr/sbin, overwriting the one that's there.
Edit /etc/ntp.conf, adding this to the end:
server 127.127.22.0 #minpoll 4 maxpoll 4 fudge 127.127.22.0 refid PPS fudge 127.127.22.0 flag3 1 # enable kernel PLL/FLL clock discipline server 127.127.28.0 prefer #minpoll 4 maxpoll 4 fudge 127.127.28.0 time1 0.235 refid SHM stratum 10
The 127.127 entries are 'virtual' reference clocks. The 22.0 one is the PPS refclock. It will use the /dev/pps0 device to get kernel timestamps on the PPS leading edges and use them to steer the kernel clock to synchronicity with GPS seconds.
The 28.0 clock is the shared memory interface to GPSD. The time1 value is an estimate of the latency to the timecode messages. The correct value will depend on the GPS receiver. 0.235 is a good value for the SkyTraq Venus838LPx-T receiver. It's not tremendously important to tune this value. The purpose for this refclock is to get the seconds close enough to allow the PPS clock to take over. You can also set up other hosts as servers instead of using the SHM refclock. Anything that will cause ntpd to get a good sync will allow the PPS refclock to begin working.