備忘録

備忘録以外の何物でもない

Raspberry Pi + GPS HATでGPS時刻に同期する

使用機材

Raspberry Pi OSのインストール

  1. Raspberry Pi OSのDLページからRaspberry Pi Imagerをダウンロードしてインストール
  2. 以下の設定でインストール
    • OS: Raspberry Pi OS Lite (64bit)
    • SSH: 有効化する
    • User: ユーザー名とパスワードを事前に設定
    • Locale: Asia/Tokyo, jp
    • Telemetry: 有効化しない

設定

システム設定

$ sudo raspi-config
  • 6 Advanced Options
    • A1 Expand Filesystem
  • 3 Interface Options
    • I5 I2C
      • Would you like the ARM I2C interface to be enabled?: Yes
    • I6 Serial Port
      • Would you like a login shell to be accessible over serial?: No
      • Would you like the serial port hardware to be enabled?: Yes

GPS+RTC HAT(RTC用)

i2c用ツールの導入
$ sudo apt-get install i2c-tools
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- 42 -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- 52 -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
$ sudo vi /boot/config.txt
# Add this line
dtoverlay=i2c-rtc,rv3028
$ sudo reboot
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- 42 -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
$ sudo apt-get -y remove fake-hwclock
$ sudo update-rc.d -f fake-hwclock remove
$ sudo systemctl disable fake-hwclock
$ sudo vi /lib/udev/hwclock-set
#!/bin/sh
# Reset the System Clock to UTC if the hardware clock from which it
# was copied by the kernel was in localtime.

dev=$1

#if [ -e /run/systemd/system ] ; then
#    exit 0
#fi

#/sbin/hwclock --rtc=$dev --systz
#/sbin/hwclock --rtc=$dev --hctosys

GPS+RTC HAT(GPS用)

$ sudo apt-get install pps-tools
$ sudo vi /boot/config.txt
# Add these lines
dtoverlay=disable-bt
dtoverlay=pps-gpio,gpiopin=18
$ sudo vi /etc/modules
# Add this line
pps-gpio
$ sudo reboot
$ 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 1663939406.000000195, sequence: 30492 - clear  0.000000000, sequence: 0
source 0 - assert 1663939407.000001610, sequence: 30493 - clear  0.000000000, sequence: 0
source 0 - assert 1663939408.000001377, sequence: 30494 - clear  0.000000000, sequence: 0
source 0 - assert 1663939409.000002274, sequence: 30495 - clear  0.000000000, sequence: 0
source 0 - assert 1663939409.999999356, sequence: 30496 - clear  0.000000000, sequence: 0
source 0 - assert 1663939410.999997753, sequence: 30497 - clear  0.000000000, sequence: 0
GPSDの導入(GPSバイスとの通信、PPSの管理)
$ sudo apt-get install gpsd gpsd-clients
$ sudo vi /etc/default/gpsd
# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="/dev/ttyAMA0 /dev/pps0"

# Other options you want to pass to gpsd
GPSD_OPTIONS="-n -s 115200"

# Automatically hot add/remove USB GPS devices via gpsdctl
USBAUTO="false"

# Start the gpsd daemon automatically at boot time
# START_DAEMON="true"
$ sudo systemctl enable  gpsd.service
$ sudo systemctl restart gpsd.service
$ sudo systemctl enable  gpsd.socket
$ sudo systemctl restart gpsd.socket

Chronyの導入(ntpdの代替)

$ sudo apt-get install chrony
$ sudo vi /etc/chrony/chrony.conf
# Welcome to the chrony configuration file. See chrony.conf(5) for more
# information about usable directives.

# Include configuration files found in /etc/chrony/conf.d.
confdir /etc/chrony/conf.d

# Use Debian vendor zone.
#pool 2.debian.pool.ntp.org iburst

# Use time sources from DHCP.
sourcedir /run/chrony-dhcp

# Use NTP sources found in /etc/chrony/sources.d.
sourcedir /etc/chrony/sources.d

# This directive specify the location of the file containing ID/key pairs for
# NTP authentication.
keyfile /etc/chrony/chrony.keys

# This directive specify the file into which chronyd will store the rate
# information.
driftfile /var/lib/chrony/chrony.drift

# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony

# Uncomment the following line to turn logging on.
#log tracking measurements statistics

# Log files location.
logdir /var/log/chrony

# Stop bad estimates upsetting machine clock.
leapsecmode slew

# This directive enables kernel synchronisation (every 11 minutes) of the
# real-time clock. Note that it can’t be used along with the 'rtcfile' directive.
rtcsync

# Step the system clock instead of slewing it if the adjustment is larger than
# one second, but only in the first three clock updates.
makestep 1 3

# Get TAI-UTC offset and leap seconds from the system tz database.
# This directive must be commented out when using time sources serving
# leap-smeared time.
leapsectz right/UTC

# NICT
server ntp.jst.mfeed.ad.jp minpoll 6 maxpoll 16 iburst

# Reference Clock
refclock SHM 0 refid SHM0 offset 0.053 noselect
refclock SHM 1 refid SHM1 pps prefer trust
refclock SHM 2 refid SHM2
$ sudo systemctl enable chrony
$ sudo systemctl start chrony
$ sudo systemctl disable systemd-timesyncd.service
$ chronyc tracking
Reference ID    : 53484D31 (SHM1)
Stratum         : 1
Ref time (UTC)  : Fri Sep 23 14:24:36 2022
System time     : 0.000000257 seconds slow of NTP time
Last offset     : -0.000000327 seconds
RMS offset      : 0.000000577 seconds
Frequency       : 10.477 ppm fast
Residual freq   : -0.001 ppm
Skew            : 0.023 ppm
Root delay      : 0.000000001 seconds
Root dispersion : 0.000012924 seconds
Update interval : 16.0 seconds
Leap status     : Normal
$ 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
===============================================================================
#? SHM0                          0   4   377    18    +30ms[  +30ms] +/-  221us
#* SHM1                          0   4   377    19   -427ns[ -734ns] +/-  192ns
#- SHM2                          0   4   377    19   -427ns[ -427ns] +/-  194ns
^? ntp2.jst.mfeed.ad.jp          2  12   377   623  -3354us[-3362us] +/-   64ms
$ 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
==============================================================================
SHM0                        6   3    80    -27.678    120.093    +32ms   608us
SHM1                        7   5    98     -0.000      0.033     -5ns   442ns
SHM2                        7   5    98     -0.000      0.032     -5ns   434ns
ntp2.jst.mfeed.ad.jp       37  20  277m     -0.014      0.057  -3814us   340us

ひとりごと

  • RTC, PPS, GPSDの導入辺りまではわりと問題なく進む
  • 今回はNTPDではなくChronyを選択したが、設定の難易度は大きくは変わらない
  • NTPDとChronyの設定で違うこと(はまりやすいポイント)
    • Chronyは、refidの文字数が4文字までに制限されている
    • Chronyは、NTPDのような疑似IPによるローカルクロックデバイス指定ではなく、refclockなどと指定する
  • 公式ドキュメントやソースコードのコメント依ると・・・、
    • GPSDのShared memoryによる時刻提供は、1つのGPSバイスに対して2つのshmアドレスを割り当て、#0がNMEA(時刻含まれているが精度低い)で#1がPPS(精度が高く、時刻情報もマージされている)とのことらしい
      • The magic pseudo-IP address 127.127.28.0 identifies unit 0 of the ntpd shared-memory driver (NTP0); 127.127.28.1 identifies unit 1 (NTP1). Unit 0 is used for in-band message timestamps and unit 1 for the (more accurate, when available) time derived from combining in-band message timestamps with the out-of-band PPS synchronization pulse. Splitting these notifications allows ntpd to use its normal heuristics to weight them. URL
    • しかしなぜか#2が存在し、しかも#2も時刻とPPSがマージされているらしい
      • 公式ドキュメントにも近しいことが書いてあるが、さらっと書いてあり、詳細なことが分からない
        • Different units — 2 (NTP2) and 3 (NTP3), respectively — must be used when gpsd is not started as root. Some GPS HATs put PPS time on a GPIO pin and will also use unit 2 (NTP2) for the PPS time correction. URL
      • #2が存在する可能性があるという表記はntpsecのドキュメントにも存在する URL
    • 以前にNTPDで同様の構築をした際も、SHMの#2 (127.127.28.2)を使用したら上手くいった(#1だと上手く同期できなかった)
    • 今回は、GPSDのntpshmmonコマンドで確認したところ、#1と#2はほぼ同じ情報だったので、#1で設定してみた
$ sudo ntpshmmon
ntpshmmon: version 3.22
#      Name  Seen@                 Clock                 Real                 L Prc
sample NTP0  1663940758.092924225  1663940758.092887892  1663940757.999768955 0 -20
sample NTP1  1663940759.000949950  1663940758.999994201  1663940759.000000000 0 -20
sample NTP2  1663940759.000978172  1663940758.999994201  1663940759.000000000 0 -20
sample NTP0  1663940759.092384292  1663940759.092269478  1663940758.999768494 0 -20
sample NTP1  1663940760.000883420  1663940759.999994188  1663940760.000000000 0 -20
sample NTP2  1663940760.000910475  1663940759.999994188  1663940760.000000000 0 -20
sample NTP0  1663940760.093384713  1663940760.092514074  1663940759.999768036 0 -20
sample NTP1  1663940761.000252488  1663940760.999996509  1663940761.000000000 0 -20
sample NTP2  1663940761.000281691  1663940760.999996509  1663940761.000000000 0 -20