Quantcast
Channel: Johannes Weber – Weberblog.net
Viewing all articles
Browse latest Browse all 311

NTP Server via GPS on a Raspberry Pi

$
0
0

This post shows how to use a GPS receiver with a Raspberry Pi to build a stratum 1 NTP server. I am showing how to solder and use the GPS module (especially with its PPS pin) and listing all Linux commands to set up and check the receiver and its NTP part, which is IPv6-only in my case. Some more hints to increase the performance of the server round things off. In summary this is a nice “do it yourself” project with a working stratum 1 NTP server at really low costs. Great. However, keep in mind that you should not rely on such projects in enterprise environments that are more focused on reliability and availability (which is not the case on self soldered modules and many config file edits).

This article is one of many blogposts within this NTP series. Please have a look!
Note that this project uses a GPS receiver with a PPS (pulse per second) pin, that is: It does not only get the imprecise time sent over a serial console (standard NMEA 0183 output, more details here), but also the highly precise sync tick which accurately declares the start of every second. This involves some soldering. ;)

At the time of writing (Nov 2018) I am using a Raspberry Pi 1 B (yes, the old one), kernel 4.14.71+ and Raspbian GNU/Linux 9 (stretch). I installed a few relevant packages and gave it a static IPv6 address. Legacy IP (IPv4) is not used at all, only IPv6.

1) Soldering the GPS Module

It’s all about the GPS module “GY-GPS6MV2”, though I am quite sure that there are some other GPS modules with a PPS pin out there. Google for it and get one from China for a couple of Euros. ;) I soldered the five cables directly to the Pi, while I used a small Pigtail from the Hirose  connector (the one on the GPS module) to SMA female (not RP-SMA!) to mount it on the housing of the Pi. This SMA connector is commonly used by GPS antennas for cars. In fact I did not use the small GPS antenna that was delivered with the GPS module at all, but a slightly more robust one that can be used outside as well.

Following is the assignment of the five cables from the GPS module to the Raspberry Pi. You need to look at the product details of the module to find the PPS pin. I used a ribbon cable, refer to the photos below:

  1. GPS module VCC, white: Pi pin 1, +3.3 V
  2. GPS module GND, black: Pi pin 6, GND
  3. GPS module RX, grey: Pi pin 8, TXD0
  4. GPS module TX, purple: Pi pin 10, RXD0
  5. GPS module PPS, blue: Pi pin 12, GPIO18

This is what it looked like on my desk:

When you’re finished you can just power on the Pi again. If you have a good GPS signal your GPS module will blink blue once a second:

 

2) Getting started with mere GPS

The first step is to check whether the GPS module is running and whether the Pi receives the generic NMEA 0183 sentences. You have to disable the serial login shell first while keeping the hardware port enabled:

sudo raspi-config
 , navigate to “5 Interface Options” -> “P6 Serial” and:
  1. Would you like a login shell to be accessible over serial? –> No
  2. Would you like the serial port hardware to be enabled? –> Yes

followed by a reboot. After that you can use “screen” to get some output:

sudo screen /dev/ttyAMA0 9600
. You can close it with
Ctrl+a \ y
. The output should be like:
$GPRMC,132628.00,A,5123.20905,N,00842.80522,E,0.416,,081118,,,A*7F
$GPVTG,,T,,M,0.416,N,0.771,K,A*21
$GPGGA,132628.00,5123.20905,N,00842.80522,E,1,09,1.50,235.3,M,47.4,M,,*5C
$GPGSA,A,3,23,16,22,03,09,02,26,06,07,,,,2.34,1.50,1.79*07
$GPGSV,3,1,12,02,30,308,21,03,28,110,39,06,58,254,27,07,30,173,21*7D
$GPGSV,3,2,12,09,87,242,12,16,08,076,21,19,10,239,,22,08,115,39*71
$GPGSV,3,3,12,23,62,066,47,26,11,050,22,30,03,191,20,39,13,117,*79
$GPGLL,5123.20905,N,00842.80522,E,132628.00,A,A*64
$GPRMC,132629.00,A,5123.20896,N,00842.80502,E,0.130,,081118,,,A*76
$GPVTG,,T,,M,0.130,N,0.241,K,A*26
$GPGGA,132629.00,5123.20896,N,00842.80502,E,1,09,1.50,235.3,M,47.4,M,,*54
$GPGSA,A,3,23,16,22,03,09,02,26,06,07,,,,2.34,1.50,1.79*07
$GPGSV,3,1,12,02,30,308,22,03,28,110,39,06,58,254,27,07,30,173,22*7D
$GPGSV,3,2,12,09,87,242,12,16,08,076,19,19,10,239,,22,08,115,39*7A
$GPGSV,3,3,12,23,62,066,47,26,11,050,22,30,03,191,20,39,13,117,*79
$GPGLL,5123.20896,N,00842.80502,E,132629.00,A,A*6C
$GPRMC,132630.00,A,5123.20904,N,00842.80489,E,0.192,,081118,,,A*7E
$GPVTG,,T,,M,0.192,N,0.355,K,A*2A
$GPGGA,132630.00,5123.20904,N,00842.80489,E,1,09,1.50,235.1,M,47.4,M,,*56
$GPGSA,A,3,23,16,22,03,09,02,26,06,07,,,,2.34,1.50,1.79*07
$GPGSV,3,1,12,02,30,308,22,03,28,110,39,06,58,254,27,07,30,173,23*7C
$GPGSV,3,2,12,09,87,242,12,16,08,076,19,19,10,239,,22,08,115,39*7A
$GPGSV,3,3,12,23,62,066,47,26,11,050,21,30,03,191,20,39,13,117,*7A
$GPGLL,5123.20904,N,00842.80489,E,132630.00,A,A*6C

Now you need to install the gpsd daemon which needs to run in the background at startup:

sudo apt-get install gpsd gpsd-clients

sudo nano /etc/default/gpsd
# adjust the following values to:
START_DAEMON="true"
USBAUTO="false"
DEVICES="/dev/ttyAMA0"
GPSD_OPTIONS="-n"

# the following symlink is needed to have gpsd started after boot correctly
# ref: https://www.raspberrypi.org/forums/viewtopic.php?f=45&t=53644#p1048128
sudo ln -s /lib/systemd/system/gpsd.service /etc/systemd/system/multi-user.target.wants/


sudo reboot

After the reboot, check that the daemon is running:

pi@ntp2-gps:~ $ sudo service gpsd status
● gpsd.service - GPS (Global Positioning System) Daemon
   Loaded: loaded (/lib/systemd/system/gpsd.service; indirect; vendor preset: enabled)
   Active: active (running) since Thu 2018-11-08 14:39:31 CET; 20h ago
 Main PID: 768 (gpsd)
   CGroup: /system.slice/gpsd.service
           └─768 /usr/sbin/gpsd -N -n /dev/ttyAMA0

Nov 08 14:39:31 ntp2-gps systemd[1]: Started GPS (Global Positioning System) Daemon.

With tools such as

cgps -s
or
gpsmon
you get user friendly information from your GPS module such as:
┌──────────────────────────┐┌─────────────────────────────────────────────────┐
│Ch PRN  Az  El S/N Flag U ││ECEF Pos: +4012339.72m +611236.24m +4812371.38m  │
│ 0   2 305  33  23 040f Y ││ECEF Vel:     +0.23m/s     +0.21m/s     +0.07m/s │
│ 1   3 113  24  42 070f Y ││                                                 │
│ 2   4   0 165   0 0110   ││LTP Pos:  50.108056321°   8.682167572°   211.70m │
│ 3   6 246  57  20 040e   ││LTP Vel:    0.23m/s 131.7°   0.22m/s             │
│ 4   7 172  34  34 070f Y ││                                                 │
│ 5   9  44  88  30 060f Y ││Time: 45 15:57:10.00                             │
│ 6  16  73  10  29 050f Y ││Time GPS: 2026+394543.000     Day: 4             │
│ 7  19 236   6  20 040f Y ││                                                 │
│ 8  22 116   5  37 070e   ││Est Pos Err   7.21m Est Vel Err   0.00m/s        │
│ 9  23  66  59  49 070f Y ││PRNs:  9 PDOP:  1.4 Fix 0x03 Flags 0xdf          │
│10  26  46  12  23 040f Y │└─────────────────── NAV_SOL ─────────────────────┘
│11  29 353   1   0 0104   │┌─────────────────────────────────────────────────┐
│12  30 191   6  36 070f Y ││DOP [H]  0.9 [V]  1.1 [P]  1.4 [T]  0.6 [G]  1.5 │
│13 123 151  28  41 061c   │└─────────────────── NAV_DOP ─────────────────────┘
│14 124 163  31   0 0110   │┌─────────────────────────────────────────────────┐
│15 126 117  13   0 0014   ││TOFF:  0.156538000       PPS:                    │
└────── NAV_SVINFO ────────┘└─────────────────────────────────────────────────┘

Good so far. Your GPS is up and running. ;D

3) Leveraging PPS

You now have to activate the pulse per second functionality:

sudo apt-get install pps-tools

sudo nano /boot/config.txt
# add the following line:
dtoverlay=pps-gpio

sudo nano /etc/modules
# add the following line:
pps-gpio

sudo reboot

After the reboot, check that the pps module is loaded, which is the case if “lsmod” lists some drivers such as this:

pi@ntp2-gps:~ $ lsmod | grep pps
pps_ldisc               2413  2
pps_gpio                3089  1
pps_core                8606  4 pps_gpio,pps_ldisc

and try to get a tick every second (cancel it with Ctrl+c as always):

pi@ntp2-gps:~ $ sudo ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1541684326.001410675, sequence: 79 - clear  0.000000000, sequence: 0
source 0 - assert 1541684327.001418675, sequence: 80 - clear  0.000000000, sequence: 0
source 0 - assert 1541684328.001424675, sequence: 81 - clear  0.000000000, sequence: 0
source 0 - assert 1541684329.001432675, sequence: 82 - clear  0.000000000, sequence: 0
source 0 - assert 1541684330.001437675, sequence: 83 - clear  0.000000000, sequence: 0
source 0 - assert 1541684331.001445675, sequence: 84 - clear  0.000000000, sequence: 0
^C

Good again.

4) Configuring NTP

I have first installed NTP via

sudo apt-get install ntp
to have all those dependencies and startup scripts in place, but built it from source afterwards as well to run the server in the latest version:
sudo nano /etc/apt/sources.list
# uncomment the line with "deb-src"
deb-src http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi

sudo apt-get update
sudo apt-get -y build-dep ntp

# download the latest ntp: http://ntp.org/downloads.html
# in this example it was 4.2.8p12
wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p12.tar.gz
cd ntp-4.2.8p12/
# note: the following step takes some time:
./configure --prefix=/usr
# note: the following "make" takes even longer, especially on an old Pi 1 B
make
sudo service ntp stop
sudo make install
# to disable updating ntp via apt-get, echo the following:
echo "ntp hold" | sudo dpkg --set-selections
sudo service ntp start

(Fun fact: Since I am running my NTP server via IPv6-only meanwhile the NTP download page is IPv4-only, I had to download and copy the ntp package from another machine to the Raspberry Pi. Sigh. Yes, I know, DNS64/NAT64 would solve the problem.)

You have just installed the latest version of NTP, while “holding” the ntp package within dpkg to not overriding/downgrading it with future “apt-get update”s. You can verify this with the following: (Note the “h” in the very first column which indicates that the ntp package is on hold.)

pi@ntp2-gps:~ $ dpkg -l | grep ntp
hi  ntp                              1:4.2.8p10+dfsg-3+deb9u2     armhf        Network Time Protocol daemon and utility programs

Similarly you can verify the running ntp version with:

pi@ntp2-gps:~ $ ntpq --version
ntpq 4.2.8p12@1.3728-o Thu Nov  8 11:55:00 UTC 2018 (1)

 

Now you need to add the reference clock (GPS + PPS) to NTP: “Drivers have addresses in the form 127.127.t.u, where t is the driver type and u is a unit number in the range 0-3 to distinguish multiple instances of the same driver”, Reference Clock Support. In fact we have to use two drivers, one for the GPS to get the rough time (driver 28, SHM, shared memory driver which uses the gpsd daemon) and another one for the PPS (driver 22).

There are a few things to consider. First, concerning the PPS: “While this driver can discipline the time and frequency relative to the PPS source, it cannot number the seconds. For this purpose an auxiliary source is required, ordinarily a radio clock operated as a primary reference (stratum 1) source; however, another NTP time server can be used as well. For this purpose, the auxiliary source should be specified as the prefer peer, as described in the Mitigation Rules and the prefer Keyword page”, PPS Clock Discipline. And about the SHM and its “flag1”: “Skip the difference limit check if set. Useful for systems where the RTC backup cannot keep the time over long periods without power and the SHM clock must be able to force long-distance initial jumps. Check the difference limit if cleared (default)”, Shared Memory Driver.

TL;DR: I had many problems running the Pi with *only* GPS/PPS, because after reboots or temporarily loss of GPS signal it did not come back into normal working mode. The solution was to add some more NTP servers with the “prefer” statement to continuously have the time synced, while PPS can then do the exact and very precise timing.

In the end I used these lines for my two drivers in the

sudo nano /etc/ntp.conf
file:
# pps-gpio /dev/pps0
server 127.127.22.0 minpoll 4 maxpoll 4
fudge 127.127.22.0 refid PPS

# gpsd clock via shm
server 127.127.28.0 minpoll 4 maxpoll 4 prefer
fudge 127.127.28.0 time1 +0.130 refid GPS flag1 1

in conjunction with some more (in my case: IPv6 capable) NTP servers:

#http://support.ntp.org/bin/view/Servers/PublicTimeServer000388
server ntp.probe-networks.de prefer
#http://support.ntp.org/bin/view/Servers/PublicTimeServer001352
server time.hueske-edv.de prefer
#http://support.ntp.org/bin/view/Servers/PublicTimeServer000840
server ntp2.301-moved.de prefer
#http://support.ntp.org/bin/view/Servers/PublicTimeServer001363
server ntp.fanlin.de prefer

(Note that I used “fudge 127.127.28.0 time1 +0.130 …” to adjust the GPS time according to the other NTP servers. You can try some higher/lower values to have the offset of your SHM compared to the offsets of those other NTP servers very small.)

Finally, it should give you something like this:

pi@ntp2-gps:~ $ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
oPPS(0)          .PPS.            0 l    9   16  377    0.000    0.002   0.004
*SHM(0)          .GPS.            0 l   11   16  377    0.000   -2.163   1.915
 ptbtime2.ptb.de .PTB.            1 u    1   64  377   10.318    0.258   0.112
+ns2.probe-netwo 124.216.164.14   2 u   22   64  377    3.591   -0.137   0.308
+2a01:4f8:201:41 192.53.103.108   2 u   14   64  377   16.534   -4.055   0.094
+2a02:a00:1009:6 193.190.230.65   2 u   65   64  377    6.683   -0.072   0.124
+2001:4ba0:ffa4: 130.149.17.21    2 u   18   64  377    6.266   -2.095   0.117

Note the ° symbol (= PPS peer when the prefer peer is valid) before the PPS, as well as the * symbol (= system peer) before the SHM; refer to Peer Status Word. These symbols must be present. Otherwise your instance is not working correctly, which would show an x instead.

Congratulations! You have your own NTP server on a Raspberry Pi with GPS and PPS up and running. Go for it! :D

5) Enhanced PPS Performance

[UPDATE Nov. 2018] Please note that I was not able to reproduce this behaviour on a fresh Raspbian stretch installation in 2018 anymore. Independent of the below mentioned settings the jitter of the PPS was about 4 µs. The following listings are from an NTP server with Raspbian jessie, kernel 4.4.26+, in 2016/2017.[/UPDATE]

I came across the very detailed page from David Taylor in which he has some thoughts about reducing the (already very good) jitter for the PPS. Section “Enhanced PPS performance” on his page.

I opened the file

sudo nano /boot/cmdline.txt
and added
nohz=off
at the end of this single line. Of course with a space before it. Followed by a
sudo reboot
.

The jitter of the PPS driver decreased from about 0.005 ms = 5 µs to about 2 µs. Wow!

# BEFORE
pi@ntp2:~ $ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
oPPS(0)          .PPS.            0 l   15   16  377    0.000   -0.005   0.005
*SHM(0)          .GPS.            0 l   14   16  377    0.000   -1.734   1.173
 name3.glorb.com 216.218.254.202  2 u    8   64  377  175.817   -2.305   0.220
 2001:19f0:6c01: 36.224.68.195    2 u   60   64  377    2.282    0.936   0.078
 isis.uni-paderb 192.53.103.108   2 u   13   64  377    9.797    1.354   6.588

# AFTER
pi@ntp2:~ $ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
oPPS(0)          .PPS.            0 l    4   16  377    0.000   -0.001   0.002
*SHM(0)          .GPS.            0 l    7   16  377    0.000   -0.166   1.897
 ntp5.netcompart 193.79.237.14    2 u   25   64  377    6.924    1.350   2.424
 headnode.szeged 0.67.4.139       2 u   20   64  377   22.045    0.309   0.220
 ftp.icm.edu.pl  210.100.177.101  2 u   61   64  377   19.681    0.261   0.146

 

6) Reducing Ethernet Latency

And even one more hint from David Taylor about reducing the Ethernet latency on a Pi: Adding another option to the single line in

sudo nano /boot/cmdline.txt
that states:
smsc95xx.turbo_mode=N
(reboot needed) indeed reduces the “delay” as shown in the “ntpq -p” output again. I used two Raspberry Pis connected via a single switch, first time without the option, second with the option set on both Pis. The delay between those NTP servers decreased from about 0.916 ms to 0.632 ms. Nice.
# BEFORE
pi@ntp1-dcf77:~ $ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 GENERIC(0)      .DCFa.           0 l    -   64    0    0.000    0.000   0.000
*2003:de:2016:33 .PPS.            1 s   28   64  377    0.916    1.125   0.759
 ptbtime2.ptb.de .PTB.            1 u   26   64  377    9.782    1.081   0.419

# AFTER
pi@ntp1-dcf77:~ $ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 GENERIC(0)      .DCFa.           0 l    -   64    0    0.000    0.000   0.000
*2003:de:2016:33 .PPS.            1 s   24   64  377    0.632    0.120   0.737
 ptbtime2.ptb.de .PTB.            1 u   35   64  377    9.444    1.592   0.554

That’s it for now. Wow! If you have any suggestions, please write a comment.

Links

As always here are some links for further reading:

Featured image “Kompass_.jpg” by Rolf Schmitz is licensed under CC BY-SA 2.0.


Viewing all articles
Browse latest Browse all 311

Trending Articles