Friday, October 7, 2011

GPSd/libgps/gps.h

GPSd is a daemon that interfaces with and monitors GPS devices. In my undergraduate research, I have been working on a project that requires a C++ program that can grab a fix from a USB GPS mouse. I do not need to constantly monitor the GPS device, I just need simple location information as part of my data collection.

Unfortunately, there is very little example code out there for GPSd's C library, libgps (gps.h). Here is my experience with the library, as well as the code that I came up with.

From what I can understand about how GPSd and GPS mouses work together, the mouse is actually constantly looking for a fix. This was a little counter-intuitive to me, since I am used to working with Android where the device doesn't actually look for a fix unless you register with it. The GPS mouse sends status updates to GPSd on a regular basis, so what your code actually needs to do is ask GPSd to give you the next update, and determine if it's a fix or not. If the mouse currently does not have a fix, the function exits (and you should try again later when the mouse says you do have a fix)

Here is my shoddily documented code:

int getGPSData(gps_data_t *gpsdata){
    //connect to GPSd
    if(gps_open("localhost", "2947", gpsdata)<0){
        fprintf(stderr,"Could not connect to GPSd\n");
        return(-1);
    }

    //register for updates
    gps_stream(gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
    
    fprintf(stderr,"Waiting for gps lock.");
    //when status is >0, you have data.
    while(gpsdata->status==0){
        //block for up to .5 seconds
        if (gps_waiting(gpsdata, 500)){
            //dunno when this would happen but its an error
            if(gps_read(gpsdata)==-1){
                fprintf(stderr,"GPSd Error\n");
                gps_stream(gpsdata, WATCH_DISABLE, NULL);
                gps_close(gpsdata);
                return(-1);
                break;
            }
            else{
                //status>0 means you have data
                if(gpsdata->status>0){
                    //sometimes if your GPS doesnt have a fix, it sends you data anyways
                    //the values for the fix are NaN. this is a clever way to check for NaN.
                    if(gpsdata->fix.longitude!=gpsdata->fix.longitude || gpsdata->fix.altitude!=gpsdata->fix.altitude){
                        fprintf(stderr,"Could not get a GPS fix.\n");
                        gps_stream(gpsdata, WATCH_DISABLE, NULL);
                        gps_close(gpsdata);
                        return(-1);
                    }
                    //otherwise you have a legitimate fix!
                    else
                        fprintf(stderr,"\n");
                }
                //if you don't have any data yet, keep waiting for it.
                else
                    fprintf(stderr,".");
            }
        }
        //apparently gps_stream disables itself after a few seconds.. in this case, gps_waiting returns false.
        //we want to re-register for updates and keep looping! we dont have a fix yet.
        else
            gps_stream(gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);

        //just a sleep for good measure.
        sleep(1);
    }
    //cleanup
    gps_stream(gpsdata, WATCH_DISABLE, NULL);
    gps_close(gpsdata);
}

This works fine with my GlobalSat BU-353 GPS mouse. I guess there is always more testing to be done, but this works consistently.
Here is how you might print the results:

void debugDump(gps_data_t *gpsdata){
    fprintf(stderr,"Longitude: %lf\nLatitude: %lf\nAltitude: %lf\nAccuracy: %lf\n\n",
                gpsdata->fix.latitude, gpsdata->fix.longitude, gpsdata->fix.altitude,
                (gpsdata->fix.epx>gpsdata->fix.epy)?gpsdata->fix.epx:gpsdata->fix.epy);
}

I'm not entirely sure that's the best way to determine the accuracy. I'm not sure exactly what the ep attributes mean, they weren't explained well in the gps.h header file. My goal is to record something analogous to Android's getAccuracy() method. If anyone knows if my solution (just grabbing the larger of the two numbers) works or not, please comment and let me know!

Something to keep in mind when working with libgps/gps.h (and I'm sure this is common sense, but I'm an idiot sometimes), you need to use the -lgps compiler flag!

4 comments:

  1. Your tutorial and comments have been REALLY helpful in demystifying GPSD for me. I'm trying to run my own GPSD code, and having problems with accessing gps.h. My compile command is:

    gcc -o gps_test gps_test.c -l gps

    And it returns the following error:

    fata error: gps.h: No such file or directory compilation terminated.

    It seems that I don't have the libraries installed, but I have already installed them by the following commands:
    sudo apt-get install gpsd gpsd-clients python-gps
    sudo apt-get install gpsd

    Am I missing something? Where do I install or access gps.h?

    ReplyDelete
    Replies
    1. Hi Micah! Glad I could be of some help. Unfortunately it's been a long time since I've used GPSd. The first thing I would recommend trying is installing the libgps-dev package. Since it seems you are missing dev headers, that is the likely solution.

      If that doesn't work, you could try building GPSd from scratch. Here is how I used to do it on Ubuntu 12.04:

      wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.7.tar.gz
      tar -xzf gpsd-3.7.tar.gz
      cd gpsd-3.7
      scons prefix=/usr
      sudo scons udev-install

      Delete
  2. This comment has been removed by a blog administrator.

    ReplyDelete