Raspberry Pi 3 Model B + USB接続GPSレシーバ + ChronyでStratum 1なNTPサーバを作る

いろんな人がすでに同じ内容で書いてたりするので今更なんですが、とりあえずLAN内に独立したNTPサーバが必要な場合に、電子工作とかなしにお手軽に作る方法を記します。
なお今回の構成ではPPSは扱えないのでそれなりの精度しか出ませんが、よほど受信環境が悪くない限りは、お手軽構成としてはそれなりの精度で稼働できると思います(多分)。

必要なもの

おそらく1万円出せばなんとかなる感じかと。

  1. Raspberry Pi 3 Model B (本体)
  2. GlobalSat SiRFstarIV BU-353S4 (USB接続GPSレシーバ)
  3. microSDカード (とりあえず8GBぐらいの)
  4. Raspbian Stretch Lite (本日時点「November 2018」、なるべく新しいものを使用)

事前準備

raspi-configとかで初期セットアップとアップデートは済ませておきます。

gpsdのインストール

gpsdとgpsd-clientsをインストールします。

pi@aeon:~ $ sudo apt update
ヒット:1 http://raspbian.raspberrypi.org/raspbian stretch InRelease
ヒット:2 http://archive.raspberrypi.org/debian stretch InRelease
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
パッケージはすべて最新です。
pi@aeon:~ $
pi@aeon:~ $ sudo apt install gpsd gpsd-clients
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  fontconfig fontconfig-config fonts-dejavu-core gnome-icon-theme gtk-update-icon-cache hicolor-icon-theme libatk1.0-0
  libatk1.0-data libavahi-client3 libblas-common libblas3 libbluetooth3 libcairo2 libcroco3 libcups2 libdatrie1
  libfontconfig1 libgail-common libgail18 libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-common libgfortran3 libgps22
  libgraphite2-3 libgtk2.0-0 libgtk2.0-bin libgtk2.0-common libharfbuzz0b libjbig0 liblapack3 libpango-1.0-0
  libpangocairo-1.0-0 libpangoft2-1.0-0 libpixman-1-0 librsvg2-2 librsvg2-common libthai-data libthai0 libtiff5
  libxcb-render0 libxcb-shm0 libxcomposite1 libxcursor1 libxdamage1 libxfixes3 libxi6 libxinerama1 libxrandr2
  libxrender1 python-cairo python-gobject-2 python-gps python-gtk2 python-numpy
提案パッケージ:
  cups-common gvfs librsvg2-bin python-gobject-2-dbg python-gtk2-doc gfortran python-dev python-nose python-numpy-dbg
  python-numpy-doc
以下のパッケージが新たにインストールされます:
  fontconfig fontconfig-config fonts-dejavu-core gnome-icon-theme gpsd gpsd-clients gtk-update-icon-cache
  hicolor-icon-theme libatk1.0-0 libatk1.0-data libavahi-client3 libblas-common libblas3 libbluetooth3 libcairo2
  libcroco3 libcups2 libdatrie1 libfontconfig1 libgail-common libgail18 libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-common
  libgfortran3 libgps22 libgraphite2-3 libgtk2.0-0 libgtk2.0-bin libgtk2.0-common libharfbuzz0b libjbig0 liblapack3
  libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 libpixman-1-0 librsvg2-2 librsvg2-common libthai-data libthai0
  libtiff5 libxcb-render0 libxcb-shm0 libxcomposite1 libxcursor1 libxdamage1 libxfixes3 libxi6 libxinerama1 libxrandr2
  libxrender1 python-cairo python-gobject-2 python-gps python-gtk2 python-numpy
アップグレード: 0 個、新規インストール: 56 個、削除: 0 個、保留: 0 個。
26.7 MB のアーカイブを取得する必要があります。
この操作後に追加で 81.9 MB のディスク容量が消費されます。

<中略>

pi@aeon:~ $

GlobalSat SiRFstarIV BU-353S4を繋ぐ

GlobalSat SiRFasterIV BU-353S4をRaspberry PiのUSBポートに挿して、下記コマンドで確認します。

“lsusb”コマンドを実行

pi@aeon:~ $ lsusb
Bus 001 Device 005: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 004: ID 0781:5583 SanDisk Corp.
Bus 001 Device 006: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
pi@aeon:~ $

※Prolific Technology, Inc. LP2303 Serial Portが認識されていればOK

“ls -al /dev/ttyUSB*” コマンドを実行

pi@aeon:~ $ ls -al /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0  1月 28 20:15 /dev/ttyUSB0
pi@aeon:~ $

※他に何も繋いでなければ「/dev/ttyUSB0」が表示される

gpsdを設定する

gpsdの設定を行います。

/etc/default/gpsd の設定

pi@aeon:~ $ sudo cp -a /etc/default/gpsd{,.orig}
pi@aeon:~ $
pi@aeon:~ $ sudo vi /etc/default/gpsd
## 変更内容 ##
--- /etc/default/gpsd.orig      2016-11-05 07:35:14.000000000 +0900
+++ /etc/default/gpsd   2019-01-28 20:17:04.096135683 +0900
@@ -8,7 +8,7 @@

 # Devices gpsd should collect to at boot time.
 # They need to be read/writeable, either by user gpsd or the group dialout.
-DEVICES=""
+DEVICES="/dev/ttyUSB0"

 # Other options you want to pass to gpsd
-GPSD_OPTIONS=""
+GPSD_OPTIONS="-N -F /var/run/gpsd.sock -b -n"
pi@aeon:~ $

※"-N -b -n"あたりが肝

/etc/systemd/system/gpsd.service の設定

pi@aeon:~ $ sudo cp -a /lib/systemd/system/gpsd.service /etc/systemd/system/
pi@aeon:~ $
pi@aeon:~ $ sudo vi /etc/systemd/system/gpsd.service
## 変更内容 ##
/etc/systemd/system/gpsd.service
--- /lib/systemd/system/gpsd.service    2016-11-05 07:35:14.000000000 +0900
+++ /etc/systemd/system/gpsd.service    2019-01-28 20:20:02.035553856 +0900
@@ -9,4 +9,5 @@
 ExecStart=/usr/sbin/gpsd -N $GPSD_OPTIONS $DEVICES

 [Install]
+WantedBy=multi-user.target
 Also=gpsd.socket
pi@aeon:~ $

※WantedByの部分が肝

gpsdの自動起動再登録、再起動

“sudo systemctl daemon-reload”でもよい気がしますが、とりあえず無効→有効にします。

gpsdの自動起動を一旦無効にする

pi@aeon:~ $ sudo systemctl disable gpsd
Synchronizing state of gpsd.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable gpsd
Removed /etc/systemd/system/sockets.target.wants/gpsd.socket.
pi@aeon:~ $

gpsdの自動起動を再度有効にする

pi@aeon:~ $ sudo systemctl enable gpsd
Synchronizing state of gpsd.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable gpsd
Created symlink /etc/systemd/system/sockets.target.wants/gpsd.socket → /lib/systemd/system/gpsd.socket.
pi@aeon:~ $

gpsdを再起動する

インストール後すでに起動しているので、”start” ではなく “restart” します。

pi@aeon:~ $ sudo systemctl restart gpsd
pi@aeon:~ $
pi@aeon:~ $ sudo systemctl -l status gpsd
● gpsd.service - GPS (Global Positioning System) Daemon
   Loaded: loaded (/etc/systemd/system/gpsd.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-01-28 20:22:55 JST; 5s ago
 Main PID: 2130 (gpsd)
   CGroup: /system.slice/gpsd.service
           mq2130 /usr/sbin/gpsd -N -N -F /var/run/gpsd.sock -b -n /dev/ttyUSB0

 1月 28 20:22:55 aeon.lunatilia.net systemd[1]: Started GPS (Global Positioning System) Daemon.
pi@aeon:~ $

gpsdの動作確認

“gpsmon”コマンドや”cgps -s”コマンドで、情報が取得できているか確認します。
ここでは”cgps -s”コマンドを使用しています。

pi@aeon:~ $ cgps -s
lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqklqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
x    Time:       2019-01-28T11:38:25.154Z   xxPRN:   Elev:  Azim:  SNR:  Used: x
x    Latitude:    33.796493 N               xx   7    39    306    21      N   x
x    Longitude:  134.505891 E               xx   8    66    310    12      N   x
x    Altitude:   39.5 m                     xx   9    10    254    14      N   x
x    Speed:      123.4 kph                  xx  20    08    061    21      N   x
x    Heading:    229.4 deg (true)           xx  27    57    025    10      N   x
x    Climb:      -630.0 m/min               xx  16    45    082    00      N   x
x    Status:     3D FIX (281 secs)          xx  18    44    185    00      N   x
x    Longitude Err:   +/- 29 m              xx  11    36    219    00      N   x
x    Latitude Err:    +/- 31 m              xx  26    24    103    00      N   x
x    Altitude Err:    +/- 73 m              xx   1    17    197    00      N   x
x    Course Err:      +/- 77 deg            xx  10    10    086    00      N   x
x    Speed Err:       +/- 226 kph           xx  30    09    320    00      N   x
x    Time offset:     -2.525                xx                                 x
x    Grid Square:     PM73gt                xx                                 x
mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqjmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj
pi@aeon:~ $

※Time/Latitude/Longitudeあたりの値が取得できていればとりあえずOK

chronyパッケージのインストール

NTPサーバとしてchronyをインストールします。

pi@aeon:~ $ sudo apt install chrony
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  libtomcrypt0 libtommath1
以下のパッケージが新たにインストールされます:
  chrony libtomcrypt0 libtommath1
アップグレード: 0 個、新規インストール: 3 個、削除: 0 個、保留: 0 個。
540 kB のアーカイブを取得する必要があります。
この操作後に追加で 1,214 kB のディスク容量が消費されます。
続行しますか? [Y/n] y

<中略>

pi@aeon:~ $

/etc/chrony/chrony.conf の設定

pi@aeon:~ $ sudo cp -a /etc/chrony/chrony.conf{,.orig}
pi@aeon:~ $ sudo vi /etc/chrony/chrony.conf
## 変更内容 ##
--- /etc/chrony/chrony.conf.orig        2017-07-23 00:24:44.000000000 +0900
+++ /etc/chrony/chrony.conf     2019-01-28 20:43:12.966559520 +0900
@@ -1,6 +1,5 @@
 # Welcome to the chrony configuration file. See chrony.conf(5) for more
 # information about usuable directives.
-pool 2.debian.pool.ntp.org iburst

 # This directive specify the location of the file containing ID/key pairs for
 # NTP authentication.
@@ -30,3 +29,9 @@
 # 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
+
+# NEMA by GPS
+refclock SHM 0 refid GPS precision 1e-1 offset 0.9999 delay 0.2
+
+# Allow NTP client access from local network.
+allow 192.168/16
pi@aeon:~ $

chronyサービスの自動起動有効・再起動

pi@aeon:~ $ sudo systemctl enable chrony
Synchronizing state of chrony.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable chrony
pi@aeon:~ $
pi@aeon:~ $ sudo systemctl restart chrony
pi@aeon:~ $
pi@aeon:~ $ sudo systemctl -l status chrony
● chrony.service - chrony, an NTP client/server
   Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-01-28 20:45:08 JST; 2s ago
     Docs: man:chronyd(8)
           man:chronyc(1)
           man:chrony.conf(5)
  Process: 4261 ExecStart=/usr/sbin/chronyd (code=exited, status=0/SUCCESS)
 Main PID: 4263 (chronyd)
   CGroup: /system.slice/chrony.service
           mq4263 /usr/sbin/chronyd

 1月 28 20:45:07 aeon.lunatilia.net systemd[1]: Starting chrony, an NTP client/server...
 1月 28 20:45:08 aeon.lunatilia.net chronyd[4263]: chronyd version 3.0 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +S
 1月 28 20:45:08 aeon.lunatilia.net chronyd[4263]: Frequency 1.490 +/- 26.371 ppm read from /var/lib/chrony/chrony.drift
 1月 28 20:45:08 aeon.lunatilia.net systemd[1]: Started chrony, an NTP client/server.
pi@aeon:~ $

時刻同期の確認

pi@aeon:~ $ chronyc sources -v
210 Number of sources = 1

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- 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    17    15   -962us[-2415us] +/-  200ms
pi@aeon:~ $

systemd-timesyncdの自動起動無効

OS再起動時に外部と同期しないよう、systemd-timesyncdを自動起動無効にします。

pi@aeon:~ $ sudo systemctl disable systemd-timesyncd
Removed /etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service.
pi@aeon:~ $

OSの再起動

一旦OSを再起動して、gpsdとchronyが自動起動しつつ、systemd-timesyncdが自動起動しないことを確認します。

pi@aeon:~ $ sudo systemctl reboot
pi@aeon:~ $ 

OS再起動後のサービス自動起動確認

pi@aeon:~ $ sudo systemctl -l status gpsd chrony systemd-timesyncd
● gpsd.service - GPS (Global Positioning System) Daemon
   Loaded: loaded (/etc/systemd/system/gpsd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-01-28 20:58:57 JST; 26s ago
 Main PID: 527 (gpsd)
   CGroup: /system.slice/gpsd.service
           mq527 /usr/sbin/gpsd -N -N -F /var/run/gpsd.sock -b -n /dev/ttyUSB0

 1月 28 20:58:57 aeon.lunatilia.net systemd[1]: Started GPS (Global Positioning System) Daemon.

● chrony.service - chrony, an NTP client/server
   Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-01-28 20:58:57 JST; 26s ago
     Docs: man:chronyd(8)
           man:chronyc(1)
           man:chrony.conf(5)
  Process: 513 ExecStart=/usr/sbin/chronyd (code=exited, status=0/SUCCESS)
 Main PID: 526 (chronyd)
   CGroup: /system.slice/chrony.service
           mq526 /usr/sbin/chronyd

 1月 28 20:58:57 aeon.lunatilia.net systemd[1]: Starting chrony, an NTP client/server...
 1月 28 20:58:57 aeon.lunatilia.net chronyd[526]: chronyd version 3.0 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SC
 1月 28 20:58:57 aeon.lunatilia.net chronyd[526]: Frequency 25.730 +/- 48.536 ppm read from /var/lib/chrony/chrony.drift
 1月 28 20:58:57 aeon.lunatilia.net systemd[1]: Started chrony, an NTP client/server.
Warning: systemd-timesyncd.service changed on disk. Run 'systemctl daemon-reload' to reload units.
● systemd-timesyncd.service - Network Time Synchronization
   Loaded: loaded (/lib/systemd/system/systemd-timesyncd.service; disabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/systemd-timesyncd.service.d
           mqdisable-with-time-daemon.conf
   Active: inactive (dead)
     Docs: man:systemd-timesyncd.service(8)
pi@aeon:~ $

時刻同期の確認

pi@aeon:~ $ chronyc sources -v
210 Number of sources = 1

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- 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    23   -188us[ -423us] +/-  200ms
pi@aeon:~ $
pi@aeon:~ $ date
Tue Jan 28 21:00:51 JST 2019
pi@aeon:~ $

※実際の時刻と照らし合わせて、合っていればOK

その他

  • もし精度にこだわるなら、GPIOで接続できるみちびきのモジュールとかを手に入れたほうがいいです。
  • CentOS Userland 7でも同様にできると思いますが、おそらくfake-hwclockは必須です。
    (ないと時刻が乖離しすぎて同期しないと思います)
  • fake-hwclockでも良いですが、RTCモジュールを載せておけば若干幸せかも。

参考にしたサイト(全部は覚えてないけど…)

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

ページ先頭へ ↑