Project 1: GPS-based NTP server
Source: Microsecond accurate NTP with a Raspberry Pi and PPS GPS
Info
This page describes setting up a GPS module with a Pi to act as a Stratum1 NTP server. The GPS module must have a 3-5V input, with serial AND PPS outputs (5 pins minimum). Example: Adafruit Ultimate GPS Breakout.
PPS = Pulse Per Second. While the serial data from the GPS module contains the actual time & date information required for the NTP server, the PPS input is required to keep sub-millisecond accuracy. the PPS output puts out EXACTLY 1 pulse per second, +/- a few nanoseconds. The PPS software running on the Pi uses this.
Setup:
- 
Install packages:
sudo apt install pps-tools gpsd gpsd-clients gpsd-tools chrony('pps/gpsd' packages are for interperating GPS data, chrony is an NTP server)
 - 
Add these lines to the end of
/boot/config.txt# the next 3 lines are for GPS & PPS signals dtoverlay=pps-gpio,gpiopin=18 enable_uart=1 init_uart_baud=9600 - 
Add this text to the end of
/etc/modules:pps-gpio 
These programs and config changes enable PPS time sync inputs on GPIO pin 18 (real Pin 12), and initializes the serial COM port with a baud rate of 9600.
- disable system handling the COM port:
sudo systemctl mask serial-getty@ttyS0.service 
This prevents the system from taking control of the port. Check this command worked after rebooting: /dev/ttyS0 should be owned by root:dialout and have permissions crw-rw----.
Wire up the GPS module:
Pinout:
- GPS PPS to RPi pin 12 (GPIO 18)
 - GPS VIN to RPi pin 2 or 4
 - GPS GND to RPi pin 6
 - GPS RX to RPi pin 8
 - GPS TX to RPi pin 10
 
Picture reference (note that all the Pi pins are seqential):

Check GPS functionality
The GPS device should put out either 3.3V or 5V on the antenna connector to power the antenna. With the GPS module powered, The on-board LED will light solid. The LED will start blinking once it has a GPS lock. The module spits data to the serial lines at all times, there is no bi-directional communication!
- 
Check that the pps serice is running:
lsmod | grep ppsShould return at least the service
pps_core. - 
Check for PPS pulses (after GPS has a lock):
sudo ppstest /dev/pps0 ### Output: trying PPS source "/dev/pps0" found PPS source "/dev/pps0" ok, found 1 source(s), now start fetching data... source 0 - assert 1655253832.999996389, sequence: 966 - clear 0.000000000, sequence: 0 source 0 - assert 1655253834.000004254, sequence: 967 - clear 0.000000000, sequence: 0 source 0 - assert 1655253835.000001120, sequence: 968 - clear 0.000000000, sequence: 0 source 0 - assert 1655253836.000000985, sequence: 969 - clear 0.000000000, sequence: 0 source 0 - assert 1655253836.999996852, sequence: 970 - clear 0.000000000, sequence: 0 source 0 - assert 1655253838.000001719, sequence: 971 - clear 0.000000000, sequence: 0 source 0 - assert 1655253839.000002586, sequence: 972 - clear 0.000000000, sequence: 0 source 0 - assert 1655253840.000001453, sequence: 973 - clear 0.000000000, sequence: 0 ### ...etc 
If there is a timout, then there is likely not a good GPS lock yet.
Set up software:
- Edit 
/etc/default/gpsd:- change 
GPSD_OPTIONS=””toGPSD_OPTIONS=”-n” - change 
START_DAEMON="false"toSTART_DAEMON="true" - change 
DEVICES=””toDEVICES=”/dev/ttyS0 /dev/pps0″ 
 - change 
 - edit 
/etc/chrony/chrony.conffile, add this block of code to the top:### GPS TIME SYNC INFO # 'delay' describes the accuracy drift of the time source, in seconds. Larger numbers deprioritizes the source. NMEA Source needs at least some delay else chrony loses sync. # 'offset' adjusts source arrival time forward and backward. Use to sync GPS and PPS signals (time in [brackets] in chrony sources list). refclock SHM 0 delay 0.1 offset 0.1165 refid NMEA refclock PPS /dev/pps0 refid PPS # Allow all LAN IP Ranges so NTP server is network-agnostic (can be used on any LAN) allow 10.0.0.0/8 allow 192.168.0.0/16 allow 172.16.0.0/12 fd00::/8 ### END GPS TIME SYNC INFO - Reboot the Pi.
 - Check: after rebooting, run the program 
gpsmon, it should display a window similar to this:
 
This confirms that the software is decoding the GPS info properly.
- Check Chrony is selecting GPS as a time source; run 
chronyc sources
More info here under the header 'Time Sources'. TLDR: 'PPS' should have a '*' next to it, and the [bracketed] time in the 'NMEA' row should be less than ~5msec. change the offset in the### Output: MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #- NMEA 0 4 377 15 +3794us[+3794us] +/- 470us #* PPS 0 4 377 16 +351ns[ +515ns] +/- 3000ns ^- time.cloudflare.com 3 6 377 70 -2009us[-2008us] +/- 16ms ^- smtp.us.naz.com 2 6 377 2 -26ms[ -26ms] +/- 105ms ^- hc-007-ntp1.weber.edu 2 6 377 5 +3131us[+3131us] +/- 72ms ^- dns2.kcweb.net 2 6 377 6 +1296us[+1296us] +/- 88mschrony.conffile to adjust this value, and restart chrony with the commandsudo systemctl restart chrony. A "Reach" of '377' indicates source was polled sucessfully all 8 of the last 8 tries, ups trustworthiness. Lower numbers mean polls have been missed, less reliable. 
Set up other machines to use the Pi as an NTP server (Linux):
- Install 
ntppackage on the machine - Edit 
/etc/ntp.confand addserver [pi.local.ip] trueto the list of servers - start the ntp service (on Arch: 
sudo systemctl start ntpd) - Check the NTP sources with 
ntpq -p:
Check back in about 20 minutes, after which one source should have a '*' next to it to indicate that server is the chosen server. Remove default NTP servers and restart the ntp service if the pi is not selected. Note:remote refid st t when poll reach delay offset jitter ============================================================================== 192.168.1.137 .PPS. 1 u 35 64 1 1.851 +144501 0.001 time.walb.tech 50.205.244.21 3 u 34 64 1 82.802 +144501 0.001 li1187-193.memb 132.163.96.3 2 u 30 64 1 179.522 +144501 0.001 time-dfw.0xt.ca 68.166.61.255 2 u 33 64 1 106.817 +144501 0.001 LAX.CALTICK.NET 17.253.26.253 2 u 32 64 1 118.527 +144501 0.001trueadded after the pi's IP in the config indicates it is more "trustworthy" than other sources, and is more likely to be picked. - If NTP source is registered correctly, and you are ready to use NTP, enable the ntp service (on Arch: 
sudo systemctl enable ntpd) 
Set up other machines to use the Pi as an NTP server (Windows):
- Install Dimension 4.
 - add the pi as an SNTP source, set the sync time to every 5 minutes.
 - In the advanced settings menu, force the program to only sync the selected server to force it to use the pi.