Revisiting the GPS Time Server

One of my first posts to this blog involved using Mikrotik’s RouterOS to create a local NTP server. I even thought about using a Raspberry Pi and a USB GPS to accomplish this project. Well…it’s time to revisit the idea using a GPS receiver with PPS and creating a high accuracy Stratum-1 NTP server. While this may not be necessary for the average homelab or someone who would like to keep the time and logs of devices connected to an air gapped or isolated network accurate, accuracy becomes quite important when applications begin timing actions over the network (such as commonly seen with industrial operations and communication systems). Throughout this post, I’ll be using posts located at https://www.jacobdeane.com/iot/2020/building-a-gps-based-time-server/ and https://weberblog.net/ntp-server-via-gps-on-a-raspberry-pi/ as references.

Needed Hardware

The hardware needed will be the following:

  • Raspberry Pi – Any Raspberry Pi with network connectivity will work but Pi 4 has the Ethernet interface connected via PCIe instead of USB so it will be a little faster compared to previous generations.
  • GPS Receiver with PPS – In this case I will be using a Goouuu Tech GT-U7 I got off of Amazon for around $15. This is a TTL device so it will interface directly with the Pi’s GPIO header.
  • Antenna for GPS – I will be using a Mikrotik ACGPSA active GPS antenna with a u.FL adapter to interface with the GPS receiver.

Software

  • Pi OS Lite – Debian 11 based image (32 bit for this particular instance).
  • pps-tools – Recognizes PPS.
  • gpsd – GPS daemon.
  • ntp – NTP server

Configuration

Assuming you have already flashed Pi OS to a flash medium and are now able access the Pi, either via KVM or via SSH. If attempting to use a serial console…try a different method as the serial port that is used for serial console is about to be disabled so it can be used for the GPS interface.

Configure the Serial Interface

First step is to disable the console interface. Enter sudo raspi-config and navigate to 3 Interface Options > I6 Serial Port and set the login shell to not be accessible over serial. Be careful not to accidentally disable the serial port in addition to the login shell.

Raspi-config, disable serial login

Reboot the system as you exit raspi-config. Next we will need to disable getty services with the following commands.

sudo systemctl stop serial-getty@ttyAMA0
sudo systemctl disable serial-getty@ttyAMA0

Since we want full control of the UART and don’t want to let Bluetooth have it…we will disable Bluetooth by adding the following to the /boot/config.txt file (which is read during the boot process) using your text editor of choice and reboot. This will use GPIO_18 as the PPS input but this may not be where the PPS signal is mapped for those using HATs.

# Disable Bluetooth for GPS
dtoverlay=disable-bt
dtoverlay=pps-gpio,gpiopin=18

Connect the GPS Receiver

Now we will need to look at the Raspberry Pi’s GPIO header to figure out where we need to connect things…

Raspberry Pi GPIO

In the case of this GPS receiver, we will be using 5V power, ground, TXD, RXD and a pin for PPS. 5V will come from either pin 2 or 4, ground from pin 6, TXD (which will go to RXD on the receiver) on pin 8, RXD (which will go to TXD on the receiver) on pin 10 and GPIO 18 for PPS on pin 12.

Once the GPS is connected, running cat /dev/ttyAMA0 should return some structured NEMA output.

Install and Configure GPS tools

Now we need to install GPS related tools and configure settings. First we will load the pps-gpio module by adding pps-gpio to /etc/modules and rebooting.

Next we will install gpsd and pps-tools with the following.

sudo apt install gpsd gpsd-clients pps-tools -y

Assuming the GPS is now hooked up and has locked, we can run a pps test to make sure our pin is configured correctly.

sudo ppstest /dev/pps0

Which should return something like the following if PPS is working correctly.

Output of ppstest

Next we will configure GPSD by editing /etc/default/gpsd and add the serial and pps interfaces, disable USB hot plugging and pass the -n flag.

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="/dev/ttyAMA0 /dev/pps0"

# Other options you want to pass to gpsd
GPSD_OPTIONS="-n"

# Automatically hot add/remove USB GPS devices via gpsdctl
USBAUTO="false"

Now we will enable the gpsd service and start it.

sudo systemctl enable gpsd
sudo systemctl start gpsd

And reboot.

To test the GPS…run gpsmon -n where the -n flag will force the output into NEMA0183 sentences. If everything is functional, you should see your position in the window.

Configure NTP

We need to start off by editing /etc/dhcp/dhclient.conf and removing the references to dhcp6.sntp-servers and ntp-servers. Now we can install the NTP server.

sudo apt install ntp -y

Now we can edit /etc/ntp.conf. In my case, I am commenting out several of the pools but in an air-gapped system these would be used to provide additional stratum-1 servers on the network but it is recommended to leave at least one or two other servers in place. Next we will add the memory register locations (which look like loopback IP addresses) to the config file.

# pps-gpio /dev/pps0
server 127.127.22.0 minpoll 4 maxpoll 4
fudge 127.127.22.0 refid PPS

# gpsd clock via shared memory
server 127.127.28.0 minpoll 4 maxpoll 4 prefer
fudge 127.127.28.0 time1 +0.000 refid GPS flag1 1

Save the file and restart the ntp service by running sudo systemctl restart ntp and give the device a few minutes. After you should be able to run ntpq -p which will display stats of the NTP servers in the config file.
That’s it. We can now point end devices at our NTP server (unicast mode) and let them sync.

Conclusion

This was an interesting exercise in building a stratum-1 NTP server. There are Raspberry Pi HATs which provide a cleaner setup. If I were planning on building this as a marketable soltuion, I would most likely look into using a Compute Module 4 on a carrier board that had PoE in support and a built in GPS interface in a 1U rack mount enclosure. A plus would also provide the PPS line to be used as a reference for a 10 MHz Reference Clock at 1 Vpp (it’s like a major radio manufacturer uses that or something). However, to really make this usable in an air-gapped setup, you really need one to three more stratum-1 NTP servers throughout the network.

Raspberry Pi with a GPS interfaced to the GPIO