Table of Contents

Time

gpsd.jpg

This time service is comprised built on a RPi with a LoRa board with a GPS module with PPS. An additional USB GPS dongle is also evaluated but is not a requirement and may even confuse the GPS daemon.

Linux

Minimal hardware configuration is necessary to enable PPS on a GPIO, enable SPI for the LoRa controller (if needed) and use a easily remembered persistent name for the dongle.

More complex kernel configuration may also be performed.

Configuration

First install some tools.

apt install cpufrequtils
update-rc.d cpufrequtils disable
update-rc.d loadcpufreq disable

config.txt

For a headless display use a minimal amount of RAM for video and enable the 1PPS output from the module on GPIO18. SPI is enabled if the LoRa module is required. On the Pi3, we switch the miniuart from the GPIO header to the BT module which we do not use.

gpu_mem=16
dtoverlay=pps-gpio,gpiopin=18
dtparam=spi=on
dtoverlay=pi3-miniuart-bt

rc.local

During start-up set the CPU to use a fixed clock rate.

/usr/bin/cpufreq-set -g userspace
/usr/bin/cpufreq-set -f 600000

After configuration, reboot the Pi.

UDEV

Add this to local rules.

# U-Blox AG [u-blox 7]
KERNEL=="ttyACM*",\
    ATTRS{idVendor}=="1546",\
    ATTRS{idProduct}=="01a7",\
    GROUP="dialout",\
    MODE:="0666",\
    RUN+="/bin/ln -sf %k /dev/ttyGPS"

New rules can be registered using udevadm.

udevadm control --reload-rules
udevadm trigger
Kernel

Building a kernel with the following options enables kernel 1PPS time synchronisation.

General setup --->
 Timers subsystem --->
  Timer tick handling --->
   (X) Periodic timer ticks
  ( ) Old Idle dynticks config
  (X) High resolution Timer Support
Device Drivers --->
 PPS support --->
  [*] PPS kernel consumer support
  <M> PPS line discipline
  <M> PPS client using GPIO

See here for more information about building a RPi kernel.

The difference to the stock kernel is this:

diff .config.orig .config
85,87c85,86
< CONFIG_NO_HZ_COMMON=y
< # CONFIG_HZ_PERIODIC is not set
< CONFIG_NO_HZ_IDLE=y
---
> CONFIG_HZ_PERIODIC=y
> # CONFIG_NO_HZ_IDLE is not set
89c88
< CONFIG_NO_HZ=y
---
> # CONFIG_NO_HZ is not set
2978a2978
> CONFIG_NTP_PPS=y

When running a kernel with the above options, the following is observed

ntpq -c kerninfo && ntptime
associd=0 status=0115 leap_none, sync_pps, 1 event, clock_sync,
pll offset:            0
pll frequency:         3.55501
maximum error:         0.505
estimated error:       1e-06
kernel status:         pll ppsfreq ppstime ppssignal nano
pll time constant:     4
precision:             1e-06
frequency tolerance:   500
pps frequency:         3.55486
pps stability:         0.0237427
pps jitter:            0.001
calibration interval   256
calibration cycles:    380
jitter exceeded:       68
stability exceeded:    0
calibration errors:    1
0:00.03s
ntp_gettime() returns code 0 (OK)
  time e68bc700.3e61f18c  Wed, Jul 27 2022 15:15:28.243, (.243682974),
  maximum error 505000 us, estimated error 1 us, TAI offset 37
ntp_adjtime() returns code 0 (OK)
  modes 0x0 (),
  offset 0.000 us, frequency 3.555 ppm, interval 256 s,
  maximum error 505000 us, estimated error 1 us,
  status 0x2107 (PLL,PPSFREQ,PPSTIME,PPSSIGNAL,NANO),
  time constant 4, precision 0.001 us, tolerance 500 ppm,
  pps frequency 3.555 ppm, stability 0.024 ppm, jitter 1.292 us,
  intervals 380, jitter exceeded 68, stability exceeded 0, errors 1.

NB ntpq and ntptime are part of ntp.

PPS

PPS is a hardware time signal delivered once per second from the GPS module. On the RPi this is connected to a GPIO, generally GPIO18, which was configured above.

When booting the system the following message will be found.

pps_core: LinuxPPS API ver. 1 registered
pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
pps pps0: new PPS source pps@12.-1
pps pps0: Registered IRQ 199 as PPS source
pps_ldisc: PPS line discipline registered
pps pps0: bound kernel consumer: edge=0x1
Test

Install the tools

apt install pps-tools

Begin teting

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 1658756336.304112829, sequence: 2941 - clear  0.000000000, sequence: 0
source 0 - assert 1658756337.304112557, sequence: 2942 - clear  0.000000000, sequence: 0
source 0 - assert 1658756338.304110046, sequence: 2943 - clear  0.000000000, sequence: 0
source 0 - assert 1658756339.304109722, sequence: 2944 - clear  0.000000000, sequence: 0
source 0 - assert 1658756340.304110908, sequence: 2945 - clear  0.000000000, sequence: 0
source 0 - assert 1658756341.304108969, sequence: 2946 - clear  0.000000000, sequence: 0
source 0 - assert 1658756342.304108645, sequence: 2947 - clear  0.000000000, sequence: 0
^C
Interrupts
egrep '(^           CPU|pps@)' /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
199:      88888          0          0          0  pinctrl-bcm2835  18 Edge      pps@12.-1
Issues

When running NTPD an error is reported regarding PPS.

refclock_nmea: time_pps_kcbind failed: Operation not supported

This error is due to LinuxPPS not supporting the bind feature, the FAQ for more details.

GPS

Install clients and server.

sudo apt-get install gpsd gpsd-clients

Disable services for now

sysv

update-rc.d gpsd disable

systemd

systemd hijacks the GPSD socket (like inetd) so you will need to try a few things to resolve this.

systemctl disable gpsd
systemctl mask gpsd
systemctl disable gpsd.sock
systemctl mask gpsd.sock
reboot

If you need ppscheck for some reason, compile it like this (not needed here).

sudo gcc -o /usr/local/bin/ppscheck /usr/share/doc/gpsd-clients/examples/ppscheck.c
GPS server

Initialise the serial port to 115200 baud

Refer to L80 GPS Protocol Specification #3.16. Packet Type: 251 PMTK_SET_NMEA_BAUDRATE

echo '$PMTK251,115200*1F\r\n' > /dev/ttyAMA0
stty -F /dev/ttyAMA0 115200

Run the daemon like this from runit or equivalent.

/usr/sbin/gpsd -G -n -N /dev/ttyAMA0 /dev/pps0 /dev/ttyGPS

Kernel logging will produce the following from the PPS module.

pps pps1: new PPS source ttyAMA0
pps pps1: source "/dev/ttyAMA0" added
pps pps2: new PPS source acm0
pps pps2: source "/dev/ttyACM0" added
GPS monitor

GPSMON can view the running state of the GPS modules handled by GPSD. To pause the display press CTRL-S and CTRL-Q to continue.

View the device attached to the LoRa board

gpsmon localhost:gpsd:/dev/ttyAMA0
/dev/ttyAMA0:/dev/ttyAMA0     NMEA0183>
┌──────────────────────────────────────────────────────────────────────────────┐
│Time: 2022-07-24T14:56:23.000Z Lat:  00 00' 00.000" N Lon:   0 00' 00.000" E  │
└───────────────────────────────── Cooked TPV ─────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ GPZDA GPRMC GPGGA GPGSA GPTXT GPGSV                                          │
└───────────────────────────────── Sentences ──────────────────────────────────┘
┌──────────────────┐┌────────────────────────────┐┌────────────────────────────┐
│Ch PRN  Az El S/N ││Time:      145623.000       ││Time:      145623.000       │
│ 0   4 155 65  21 ││Latitude:     0000.0000 N   ││Latitude:  0000.0000        │
│ 1   3  71 63  23 ││Longitude:   00000.0000 E   ││Longitude: 00000.0000       │
│ 2  19 272 45  40 ││Speed:     0.00             ││Altitude:  8.7              │
│ 3  17 236 42  44 ││Course:    71.70            ││Quality:   1   Sats: 10     │
│ 4   9 200 37  29 ││Status:    A       FAA: A   ││HDOP:      0.84             │
│ 5   1 134 33  16 ││MagVar:                     ││Geoid:     47.0             │
│ 6   6 304 32  22 │└─────────── RMC ────────────┘└─────────── GGA ────────────┘
│ 7 120 199 29   0 │┌────────────────────────────┐┌────────────────────────────┐
│ 8  31  53 21  18 ││Mode: A3 Sats: 4 3 19 17 9  ││UTC:           RMS:         │
│ 9  21 137 11  16 ││DOP: H=0.84  V=0.80  P=1.15 ││MAJ:           MIN:         │
│10  12 331  6  25 ││TOFF:  0.212566738          ││ORI:           LAT:         │
│11  22  40  6   0 ││PPS:                        ││LON:           ALT:         │
└────── GSV ───────┘└──────── GSA + PPS ─────────┘└─────────── GST ────────────┘

View the USB dongle.

gpsmon localhost:gpsd:/dev/ttyGPS
/dev/ttyGPS:/dev/ttyGPS       u-blox>
┌──────────────────────────────────────────────────────────────────────────────┐
│Time: 2022-07-24T14:55:38.000Z Lat:  00 00' 00.000" N Lon:   0 00' 00.000" E  │
└───────────────────────────────── Cooked TPV ─────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ GPRMC GPVTG GPGGA GPGSA GPGSV GPGLL                                          │
└───────────────────────────────── Sentences ──────────────────────────────────┘
┌──────────────────┐┌────────────────────────────┐┌────────────────────────────┐
│Ch PRN  Az El S/N ││Time:      145538.00        ││Time:      145538.00        │
│ 0   1 134 33   0 ││Latitude:    0000.00000 N   ││Latitude:  0000.00000       │
│ 1   3  71 63  20 ││Longitude:  00000.00000 E   ││Longitude: 00000.00000      │
│ 2   4 155 65   0 ││Speed:     0.440            ││Altitude:  -17.9            │
│ 3   6 304 32  34 ││Course:                     ││Quality:   1   Sats: 05     │
│ 4   9 200 37  36 ││Status:    A       FAA: A   ││HDOP:      1.97             │
│ 5  12 331  6   0 ││MagVar:                     ││Geoid:     45.5             │
│ 6  17 236 42  35 │└─────────── RMC ────────────┘└─────────── GGA ────────────┘
│ 7  19 273 45  38 │┌────────────────────────────┐┌────────────────────────────┐
│ 8  21 136 11   0 ││Mode: A3 Sats: 3 6 9 17 19  ││UTC:           RMS:         │
│ 9  22  40  6   0 ││DOP: H=1.97  V=5.85  P=6.17 ││MAJ:           MIN:         │
│10  25   7  1   0 ││TOFF:  0.064302730          ││ORI:           LAT:         │
│11  31  53 21  25 ││PPS:                        ││LON:           ALT:         │
└────── GSV ───────┘└──────── GSA + PPS ─────────┘└─────────── GST ────────────┘

Inspect the PPS dialog.

gpsmon localhost:gpsd:/dev/pps0
tcp://localhost:gpsd:/dev/pps0JSON slave driver>
 ------------------- PPS offset:  0.000006771 ------
 ------------------- PPS offset:  0.000006804 ------
 ------------------- PPS offset:  0.000007567 ------
 ------------------- PPS offset:  0.000007550 ------
 ------------------- PPS offset:  0.000007119 ------
 ------------------- PPS offset:  0.000007523 ------
 ------------------- PPS offset:  0.000006574 ------
 ------------------- PPS offset:  0.000007086 ------
 ------------------- PPS offset:  0.000006922 ------
 ------------------- PPS offset:  0.000006395 ------
 ------------------- PPS offset:  0.000006911 ------
 ------------------- PPS offset:  0.000006647 ------
 ------------------- PPS offset:  0.000007427 ------
 ------------------- PPS offset:  0.000006958 ------
 ------------------- PPS offset:  0.000005552 ------
 ------------------- PPS offset:  0.000005591 ------
 ------------------- PPS offset:  0.000006516 ------
 ------------------- PPS offset:  0.000006557 ------
 ------------------- PPS offset:  0.000006965 ------
 ------------------- PPS offset:  0.000006905 ------
 ------------------- PPS offset:  0.000007264 ------
 ------------------- PPS offset:  0.000005958 ------
 ------------------- PPS offset:  0.000006163 ------
GPS test clients CGPS & XGPS

cgps is the terminal client and xgps the X11 client. With XGPS we can see relative satellite positions in the sky.

cgps
┌───────────────────────────────────────────┐┌─────────────────────────────────┐
│    Time:       2022-07-24T15:22:54.000Z   ││PRN:   Elev:  Azim:  SNR:  Used: │
│    Latitude:     0.000000 N               ││   3    52    079    21      Y   │
│    Longitude:    0.000000 E               ││   6    43    303    37      Y   │
│    Altitude:   9.5 m                      ││   9    50    201    37      Y   │
│    Speed:      0.0 kph                    ││  17    32    226    37      Y   │
│    Heading:    74.9 deg (true)            ││                                 │
│    Climb:      n/a                        ││                                 │
│    Status:     3D FIX (17 secs)           ││                                 │
│    Longitude Err:   +/- 11 m              ││                                 │
│    Latitude Err:    +/- 15 m              ││                                 │
│    Altitude Err:    +/- 18 m              ││                                 │
│    Course Err:      n/a                   ││                                 │
│    Speed Err:       n/a                   ││                                 │
│    Time offset:     0.356                 ││                                 │
│    Grid Square:     JO01bl                ││                                 │
└───────────────────────────────────────────┘└─────────────────────────────────┘
Shared memory (SHM)

When running the GPS daemon shared memory segments as follows are created.

ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x4e545030 0          root       600        80         2
0x4e545031 1          root       600        80         1
0x4e545032 2          root       666        80         2
0x4e545033 3          root       666        80         1
0x4e545034 4          root       666        80         1
0x4e545035 5          root       666        80         1
0x4e545036 6          root       666        80         1
0x4e545037 7          root       666        80         1
0x47505344 8          root       666        8928       1

cat /proc/sysvipc/shm
       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime        rss       swap
1314148400          0   600         80  1372  6549      2     0     0     0     0 1658675058 1658675004 1626289289       4096          0
1314148401          1   600         80  1372  6549      1     0     0     0     0 1658675058 1658675004 1626289289       4096          0
1314148402          2   666         80  1372  8881      2     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1314148403          3   666         80  1372  8881      1     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1314148404          4   666         80  1372  8881      1     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1314148405          5   666         80  1372  8881      1     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1314148406          6   666         80  1372  8881      1     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1314148407          7   666         80  1372  8881      1     0     0     0     0 1658677299 1658677302 1626289289       4096          0
1196446532          8   666       8928  1372  6549      1     0     0     0     0 1658675058 1658675004 1626289289      12288          0
Shared memory monitor for NTP

Inspect shared memory for GPS daemon updates.

ntpshmmon -n 10
ntpshmmon version 1
#      Name   Seen@                Clock                Real               L Prec
sample NTP2 1658845391.052754250 1658845390.999997337 1658845391.000000000 0 -20
sample NTP3 1658845391.053073679 1658845390.064010972 1658845390.000000000 0  -1
sample NTP0 1658845391.195810090 1658845391.195208369 1658845391.000000000 0  -1
sample NTP3 1658845391.553218802 1658845391.064837160 1658845391.000000000 0  -1
sample NTP2 1658845392.000572481 1658845391.999998990 1658845392.000000000 0 -20
sample NTP3 1658845392.054219398 1658845391.064837160 1658845391.000000000 0  -1
sample NTP0 1658845392.197965917 1658845392.197500030 1658845392.000000000 0  -1
sample NTP3 1658845392.555309369 1658845392.063063232 1658845392.000000000 0  -1
sample NTP2 1658845393.000207934 1658845392.999998194 1658845393.000000000 0 -20
sample NTP3 1658845393.056027671 1658845392.063063232 1658845392.000000000 0  -1

NTP

NTP daemon is used to keep the time and provide a network service. In this example it will sync to internet time services and the local PPS clock pulse.

Install
apt install ntp ntpdate
/etc/init.d/ntp stop
update-rc.d ntp disable

Bookworm

/etc/init.d/ntpsec stop
update-rc.d ntpsec disable
systemctl stop ntpd
systemctl disable ntpd
systemctl mask ntpd
Master config
leapfile /usr/share/zoneinfo/leap-seconds.list

statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats rawstats
filegen loopstats  file loopstats  type day enable
filegen peerstats  file peerstats  type day enable
filegen clockstats file clockstats type day enable
filegen rawstats   file rawstats   type day enable

server ntp0.linx.net prefer
server ntp1.linx.net
server ntp2.linx.net

pool uk.pool.ntp.org iburst

# ACL; /usr/share/doc/ntp-doc/html/accopt.html
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited
restrict 127.0.0.1
restrict ::1
restrict source notrap nomodify noquery
#restrict 192.168.0.0 mask 255.255.255.0 notrust

broadcast 192.168.0.255
disable auth

# https://doc.ntp.org/documentation/4.2.8-series/clockopt/
# https://doc.ntp.org/documentation/4.2.8-series/refclock/

# https://doc.ntp.org/documentation/drivers/driver22/
# PPS Clock Discipline

server 127.127.22.0 minpoll 3 maxpoll 3
fudge 127.127.22.0 flag2 0 flag3 1 flag4 1

# https://doc.ntp.org/documentation/drivers/driver28/
# https://gpsd.gitlab.io/gpsd/gpsd-time-service-howto.html
# Shared Memory Driver

# GPS Serial data reference (NTP0)
server 127.127.28.0 minpoll 3 maxpoll 3
fudge 127.127.28.0 flag1 1 time1 +0.130 refid GPS

# GPS PPS reference (NTP1)
server 127.127.28.1
fudge 127.127.28.1 flag1 1 refid PPS

# Local PTP reference (NTP2)
server 127.127.28.2
fudge 127.127.28.2 flag1 1 refid PTP
Slave config
leapfile /usr/share/zoneinfo/leap-seconds.list

statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats rawstats
filegen loopstats  file loopstats  type day enable
filegen peerstats  file peerstats  type day enable
filegen clockstats file clockstats type day enable
filegen rawstats   file rawstats   type day enable

#server ntp.example.com
broadcastclient
disable auth
Run it

Run it something like this

/usr/sbin/ntpd -n -p /var/run/ntp/ntpd.pid -c /etc/ntp.conf -g -N -u ntp:ntp
Test

This test was performed on a Pi 3 with the CPU clock fixed at 1GHz and only using the L80 GPS and not the U-blox.

ntpq -c rv -pn
associd=0 status=011d leap_none, sync_pps, 1 event, kern,
version="ntpd 4.2.8p15@1.3728-o Wed Sep 23 11:46:38 UTC 2020 (1)",
processor="armv7l", system="Linux/5.15.56-v7-KEWL+", leap=00, stratum=1,
precision=-20, rootdelay=0.000, rootdisp=1.015, refid=PPS,
reftime=e68e93a8.0e87acf6  Fri, Jul 29 2022 18:13:12.056,
clock=e68e93aa.080ea225  Fri, Jul 29 2022 18:13:14.031, peer=53321, tc=3,
mintc=3, offset=+0.000603, frequency=-0.143, sys_jitter=0.000954,
clk_jitter=0.001, clk_wander=0.001, tai=37, leapsec=201701010000,
expire=202212280000

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 uk.pool.ntp.org .POOL.          16 p    -   64    0    0.000   +0.000   0.001
o127.127.22.0    .PPS.            0 l    2    8  377    0.000   +0.001   0.001
-127.127.28.0    .GPS.            0 l    8    8  377    0.000  -12.042   9.783
+127.127.28.1    .PPS.            0 l   24   64  377    0.000   +0.000   0.001
+127.127.28.2    .PTP.            0 l   24   64  377    0.000   +0.000   0.001
*2a01:40:5459:7: .PPS.            1 u   53   64  377   12.519   +2.788   0.651
+2a01:40:5459:7: .PPS.            1 u   56   64  377   12.567   +2.797   0.845
-2a01:40:5459:8: .GPS.            1 u   21   64  377   13.571   +2.954   0.488
-185.103.119.60  202.70.69.81     2 u   56   64  377   12.191   +2.466   2.297
-188.114.116.1   202.70.69.81     2 u    7   64  377   13.345   +2.479   0.746

The jitter seems very high on this device. I have no advice about that and the only option is to find other sample devices to test and prefer.

The first character of each peer line above is called the ntp tally code.

Code  Message         T       Description
0     sel_reject              discarded as not valid (TEST10-TEST13)
1     sel_falsetick   x       discarded by intersection algorithm
2     sel_excess      .       discarded by table overflow (not used)
3     sel_outlier     -       discarded by the cluster algorithm
4     sel_candidate   +       included by the combine algorithm
5     sel_backup      #       backup (more than tos maxclock sources)
6     sel_sys.peer    *       system peer
7     sel_pps.peer    o       PPS peer (when the prefer peer is valid)
Debug
apt install tcpdump && rehash
tcpdump -ni wlan0 port 123 and udp

PTP

Precision time protocol.

This can be used to sync clocks on the local network.

PTPD

PTPD verison 2.

Install

apt install ptpd
/etc/init.d/ptpd stop
update-rc.d ptpd disable

Run server

ptpd -V -n -M -i eth0

Run client

ptpd -C -s -i eth0

Run master/slave with config file

Master or slave may be run with a configuration file.

mkdir -p /var/run/ptpd && ptpd -c /etc/ptpd.conf

Example slave config.

ptpengine:interface=eth0
ptpengine:ip_mode=multicast
ptpengine:use_libpcap=n
ptpengine:domain=0
ptpengine:preset=slaveonly

global:lock_file=/var/run/ptpd/pid

global:foreground=Y
global:verbose_foreground=N

global:log_status=Y
global:status_file=/var/run/ptpd/status

global:log_statistics=Y
global:statistics_file=/var/run/ptpd/statistics
global:statistics_log_interval=10
global:statistics_file_max_size=1024
global:statistics_file_max_files=5
global:statistics_file_truncate=Y

Issue

PTPD on Raspberry PI OS (debian) can freeze after a while and stop working.

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 5362 root      20   0    6912   2828   2400 R 100.0   0.1 937:21.95 ptpd

Using strace we see an infinite loop like this:

strace -p 5362
strace: Process 5362 attached
--- SIGALRM {si_signo=SIGALRM, si_code=SI_TIMER, si_timerid=0x1, si_overrun=5, si_value={int=33321672, ptr=0x1fc72c8}} ---
rt_sigreturn({mask=[]})                 = 1
...

The version matches the version available on SF.net

ptpd -v
ptpd2 version 2.3.1
Linux PTP

This service requires driver support of transmit and receive of timestamps. Not all drivers support this.

Check with ethtool -T

Install

apt install linuxptp
systemctl stop timemaster
systemctl disable timemaster
systemctl mask timemaster

More later…

Resources

L80 GPS overview

L80 GPS hardware

L80 GPS protocol

PTPD version 2 on SourceForge

Linux PTP