gpsd客户端数据缓冲区

gpsd client data buffer

本文关键字:缓冲区 数据 客户端 gpsd      更新时间:2023-10-16

我正在开发一个C++应用程序,该应用程序应该使用gpsd检索收到的$GPGGA类型的NMEA语句。这个想法是大约每秒从gpsd读取一次,并解析最后一个$GPGGA接收到的句子,提取我感兴趣的两个字段:质量指标和参考站ID。我使用C++libgpsmm库,定期调用gpsmm::read()gpsmm::data(),直接访问客户端数据缓冲区。

起初,我使用gpsfake和一个假的GPS日志进行了几次测试(指定gpsfake选项"-c 0.5",以便每秒有两句话)。当两个对gpsd的请求之间的时间小于或等于400ms时,结果是可以的。如果我试着花更多的时间,结果是出乎意料的,每次阅读一段NMEA句子时都会有很多重复的数据和一些截断的句子。当我尝试使用一个每秒能写大约40句话的真正GPS时,情况真的更糟:在这种情况下,为了得到正确的结果,两次阅读之间的时间应该是大约10ms甚至更短。

下面是一个更简单的程序,用于打印收到的NMEA语句。它运行良好,与模拟,甚至与真正的GPS。但是,如果我取消对usleep()调用的注释,使程序每秒检查一次缓冲区,则客户端数据缓冲区不会给出合理的结果。

#include <iostream>
#include "libgpsmm.h"
using namespace std;
#define WAITING_TIME 5000000
#define RETRY_TIME 5
#define ONE_SECOND 1000000
int main(void)
{
for(;;){
//For version 3.7
gpsmm gps_rec("localhost", DEFAULT_GPSD_PORT);
if (gps_rec.stream(WATCH_ENABLE|WATCH_NMEA) == NULL) {
cout << "No GPSD running. Retry to connect in " << RETRY_TIME << " seconds." << endl;
usleep(RETRY_TIME * ONE_SECOND);
continue;    // It will try to connect to gpsd again
}
const char* buffer = NULL;
for (;;) {
struct gps_data_t* newdata;
if (!gps_rec.waiting(WAITING_TIME))
continue;
if ((newdata = gps_rec.read()) == NULL) {
cerr << "Read error.n";
break;
} else {
buffer = gps_rec.data();
// We print the NMEA sentences!
cout << "***********" << endl;
cout << buffer << endl;            
//usleep(1000000);
}
}
}
}

以下是对usleep()调用进行注释的输出(即连续读取数据):

$      ./GPSTest1
***********
{"class":"VERSION","release":"3.7","rev":"3.7","proto_major":3,"proto_minor":7}
***********
{"class":"WATCH","enable":true,"json":false,"nmea":true,"raw":0,"scaled":false,"timing":false}
***********
$GPGGA,202010.00,3313.9555651,S,06019.3785868,W,4,09,1.0,39.384,M,16.110,M,10.0,*46<CR><LF>
***********
$GPGGA,202011.00,3313.9555664,S,06019.3785876,W,4,09,1.0,39.386,M,16.110,M,11.0,*4D<CR><LF>
***********
$GPGGA,202012.00,3313.9555668,S,06019.3785882,W,4,09,1.0,39.394,M,16.110,M,12.0,*49<CR><LF>
***********
$GPGGA,202013.00,3313.9555673,S,06019.3785911,W,4,09,1.0,39.395,M,16.110,M,13.0,*49<CR><LF>
***********
$GPGGA,202014.00,3313.9555670,S,06019.3785907,W,4,09,1.0,39.409,M,16.110,M,14.0,*4F<CR><LF>
***********
$GPGGA,202015.00,3313.9555657,S,06019.3785905,W,4,09,1.0,39.395,M,16.110,M,15.0,*4A<CR><LF>

这是注释行时的输出(即每秒检查一次缓冲区):

$    ./GPSTest2
***********
{"class":"VERSION","release":"3.7","rev":"3.7","proto_major":3,"proto_minor":7}
***********
{"class":"DEVICE","path":"/dev/pts/0","activated":"2012-11-05T23:48:38.110Z","driver":"Generic NMEA","native":0,"bps":4800,"parity":"N","stopbits":1,"cycle":1.00}
$GPGGA,202013.00,3313.9555673,S,06019.3785911,W,1,09,1.0,39.395,M,16.110,M,13.0,*49<CR><LF>
0}
$GPGGA,202013.00,3313.9555673,S,06019.3785911,W,1,09,1.0,39.395,M,16.110,M,13.0,*49<CR><LF>
":"Generic NMEA","native":0,"bps":4800,"parity":"N","stopbits":1,"cycle":1.00}
$GPGGA,202013.00,3313.9555673,S,06019.3785911,W,1,09,1.0,39.395,M,16.110,M,13.0,*49<CR><LF>
***********
$GPGGA,202013.00,3313.9555673,S,06019.3785911,W,1,09,1.0,39.395,M,16.110,M,13.0,*49<CR><LF>
***********
$GPGGA,202016.00,3313.9555642,S,06019.3785894,W,1,09,1.0,39.402,M,16.110,M,16.0,*4E<CR><LF>
$GPGGA,202017.00,3313.9555643,S,06019.3785925,W,1,09,1.0,39.404,M,16.110,M,17.0,*42<CR><LF>
$GPGGA,202017.00,3313.9555643,S,06019.3785925,W,1,09,1.0,39.404,M,16.110,M,17.0,*42<CR><LF>
$GPGGA,202017.00,3313.9555643,S,06019.3785925,W,1,09,1.0,39.404,M,16.110,M,17.0,*42<CR><LF>
***********

有什么建议吗?起初,我试图直接分析gps_data_t结构,但与NMEA句子中的搜索相比,在结构的所有字段中,以这种方式识别质量指标和参考站ID似乎更困难。

我不熟悉gpsd服务,但您所描述的内容看起来很像接收缓冲区被损坏(覆盖)。GPS接收器持续输出NMEA信息,当应用程序处于休眠状态时,这些字符将累积在缓冲区中,如果接收到太多字符,则缓冲区将被覆盖。

增加串行端口接收器缓冲区大小(如果可能),或者在唤醒后清除缓冲区,然后等待下一条GGA消息(在最坏的情况下,可能长达一秒钟)。

GPS接收器应配置为以1Hz(每秒一次)输出信息,在这种情况下,设备应每秒仅输出约8个哨兵。如果你看到40个岗哨,那么你的接收器似乎在5Hz左右输出信息,这对你的特定兴趣来说听起来有些过头了。