GPSを使ったNTPサーバの構築 その1

# はじめに
年末にPPS信号を出力可能なUSBタイプのGPSレシーバーを購入してほったらかしていたが、時間ができたのでNTPサーバを構築してみる。

10年くらい前にも安いUSBタイプのGPSレシーバーを購入して構築したことはあったが、PPS無しだと精度が悪くNICTとかのタイムサービスのクライアントにした方がずっとましだった(PPS信号の出力可能なUSBタイプのGPSレシーバーは値段が高い)。今ではラズパイと安いGPSレシーバーの組み合わせで手軽にPPS使ったNTPサーバを構築できるので、わざわざUSBタイプのGPSレシーバーを使う必要は無いが、動かしたいのは自宅に構築してあるIntel FreeBSDサーバで。

ということで今回構築にあたってネットで調べてみたが、GPSレシーバーのチューニングまで踏み込んだ情報が得られなかったので、検証しながら備忘録代わりに記事を書いてみることにした。

# 使用する機材
## PC&OS
PCが古いのはメインPCを入れ替えたときの余剰パーツでサーバを作ってるため。
| 項目 |  |
|:-|:-|
| CPU | Core i7-7700T |
| マザーボード | H270M-ITX |
| OS | FreeBSD 13.0R |
## GPSレシーバー
いずれもAmazonで購入。
| GPSレシーバー | モジュール | PPS | 転送速度 | ドライバ |
|:-|:-|:-|:-|:-|
| VFAN GPS USBレジーバー(~~たぶんGPS~~ UG-353) | UBX-G70xx系(u-blox7) | 無 | 9600(デフォルト),19200,38400,56700,115200 | uplcom |
| GLOBALSAT BU-353S4 | EB-5365RC SiRF IV | 無 | 4800(デフォルト),9600,19200,38400 | umodem |
| LOCOSYS LS23030-V2 | MediaTek MT3339 | 有 | 115200(デフォルト),230400,46080,921600,3000000 | umodem |

```:usbconfigの出力
ugen0.4: <u-blox AG - www.u-blox.com u-blox 7 - GPS/GNSS Receiver> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)
ugen0.3: <Prolific Technology Inc. USB-Serial Controller D> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)
ugen0.5: <Prolific Technology Inc. USB-Serial Controller> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)
```

# 準備
## インストールするソフト
ntpdはOS標準のものを使用。
| ソフトウェア | 用途 |
|:-|:-|
| gpsd | GPSの動作確認とubxtoolを使ったu-bloxモジュール設定 |
| munin | 各remoteのoffsetをグラフにするため、下記のntp_offsetsをテキトーに書き換えたpluginを5分間隔で実行 |
| topless | ntpqを定期実行して状態の確認に使用(Linuxのwatchコマンドに相当) |


```sh:ntp_peers
#!/bin/sh
# -*- sh -*-
nodelay=${nodelay:-}
do_autoconf () {
  ntpq -c help >/dev/null 2>&1 || { echo 'no (no ntpq program)'; exit 0; }
  case "$(ntpq -n -p 2>/dev/null | wc -l)" in
    0)
      echo 'no (unable to list peers)'
      exit 0
      ;;
    1|2)
      echo 'no (no peers?)'
      exit 0
      ;;
    *)
      echo yes
      exit 0
      ;;
  esac
}
do_config () {
    cat <<EOF
graph_title NTP Peers
graph_args --base 1000 --vertical-label msec --lower-limit 0
graph_category time
graph_info Currently our peer.
EOF
ntpq -p -w | tail -n +3 | sed -E -e 's/^ ([a-zA-Z])/.\1/g' -e '/^[^ ]*$/ N;s/\n//g' \
  | grep -v POOL \
  | sed -E -e 's/^(.)([^ .]+)([^ ]*) .*([-+][.0-9]+) +[0-9.]+$/\2.label \1 \2\3/g' | sort

    case $nodelay in
      1) echo "delay.graph no";;
    esac
}
do_ () {
  ntpq -p -w | tail -n +3 | sed -E -e 's/^ ([a-zA-Z])/.\1/g' -e '/^[^ ]*$/ N;s/\n//g' \
    | grep -v POOL \
    | sed -E -e 's/^(.)([^ .]+)([^ ]*) .*([-+][.0-9]+) +[0-9.]+$/\2.value \4/g' | sort
}
case $1 in
    autoconf|config|'')
       "do_$1"
       exit $?
       ;;
    *)
       echo "Don't know how to do that" >&2
       exit 1
       ;;
esac

echo 'What am I doing here?' >&2
exit 1
```

## OSの設定
昔のFreeBSDはカーネルコンパイルが必要だったが、今はsysctlの設定だけでPPSを使用できるようになっている(man ucom参照)。
```script:loader.conf
hw.usb.ucom.pps_mode="2"
```

## gps用デバイス
ntpdではデバイスが/dev/gpsN, /dev/ppsN, /dev/gpsppsN(NMEAドライバでPPSを使う場合)と決まっているので、元のttyデバイスのリンクを張る必要がある。ただ、今回使用するBU-353S4とLS23030-V2は同じドライバになっており、検証のために抜き差してデバイス番号変わると面倒。なのでdevdから呼び出したスクリプトでリンクを張る(ついでに転送速度の設定)。なおスクリプトにはGPSレシーバーの設定も追記する予定。
```sciprt:/etc/devd/gps.conf
# GPS-353 (UBX-G70xx | u-blox 7)
attach 20 {
        device-name "umodem[0-9]+";
        match "vendor"  "0x1546";
        match "product" "0x01a7";
        action "/root/bin/gps0.sh $device-name 0";
};
# GLOBALSAT BU-353S4 (G Star IV)
attach 20 {
        device-name "uplcom[0-9]+";
        match "vendor"  "0x067b";
        match "product" "0x2303";
        action "/root/bin/gps1.sh $device-name 1";
};
# LS23030-V2
attach 20 {
        device-name "uplcom[0-9]+";
        match "vendor"  "0x067b";
        match "product" "0x23a3";
        action "/root/bin/gps2.sh $device-name 2";
};
```

```sh:gps2.sh
#!/bin/sh
sysctl_arg=`echo $1 | sed -e 's/^\([^0-9]*\)\(.*\)$/dev.\1.\2/g'`
ttyname=`sysctl -n ${sysctl_arg}.ttyname`
ln -sf /dev/cua${ttyname} /dev/gps$2
ln -sf /dev/cua${ttyname} /dev/pps$2
stty -f /dev/cua${ttyname}.init 115200
```

## ntpの設定
serverとfudgeのみ抜粋。まず各GPSレシーバーの特性を把握、設定してから運用に入るので、GPSは全部noselect付。またgpsdは使用しない。
参照するタイムサーバはNICT、INTERNET MULTIFEED、Google、Cloudflare。
```conf:ntp.conf
# GPS-353
server 127.127.20.0 mode 32 minpoll 2 maxpoll 4 iburst noselect
fudge  127.127.20.0 time2 0.000 refid GPS0
# BU-353S4
server 127.127.20.1 mode 0 minpoll 2 maxpoll 4 iburst noselect
fudge  127.127.20.1 time2 0.000 refid GPS1
# LS23030-V2
server 127.127.20.2 mode 80 minpoll 2 maxpoll 4 iburst noselect
fudge  127.127.20.2 time2 0.000 refid GPS2
# LS23030-V2 for PPS
server 127.127.22.2 minpoll 2 noselect
fudge  127.127.22.2 time1 0.000 refid PPS2

pool ntp.nict.jp minpoll 6 maxpoll 9 iburst prefer
server ntp1.jst.mfeed.ad.jp minpoll 6 maxpoll 9 iburst prefer
server ntp2.jst.mfeed.ad.jp minpoll 6 maxpoll 9 iburst prefer
server ntp3.jst.mfeed.ad.jp minpoll 6 maxpoll 9 iburst prefer
server time1.google.com minpoll 6 maxpoll 9 iburst prefer
server time.cloudflare.com nts minpoll 6 maxpoll 9 iburst prefer
```
GPS-353はNMEAではなくバイナリモードになっていたので、ubxtoolを使ってNMEAモードに変更しておく。

# 動作確認
検証は数日動かしてから。
```
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 GPS_NMEA(0)     .GPS0.           0 l    2    8  377    0.000  -87.574   2.895
 GPS_NMEA(1)     .GPS1.           0 l    8    8  377    0.000  -302.28   0.979
 GPS_NMEA(2)     .GPS2.           0 l    5    8  377    0.000  -107.98   3.214
 PPS(2)          .PPS2.           0 l    4    8  377    0.000  -108.94   0.031
 ntp.nict.jp     .POOL.          16 p    -   64    0    0.000   +0.000   0.004
#time.cloudflare 10.23.8.4        3 u  247  512  377  174.823  -51.882   6.364
*ntp1.jst.mfeed. 133.243.236.17   2 u   57  512  377   18.306   -2.175  10.588
+ntp2.jst.mfeed. 133.243.236.18   2 u  413  512  377   17.074   -5.456   3.678
+ntp3.jst.mfeed. 133.243.236.19   2 u  144  512  377   18.452   -2.925   4.138
+time1.google.co .GOOG.           1 u  449  512  377   51.406   -3.223   2.745
+ntp-b2.nict.go. .NICT.           1 u  133  512  377   12.050   -4.611   3.268
```

# 補足
## gpsdによるGPSレシーバー設定変更の抑制
gpsdとかで動作確認してると、NMEAからバイナリモードに変わったりすることがあった。gpsdのドキュメント読んだらgpsdはGPSレシーバーとネゴシエーションして最適な設定に変更するとのこと。設定を変えられたく無い場合は--passiveを付ける。