Background
We where frustrated at work about the central IT organisation blocking outgoing 123/udp connections. This gave us the idea to buy a GPS controlled NTP server like this one: https://www.meinbergglobal.com/english/products/rack-mount-1u-ntp-server.htm. While doing some research on this subject I found that alot of people seem to build their own stratum 1 NTP servers at home. So I decided to build one myself to use at home.
Prerequisites
There is alot of information floating around on this subject but I wanted to use FreeBSD and Raspberry Pi. It took me a while to figure this out but I bought this set of hardware:
Raspberry Pi 2 Model B
Adafruit Raspberry Pi Case
Adafruit Ultimate GPS Breakout
Adafruit Perma-Proto HAT
External antenna
Basic setup
There is now a new kernel module named gpiopps written by ian@freebsd.org that you can use to get PPS input on any gpio pin. To configure what gpio pin to use you need to rebulid the device tree of your raspberry pi (/usr/src/sys/boot/fdt/dts/arm/rpi2.dts). This is the changes i made:
# svnlite diff Index: rpi2.dts =================================================================== --- rpi2.dts (revision 309114) +++ rpi2.dts (working copy) @@ -337,6 +337,13 @@ broadcom,depth = ; /* Set by VideoCore */ }; + + pps@0 { + compatible = "pps-gpio"; + gpios = <&gpio 17 0>; + status = "okay"; + }; + leds { compatible = "gpio-leds";
and then rebuilt the tree
# cd /usr/src/sys/tools/fdt # setenv MACHINE arm # ./make_dtb.sh /usr/src/sys /usr/src/sys/boot/fdt/dts/arm/rpi2.dts rpi2.dtb # cp rpi2.dtb /boot/msdos
Then you can hook up the pps output of the GPS to gpio pin 17 of your raspberry pi and make sure gpiopps is loaded by adding this line to /boot/loader.conf.
gpiopps_load="YES"
Reboot the pi and then you should see something like this in the boot messages
gpiopps0: on ofwbus0 gpiopps0: PPS input on gpio0 pin 17
Physical setup
For the physical setup I used the Pi, a case and the “perma proto hat” and did some simple soldering to hook up the serial interface of the GPS to the uart serial interface of the Pi, and of course the PPS output to gpio pin 17. I also added a LED to the pps output so I can visually see when I have a working PPS signal.
Finished product:
GPS configuration
The GPS is actually very easy to talk to. It has a serial interface configured to 9600 baud by default and a dedicated PPS output.
To control the GPS you can send basic text-strings to the unit, for example setting the update rate to 1Hz:
# printf '$PMTK251,57600*2C\r\n' > /dev/cuau0 (Set baudrate to 57600) # printf '$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29\r\n' > /dev/cuau0 (ask only for GPRMC sentences) # printf '$PMTK220,1000*1F\r\n' > /dev/cuau0 (echo sentences once a second) # printf '$PMTK300,1000,0,0,0,0*1C\r\n' > /dev/cuau0 (update fix once a second)
If you want a complete set of instructions(commands) you can send to device please see the command set sheet.
NTP configuration
I have four different kind of time sources configured on my stratum 1 server.
1. External time sources (internet). In Sweden we have a pretty neat project at ntp.se where they have built custom ntp-servers using FPGAs and atomic clocks.They should be able to server time at 10Gbit/s line rate. You can read more about this here: http://www.ntp.se. I use all of these servers as reference clocks.
2. Local servers that run ntpd. Just my gateway and my server. In the case that all other references fail they can still discipline each other.
3. PPS input from the GPS module. This is the main thing about this article. The GPS outputs a pulse every second that is then used to discipline ntpd.
4. GMEA data from the GPS module. The GPS also outputs coordinates and times on the serial console. But these timestamps are pretty imprecise, at best its close within a second. (But since we also have PPS this is good enough)
You can see my ntp.conf here below but I will only talk about the GPS-stuff from now on.
I use two different drivers in ntpd. 20 and 22. These are the NMEA and PPS drivers. My settings for the PPS driver is pretty basic, it will automatically look for /dev/pps0 and try to fix to a pps signal. The gpiopps driver creates gpiopps0 so I have added “link gpiopps0 pps0” to /etc/devfs.conf
For the NMEA driver we have a few more settings. First of all “mode 17” sets what type of output the look for from the GPS and what baudrate to use. From the driver documentation you find that bit 0 is used to set processing of $GPMRC sentences from the GPS. Bit 4-6 is used to set baudrate and decimal “16” is 9600. So 9600+$GPMRC=17 right? 🙂 This driver looks for /dev/gps0 by default so I have added “link cuau0 gps0” to /etc/devfs.conf
The fudge time2 is used to compensate for the delays we have in the serial interface of the GPS to make it match PPS more closely.
Please refer to the driver documentation for more settings:
http://doc.ntp.org/4.2.8p8/drivers/driver20.html
http://doc.ntp.org/4.2.8p8/drivers/driver22.html
# Allow traffic to external servers restrict 194.58.203.20 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.203.148 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.204.20 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.204.148 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.202.20 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.202.148 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.205.20 mask 255.255.255.255 nomodify notrap noquery restrict 194.58.205.148 mask 255.255.255.255 nomodify notrap noquery # Allow traffic to internal servers restrict 172.25.0.25 mask 255.255.255.255 nomodify notrap noquery restrict 172.25.0.1 mask 255.255.255.255 nomodify notrap noquery # PPS server 127.127.22.0 flag3 0 # NMEA server 127.127.20.0 mode 17 fudge 127.127.20.0 time2 +0.767 # Servers server 194.58.203.20 iburst prefer server 194.58.203.148 iburst server 194.58.204.20 iburst server 194.58.204.148 iburst server 194.58.202.20 iburst server 194.58.202.148 iburst server 194.58.205.20 iburst server 194.58.205.148 iburst server 172.25.0.25 server 172.25.0.1 driftfile /var/db/ntp.drift # save ntp performance stats statistics loopstats statsdir /var/log/ntp/ filegen peerstats file peerstats type day enable filegen loopstats file loopstats type day enable filegen clockstats file clockstats type day enable
Running ntpd
So now when we have configured ntpd how does it actually work? This is some output from ntpq -p efter some 30mins of ntpd running:
root@ntp:/dev # ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== torus.pean.org 172.25.0.10 2 u 20 64 377 0.417 0.816 0.033 gw.pean.org 172.25.0.10 2 u 18 64 377 0.609 0.462 0.102 oPPS(0) .PPS. 0 l 5 64 377 0.000 0.133 0.011 xGPS_NMEA(0) .GPS. 0 l 4 64 377 0.000 62.005 6.586 *gbg1.ntp.se .PPS. 1 u 21 64 377 7.467 0.052 0.054 +gbg2.ntp.se .PPS. 1 u 25 64 377 7.514 0.167 0.074 +mmo1.ntp.se .PPS. 1 u 14 64 377 11.469 0.127 0.221 +mmo2.ntp.se .PPS. 1 u 22 64 377 11.386 0.152 0.139 +sth1.ntp.se .PPS. 1 u 12 64 377 2.087 0.130 0.235 +sth2.ntp.se .PPS. 1 u 17 64 377 2.255 0.158 0.292 -svl1.ntp.se .PPS. 1 u 13 64 377 6.686 -0.118 1.343 +svl2.ntp.se .PPS. 1 u 6 64 377 6.015 0.144 1.177
The o in oPPS denotes that ntpd have PPS signal. The x in xGPS_NMEA denotes that this source is marked as a false ticker. The reason for this could be that I have entered a to large fudge factor, its seem to be running 62ms fast at this point. I will keep ntpd running for a few hours and the try to adjust the time fudge accordingly.
Hi,
thank you for this hack.I use a raspberry3 with FreeBSD 12.0. The basic setup is OK.
and the modul gpiopps.ko is loaded. But I have no device gpiopps or boot messages about.
with regards
Claus
Hi!
Have you created and installed a new device tree and then rebooted?
I found my mistake in the rpi2.dts. The line with ” broadcom,depth = ; /* Set by VideoCore */ must be unchanged as”broadcom,depth = ; /* Set by VideoCore */”.
Hey there, just a heads up – I’ve been running a Raspi NTP server for a couple of years, and after setting up ntpviz graphs recently, it became plainly clear how sensitive the gps clock crystal is to temperature variations. I use the already-built-out Adafruit GPS hat, which has the chip on the top of the HAT, rather than the bottom as you have. I’ve switched to a case that insulates the whole device, and I added a thin foam insulating pad to the bottom of the GPS board to slow the temp variations. You might consider doing that as well, depending upon how ‘OCD’ you are about having the most accurate time 🙂
The case I used is the c4labs.net case that’s specifically designed for the adafruit gps hat, and the pads I got on amazon. I don’t like linking out to specific stuff, seems spammy, so to find the ones I got, search on “4in4in1/16in 20Pcs Foam Rubber Mats, Self Stick Weather Strip Non-slip Furniture Pads Black Neoprene Rubber Insulation 4 Inch Long X 4 Inch Wide X 1/16 Thickness “.
Instead of roughly 8 to 10 deg C variation during the course of 24 hours, it now fluctuates only about 2 deg C.
I’d link to my ntpviz graphs, but they look like crap due to all the changes i’ve been making over the last couple of days. Maybe I’ll post the link in a few days. 🙂
Hi! Thanks for your comments and suggestions.
I have also noticed that its very sensitive to temperature changes and I have thought about a few things to fix it. One of the more invasive ones being to switch out the actual crystal on the chip: Switch out the X1 Oscillator on a RPI 2/3.
Now I have basically just given up, its still a lot better than using npt servers on the internet.
But thans for the tip with the rubber mats, I will definitely look into that.
Yeah, since I don’t have an external temperature sensor for the actual GPS hat board, I don’t really know for sure how much the rubber mat helps – but I have to assume that a closed-cell rubber foam would provide good insulation. At worst, it’ll smooth out the temperature spikes.
By the way, have you considered running NTPsec?
Peter, I’m trying to do this with FreeBSD 13.1 but am running into an issue where when the GPS hat is connected, the pi halts at the U-boot prompt due to incoming GPS data that interrupts auto boot. Have you run into this? When I remove the hat, the pi boots up FreeBSD normally.
Yes! Actually I booted the pi before adding the hat for a while. And then I just skipped the serial connection and cut that wire.
I have searched extensivley for a solution without finding any.. sorry.