Linux で IEEE1588 PTP (Precision Time Protocol) を試してみる

[Aptpod Advent Calendar](https://qiita.com/advent-calendar/2018/aptpod) 17日目は、aptpod の何でも屋 岩田 が担当します。

# はじめに
弊社は、高頻度 IoT データの収集・中継プラットフォーム [intdash](https://www.aptpod.co.jp/products/intdash/) を開発・提供している会社です。

intdash は、 IoT でよくある **軽量・超多数のデバイス（だけどデータの発生頻度は低め）** というよりは、**単体でも高頻度にデータを発生させる重厚なデバイス（だけどデバイス数は少なめ）** をターゲットにした、ちょっと変わったプラットフォームです。（自動車やロボットの研究開発部門などを中心にご利用いただいています）

取り扱うデータの頻度が **数ms~数100ms** と多少高頻度であったり、 **研究開発用途での利用が多い** 関係で、製品を作る際に時刻の精度・確度には気を遣うことが多いです。

今日の記事では、時刻の同期精度に関連して、 IEEE 1588 PTP を Linux（CentOS）で利用するまでの手順をご紹介します。

# IEEE 1588 PTP とは
**IEEE 1588 PTP (Precision Time Protocol)** とは、 IEEE 1588 で定義された、LANで接続された複数のデバイスを同期するための同期プロトコルです。

サーバー間の時刻同期などでよく使われるプロトコルとして  **NTP (Network Time Protocol)** が有名ですが、こちらはインターネットを経由してデバイスを同期するように設計されたプロトコルであり、 **数ミリ秒程度** の同期精度を達成することを目的としています。

対して PTP は、 NTP では同期精度が不足する、 **音響システムやロボット制御** などのユースケースを対象に、 "ネットワークを介して" より高精度な時刻同期を可能とするよう新たに設計されました。（専用クロック線を引くよりも低コストに、NTPよりも高精度に同期したい、というニーズを狙っているようです） PTPでは、基本的にLAN内での利用に適用範囲を限定することによって、専用ハードウェアによる支援（下記参照）を可能にし、 **サブミリ秒** での同期精度を達成します。

- LAN内のスイッチを専用スイッチ（仕様では透過クロックと呼ばれる）に置き換えることによって、スイッチ内でのソフトウェアレイテンシの影響を取り除く
- クライアントデバイスに専用ハードウェアを搭載し、ハードウェアタイムスタンプにより高精度な遅延測定を可能にする

詳細については、よくまとまっているページがすでにあるので、ここでは特に記載はしません。

- [SHOSHINさんのページ](https://www.shoshin.co.jp/c/endrun/1588ptp.html)
- [NIさんのページ](http://www.ni.com/tutorial/2822/ja/)


# 実際に使ってみる
それでは、実際に環境を構築し、動かしてみます。

## Vagrant で centos7 を UP
マスターとスレーブ用に２つ仮想マシンを作ります。
centos7 の box はよしなに入手してきてください。

```sh
$ vi Vagrantfile
$ vagrant up && vagrant ssh
```

```text:ptp1/Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "centos7"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.ssh.forward_agent = true
end
```

```text:ptp2/Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "centos7"
  config.vm.network "private_network", ip: "192.168.33.11"
  config.ssh.forward_agent = true
end
```


## LinuxPTP を インストール

一応 NIC を確認します。
仮想マシンなので当然ハードウェアタイムスタンプは非対応です。

対応している NIC は [The Linux PTP Project](http://linuxptp.sourceforge.net/) で確認できます。

```sh
$ lspci | grep Ethernet
00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 02)
00:08.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 02)
```

```sh
$ sudo yum install -y linuxptp
```


## NTP クライアントの停止

NTP クライアントが邪魔をしないように、止めてやります。

```sh
$ sudo systemctl disable chronyd
$ sudo systemctl stop chronyd
$ systemctl list-units --type=service
```


## Firewallの設定

Firewall が邪魔をしないように、 `319/udp` と `320/udp` を開けます。

```sh
$ sudo firewall-cmd --add-port=319/udp
$ sudo firewall-cmd --add-port=320/udp
$ sudo firewall-cmd --permanent --add-port=319/udp
$ sudo firewall-cmd --permanent --add-port=320/udp
```


## システムクロックの手動変更

同期がちゃんと実行されたことを確認するために、無理やりシステムクロックをずらしておきます。

```sh
$ sudo date -s '2017-01-01 00:00:00'
$ date
```


## LinuxPTP の起動

作った２つの仮想マシンでそれぞれ起動して、同期を確認します。
LinuxPTP を起動すれば、自動的にシステムクロックも補正してくれるようです。


```sh
$ ip a # ネットワークデバイスを確認
$ sudo ptp4l -i eth1 -m -S
```

`-s`をつけると、スレーブモードに限定できます。

```sh
$ ip a # ネットワークデバイスを確認
$ sudo ptp4l -i eth1 -m -S -s # Slaveに限定
```


## おまけ： LinuxPTP の自動起動設定

自動的に起動させるには、以下のように設定ファイルを書き、`systemctl`でリロードします。

```sh
$ sudo vi /usr/lib/systemd/system/ptp4l.service
[Unit]
Description=Precision Time Protocol (PTP) service

[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/ptp4l
ExecStart=/usr/sbin/ptp4l -i eth1 -m -S
ExecStop=/bin/pkill -f ptp4l

[Install]
WantedBy=multi-user.target
```

```sh
$ sudo systemctl daemon-reload
$ sudo systemctl start ptp4l
$ systemctl list-units --type=service
```


## おまけ2: ubuntu に ptpd をインストールする

Ubuntu では ptp4l ではなく ptpd を使うようです。

```sh
$ sudo apt-get update
$ sudo apt-get install ptpd
$ sudo ptpd -g -b eth1 -C # スレーブとして起動
$ sudo ptpd -G -b eth1 -C # NTP有りでマスターとして起動
$ sudo ptpd -W -b eth1 -C # NTP無しでマスターとして起動
```

# さいごに
今回は仮想マシンを使用してサクッと動かしてみただけなので、 PTP の目玉であるハードウェアによる支援は何も受けられません。実際にハードウェア支援を受けるためには、専用ハードを購入して、きちんとネットワークを設計する必要がありますが、それなりの機器はそれなりのお金がかかってしまうので、そこだけはご注意ください。

また、 PTP が威力を発揮するのは、FAでのロボットアーム間の同期や、コンサート会場でのスピーカー間のタイミング同期など、 **専用線を引くには遠いが、NTPでは同期精度が足りない（かつネットワークは自前で作れる）** という比較的大規模なケースになるかと思われます。あんまり、趣味でやってみれるような類のものではないので、こちらも併せてご注意ください（こんなネタですみませんでした）


# 参考
- [Centos7へLinuxPTP（Software Timestamping）を導入](http://qiita.com/rp_pen/items/467953d1d38e559e6c64)
- [LinuxPTP（Software Timestamping）で時刻同期](http://qiita.com/rp_pen/items/9e8658ae70ab09b7249a)