Raspberry Pi as NTP server

A couple of years ago, I made an attempt to create a NTP server using a GPS receiver. I failed.

I tried again in 2025, this time I succeeded.

This time around, I mostly followed Austin Pivarnik’s updated time server post, which is wonderful, but there are a few details and clarifications I feel like I can add to the discussion.

Raspberry Pi Hardware

I did not try to use my Dell R530 server or my laptop as the NTP server. I used Raspberry Pi boards, as everyone else seems to.

I had a RasPi 3 that I used. It works with both GPS modules I tried.

I also bought a Raspberry Pi 5 because that’s what Austin used. It also works with both GPS modules.

Raspberry Pi OS and configuration

I downloaded 2025-05-06-raspios-bookworm-arm64-full.img.xz from https://www.raspberrypi.com/software/operating-systems/ That’s Raspberry Pi OS with desktop and recommended software

I used a USB micro SD card reader/writer to have the SD card as a Linux device. Inserting the USB reader/writer created device files /dev/sdb and /dev/sdb1. I bought the micro SD cards from MicroCenter. It had a FAT filesystem on it.

$ unxz 2025-05-06-raspios-bookworm-arm64-full.img.xz
$ sudo dd bs=4M if=2025-05-06-raspios-bookworm-arm64-full.img of=/dev/sdb

The Raspberry Pi 5 has a USB-C power connector. Luckily, I had an official Raspberry Pi power supply with a USB-C connector, but it’s the 27-Watt version. There was a message during boot about how the power supply was inadequate for some high powered sensors. The 27W power supply seems to work with both GPS modules on the RasPi 5.

You need a monitor (tiny HDMI connector), a mouse (USB-A) and a keyboard (USB-A) to finish installing Raspberry Pi OS, start sshd, and do a bit of configuring.

I had to select a keyboard, a timezone, and create a user ID as part of the OS installation. I also had it join my WiFi network.

I issued these commands from the Raspberry Pi OS “terminal” app:

sudo apt install pps-tools gpsd gpsd-clients chrony
sudo systemctl enable ssh
sudo systemctl start ssh

I added 3 lines to the bottom of /boot/firmware/config.txt

dtoverlay=pps-gpio,gpiopin=18 
enable_uart=1
init_uart_baud=9600

I added one line to /etc/modules:

pps-gpio

I commented out a line in file /etc/chrony/chrony.conf after I found that my DHCP server gives out an NTP server, and Raspberry PI OS is happy to include that NTP server in the mix with the GPS time.

# Use time sources from DHCP.
#sourcedir /run/chrony-dhcp

Wiring

Wiring is always nerve wracking. Lots of articles and posts casually mention pin connections, when doing the connections requires knowing exactly which pins on some peripheral connect to which specific pins on the Raspberry PI.

Here’s the diagram I used, and it worked:

Raspberry Pi 5 GPIO header pinout

Even numbered pins are on the “outboard” side of the header. I used female-female DuPont wires to connect the GPS module headers to the RasPi GPIO headers. The connection that’s hard to keep in mind is the TX-RX crossover. The GPS module’s RX pin is connected to the RasPI’s TX pin. The GPS module’s TX pin is connected to the RasPI’s RX pin.

The GPS modules gets powered entirely from the RaspPi pins, one important fact Austin doesn’t write down explicitly

Raspberry Pi and GT-U7 module wiring

Above, how I wired a Raspberry Pi and a GT-U7 module together.

back size of GT-U7 module showing pinout

The back side of the GT-U7 module, showing the the names of the pins.

GT-U7 Pin label Wire color RasPi Pin
PPS Orange 12
TXD Red 10 (RXD)
RXD Brown 8 (TXD)
GND White 6
VCC Black 2

GT-U7 TXD pin wired to RasPi RXD pin. GT-U7 RXD pin wired to RasPi TXD pin.

There’s a similar type of wiring for the NEO-M8T. It has an 11-pin header. Six of its pins aren’t connected to the RasPi.

Neo M8T header view

The Neo M8T was easier to get wired correctly because the pin labels are on the top of the module. No mental rotations necessary.

GPS Modules

I ended up using two ublox-based GPS modules.

I soldered headers on both. I can’t figure out why they don’t come with soldered on headers, but rather loose headers sloshing around in the packaging. It’s weird. I’m not a great solderer, but 5 pins on the GT-U7 and 11 on the Neo-M8T wasn’t a huge effort.

Power

Both boards are powered by the +5 V pin on RasPi, pins 2 and 4 on the RasPi GPIO header. They need a connection to “ground” to make a circuit, pin 6 on the RasPi header.

Serial Port Device File

Austin (and other sources) only seem to metion /dev/ttyAMA0 as the serial port device file that refers to GPIO pins 8 and 10 on the RasPi header. I got 3 different TTY device files: /dev/ttyS0, /dev/ttyACM0 and /dev/ttyAMA0. I believe that /dev/ttyACM0 is what you get if you plug in a GPS board with a USB cable, which I did before I understood that the boards can be powered straight from the GPIO pins. I got /dev/ttyS0 on my RasPi 3 with both GPS boards, and /dev/ttyAMA0 on the RasPi 5 with both boards. I ran the same OS on both RasPi models, the TTY name must be related to something in the RasPi hardware, not the GPS modules.

I did not have Austin’s problem of slow NMEA text reception getting matched with the previous pulse, so I left serial connections at 9600 baud. I tried disabling binary messages, and every message but the ZDA messages of the - MakerFocus GT-U7 board using ubxtool, but that didn’t work. The GT-U7 immediately lost its GPS fix, and wouldn’t regain it.

When gpsd isn’t running, and the GPS board has a fix, you can run cat /dev/ttyAMA0 to get near gibberish like this:

,OXOSV,4,9,13,85,51,17���'      ²b��bª�b����b��b��¢b�f�b�c���b3²*7�
J�GPGSV,4,r,±s�q²l56,199�&'-������ᱱ���ű����ɱ��l�1l1p�ls6*w�J$��GSVl4�3,q3lr1<61,101L&�ձ�����ݱ�ű��������ݱ���ͱ��ѱ��j·EM
��PGSV,4l4,13�s1,02,324�LL/STH�º1]MY�ͱ����XljXb`Xbj��38,71,23,ؚ6)ªb��,31,p9y,3².77.s2,211,31*¶q�
$gNGSV,3,2,1�¬78¬44¬293/38,Sb�b��«,2v,86,12,06w,23,9�ºb��²b�թ67�
$gLGsV,3,3,10,88,43,297,36,,,,41*5F

$GNGLL,3829.90994,N,10352.16654,W,154632.00,A,A*62

I believe you should see some legible letters like the finale “$GNGLL” line. That should let you know that the baud rate and other TTY settings are correct.

Pulse Per Second

Both boards do the pulse-per-second only when they’ve got a GPS fix. Both boards have an LED that blinks when they pulse. The ppstest /dev/pps0 command also only works when a board has a GPS fix.

GPS and NTP utilities

gpsprof localhost:2947:/dev/ttyACM0
chronyc sources -v
ubxtool -e NMEA --file /dev/ttyACM0
stty -F /dev/ttyACM0
ppstest /dev/pps0
gpsmon
cgps
gpsctl --speed 115200  /dev/ttyACM0

clockdiff

clockdiff is in iputils package in Arch, iputils-clockdiff in Raspbian.

1676 % sudo clockdiff modest4
..................................................
host=modest4 rtt=1(0)ms/1ms delta=-5ms/-5ms Sat Jun 28 09:17:17 2025

That’s on my laptop, checking the clock difference between it and my Qotom fanless server. My laptop uses the Arch Linux NTP pool, the Qotom uses the GPS NTP servers. There’s a tiny difference, 5 milliseconds, between the Qotom’s clock and my laptop’s clock. That’s typical after chronyd runs for a while.

chronyc sources -v

1002 % chronyc sources -v                                                                  # /home/bediger

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current best, '+' = combined, '-' = not combined,
| /             'x' = may be in error, '~' = too variable, '?' = unusable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* rpi5ntpeth4                   1   8   377   118    +20us[  +57us] +/-  309us
^+ rpi3ntpeth4                   1   8   377   207    +15us[  +53us] +/-  439us

Hostname rpi5ntpeth4 represents the Raspberry 5 SBC connected to Neo M8T GPS hat. Hostname rpi3ntpeth4 represents the Raspberry 3 SBC connected to GT-U7 module. The Neo M8T was about $125 bucks, the GT-U7 was about $11 when I bought it in 2023. I’m not sure the extra fancy GPS hat is actually worth it. I don’t plan on doing anything with Precision Time Protocol (PTP), so the Raspberry Pi 5 is overkill, too.

Lessons learned

  1. NTP servers should be cabled, not connected wirelessly, WiFi introduces a lot of jitter and latency for NTP clients.
  2. GPS antennas don’t work at all in basements. They do work in front offices or bedrooms with large windows facing the sky.