Jeff M Belt
© 2018 - All Rights Reserved
Sourced from http://www.adafruit.com
• Sourced from http://www.adafruit.com
There are two signals, the NMEA Sentences and the PPS signal.
The GPS module is connected via serial (9600 baud) to the RPi. Every second it sends NMEA sentences. This are simple lines of text values in a comma separated format. The first column indicates the type of data that follows. The most important ones are $GPRMC, $GPGLL, $GPGGA. These indicate the current position, but more important in this case, they also contain the current time. The downside is that this time information is not very accurate, it could easily be 100ms off and there is quite a bit of jitter. In practice, you get a more accurate time using sources on the internet. So this information is mainly useful when you do not want to depend on internet sources.
What baud rate?
While it is true the faster the baud rate on the serial line provides more data, there is a practical limit in time keeping. Later on in this guide, we will be configuring the NEMA sentences to use. More sentences does not, in reality, translate to more accurate time discipline. So we will be limiting the GPS to only those which make practical sense for us to use. Additional, to ensure accuracy, we need a time pulse once every second. In NTP and time keeping terms, this is known as a Pulse Per Second or PPS.
The PPS signal is the most important feature for accurate time keeping. It does not provide the current time but instead its a signal that sends one pulse every second. This pulse indicates the start of the second with a high accuracy. The signal is simply a voltage being applied on a GPIO pin for 100 milliseconds, after which it goes back to 0 for 900ms.
In case of the Adafruit Ultimate GPS Hat for Raspberry Pi module, the rising edge indicates the start of the second. Combined with a coarse grained time (accurate to ~0.4 seconds) obtained from the NMEA sentences on the serial port, or another time source such as an NTP server on the internet, this results in a very accurate clock.
You need a kernel with PPSAPI support. Luckily this is included in the current raspbian kernels so this does not require any recompiling. Install the operating system as usual. Start by installing all the latest updates first and assign a static IP to the machine. If you want to use DHCP, beware that some additional configuration may be required to prevent it from overwriting the NTP configuration file. Even thought I use a custom built version of Raspbian, for this procedure, I recommending using RASPBIAN JESSIE LITE to keep the OS footprint to a minimum. This has a side affect of keeping the attack footprint to a minimum as-well.
NOTE: The image provided in my downloads page uses a custom build to keep the image attach surface as small as possible. If you want to use the custom built Raspbian this image started with, see Raspbian UA NetInst. This provides an even smaller installation footprint than RASPBIAN JESSIE Lite; only about 600 Megs. This is for more advanced users as there will be additional packages, such as GCC which, in order to get a working NTP server. This page does not cover the steps or additional packages required.
First of all, change the kernel parameters so that GPIO4 is used as PPS source and disable the serial console:
Add the following line to /boot/config.txt:
Edit /boot/cmdline.txt and remove the following:
After making these changes, reboot the Pi. Before you do, make sure the SSH Services from the Raspberry Pi orgianization for information on how to do this.
The ntp package included in raspbian does not support the ATOM (PPS) reference clock so next you need to recompile the ntp package. Some people use the Raspbian packages for this, but I personally like to get the latest NTP versions from the source to know any security or bug fixes have been incorporated. Do not worry about removing the Raspbian package prior to starting the setup below. We will be using some of its components (specifically, the start / stop scripts and directory support structure.
• Download the latest NTP source from http://www.ntp.org/downloads.html
• Unpack and configure using the following options
# ./configure --prefix=/usr \
--enable-all-clocks --enable-parse-clocks --enable-SHM \
--disable-debugging --sysconfdir=/var/lib/ntp \
Compile the source code
# make -j 4
NOTE: I am using -j 4 as my Raspberry Pi has four (4) cores. Might as well use them all.
Install the newly compiled source.
# make install
By now, some may have noticed we did not uninstall the original NTP package. This was on purpose, so we could use it for any dependcies, and leverage its startup scripts. Since you have your newly compiled NTP is installed, lets prevent apt from updating it by executing the following:
# dpkg --get-selections | grep ntp
# apt-mark hold ntp
ntp set on hold.
# apt-mark hold ntpdate
ntpdate set on hold.
With NTP updates out of the way, it is time to setup device links so we can allow NTP to use our hardware. Add the following udev rules which create the symlink required so that ntpd can use the GPS module. Create a new file /etc/udev/rules.d/09-pps.rules with the following contents and restart the RPi:
KERNEL=="pps0", OWNER="root", GROUP="tty", MODE="0777", SYMLINK+="gpspps0"
This will result in /dev/gps0 and /dev/pps0 devices being created. The two devices are referenced by NTP's NEAM and PPS modules.
Now that PPS support is compiled into NTP, all that is left is configuring NTPd to use the PPS source. There are several ways in which this can be configured, either using the NMEA driver with PPS support enabled, or using the ATOM driver with a preferred peer. I personally like the NMEA driver with PPS support approach as it is less code modules running on the system, hence a smaller attach footprint. (Anyone sensing a pattern in my preferred approach here?)
The easiest and most reliable method is to use the NMEA driver (20) with flag1 (PPS) enabled. The configuration required is:
server 127.127.20.0 mode 27 minpoll 3 iburst prefer
fudge 127.127.20.0 flag1 1 time2 0.496
Configuration options for our GPS setup:
• server 127.127.20.0 tells it that we have a serial GPS reciever on /dev/gps0
• mode 27 tells it to look for $GPRMC messages at a baud of 9600 bps
• fudge 127.127.20.0 tells it that we have some further configuration for our GPS
• flag1 1 tells it that we have a PPS interface from our GPS on /dev/pps0
• time2 0.496 specifies the serial end of line time offset calibration factor, in seconds and fraction
• refid GPS tells it to label this time source with the reference ".GPS."
Full documentation for the ntpd NMEA driver (for if you require different baud rates etc) can be found here.
NOTE: Some recommend setting 'true' on the server line for the GPS. THIS IS A BAD IDEA. This tells NTP to 'believe this clock even if it appears to be insane'. The only reason to do this would be if the NMEA time has large jitter & is the only source. Using true can cause PPS could be discarded by the selection/clustering algorithms.
Create the following shell script, and install it into “/usr/local/bin/gps-init.sh”:
# Only output
/bin/echo -e "\$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*29\r\n"
# Output once a second
/bin/echo -e "\$PMTK220,1000*1F\r\n"
# Sample once a second
/bin/echo -e "\$PMTK300,1000,0,0,0,0*1C\r\n"
# Set baud to 115200
/bin/echo -e "\$PMTK251,9600*1F\r\n"
) > $DEVICE
# Set Port speed to 9600
/bin/stty -F $DEVICE raw 9600 clocal -cstopb
Make sure it’s executable:
# chmod a+x /usr/local/bin/leap-seconds.sh
Now add the following to /etc/rc.local to initialize the GPS on system boot
There is much discussion out there on calibrating the time2 value using the Raspberry Pi as an NTP server. Before determining this value, what is time2?
time2 is an offset between the (end of the) NEMA sentence and the correct time. Or another way of thinking about it, time2 is the delay from the time the GPS receives the PPS signal and the tick tocks to process NEMA sentences on the CPU. Unfortunately, time2 is interpreted in a driver-dependent way.
The easiest way to find the time2 value, is to disable the GPS receiver in NTP, lock the configuration to a good set of external servers. Personally, I recommend 'time.nist.gov' and 'time.mit.edu,' but PLEASE select NTP servers closer to your geographic location. Then, use 'ppstest' to show the timestamp of the received PPS interrupts. Use that to set time2 so that NTP gets the right value when the GPS receiver is enabled.
The concept here is once PPS enabled (and working), to set time2's value so that the time converges quickly. Ironically, once a PPS lock is attained, time2 isn't important.
Ironically, time2 is not used by the Generic NMEA GPS Receiver used by the configuration in this implementation, so the value of time2 is irrelevant. See the driver reference documentation for details as time2 is interpreted differently depending on the driver being used.
So why do I have 0.496 in the NTP configuration? Simple, it is a carry over from my Garmin 18x NTP server configuration which I never removed.
What can I say, I am lazy. No really, it is included just to be able to explain some of the hype out there on what time2 is, and why it is important.
Create the following shell script, and install it into “/usr/local/bin/leap-seconds.sh”:
wget -q ftp://time.nist.gov/pub/leap-seconds.list &> /dev/null
service ntp restart &> /dev/null
Then, create an entry in root’s crontab:
0 0 31 6,12 * /usr/local/bin/leap-seconds.sh
Make sure it’s executable:
# chmod a+x /usr/local/bin/leap-seconds.sh
Modify /etc/ntp.conf to resemble below
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
Verify your NTP server is using the PPS Signal of the GPS.
# ntpq -p
remote refid st t when poll reach delay offset jitter
oGPS_NMEA(0) .GPS. 0 l 5d 8 0 0.000 0.004 0.002
*nist1-lnk.binar .ACTS. 1 u 23 64 367 46.798 0.437 0.596
+kerberos.mit.ed 126.96.36.199 2 u 25 64 377 7.475 -6.000 0.292
If everything is working, you should see an "o" on the GPS_NMEA(0) line. It may take a while for your GPS to obtain a PPS lock, so be patient. The first time I did it, it took almost 2 hours, but after moving the GPS receiver to have a better view of the sky, it takes only a few minutes now, if that.
Interpreting NTPQ Output:
• remote – The remote peer or server being synced to. “LOCAL” is this local host (included in case there are no remote peers or servers available);
• refid – Where or what the remote peer or server is itself synchronised to;
• st – The remote peer or server Stratum
• t – Type (u: unicast or manycast client, b: broadcast or multicast client, l: local reference clock, s: symmetric peer, A: multicast server, B: broadcast server, M: multicast server, see “Automatic Server Discovery“);
• when – When last polled (seconds ago, “h” hours ago, or “d” days ago);
• poll – Polling frequency: rfc5905 suggests this ranges in NTPv4 from 4 (16s) to 17 (36h) (log2 seconds), however observation suggests the actual displayed value is seconds for a much smaller range of 64 (26) to 1024 (210) seconds;
• reach – An 8-bit left-shift shift register value recording polls (bit set = successful, bit reset = fail) displayed in octal;
• delay – Round trip communication delay to the remote peer or server (milliseconds);
• offset – Mean offset (phase) in the times reported between this local host and the remote peer or server (RMS, milliseconds);
• jitter – Mean deviation (jitter) in the time reported for that remote peer or server (RMS of difference of multiple time samples, milliseconds);
Select Field Code: (first column)
The first character displayed in the table (Select Field Code) is a state flag (see Peer Status Word) that follows the sequence ” “, “x”, “-“, “#”, “+”, “*”, “o”:
• ” ” – No state indicated for: non-communicating remote machines, “LOCAL” for this local host, (un-utilized) high stratum servers, remote machines that are themselves using this host as their synchronization reference;
• “x” – Out of tolerance, do not use (discarded by intersection algorithm);
• “–” – Out of tolerance, do not use (discarded by the cluster algorithm);
• “#” – Good remote peer or server but not utilized (not among the first six peers sorted by synchronization distance, ready as a backup source);
• “+” – Good and a preferred remote peer or server (included by the combine algorithm);
• “*” – The remote peer or server presently used as the primary reference;
• “o” – PPS peer (when the prefer peer is valid). The actual system synchronization is derived from a pulse-per-second (PPS) signal, either indirectly via the PPS reference clock driver or directly via kernel interface.
# ntpq -c rv
associd=0 status=0615 leap_none, sync_ntp, 1 event, clock_sync,
version="ntpd firstname.lastname@example.org Wed Dec 23 20:59:33 UTC 2015 (2)",
processor="armv7l", system="Linux/4.9.30-v7+", leap=00, stratum=2,
precision=-18, rootdelay=44.268, rootdisp=2.082, refid=188.8.131.52,
reftime=dceac6d2.1b1e65a9 Tue, Jun 13 2017 16:17:22.105,
clock=dceac708.21d2cd63 Tue, Jun 13 2017 16:18:16.132, peer=37296, tc=6,
mintc=3, offset=0.206530, frequency=-1.429, sys_jitter=1.061874,
clk_jitter=0.728, clk_wander=0.028, tai=36, leapsec=201507010000,
NOTE: For those meticulous people out there who REALLY know this stuff, the two ntpq commands were run from different systems at different times and days, so statistically, they won't align properly.
Change the CPU Governor from the default of ‘ondemand’ to ‘performance.‘
# echo 'performance' > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
Add the above to /etc/rc.local so that’s applied on boot.
Lastly, reconfigure the local timezone (optional)
# dpkg-reconfigure tzdata
Now that you have read all of that, you can also download my image from the downloads page here.