Close

Configuring the software

A project log for Repurposed wifi controller as GPS-based NTP server

An old Meru MC1500 wifi controller retrofitted with Debian and a GPS receiver becomes an accurate private NTP server

remi-serriereRemi Serriere 04/23/2023 at 17:440 Comments

Awesome! We have wired a GPS with its PPS to the motherboard, and now what?

First, we need a few missing components: "gpsd" will handle the GPS, while "chrony" will take care of the NTP.

# Installing required packages
apt install gpsd gpsd-clients pps-tools chrony

# Making sure PPS kernel module is loaded
modprobe pps_ldisc

# Attaching PPS device on serial port
ldattach PPS /dev/ttyS1

# Allow Chrony sockets in AppArmor
apt install apparmor-utils
aa-complain /usr/sbin/chronyd

By now, the PPS LED on the GPS module is ticking meaning we should have a GPS fix. Let's test the PPS output to see if it is working. It is indeed ticking as expected:

root@rsfo-ntp01:~# 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 1682168016.866888991, sequence: 2171 - clear  1682168015.966901011, sequence: 2952
source 0 - assert 1682168016.866888991, sequence: 2171 - clear  1682168016.966901919, sequence: 2953
source 0 - assert 1682168017.866881448, sequence: 2172 - clear  1682168016.966901919, sequence: 2953
source 0 - assert 1682168017.866881448, sequence: 2172 - clear  1682168017.966892630, sequence: 2954
source 0 - assert 1682168018.866880401, sequence: 2173 - clear  1682168017.966892630, sequence: 2954
source 0 - assert 1682168018.866880401, sequence: 2173 - clear  1682168018.966878452, sequence: 2955
source 0 - assert 1682168019.866878585, sequence: 2174 - clear  1682168018.966878452, sequence: 2955
source 0 - assert 1682168019.866878585, sequence: 2174 - clear  1682168019.966890814, sequence: 2956
source 0 - assert 1682168020.866877816, sequence: 2175 - clear  1682168019.966890814, sequence: 2956
source 0 - assert 1682168020.866877816, sequence: 2175 - clear  1682168020.966887531, sequence: 2957

Now, let's configure the GPSD daemon. This daemon will talk to the GPS module while allowing multiple processes to access the GPS information. This would not be possible if we were using the serial port directly from the NTP server. Edit the file "/etc/default/gpsd":

# GPS is conected to ttyS1, the 2nd hardare serial port
DEVICES="/dev/ttyS1"

# Options we want to pass to gpsd
# -s 9600 = baudrate, -G = listen to any address, -n = don't wait for clients to poll GPS
GPSD_OPTIONS="-s 9600 -G -n"

# We don't care about USB modules, let's ignore them!
USBAUTO="false"

We also need to configure the Chrony daemon by editing the file "/etc/chrony/chrony.conf". The "makestep" should not be required, but since the Meru has a bad RTC/BIOS battery the time would keep reverting to 2002... Chrony doesn't like that and doesn't update the time with such an offset in timing. With this command, Chrony will force updates on the first 10 syncs. 

# Allow connections from remote
allow

# Reference clocks, PPS locked on GPS
refclock SOCK /run/chrony.ttyS1.sock refid GPS precision 1e-1
refclock PPS /dev/pps0 lock GPS refid PPS precision 1e-9 poll 2

# Step the clock on first 10 updates if ofset is larger than one second
makestep 1 10

I had trouble starting Chrony on boot because the PPS device (/dev/pps0) would not be initialized yet. So I added an "ExecStartPre" command in Chrony's daemon service file located at "/lib/systemd/system/chrony.service":

[Unit]
Description=chrony, an NTP client/server
Documentation=man:chronyd(8) man:chronyc(1) man:chrony.conf(5)
Conflicts=openntpd.service ntp.service ntpsec.service
Wants=time-sync.target
Before=time-sync.target
After=network.target
ConditionCapability=CAP_SYS_TIME

[Service]
Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/default/chrony
ExecStartPre=bash -c '/usr/sbin/ldattach PPS /dev/ttyS1 && /bin/sleep 10'
ExecStart=/usr/sbin/chronyd $DAEMON_OPTS
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes

[Install]
Alias=chronyd.service
WantedBy=multi-user.target

Make sure the GPSD service is set to start after Chrony, edit the file "/lib/systemd/system/gpsd.service" and look for the line "After=chronyd.service":

[Unit]
Description=GPS (Global Positioning System) Daemon
Requires=gpsd.socket
# Needed with chrony SOCK refclock
After=chronyd.service

[Service]
Type=forking
EnvironmentFile=-/etc/default/gpsd
ExecStart=/usr/sbin/gpsd $GPSD_OPTIONS $OPTIONS $DEVICES

[Install]
WantedBy=multi-user.target
Also=gpsd.socket

Reboot the server with the "reboot" command to make sure everything starts correctly on boot... Then let's have a look at the GPS output with the "gpsmon" command. U-Blox is detected, we have a 3D fix, TDOP is low, that's a good start.

/dev/ttyS1                    u-blox>
lqqqqqqqqqqqqqqqqqqqqqqqqqqklqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
xCh PRN  Az  El S/N Flag U xxECEF Pos: +45xxxxx.61m +3xxxxx.98m +43xxxxx.21m  x
x 0   2  52  60  10 040d Y xxECEF Vel:     -0.01m/s     -0.03m/s     -0.02m/s x
x 1   7 331   4   0 0104   xx                                                 x
x 2   8 280  13  25 040d Y xxLTP Pos:  43.xxxxxxxxxf   4.xxxxxxxxf   106.00m x
x 3  10 154  24  30 050d Y xxLTP Vel:    0.00m/s   0.0f   0.00m/s             x
x 4  16 296  69  23 040d Y xx                                                 x
x 5  18  53  56  25 040d Y xxTime: 6 13:05:47.00                              x
x 6  22   0 -91   0 0110   xxTime GPS: 2258+565547.000     Day: 6             x
x 7  23 117  40  33 060d Y xx                                                 x
x 8  26 176  65  39 060d Y xxEst Pos Err  10.93m Est Vel Err   0.00m/s        x
x 9  27 291  45  20 040d Y xxPRNs:  9 PDOP:  1.6 Fix 0x03 Flags 0xdd          x
x10  29  86   5  13 040d Y xmqqqqqqqqqqqqqqqqqqq NAV_SOL qqqqqqqqqqqqqqqqqqqqqj
x11  31 202  13   7 030c   xlqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
x12 120 207  36   0 0110   xxDOP [H]  1.1 [V]  1.2 [P]  1.6 [T]  0.8 [G]  1.8 x
x13 124 155  37   0 0110   xmqqqqqqqqqqqqqqqqqqq NAV_DOP qqqqqqqqqqqqqqqqqqqqqj
x14 126 151  35   0 0110   xlqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
x15                        xxTOFF: -0.295365554       PPS: -0.429079607       x
mqqqqqq NAV_SVINFO qqqqqqqqjmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj
blablablablablablablabla
(26) blablablablablablablabla
(24) blablablablablablablabla

Let's now have a look at the NTP with the "chronyc" command... Not bad, everything is working :)

root@rsfo-ntp01:~# chronyc sources -v
  .-- 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
===============================================================================
#- GPS                           0   4   377    11   -796ns[ -951ns] +/-  100ms
#* PPS                           0   2   377     2  -1965ns[-2129ns] +/- 3142ns
root@rsfo-ntp01:~#
root@rsfo-ntp01:~#
root@rsfo-ntp01:~# chronyc sourcestats -v
root@rsfo-ntp01:~# chronyc sourcestats -v                             .- Number of sample points in measurement set.                            /    .- Number of residual runs with same sign.                           |    /    .- Length of measurement set (time).                           |   |    /      .- Est. clock freq error (ppm).                           |   |   |      /           .- Est. error in freq.                           |   |   |     |           /         .- Est. offset.                           |   |   |     |          |          |   On the -.                           |   |   |     |          |          |   samples. \                           |   |   |     |          |          |             |
Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
==============================================================================
GPS                        12   9   177     -0.012      0.015   -811ns   612ns
PPS                        59  25   232     -0.000      0.013     -1ns  1987ns
root@rsfo-ntp01:~#
root@rsfo-ntp01:~#
root@rsfo-ntp01:~# chronyc tracking
Reference ID    : 50505300 (PPS)
Stratum         : 1
Ref time (UTC)  : Sun Apr 23 16:58:14 2023
System time     : 0.000000327 seconds slow of NTP time
Last offset     : -0.000000122 seconds
RMS offset      : 0.000000652 seconds
Frequency       : 2.603 ppm slow
Residual freq   : -0.000 ppm
Skew            : 0.032 ppm
Root delay      : 0.000000001 seconds
Root dispersion : 0.000007401 seconds
Update interval : 4.0 seconds
Leap status     : Normal

Discussions