GPSBots.com
Home :: [Photos] : [Tutorials] : [Projects] : [Resources] : [About Us]

Linux Tutorial: Decoding GPS Data

gps.cpp
GPS packets come in many different forms, you can get the details on all the different types in the Reference Section. The following functions are to decode the following data type:

$GPGLL,3354.4970,N,11759.5354,W,025604,V,S*52

which will give you the current latitude and longitude in decimal notation. Decimal notation makes GPS calculations a lot easier, for example: 120o27'W converts to -120.45o. It also converts the time to standard Unix format instead of a packed string.


using namespace std;
#include <iostream>
#include <stdio.h>
#include <time.h>      // mktime, struct tm

typedef struct s_gps {
     double lon;      // - = south, in metric
     double lat;      // - = west, in metric
     double time;      // time of fix, seconds since 1970
};
s_gps DecodeGPS(char *val);      // decode the gps packet off the serial line
int GPSGoodData(char *val, char *checksum);      // check the checksum

s_gps DecodeGPS(char *val) {      // decode the GPS packet and return the struct
     s_gps data;      // gps packet

     if (GPSGoodData(val, "")) {      // error messages are handled inside gpsgooddata
          if (strncmp(val, "", 6) == 0) {
     //     ,3354.4970,N,11759.5354,W,025604,V,S*52 lat/lon; V(a=valid, v=invalid)
     //     0 1 2 3 4 5 6 7
               double lat_deg_nmea, lon_deg_nmea;
               char lat_dir, lon_dir;
               int hms;
               char valid;
               struct tm tm;
               sscanf(val, ",%lf,%c,%lf,%c,%d,%c,", &lat_deg_nmea, &lat_dir, &lon_deg_nmea, &lon_dir, &hms, &valid);
               tm.tm_sec = hms % 100;
               hms = hms / 100;
               tm.tm_min = hms % 100;
               hms = hms / 100;
               tm.tm_hour = hms % 100;
               data.time = mktime(&tm) + time(0);

     //     lat_deg is in the format ddmm.mmmm
               double grades, frac;
               frac = modf(lat_deg_nmea / 100.0, &grades);
               data.lat = (double)(grades + frac * 100.0 / 60.0) * (lat_dir == 'S' ? -1.0 : 1.0);
               frac = modf(lon_deg_nmea/ 100.0, &grades);
               data.lon = (double)(grades + frac * 100.0 / 60.0) * (lon_dir == 'W' ? -1.0 : 1.0);
          }
     }
     return data;     
}

int GPSGoodData(char *val, char *checksum) {
     // NMEA 0183 sentences begin with $ and and with CR LF
     if (val[0] != '$') {      // Does not start with $
          return false;
     }
     // ending must be CRLF (\n\r)
     if (val[strlen(val)-2] != '\n') {      // Does not end-1 with \n
          return false;
     }
     if (val[strlen(val)-1] != ' ') {      // Does not end with \r
          return false;
     }
     return true;
}