最近在研究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" 在文件cx1910_single_pmu.dts中
ublox_gps {
compatible = "pps-gpio";//与驱动中的进行匹配
gpios = <&gpio 109 0>;//引脚初始化,名字一定是gpios
pinctrl-names = "default";//
pinctrl-0 = <&gps_pps_pin>;//pinctrl配置的引脚
status = "okay";
};
因为源码中的驱动会进行匹配设备树的compatible
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
[ 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信号处理服务。此处主要介绍起使用,不做编译以及功能详细说明。
1. gpsd源码下载
地址:GPSd — Put your GPS on the net!
点击下面的链接,可以从git库上拉取,也可以直接下载源码压缩包。
压缩包下载地址:Index of /releases/gpsd/
我这里下载的是:gpsd-3.18.tar.gz
2. ubuntu 下安装交叉编译工具链以及编译工具scons
1) 确保ubuntu系统已经安装python
2) 确保交叉编译工具arm-none-linux-gnueabihf-gcc正常可用
这个地方根据自己使用的交叉编译链而定,一般进行交叉编译gpsd,交叉编译链都是安装好的,不一定是arm-linux-gnueabihf-gcc。
3. 交叉编译依赖库libusb\libncurses
交叉编译gpsd,需要安装依赖库 libusb 、libncurses、libtinfo。
(依赖库安装包我已上传资源,需要的话可以下载:交叉编译-gpsd.rar_gpsd交叉编译-Linux文档类资源-CSDN下载)
(1)安装libusb
下载libusb
下载网址:libusb - Browse Files at SourceForge.net
我这里下载是:libusb-1.0.26.tar.bz2
2) 交叉编译libusb
将下载好的libusb压缩包解压后进入文件libusb-1.0.22。
执行下列指令:
./configure CC=arm-none-linux-gnueabihf-gcc --host=arm-linux --prefix=$PWD/arm_install --disable-udev;
CC指定交叉编译工具
–host指定目标主机,linux表示linux平台,arm-linux表示arm平台
–prefix指定安装目录$PWD/arm_install表示在当前目录下的arm_install目录。
为防止报错,–disable-udev ,用于去掉对udev库的依赖。
3) 编译:make
4) 安装:make install
5) 交叉编译生成的库文件目录:
arm_install/lib/
需要将 libusb-1.0.so 等文件压缩打包(有软连接)拷贝至 gpsd源码目录下,gpsd交叉编译时要用到。
6)编译时报错处理
若执行./configure命令时报错,找不到libudev,解决办法如下:
1) 下载源码包
ncurses库下载地址:Index of /pub/gnu/ncurses
我下载的是
解压缩源码包,进入文件:
2) 执行配置命令:
./configure CC=arm-none-linux-gnueabihf-gcc --host=arm-linux --prefix=$PWD/arm_install --with-shared
其中:
–prefix 是指定生成文件的路径
–host 指定的是交叉编译工具链的前缀
–with-shared 生成动态库
3) 编译:make (时间较长,耐心等待)
4) 安装:make install
make install 时报错了,错误如下:
(解决办法参考:移植ncurses库到arm开发板和一个ncurses例子程序-bufre-ChinaUnix博客)
该错误的意思是 进入到目录下ncurses-6.1/progs 下执行 tic 可执行文件时,无法识别文件格式。 使用 file 命令查看一下这个 tic 文件的格式:
发现它是arm平台的,当然无法在pc上运行。
如果pc机上已经安装过ncurses(ncurses是配置内核、根文件系统等需要用到make menuconfig必备的库),那么pc上一定有可以在pc运行的tic。此时,我们find一下tic 。
命令:find / -name “tic”
发现/usr/bin/目录下有tic:
file /usr/bin/tic
这个是x86平台的tic,我们将 /usr/bin下的tic拷贝至 /home/heat/tronlong/debug_tools/gpsd/ncurses-6.1/progs 目录,替换该目录下的tic
返回源码根目录下,继续 make install ,又报错:
无法识别二进制文件 toe。问题和上面的 tic 类似。我们看一下 ncurses-6.1/progs 文件列表:
发现还有好几个可执行文件,索性一次性都从 /usr/bin 下拷贝过来:
返回源码根目录下,继续 make install
5) 交叉编译生成的库文件目录:
arm_install/lib/
我这里将生成的 libncurses 相关的文件全部打包后拷贝至 gpsd 源码目录下,然后解压缩,因为libncurses库建立了几个软连接,直接拷贝过去软连接就木有了,所以先压缩后解压缩。这里就是把上面两个交叉编译得到的库放到gpsd源码目录下。
依赖库编译完了,下面就该转战 gpsd 的源码目录下,去编译gpsd了。
(3)安装libtinfo
libtinfo 库是 libncurses 的组成部分,所以建立libtinfo软连接:
还是在ncurses-6.1/arm_install/lib 目录下,执行以下命令:
ln -s libncurses.so.6 libtinfo.so.6
ln -s libtinfo.so.6 libtinfo.so
1
2
执行命令后:
将生成的libtinfo库压缩,拷贝至gpsd 的源码目录,然后解压缩。
如果已经将libncurses库拷贝至gpsd 的源码目录,也可以到gpsd 的源码目录下建立libtinfo 的软连接。不必来回拷贝,麻烦。
4. 交叉编译 gpsd
1、首先确定libusb库和ncurses库已经放到gpsd源码目录下,并且libtinfo软连接也建立完成。然后将两个依赖库编译出来的头文件也放到gpsd源码目录下。
2、创建cache 文件
首先创建了cache 文件,该文件一开始并不存在:
vi .scons-option-cache
libgpsmm = False
python = False
prefix = '/home/heat/tronlong/debug_tools/gpsd/gpsd-3.18/bin/gpsd'
target = ' arm-none-linux-gnueabihf'
这里根据自己交叉编译工具的实际情况和想要安装的实际路径进行填写。
3、执行scons进行编译。
编译过程中可能会报错,根据错误内容进行修改即可。
我遇到的报错如下:
(1)头文件错误修改为”curses.h”
EFAULT_SOURCE -D_XOPEN_SOURCE=600 cgps.c
cgps.c:96:10: fatal error: curses.h: No such file or directory
96 | #include
| ^~~~~~~~~~
compilation terminated.
scons: *** [cgps.o] Error 1
scons: building terminated because of errors.
(2)头文件错误修改为”curses.h”
In file included from gpsmon.c:32:
gpsmon.h:11:10: fatal error: curses.h: No such file or directory
11 | #include
| ^~~~~~~~~~
compilation terminated.
scons: *** [gpsmon.o] Error 1
scons: building terminated because of errors.
这两个报错的文件都是要编译出工具的cpgs和gpsmon。
解决报错后编译通过
4、scons install
运行命令进行安装,安装目录是在.scons-option-cache中配置的prefix参数。
5、运行gpsd
(1)将生成的库移植到嵌入式终端,一般放在lib目录下。然后直接在终端运行gpsd即可,使用默认配置server为localhost ,port为2947。
[/]#gpsd /dev/ttyS2 /dev/pps1
(2)运行cgps,可能会报错如下:
[/]#cgps
cgps: no gpsd running or network error: -2, can't get host entry
查看cgps的源码发现,应该是通过localhost查找IP地址找不到,这是因为rootfs中缺少hosts文件。查看/etc下是否存在hosts文件,并且打开文件查看是否有如下内容:
127.0.0.1 localhost
127.0.0.1 localhost
127.0.1.1 buildroot chenxin buildroot
(3)修改终端环境变量和配置
即使进行了上述配置也可能报以下错误:
[/system/lib]#cgps
Error opening terminal: vt102.
这是因为终端环境变量有问题,进行查看
[/system/lib]#echo $TREM
[/system/lib]#echo $TREMINFO
[/system/lib]#
发现都是空,需要进行配置。将如下文件放入/system/usr/share目录下。
然后配置环境变量
[/system/lib]#export TERMINFO=/system/usr/share/terminfo
再次执行CGPS,效果如下。这是我没有接收到GPS信号,接收到GPS信号后便会有相应的经纬度值。