转载自关于GPS的1PPS时间同步功能探索与测试_学一点IOT-程序员宅基地 - 程序员宅基地
最近在研究GPSD相关信息,查阅到GPSD可以与NTPD相配合实现高精度时间同步功能,因此才涉及到此主题。
目前手头用的是Ublox F9P模块,UART输出NEMA数据,另外一个GPIO输出1PPS脉冲
首先看一张时序图:
1. NEMA中包含有时间信息,一般是秒级别,也有部分带有毫秒
2. 1PPS即每秒输出一个脉冲,图中以高电平触发为例(没画下降沿),接收及处理1PPS脉冲的时间也在ns级别
3. 因为NEMA是通过串口发送和接收,而且一次NEMA数据量也有KB级别大小,处理时间远比1PPS时间长
4. 通过NEMA中的秒级时间和1PPS脉冲相配合,即可实现高精度时间同步(ns级:依据1PPS的响应时间)
具体时间同步实现,以Linux为例,常用组合方式为:kenel pps.ko,GPSD,chronyd或者NTPD
首先Kernel pps.ko:
当前kernel是支持pps处理的,因为我用的ublox的pps是接到gpio的,所以选择gpio方式
1. kernel timer client 是内核软件模拟的pps信号,用于测试
2. pps client using gpio 是以gpio作为pps信号源
pps-gpio.c源码实现也比较简单,主要通过注册gpio中断,当gpio电平变化时,记录当前系统运行时刻,然后post event到用户空间。
因为使用了外部GPIO,因此在使用该模块之前,需要在dts中指定相关的gpio引脚,compatible 为 "pps-gpio"
static const struct of_device_id pps_gpio_dt_ids[] = {
{ .compatible = "pps-gpio", },
{ /* sentinel */ }
};
配置后编译启动,查阅dmesg
root@imx8qxpmek:~# dmesg |grep pps
[ 0.708441] pps_core: LinuxPPS API ver. 1 registered
[ 0.713357] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[ 1.737515] pps pps0: new PPS source ktimer
[ 1.741727] pps pps0: ktimer PPS source registered
[ 1.747556] pps pps1: new PPS source pps.-1
[ 1.751804] pps pps1: Registered IRQ 115 as PPS source
[ 236.866057] pps pps1: unsupported capabilities (2)
此处, PPS0为内核模拟的pps信号,pps1 ublox模块的pps 信号
在应用层,使用ppstest工具可查看pps信号时间值(pps信号发生时刻的系统时间点)
root@imx8qxpmek:~# 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 1591828917.828448156, sequence: 85791 - clear 0.000000000, sequence: 0
source 0 - assert 1591828918.852533634, sequence: 85792 - clear 0.000000000, sequence: 0
source 0 - assert 1591828919.876534727, sequence: 85793 - clear 0.000000000, sequence: 0
^C
root@imx8qxpmek:~#
root@imx8qxpmek:~# ppstest /dev/pps1
trying PPS source "/dev/pps1"
found PPS source "/dev/pps1"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1591828923.065352191, sequence: 87742 - clear 0.000000000, sequence: 0
source 0 - assert 1591828924.065348846, sequence: 87743 - clear 0.000000000, sequence: 0
source 0 - assert 1591828925.065347127, sequence: 87744 - clear 0.000000000, sequence: 0
source 0 - assert 1591828926.065348783, sequence: 87745 - clear 0.000000000, sequence: 0
^C
查看pps中断:
root@imx8qxpmek:~# date
Thu Jun 11 06:43:49 CST 2020
root@imx8qxpmek:~# cat /proc/interrupts |grep pps
115: 87851 0 0 0 gpio-mxc 16 Edge pps.-1
root@imx8qxpmek:~# date
Thu Jun 11 06:43:53 CST 2020
root@imx8qxpmek:~# cat /proc/interrupts |grep pps
115: 87855 0 0 0 gpio-mxc 16 Edge pps.-1
通过date时间打印,可以看到1s产生一次中断。
至此,pps配置完毕,接下来处理NEMA数据,使用gpsd
gpsd是一个支持多设备,多协议以及提供丰富工具集的专用gps信号处理服务。此处主要介绍起使用,不做编译以及功能详细说明。
通过gpsd -l可以查阅支持的gps协议或者说模块,我裁剪过,只保留NEMA和ublox,所以只显示两个
root@imx8qxpmek:~# ./gpsd -l
NMEA0183
n b c * u-blox
# n: mode switch, b: speed switch, c: rate switch, *: non-NMEA packet type.
# Socket export enabled.
# Shared memory export enabled.
# Time service features enabled.
另外也可以看到,支持socket 和共享内存方式通讯,比如后续要用到的chronyd即是采用共享内存方式,同时也开启了时间服务,当然此处没有用,而是用chrony单独实现。
启动gpsd服务后,可以通过其提供的各种工具集获取gps状态,比如gpsmon:
接下来配置chrony,chrony为一个专用的时间服务程序,与NTPD类似
其配置为/etc/chrony.conf
leapsectz right/UTC
makestep 1.0 -1
rtcsync
refclock PPS /dev/pps1 lock GPSD prefer refid PPS
refclock SHM 0 offset 0.0 delay 0.2 refid GPSD
allow
driftfile - 根据实际时间计算出计算机增减时间的比率,将它记录到一个文件中是最合理的,它会在重启后为系统时钟作出补偿,甚至可能的话,会从时钟服务器获得较好的估值。
rtcsync -指令将启用一个内核模式,在该模式中,系统时间每11分钟会拷贝到实时时钟(RTC)。
allow / deny - 这里你可以指定一台主机、子网,或者网络以允许或拒绝NTP连接到扮演时钟服务器的机器。
makestep - 通常,chronyd将根据需求通过减慢或加速时钟,使得系统逐步纠正所有时间偏差。在某些特定情况下,系统时钟可能会漂移过快,导致该调整过程消耗很长的时间来纠正系统时钟。该指令强制chronyd在调整期大于某个阀值时步进调整系统时钟,但只有在因为chronyd启动时间超过指定限制(可使用负值来禁用限制),没有更多时钟更新时才生效
refclock driver parameter[:option]… [option]…
The refclock directive specifies a hardware reference clock to be used as a time source. It has two mandatory parameters, a driver name and a driver-specific parameter. The two parameters are followed by zero or more refclock options. Some drivers have special options, which can be appended to the driver-specific parameter using the : character.
There are four drivers included in chronyd:
PPS
Driver for the kernel PPS (pulse per second) API. The parameter is the path to the PPS device (typically /dev/pps?). As PPS refclocks do not supply full time, another time source (e.g. NTP server or non-PPS refclock) is needed to complete samples from the PPS refclock. An alternative is to enable the local directive to allow synchronisation with some unknown but constant offset. The driver supports the following option:
clear
By default, the PPS refclock uses assert events (rising edge) for synchronisation. With this option, it will use clear events (falling edge) instead.
Examples:
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
详细的各项配置可以参阅官方文档:chrony – Documentation
运行chronyd之后,可以看到时间同步过程:
root@imx8qxpmek:~# chronyd -d -f /etc/chrony.conf
2020-06-10T17:00:41Z chronyd version 3.2 starting (+CMDMON +NTP +REFCLOCK +RTC -PRIVDROP -SCFILTER -SECHASH -SIGND +ASYNCDNS +IPV6 -DEBUG)
2020-06-10T17:00:41Z Initial frequency -116.709 ppm
2020-06-10T17:00:41Z Timezone right/UTC failed leap second check, ignoring
2020-06-10T17:01:28Z Selected source PPS
2020-06-10T17:01:28Z System clock wrong by 34565.191649 seconds, adjustment started
2020-06-11T02:37:33Z System clock was stepped by 34565.191649 seconds
在客户端方面,使用ntpd测试如下:
root@OpenWrt:~# ntpd -d -n -p 192.168.3.1
ntpd: sending query to 192.168.3.1
ntpd: reply from 192.168.3.1: offset:+6152.992411 delay:0.008204 status:0x24 strat:1 refid:0x00535050 rootdelay:0.000015 reach:0x01
ntpd: sending query to 192.168.3.1
ntpd: reply from 192.168.3.1: offset:+6152.990378 delay:0.004154 status:0x24 strat:1 refid:0x00535050 rootdelay:0.000015 reach:0x03
ntpd: setting time to 2020-06-11 10:42:38.090639 (offset +6152.990378s)
至此以NEMA,1PPS,GPSD,chronyd 等在linux平台搭建的时间服务器完毕。