1. 程式人生 > >Raspberry Pi與GPS構建NTP伺服器

Raspberry Pi與GPS構建NTP伺服器

A) ntp伺服器簡介

參考

NTP伺服器【Network Time Protocol(NTP)】是用來使計算機時間同步化的一種協議,它可以使計算機對其伺服器或時鐘源(如石英鐘,GPS等等)做同步化,它可以提供高精準度的時間校正(LAN上與標準間差小於1毫秒,WAN上幾十毫秒),且可介由加密確認的方式來防止惡毒的協議攻擊。時間按NTP伺服器的等級傳播。按照離外部UTC源的遠近把所有伺服器歸入不同的Stratum(層)中。

NTP提供準確時間,首先要有準確的時間來源,這一時間應該是國際標準時間UTC。 NTP獲得UTC的時間來源可以是原子鐘、天文臺、衛星,也可以從Internet上獲取。這樣就有了準確而可靠的時間源。時間按NTP伺服器的等級傳播。按照離外部UTC 源的遠近將所有伺服器歸入不同的Stratum(層)中。Stratum-1在頂層,有外部UTC接入,而Stratum-2則從Stratum-1獲取時間,Stratum-3從Stratum-2獲取時間,以此類推,但Stratum層的總數限制在15以內。所有這些伺服器在邏輯上形成階梯式的架構相互連線,而Stratum-1的時間伺服器是整個系統的基礎。

B) GPS模組

目前在淘寶買高階版的UBLOX gps模組並不方便,原因在於國內為了扶持北斗系統採取了一定的禁售。目前gps比北斗的定位仍然比較優秀,而授時服務基本沒有差距。
根據我們的需求,我們只需購買對應的國產的雙模接受模組(GPS+北斗)
我們選擇了淘寶上的UM220-III型晶片,模組照片如圖:
UM220-III
我們使用上方的串列埠1進行設定和測試,相應的引腳分別是:

vcc_3, RXD1, TXD1, GND

帶有短接帽的引腳連線的是pps訊號和一顆led,在短接帽連線下,pps訊號會驅動led一閃一閃。由於我們要使用pps,拔出短接帽,使用右邊的pps訊號來源口。

B.a) TTL和RS232電平

電平名稱 輸出L 輸出H 輸入L 輸入H
TTL <0.8V >2.4V <1.2V >2.0V
RS232 +3~+15V -3~-15V +3~+15V -3~-15V
CMOS <0.1*Vcc >0.9*Vcc <0.3*Vcc >0.7*Vcc

B.b) GPS模組測試

按照淘寶說明在windows下連線測試可用

B.c) 模組配置

預設的波特率是9600, 主要完成3方面的配置:

  • 關閉北斗頻點,只使用GPS(由於後面的ntpd只識別GPS的NMEA語句,不支援擴充套件的北斗NMEA語句,故我們只使用GPS)
  • 設定授時脈衝,週期為1000ms,脈衝長為100ms,脈衝上升沿與整秒對齊。只在授時有效時才輸出授時脈衝(這一點暫時保留爭議,看後續的使用)
  • 設定動態模型為車載模式

C) Raspberry-Pi

使用的是Raspberry-Pi 3 model B。
這裡寫圖片描述
其GPIO介面定義如下:
這裡寫圖片描述
我們將要使用其中的串列埠15作為gps的NMEA語句接收埠,GPIO 18作為pps的接收埠。

raspberry pi的系統我們使用官方推薦raspbian,安裝參考官方的安裝說明

C.a) 連線

raspberry pi gps model
3.3v vcc_3
Ground GND
GPIO 15 TXD1
GPIO 18 pps

C.b) 準備Raspberry-pi的串列埠

C.b.a) 關閉串列埠登陸功能

關閉樹莓派的串列埠登陸功能,使得樹莓派可以使用GPIO 14和GPIO15作為串列埠通訊。
進入系統後,進行配置:

sudo raspi-config

找到Serial這一項,disable之
這裡寫圖片描述

C.b.b) 關閉藍芽功能

需要注意的是Raspberry-Pi 3相比於1和2在使用串列埠的時候會有問題

原因是樹莓派CPU內部有兩個串列埠,一個是硬體串列埠(官方稱為PL011 UART),一個是迷你串列埠(官方成為mini-uart)。在樹莓派2B/B+這些老版樹莓派上,官方設計時都是將“硬體串列埠”分配給GPIO中的UART(GPIO14&GPIO15),因此可以獨立調整串列埠的速率和模式。而樹莓派3的設計上,官方在設計時將硬體串列埠分配給了新增的藍芽模組上,而將一個沒有時鐘源,必須由核心提供時鐘參考源的“迷你串列埠”分配給了GPIO的串列埠,這樣以來由於核心的頻率本身是變化的,就會導致“迷你串列埠”的速率不穩定,這樣就出現了無法正常使用的情況。

操作如下:

step 1
/boot/config.txt修改

enable_uart=0

enable_uart=1

然後新增

dtoverlay=pi3-miniuart-bt
force_turbo=1

step 2
修改/boot/cmdline.txt
刪除所有的console=xxx的語句,例如將

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

改成

dwc_otg.lpm_enable=0 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

step 3
修改藍芽服務,編輯/lib/systemd/system/hciuart.service
修改[Unit]中的After欄位,由

After=dev-serial1.device

改至

After=dev-ttyS0.device

修改[Service]中的ExecStart欄位,由

ExecStart=xxx(根據具體表示)

改成

ExecStart=/usr/lib/hciattach /dev/ttyS0 bcm43xx 460800 noflow -

注:其中的bcm43xx真的是xx哦

重啟Raspberry Pi, 串列埠接下來可以用作通訊

C.b.c) 測試串列埠

試試

cat /dev/ttyAMA0

能不能讀到類似

$GPGSA,A,3,02,04,12,25,24,05,10,,,,,,02.4,01.1,02.1*07 
$GPGLL,2834.2631,N,07720.5426,E,102954.00,A,A*6E 
$GPGSV,3,1,07,02,71,345,50,04,40,038,48,12,62,315,47,25,20,321,45*7F 
$GPGSV,3,2,07,05,41,167,23,10,48,086,23,24,24,236,30,,,,*48 
$GPGSV,3,3,07,,,,,,,,,,,,,,,,*7E 

的資料,如果ok,那就證明串列埠正常,如果不行,用minicom或者stty設定埠的波特率為9600 8n1之後再試

你也可以使用

gpsmon  /dev/ttyAMA0

來讀取串列埠的gps資料,你將會看到類似如下資訊
這裡寫圖片描述

C.b.d) 測試pps

step 1
安裝工具

sudo apt install pps-tools

step 2
修改/boot/cmdline.txt,新增

dtoverlay=pps-gpio,gpiopin=18

step 3
修改/etc/modules,新增

pps-gpio

step 4
重啟樹莓派,
使用命令lsmod | grep pps
應該能看到下面類似訊息

pps_gpio          2555   0
pps_core          7092   1  pps_gpio

使用命令dmesg | grep pps
會看到
這裡寫圖片描述

step 5
這個時候使用命令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 1421066632.974111422, sequence: 1698 - clear  0.000000000, sequence: 0
source 0 - assert 1421066633.974045488, sequence: 1699 - clear  0.000000000, sequence: 0

每一秒輸出一條,如果發現輸出間隔不是1s,可能是連線錯誤或者gps模組未設定導致。

D) 配置ntp服務

總結來說chrony的優點有:

  • 同步更快,只需要幾分鐘收斂到最小誤差,而ntpd可能需要幾個小時
  • 對變頻環境有優化
  • 對於斷線和延遲有更好的優化

所以,對於移動和不穩定的網路結構,建議使用chrony,對於穩定的網路結構,ntpd不失為可靠的選擇,而且ntp還有認證的安全功能。

下面我們將對兩種方案在Raspberry pi上的部署進行說明

D.a) ntpd

D.a.a) 編譯安裝ntpd

系統apt安裝的nptd是一個裁剪版,不支援PPS,所以得自己下載原始碼編譯一個。
如果系統裡頭已經安裝了ntpd,先解除安裝

sudo service ntp stop
sudo apt remove ntp

先安裝libcap-dev:

sudo apt install libcap-dev
wget http://archive.ntp.org/ntp4/ntp-4.2.8p1.tar.gz

解壓、配置、編譯:

tar xzf ntp-4.2.8p1.tar.gz
cd ntp-4.2.8p1/
./configure --enable-linuxcaps

然後make,安裝:

make
sudo make install 
sudo cp /usr/local/bin/ntp* /usr/bin/
sudo cp /usr/local/sbin/ntp* /usr/sbin/

重新啟動ntpd

sudo service ntp start

D.a.b) 配置ntpd從串列埠連線本地串列埠

編輯/etc/ntp.conf

# NMEA refclock driver directly from serial port
server 127.127.20.0 mode 16 minpoll 4 iburst prefer true
fudge 127.127.20.0 flag1 1 flag2 0 flag3 0 flag4 0 time1 0.1 refid GPS
# ATOM PPS driver directly from interrupt through /dev/pps1
server 127.127.22.0 minpoll 4 maxpoll 4 iburst true
fudge 127.127.22.0 flag2 0 flag3 0 flag4 1 time1 0.1 refid PPS

server, fudge的配置說明請參看man ntp.conf。這裡的127.127.t.u並不是真實的ip地址,而是Reference clock,根據t來指定實際型別,根據u來指定裝置。t對應的實際裝置需要看文件,在原始碼目錄ntp/html/drivers/driverxx.html中也有。

配置完成以後重啟ntpd

sudo service ntp restart

D.a.c) 測試

執行

ntpq -crv -p

這裡寫圖片描述

這裡寫圖片描述

至此,ntp服務已經在樹莓派上起來了。

D.b) chrony

官網上有相關的說明。

ntp支援多種裝置驅動,而chrony除了對pps的支援,對於GPS的串列埠連線的NMEA語句沒有支援,只有通過gpsd的共享記憶體的方式來獲取。

開始之前,先執行rcconf來將ntpd的開機啟動關閉吧(如果沒有rcconf可以通過apt安裝哈)

D.b.a) 安裝gpsd

執行命令:

sudo apt install gpsd gpsd-clients python-gps

修改/etc/default/gpsd檔案,修改

DEVICES="/dev/ttyAMA0"
GPSD_OPTIONS="-n -G"

-n = don’t wait for client connects to poll GPS
-G = make gpsd listen on INADDR_ANY

接下來配置開機啟動,由於這個版本的raspbian使用systemd作為系統管理,我們可以通過systemctl工具來配置開機啟動。
首先,修改/lib/systemd/system/gpsd.service檔案,在[install]後面新增

WantedBy=multi-user.target

修改[Unit]中的After=chrony.serviceBefore=chrony.service
ps: 其他項的設定可能也要實際的情況修改
然後執行

sudo systemctl enable gpsd

之後重啟即可

接下來通過執行

cgps -s

來檢視gpsd的資料,你在開闊的地方,你將會看到類似下面的輸出
這裡寫圖片描述

D.b.b) 安裝chrony

raspbian源上的chrony貌似挺新的,直接通過apt安裝

sudo apt install chrony

配置檔案是/etc/chrony.conf或是/etc/chrony/chrony.conf
確保有以下內容(如果沒有則新增)

leapsectz right/UTC
makestep 1.0 -1
rtcsync

參考chrony.conf
其中 :
makestep 1.0 -1表示超過1秒立刻修正,不迴歸
rtcsync每11分鐘自動矯正系統rtc時鐘

參考 GPSD說明
chrony.conf檔案中繼續新增pps和gpsd的連線配置,

refclock PPS /dev/pps0 lock GPSD prefer refid PPS
refclock SHM 0 offset 0.0 delay 0.2 refid GPSD

ps:注意這裡的delay的大小,delay代表訊號源往返訊號的時間預估,其值的一半會作為訊號的誤差,且不同的訊號源的範圍需要overlay。比如pps的delay和offset都很小,接近0,而gpsd的offset如果在100ms左右,這個時候就需要設定delay大於0.2(兩倍100ms,我會設定0.3)。否則的話,通過`chronyc sources -v`會看到型號元前面是time may be error的標誌

配置完成後重啟服務

sudo service chrony restart

可以通過下面兩個命令來檢視連線狀態

資料統計:chronyc sources -v
源狀態: chronyc sourcestats -v

如果想要連續觀察,可以藉助linux的watch命令,即

watch -n1 chronyc sources -v
watch -n1 chronyc sourcestats -v

E) 使用NTP服務

有很多種方法。我們下面講下利用chrony的自動更新功能來更新本地時鐘。

E.a) 配置主NTP

我們首先講樹莓派和需要連線的主機放在同一個局域網裡頭,這裡假設是192.168.2.xx網段。樹莓派需要配置靜態ip,這裡假設是192.168.2.100
在樹莓派的chrony.conf中新增一行:

allow

這將允許所有的ipv4和ipv6的地址的客戶端的訪問.
同時確定下沒有攔截樹莓派的ntp服務埠123

E.b) 配置從NTP

step 1
在所有的需要同步的主機安裝chrony

step 2
所有從主機的/etc/hosts檔案新增

192.168.2.100 rpi

step 3
在主機的chrony.conf中新增

server rpi minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2 iburst

leapsectz right/UTC
makestep 1.0 -1
rtconutc
rtcsync

具體引數說明可以參考chrony.conf
需要說明的是:

  • maxdelaydevratio 一種類似測量標準差般的存在,越大對錯誤資料的容忍度越高,錯誤資料的擾動越大。預設是10
  • rtconutc 表明更新RTC時鐘到UTC時間
  • rtcsync 在linux是每11分鐘更新,macos是每60分鐘更新,其餘系統上無效

重啟每臺主機的chrony服務即完成基於樹莓派的ntp服務對本地時鐘的校準