Question

I am developing a C++ application that should retrieve the received NMEA sentences of type $GPGGA, using gpsd. The idea is to read from gpsd approximately once per second and to parse the last $GPGGA received sentence, extracting the two fields of my interest: the quality indicator and the reference station ID. I used the C++ libgpsmm library, periodically calling to gpsmm::read() and to gpsmm::data(), accessing directly to the client data buffer.

At first, I have made several tests using gpsfake and a fake GPS log (specifying the gpsfake option "-c 0.5", in order to have two sentences per second). The results are OK when the time between two requests to gpsd is less or equal to 400ms. If I try with a greater time, the results are unexpected, having in each reading a piece of NMEA sentences with lots of repeated data as well as some truncated sentences. The things are really worse when I try with a real GPS that writes ~40 sentences per second: in this case the time between reading should be ~ 10ms or even less in order to have correct results.

The following is a simpler program that prints the NMEA sentences that are received. It works well, with the simulated and even with the real GPS. But if I uncomment the usleep() call, which makes the program to check the buffer once per second, client data buffer does not give reasonable results.

#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);
            }
        }
    }
}

Here is the output having the usleep() call commented (ie. continually reading data):

$      ./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>

And this is the output when the line is commented (ie. the buffer is checked once per second):

$    ./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>
***********

Any suggestion? At first, I tried to directly analyze the gps_data_t structure, but It seems to be harder to identify the quality indicator and the reference station ID that way, among all the fields of the structure, in comparison with the search within a NMEA sentence.

Was it helpful?

Solution

I am not familiar with the gpsd service, but what you describe looks a lot like the receive buffer is being corrupted (overwritten). The GPS receiver is outputting NMEA information continually and when your application is sleeping these characters will accumulate in the buffer, if too many characters are recieved then the buffer will get overwritten.

Either increase the serial port receiver buffer size (if possible) or maybe clear the buffer after the wakeup and then wait for the next GGA message (which may be up to one second away in the worst case).

The GPS receiver should be configured to output information at a 1Hz (once per second), in this case the the device should only output about 8 sentances per second. If you're seeing 40 sentances then your receiver would seem to be outputting information at around 5Hz which sounds like overkill for your particular interest.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top