NanoPi NEOとGPSモジュールでNTPサーバ 高精度PPS編

GPSモジュールと接続
写真は使い回し

NanoPi NEOとarmbianの組み合わせでPPSを使ったNTPサーバを作ろうとしたのに、どうやってもGPIOをPPSデバイスとして認識させることができなくて困っていた。他の機種用のソースを参考に自前でパッチを作成してビルドするも失敗。(ヘッポコなので仕方ない)
と、諦めかけてたところ、ソースツリーを更新したときに表示されたのが patch/kernel/sun8i-dev/add-h3-overlays.patch というパッチ。SoCがH3なSBC用と思われるOverlays周りのパッチ。これは期待できるかもとソースを見た。

patch/kernel/sun8i-dev/add-h3-overlays.patch (説明の一部)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
++### pps-gpio
++
++Activates pulse-per-second GPIO client
++
++Parameters:
++
++param_pps_pin (pin)
++	Pin PPS source is connected to
++	Optional
++	Default: PD14
++

キタ――♪ o(゚∀゚o) (o゚∀゚o) (o゚∀゚)o キタ――♪
自分でできなくて実現したかったズバリが入ってる。
更新したソースでフルビルドする。

ビルド時のカーネルオプション選択 (のPPS関連主要部)

General setup > Timers subsystem > Timer tick handling
	(X) Periodic timer ticks (Constant ratem no dynticks)

General setup > Timers subsystem
	[ ] Old Idle dynticks config ← * を外した
	[*] High Resolution Timer Support ← 標準で選択済

Device Drivers > PPS Support
	[*] PPS kernel comsumer support (NEW)
	[M] PPS line discipline
	[M] PPS client using GPIO

Device Drivers > PTP Clock Support
	< *> PTP clock support

Kernel Features > Timer frequency
	(X) 1000 Hz

その他サウンド関係は削除

Armbian_5.27_Nanopineo_Debian_jessie_dev_4.10.3-gatolabo.7z
PPSを有効にしてビルドしたイメージファイル NaniPi NEO用 (2017/04/02)

ビルドしたイメージファイルをmicroSDカードに書き込んで起動後、Overlay周りの実ファイルを見てみる。

$ cd /boot/dtb/overlay
# dtc -I dtb -O dts -o sun8i-h3-pps-gpio.dts sun8i-h3-pps-gpio.dtbo
$ cat sun8i-h3-pps-gpio.dts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/dts-v1/;

/ {
    compatible = "allwinner,sun8i-h3";

    fragment@0 {
        target = <0xffffffff>;

        __overlay__ {

            pps_pins {
                pins = "PD14";
                function = "gpio_in";
                linux,phandle = <0x1>;
                phandle = <0x1>;
            };
        };
    };

後略

pps-gpioのピンの初期値はPD14 (NanoPi NEOには無い?)になっているみたい。この値が要変更。

/boot/dtb/overlay/sun8i-h3-fixup.scr

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
前略
if test -n "${param_pps_pin}"; then
	setenv tmp_bank "${param_pps_pin}"
	setenv tmp_pin "${param_pps_pin}"
	run decompose_pin
	fdt set /soc/pinctrl@01c20800/pps_pins pins "${param_pps_pin}"
	fdt get value tmp_phandle /soc/pinctrl@01c20800 phandle
	fdt set /pps@0 gpios "<${tmp_phandle} ${tmp_bank} ${tmp_pin} 0>"
	env delete tmp_pin tmp_bank tmp_phandle
fi
後略

パッチの説明書きどおり、param_pps_pinで値を指定すれば良さげ。

イケそうなので、Overlay設定を行う。

NanoPi NEOピン配置
©FriendlyARM.
GPIO #18 pinを使う場合は上の図の一番右列の下から4番めなので"PG9"となる。
設定に書く時はpg9やPG09はダメ、PG9と書く必要があるので注意。
# vim /boot/armbianEnv.txt
1
2
overlays=pps-gpio
param_pps_pin=PG9

今回はNTP用なのでPPSだけでなくNMEAも使いたい。で、前回にも書いたがMainline系ではUART1が標準で無効になっているのでoverlayで有効にしてやる必要がある。UART1の有効化ではパラメーター(param_hoge)は不要。

で、本来は overlays=sun8i-h3-uart1 と書く。(前回の内容)
しかし、 /boot/armbianEnv.txt に overlay_prefix=sun8i-h3 が入っているのでプリフィックスの sun8i-h3-は要らない。

overlays=uart1

UART1の有効は以上のように書く。
ただし、pps-gpioも同じ行に書くので半角スペースを挟んで並べる。

# vim /boot/armbianEnv.txt

1
2
3
4
5
6
7
verbosity=1
console=tty0
overlay_prefix=sun8i-h3
rootdev=UUID=b5649d7f-59d4-4b79-998e-48ebdaecbc45
rootfstype=ext4
overlays=uart1 pps-gpio
param_pps_pin=PG9

こうなった。
再起動を行う。

# dmesg | grep pps
[    3.245898] pps_core: LinuxPPS API ver. 1 registered
[    3.245906] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti @linux.it>
[    8.015553] pps pps0: new PPS source pps@0.-1
[    8.015650] pps pps0: Registered IRQ 81 as PPS source

下2行でpps0が登録されていることがわかる。
次にデバイスファイルが生えたか確認する。

# ls -l /dev | grep pps
crw------- 1 root root    251,   0 Mar 24 22:48 pps0

/dev/pps0も無事生えてOK.

ネットのRaspberryPiのpps-gpio関連の記事を見ると/etc/modulesに追記するだの/etc/modprobe.dか/etc/modules-load.dにファイルを作成とか/boot/config.txtに行追加とかdtoverlayだとかいろいろ出てくるが、NanoPi NEO + armbian Mainline系では全然当てはまらないっぽい。

# apt install pps-tools
# 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 1490404439.001447609, sequence: 41083 - clear  0.000000000, sequence: 0
source 0 - assert 1490404440.001431655, sequence: 41084 - clear  0.000000000, sequence: 0
source 0 - assert 1490404441.001448742, sequence: 41085 - clear  0.000000000, sequence: 0
source 0 - assert 1490404442.001446747, sequence: 41086 - clear  0.000000000, sequence: 0
source 0 - assert 1490404443.001448001, sequence: 41087 - clear  0.000000000, sequence: 0
source 0 - assert 1490404444.001426256, sequence: 41088 - clear  0.000000000, sequence: 0

pps-toolsをインストールして/dev/pps0をデバイスに指定して実行。
1秒ごとに1行ずつ表示されて問題ないことを確認。[Ctrl]+[C]で終了。

NTPのビルド・インストール

前回は「なんちゃってPPS」だったのでNTPのビルドは不要だったが、今回は本物のPPSなのでそれに対応したNTPをビルド・インストールする。

# apt install libcap-dev
# /etc/init.d/ntp stop
# apt remove ntp
$ cd ~
$ wget http://archive.ntp.org/ntp4/ntp-4.2.8p8.tar.gz
$ tar zxvf ntp-4.2.8p8.tar.gz
$ cd ntp-4.2.8p8
$ ./configure --enable-linuxcaps --enable-ATOM --enable-NMEA --enable-ipv6
$ make -j4
# make install

# ln -s /usr/local/sbin/ntpd /usr/sbin/ntpd
# ln -s /usr/local/bin/ntpdc /usr/bin/ntpdc      #必要なら
# ln -s /usr/local/bin/ntpq /usr/bin/ntpq          #必要なら
# ln -s /usr/local/bin/ntpsweep /usr/bin/ntpsweep  #必要なら
# ln -s /usr/local/bin/ntptrace /usr/bin/ntptrace  #必要なら

また、前回はgpsd経由だったが、今回はgpsdを使わない。

NMEAの設定追加

# stty -F /dev/ttyS1 9600  ←シリアル速度をGPSモジュールに合わせてやる
# cat /dev/ttyS1

/dev/ttyS1だとNTPが読めないので以下2行。

# chmod 0666 /dev/ttyS1
# ln -s /dev/ttyS1 /dev/gps0

/etc/ntp.confのベースは前回のNTPの記事のものとする。
/etc/ntp.confに以下2行追記(前回の記事のntp.confなら最後の2行を編集)。mode 16はGPSモジュールのシリアル最高速度が9600bpsの場合に指定。19200bpsならmode 32、38400bpsならmode 48、57600bpsならmode 64を指定。gpsd経由じゃないのでここはしっかり指定したい。なお、NMEAセンテンスからの取り方によってはmodeの値は9600bpsなら16+αになる。αの部分はこちらのページがわかりやすい。$GPRMCならαは1、$GPGGAならαは2など

1
2
server 127.127.20.0 mode 17 minpoll 4 prefer
fudge  127.127.20.0 refid NMEA
mode 17は9600bps + RMCセンテンス

NTPを再起動して確認する。

# /etc/init.d/ntp restart (またはservice ntp restart)
少し待ってから
# ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
-GPS_NMEA(1)     .NMEA.           0 l    6   16   17    0.000  -187.84 102.432
*ntp1.jst.mfeed. 133.243.236.17   2 u   53   64    1   15.076   -2.403   0.008
+ntp2.jst.mfeed. 133.243.236.17   2 u   53   64    1   14.021   -2.540   0.008
+ntp3.jst.mfeed. 133.243.236.17   2 u   54   64    1   14.962   -1.863   0.008

NMEAの行のpoll,reach,offsetが0以外で機能していることを確認。

PPSの設定追加

PPSについてはNTPは/dev/pps0から取得してくれる筈だがパーミッションがおそらくNTPで読めないものなので変更。

# chmod 0666 /dev/pps0

/etc/ntp.confに以下2行追記。

1
2
server 127.127.22.0 minpoll 4 maxpoll 4
fudge  127.127.22.0 flag3 1 refid PPS

設定が済んだらNTPを再起動して確認。

# /etc/init.d/ntp restart (またはservice ntp restart)
5分以上待ってから
# ntptime
ntp_gettime() returns code 0 (OK)
  time dc80dcac.24700044  Sat, Mar 25 2017 21:10:20.142, (.142334118),
  maximum error 99917 us, estimated error 138 us, TAI offset 0
ntp_adjtime() returns code 0 (OK)
  modes 0x0 (),
  offset 0.000 us, frequency -32.493 ppm, interval 128 s,
  maximum error 99917 us, estimated error 138 us,
  status 0x2107 (PLL,PPSFREQ,PPSTIME,PPSSIGNAL,NANO),
  time constant 6, precision 0.001 us, tolerance 500 ppm,
  pps frequency -32.875 ppm, stability 0.650 ppm, jitter 31.055 us,
  intervals 20, jitter exceeded 2, stability exceeded 0, errors 0.

PPSが機能していることが確認できたらOK.上の水色のstatus行を注視。PPSは完全に機能してntpq -pで確認できる状態になるまで5分程度はかかる。

システムを再起動すると/dev内のgps0が無くなり、pps0やttyS1のパーミッションが元に戻ってしまうのでシステム起動後に自動的に設定を行うようにする。
# vim /etc/udev/rules.d/10-gps.rules (新規ファイル作成)

1
2
KERNEL=="ttyS1",SYMLINK+="gps0",MODE="0666"
KERNEL=="pps0",MODE="0666"

ntpd起動のタイミングが悪いのかNMEAとPPSが読めないようなので /etc/rc.local ファイルの最後のexit 0の直前に3行挿入。(NTP再起動という手抜き)

1
2
3
/bin/systemctl stop ntp.service
/bin/stty -F /dev/ttyS1 9600
/bin/systemctl start ntp.service

システムを再起動し、ntpq -pででNMEAとPPSに値が入ることを確認。(PPSは5分以上待つ)

出来たと思ったら・・

# ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
xGPS_NMEA(0)     .NMEA.           0 l    9   16  377    0.000  -181.01  12.761
xPPS(0)          .PPS.            0 l    9   16  377    0.000    0.001   0.004
*ntp2.jst.mfeed. 133.243.236.17   2 u   12   64   17   13.774   -0.318   1.808
+ntp3.jst.mfeed. 133.243.236.17   2 u   24   64    7   14.750   -0.306   2.135
+ntp1.jst.mfeed. 133.243.236.17   2 u   18   64   17   14.810   -0.029   1.791

ぱっと見では良さ気なんだけどNMEAとPPSに x が付いてしまう。

# ntpq -c assoc
ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 55298  9124   yes   yes  none falsetick   reachable  2
  2 55299  9124   yes   yes  none falsetick   reachable  2
  3 55300  941a   yes   yes  none candidate    sys_peer  1
  4 55301  9424   yes   yes  none candidate   reachable  2
  5 55302  961a   yes   yes  none  sys.peer    sys_peer  1

上と同じこと。インデックス1と2がNMEAとPPSだが、コンディションがfalsetick (上の x の意味)になっている。
で、かなり悩んだが、最初に戻って調べていたら、GPIOから受け取っているPPS信号がNTPが欲しがってるのと逆なんじゃないかと思い立つ。

パルスの立ち上がりと立ち下がり
$ cat /boot/dtb/overlay/README.sun8i-h3-overlays
前略
param_pps_falling_edge (bool)
	Assert by falling edge
	Optional
	Default: 0
	When set (to 1), assert is indicated by a falling edge
		(instead of by a rising edge)
後略

rising edgeとfalling edgeのどちらを使うかだが、標準が0だから1に変更。

# echo "param_pps_falling_edge=1" >> /boot/armbianEnv.txt

上のように推測したけどハズレっぽい

システムを再起動。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*GPS_NMEA(0)     .NMEA.           0 l    4   16  377    0.000  -75.223  16.482
oPPS(0)          .PPS.            0 l    2   16  377    0.000    0.001   0.004
 LOCAL(0)        .LOCL.          10 l  436   64  100    0.000    0.000   0.002
+ntp1.jst.mfeed. 133.243.236.17   2 u   35   64  177   14.852    3.078  67.476
+ntp2.jst.mfeed. 133.243.236.17   2 u   28   64  177   14.161  102.508  72.278
-ntp3.jst.mfeed. 133.243.236.17   2 u   45   64  177   14.415  101.434  84.172

起動後暫くはxが付いた状態だが、数分後には上のようになる。(正常)
なお、起動後に"ntpdate -u 他所のNTPサーバ"と"hwclock -w"でRTC timeをしっかり修正しておかないとダメっぽい。(ここ超重要)

酷いと実時間10秒で1秒狂う時計がデタラメすぎてこのままでは直ぐに同期が切れるっぽい。対応は(できるか不明だけど)別記事で。

armbianとH3なSBCを使ったGPS(PPS)使用のNTPサーバの情報が全然無いので正直苦労した。この件に限っては?RaspberryPiの情報が全然参考にならないんだけどNanoPi,OrangePi等の他のユーザーさんはどうしてるんだろ?

関連記事: