буфер данных клиента gpsd

Я разрабатываю приложение C++, которое должно извлекать полученные предложения NMEA типа $GPGGA, используя gpsd. Идея состоит в том, чтобы читать из gpsd примерно раз в секунду и анализировать последнее полученное предложение $GPGGA, извлекая два интересующих меня поля: индикатор качества и идентификатор опорной станции. Я использовал библиотеку C++ libgpsmm, периодически вызывая gpsmm::read() и gpsmm::data(), обращаясь непосредственно к буферу данных клиента.

Сначала я сделал несколько тестов, используя gpsfake и поддельный журнал GPS (указав опцию gpsfake "-c 0.5", чтобы иметь два предложения в секунду). Результаты в порядке, когда время между двумя запросами к gpsd меньше или равно 400 мс. Если я попытаюсь увеличить время, то результаты будут неожиданными: при каждом чтении будет фрагмент предложений NMEA с большим количеством повторяющихся данных, а также некоторые усеченные предложения. Дела обстоят намного хуже, когда я пытаюсь использовать настоящий GPS, который записывает ~40 предложений в секунду: в этом случае время между считываниями должно быть ~ 10 мс или даже меньше, чтобы получить правильные результаты.

Ниже приведена более простая программа, которая печатает полученные предложения 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-предложения.


person pabloderosario    schedule 28.11.2012    source источник


Ответы (1)


Я не знаком со службой gpsd, но то, что вы описываете, очень похоже на то, что буфер приема поврежден (перезаписан). Приемник GPS постоянно выводит информацию NMEA, и когда ваше приложение находится в спящем режиме, эти символы будут накапливаться в буфере, и если будет получено слишком много символов, буфер будет перезаписан.

Либо увеличьте размер буфера приемника последовательного порта (если возможно), либо, возможно, очистите буфер после пробуждения, а затем дождитесь следующего сообщения GGA (в худшем случае это может занять до одной секунды).

Приемник GPS должен быть настроен на вывод информации с частотой 1 Гц (один раз в секунду), в этом случае устройство должно выводить только около 8 предложений в секунду. Если вы видите 40 предложений, то ваш приемник, похоже, выводит информацию с частотой около 5 Гц, что звучит как излишество для вашего конкретного интереса.

person timrorr    schedule 29.11.2012
comment
Спасибо за ответ, тимрорр! Некоторые комментарии: 1) Я думал об увеличении размера буфера раньше... но я не мог идентифицировать его среди всех определений, сделанных libgps, и я думаю, что должен быть обычный способ получить то, что я хочу. 2) Очистка буфера после пробуждения не решает мою проблему, потому что мое реальное приложение не может быть заблокировано в ожидании сообщений, поскольку тем временем оно должно обрабатывать другие вещи. 3) Я работаю с приемниками GPS на частоте 20 Гц: я получаю 10 GGA, 10 TVG и 1 ZDA в секунду. - person pabloderosario; 05.12.2012
comment
Определенно, лучшим способом решения моей проблемы было разделение сбора данных GPS и потребления этой информации на два разных потока. Таким образом, буфер не повреждается, а также нет необходимости в более частых чтениях. - person pabloderosario; 13.02.2013