A Raspberry Pi can make an outstanding NTP time server for your network. Below, I document the bare minimum needed to get a Raspberry Pi 3 Model B with the Adafruit Ultimate GPS Hat working as an NTP server. You can do this, even buying all the parts new, for under $100 USD.
What makes this project interesting is that you end up with potentially a very compact time server – a Pi with a nicely stacked board on top. Just bring network and power to the box, put the box near a window, and it’s otherwise self-contained. For an even simpler deployment, in a corporate environment with PoE switches, use a PoE to USB cable like this one (disclaimer: this is an Amazon affiliate link, so I get a tiny amount of money if you buy something here): PoE to USB Splitter – then all you need is a network cable (no wall wart is required) which supplies connectivity and power. Note that the PoE (Power over Ethernet) adapter requires that you have ethernet switches that support PoE. Your business might have these, but you probably don’t have them at home (they cost a lot more money).
The only thing I’m missing is a good case – if anyone knows of a good Raspberry Pi 3 case that fits well with the Adafruit hat installed, I’m very interested — please comment!
Why do you want to do this? There are several reasons:
- You have a need for “correct” time on your servers (to do log correlation or for regulatory requirements)
- You have an asymmetric internet connection. There is no way for internet time servers to be more accurate than 10ms or so if you have this type of connection – virtually any DSL or cable internet connection. On these connections, you have a faster download than upload. That means that that it takes several ms (milliseconds) less time to travel from the internet to your machines than it takes to travel from your machines to the internet. Because NTP measures round trip delay and divides it by two, assuming equal delay on both paths, your time will be off in such cases. Whether it’s off enough to bother you is a different question!
- Related to the above, it is very unusual for internet backbone paths to be the same for both directions of your connection. In fact, this is more common than not. This causes issues with NTP.
- During network congestion (whether it’s a peering point, a congested NTP server, or your internet connection), you will see additional delay added, typically more in one direction than another. Right now, NIST’s Maryland servers (time-a.nist.gov, time-b.nist.gov, time-c.nist.gov) have about 70ms of extra delay due to congestion at their end – this makes the time about 35ms off (70 divided by 2) for anyone synchronizing to their servers.
- You need accurate time on a network not connected to the internet and/or during internet outages
- You don’t want to be dependent upon volunteer server operators for the correct time
- You might just like knowing your time is more correct than your friend’s!
There are lots of tutorials on the building NTP servers from Pis, but this one is a bit different because:
- It’s based on the Pi 3 Model B instead of the 2 or older. There are some serial oddities on the Pi 3, so you do need to do minor modifications.
- Most tutorials connect to gpsd via shared memory to fetch the time of day while using the kernel PPS module to get the seconds to start at the right instant. This means you can have time that is several hundred milliseconds off reality upon startup of NTP. This can be mitigated slightly by fudging the stratum of the GPS and having internet time services also configured, but it can’t be eliminated completely.
- I don’t generally manage computers from the console, I use network-based access to do so. Most tutorials assume it’s convenient to haul a monitor out to the Pi. This tutorial never requires you to connect a monitor (but you can if you want).
- This tutorial uses versions of ntpd and gpsd I compiled from more recent sources than come with the stable branch of Raspbian. The ntp code also has a patch I’ve made to allow it to see both the GPS and PPS devices from gpsd easily.
So, here’s the procedure:
- You need a working understanding of the Unix command prompt. In particular, you need to know how to use a text editor to edit a file. I assume you’re using vi but you can replace vi with whatever editor you’re comfortable using.
- Procure a Pi (See also Adafruit or Sparkfun for purchase links in the US – and plenty of cool stuff besides Pis) and an Ultimate GPS Hat. The Pi runs just under $40 plus shipping in the USA, while the Hat runs at about $45. You should also pick up a battery that works with the GPS hat (see the link above which has information on this).
- You’ll need an SD card, ethernet cable, and power supply as well. Any size SD card big enough for Raspbian is big enough for this. I use 32G cards, but they end up having 28G of free space.
- The Pi will need to be networked via wired ethernet. There is not much point using wifi for your time server because wifi will add variable amounts of delay in each direction.
- You’ll need the appropriate SD card. It’s important to ensure that there is nothing you care about on this card – you will be overwriting it.
- Generally, you probably shouldn’t run any applications on this Pi if you plan on using it for time sync – you want a predictable, very lightly utilized system to maintain stable time. That said, you can certainly do this on a Pi you use for other stuff, but your time won’t be quite as accurate (it’ll probably still be better than syncronizing over the internet)
- The Pi will need to have a good view of GPS satellites (or have an active external antenna hooked up to it – and that antenna then needs a good view). For a home, near a south-facing window is probably fine. At a business, you may need an exterior antenna, depending on what type of windows you have. It’s unlikely that this will work in a windowless basement room without an external antenna, but you can of course still try and see – you could get lucky. The more of the sky your GPS antenna can see, the more accurate your time will be.
- Get the current (Jessie) distribution of Raspbian onto the SD Card (the “Lite” version of Raspbian is fine). See the Raspberry Pi site’s page on Raspbian for download links and installation instructions. Should you be reading this when a newer version of Raspbian than Jessie is distributed, these instructions may not be valid.
- If you won’t be connecting a monitor and keyboard for setup, enable SSH on the Pi. Using the computer you used to create the SD card, create a file named ssh (no extension!) on the boot partition. On a Windows machine, the boot partition is the only partition that will be visible to file explorer. Security warning: your Pi will come up with a default username of pi and password of raspberry. Make sure you have a firewall in front of your Pi that blocks SSH, or your Pi will become part of some hacker’s botnet! Even being on the internet for a few seconds will be long enough for hackers to take control of your device!
- Put the SD card into the Pi, connect the Pi to the ethernet network (that provides DHCP addresses – most home routers do this automatically), and boot it. If you’re not using a monitor to configure this initially, you’ll need to find out what IP address was assigned to the Pi from your router, which will be different for every router brand and thus is not documented here.
- Either ssh to the Pi (using a username of “pi” and the IP address you found above) or connect a monitor and keyboard to log in as a user of pi with a password of raspbian. If you log into a graphical user interface, open a terminal window.
- Change the password (follow the prompts) by typing, at the shell prompt:
- Optional: You can configure many things with
raspi-config. In particular:
- memory split (16M is plenty for the GPU unless you are running a graphical interface on the Pi)
- locale (use en_US.UTF-8 if you are in the USA, otherwise select the appropriate locales)
- timezone (this doesn’t impact NTP, but will change how you see times represented on the server)
- keyboard (the default keyboard is a UK keyboard, so if you’re configuring from the console and not living in the UK, you almost certainly want to change this – it has no effect if you’re using SSH to configure the machine)
- Become root (this is bad practice, but this is a very simple server and this makes it quicker to configure):
sudo su -
- Execute the following commands to configure the hardware, update the OS & firmware, and install the modified ntpd and gpsd that I’ve precompiled:
echo dtoverlay=pps-gpio,gpiopin=4 >>/boot/config.txt echo enable_uart=1 >>/boot/config.txt echo pps-gpio >>/etc/modules apt-get update apt-get upgrade apt-get install -y apt-transport-https wget -O /etc/apt/sources.list.d/rpi-ntpd.list http://jmaslak.github.io/rpi-ntpd/raspbian/rpi-ntpd.list wget -O - http://jmaslak.github.io/rpi-ntpd/raspbian/conf/jmaslak.gpg.key | apt-key add - apt-get update apt-get install -y gpsd gpsd-clients pps-tools ntp rpi-update rpi-update
- Edit /boot/cmdline.txt (
vi /boot/cmdline.txt) and remove the part that reads console=serial0,115200
- Reboot (use the command
- Log in as pi with the password you previously set
- Become root again with:
sudo su -
rpi-updateagain to be sure that you have all updates (this step may be unnecessary). If changes are found, reboot using the
rebootcommand and log back in after the Pi reboots.
- Edit /etc/default/gpsd (
vi /etc/default/gspd) and:
- Change the DEVICES=”” line to read:
- Change the
GPSD_OPTIONS=""line to read:
- Change the DEVICES=”” line to read:
update-rc.d gpsd enable
- Edit /etc/dhcp/dhclient.conf – modify the “request” section by removing the ntp-servers entry (don’t remove the trailing semi-colon)
- Edit /etc/dhcpcd.conf – Comment out the option ntp_servers line (add a # in front of it)
service dhcpcd stop
rm /var/lib/ntp/ntp.conf.dhcp(It’s okay if this generates an error)
service ntp stop
- Edit /etc/ntp.conf –
- Set options to your preference. Default options are likely fine
- Add the following lines to the file (the first pair of lines tells ntpd to listen to the gpsd server, the second pair tells ntpd to use the kernel PPS discipline):
server 127.127.46.255 prefer mode 1 minpoll 3 maxpoll 3 fudge 127.127.46.255 refid GPS server 127.127.22.0 minpoll 3 maxpoll 3 fudge 127.127.22.0 refid GPS
- Reboot, and it should now be an NTP server
Troubleshooting / Determining it is Working
The gpsmon command connects to the gpsd server and displays output about the connected GPS. You should see a list of satellites on the left (0 through 11) along with S/N ratios (some should be in the 30s or higher if you have good antenna placement). You should see values for “Latitude” and “Longitude”. If you have a good antenna positioning and you’re in the USA, you should see “FAA: D” in the middle box. The middle bottom box should also indicate a PPS value (a decimal number, which should be very small if your time on your machine is close to correct).
The cgps command does similar functions, although it doesn’t report receiving PPS marks.
If you don’t see the output from the gpsmon or cgps programs, there are three possible causes. The simplest is that you just need to wait longer! When a GPS is first powered on after being off for a while, or after moving a large distance while turned off, it will need to search for available satellites, which can take up to 30 minutes or so. If you’ve waited long enough, the second possibility is that it doesn’t have a clear view of the sky. You should, after it has 30 minutes of good sky view, see the FIX light on the GPS hat blink once every 15 seconds or so – if it blinks at that rate, it is receiving GPS. If it blinks much faster, it is trying to receive the signal. If it doesn’t turn on at all, something may be wrong with the board (highly unlikely), which is the third possibility.
To see how ntp is doing, execute ntpq -c peers which will list all the NTP peers. You should see an entry that starts with something like *GPSD_JSON(255). This is the GPS input into NTPD – the star indicates that it’s the currently selected time source. If you see a “x”, that means NTP thinks the time is wrong. Most likely that means you’re also synchronizing your NTP to a bunch of servers that have a very bad idea of the current time and NTP is trying to eliminate what appears to be a broken clock from the algorithm. If you don’t see a * or any other character in front of GPSD_JSON, that means that you aren’t receiving the GSPD data. Make sure you see the data with gpsmon as described above, including the PPS signal.
In the ntpq -c peers output, you’ll see a similar entry, but with an “o” instead of an “*” in front of it. If things are working well, you’ll see oPPS(0) starting one of the lines. This indicates this source is the Pulse Per Second (PPS) signal received by the kernel, and it’s being processed. The kernel will be slightly more accurate than GPSD, so it’s configured to help ntpd know when a second starts. You can’t syncronize to the PPS signal unless you’ve synchronized to another preferred source (typically only the GPSD_JSON entry), so if you don’t see a star in front of GPSD_JSON, you won’t see an “oPPS” entry (unless you’ve set another clock source as “preferred” in NTP – you’ll know you did this if you did it).
The most interested entry, once its’ syncronized, are the offset and jitter columns. The offset column tells you how different the GPS clock is from your various reference clocks and peers. On my Pi, I consistently see 0.000 or 0.001 meaning that I’m seeing close to zero or one MICROsecond of difference between the Pi’s clock and the GPS’s idea of clock. However, when you first turn on the Pi, it’s not unusual for this to be off by a significantly higher value as NTP slowly adjusts the time.
I hope this works for you – feel free to post comments, although I can’t guarantee I’ll respond to all questions.
One thought on “A Minimal Raspberry Pi 3 GPS Time Server”
Thanks for the writeup! This works way better than the Stratum server with ntp only. So while PPS is working great, I am trying to get gpsd to accept incoming requests over the network on port 2947 to export position info (OpenCPN). I edited /etc/default/gpsd to add the -G option GPSD_OPTIONS=”-n -G” but external requests not happening. If I stop gpsd (sudo service stop gpsd), and invoke gps in the foreground (/usr/sbin/gpsd -N -n -G /dev/ttyAMA0 /dev/pps0, all works fine! So I am guessing there is a permissions problem starting the gpsd as a daemon, but I haven’t figured it out yet. If you have any hints, I’d be grateful.