Please be aware of all shipping deadlines before placing your order - we cannot guarantee orders will arrive before Christmas!

Adafruit_GPS parsing example
Moderators: adafruit_support_bill, adafruit

Adafruit_GPS parsing example

by astronomerdave on Wed Dec 19, 2012 12:16 pm

This isn't about the GPS shield per se, but more specifically the library and example code supplied. Here is the "parsing" example code, modified as I've used it, with certain lines stripped for simplicity , but I've not done anything that modifies the functionality of it.
Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

Adafruit_GPS GPS(&Serial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  true

// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

void setup() {
  Serial.begin(115200);
  Serial.println("Adafruit GPS library basic test!");
  useInterrupt(false);
  delay(1000);
  }

// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  if (GPSECHO)
    if (c) UDR0 = c; 
    // writing direct to UDR0 is much much faster than Serial.print
    // but only one character can be written at a time.
  }

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
    }
  else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
    }
  }

void loop() {
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    // if you want to debug, this is a good time to do it!
    if (GPSECHO) if (c) UDR0 = c;
    }
 
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    //Serial.println(GPS.lastNMEA());   // this also sets the newNMEAreceived() flag to false
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
      }
    }

If I call with useInterrupt(false) then it prints every NMEA sentence as expected,
Code: Select all | TOGGLE FULL SIZE
$GPRMC,160427.600,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*76
$GPRMC,160427.800,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*78
$GPRMC,160428.000,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*7F
$GPRMC,160428.200,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*7D
$GPRMC,160428.400,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*7B
$GPRMC,160428.600,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*79
$GPRMC,160428.800,A,3416.1119,N,11818.8537,W,0.00,206.03,191212,,,D*77

BUT, if I call with useInterrupt(true) then most of the sentences are missing the newline.
Code: Select all | TOGGLE FULL SIZE
$GPRMC,160615.800,A,3416.1185,N,11818.8520,W,0.01,219.89,191212,,,A*7$GPRMC,160616.000,A,3416.1187,N,11818.8516,W,0.02,219.89,191212,,,A*E$GPRMC,160616.200,A,3416.1187,N,11818.8514,W,0.01,219.89,191212,,,A*7$GPRMC,160616.400,A,3416.1185,N,11818.8514,W,0.02,219.89,191212,,,A*7$GPRMC,160616.600,A,3416.1185,N,11818.8513,W,0.03,219.89,191212,,,A*7
$GPRMC,160616.800,A,3416.1184,N,11818.8512,W,0.02,219.89,191212,,,A*7$GPRMC,160617.000,A,3416.1184,N,11818.8512,W,0.00,219.89,191212,,,A*7$GPRMC,160617.200,A,3416.1184,N,11818.8511,W,0.03,219.89,191212,,,A*7
$GPRMC,160617.400,A,3416.1184,N,11818.8510,W,0.00,219.89,191212,,,A*7$GPRMC,160617.600,A,3416.1184,N,11818.8508,W,0.01,219.89,191212,,,A*7$GPRMC,160617.800,A,3416.1184,N,11818.8507,W,0.05,219.89,191212,,,A*7$GPRMC,160618.000,A,3416.1184,N,11818.8505,W,0.01,219.89,191212,,,A*7$GPRMC,160618.200,A,3416.1184,N,11818.8505,W,0.03,219.89,191212,,,A*7$GPRMC,160618.400,A,3416.1182,N,11818.8506,W,0.04,219.89,191212,,,A*7$GPRMC,160618.600,A,3416.1180,N,11818.8507,W,0.04,219.89,191212,,,A*6$GPRMC,160618.800,A,3416.1179,N,11818.8508,W,0.04,219.89,191212,,,A*7$GPRMC,160619.000,A,3416.1179,N,11818.8509,W,0.02,219.89,191212,,,A*7$GPRMC,160619.200,A,3416.1177,N,11818.8511,W,0.05,219.89,191212,,,A*7$GPRMC,160619.400,A,3416.1177,N,11818.8511,W,0.01,219.89,191212,,,A*7$GPRMC,160619.600,A,3416.1175,N,11818.8511,W,0.04,219.89,191212,,,A*7$GPRMC,160619.800,A,3416.1175,N,11818.8511,W,0.01,219.89,191212,,,A*7$GPRMC,160620.000,A,3416.1174,N,11818.8512,W,0.03,219.89,191212,,,A*3$GPRMC,160620.200,A,3416.1173,N,11818.8513,W,0.02,219.89,191212,,,A*7$GPRMC,160620.400,A,3416.1172,N,11818.8513,W,0.04,219.89,191212,,,A*7$GPRMC,160620.600,A,3416.1171,N,11818.8513,W,0.04,219.89,191212,,,A*6$GPRMC,160620.800,A,3416.1170,N,11818.8513,W,0.03,219.89,191212,,,A*7$GPRMC,160621.000,A,3416.1170,N,11818.8513,W,0.01,219.89,191212,,,A*7$GPRMC,160621.200,A,3416.1169,N,11818.8514,W,0.01,219.89,191212,,,A*7$GPRMC,160621.400,A,3416.1168,N,11818.8515,W,0.02,219.89,191212,,,A*7$GPRMC,160621.600,A,3416.1167,N,11818.8514,W,0.04,219.89,191212,,,A*7$GPRMC,160621.800,A,3416.1166,N,11818.8515,W,0.05,219.89,191212,,,A*7
$GPRMC,160622.000,A,3416.1165,N,11818.8516,W,0.04,219.89,191212,,,A*7
$GPRMC,160622.200,A,3416.1163,N,11818.8517,W,0.04,219.89,191212,,,A*7$GPRMC,160622.400,A,3416.1162,N,11818.8518,W,0.01,219.89,191212,,,A*7$GPRMC,160622.600,A,3416.1162,N,11818.8519,W,0.04,219.89,191212,,,A*7

and that is the ONLY change I make.

Of course, if it's missing the newline then recvdflag never gets set true, so it doesn't think it's receiving valid sentences.

Also (and forgive my ignorance as I'm just picking up Arduinos this week) but this doesn't really seem interrupt driven. Not that it matters, really, but it seems like you're generating an interrupt with a timer, and then using that to poll. So it still seems like polling but polling at specific intervals, no? (as opposed to an interrupt being generated when data arrives, for example)

Anyway, I don't see how the \n is being lost.

Thanks!
astronomerdave
 
Posts: 15
Joined: Thu Dec 06, 2012 3:17 am

Re: Adafruit_GPS parsing example

by astronomerdave on Thu Dec 20, 2012 3:18 am

I still don't know why using the interrupt causes the loss of \n but I thought I'd share a minor bug I found in the Adafruit_GPS library. In the GPS.parse() function, in Adafruit_GPS.cpp, to get the milliseconds it calls fmod:
Code: Select all | TOGGLE FULL SIZE
    milliseconds = fmod(timef, 1.0) * 1000;

It seems that using floats introduces a rounding error (I'm not sure if it's fmod or someplace else). Another way to get this would be to multiply by 1000 to make it an integer and then take the mod 1000 of that int, E.G.
Code: Select all | TOGGLE FULL SIZE
    milliseconds = (long)(timef * 1000) % 1000;


This can be demonstrated by this simple (standalone) example:
Code: Select all | TOGGLE FULL SIZE
void setup(){
  float f=64839.600;
  Serial.begin(115200);
  Serial.println(f);
  Serial.println( fmod(f, 1.0) );
  Serial.println( fmod(f, 1.0) * 1000. );
  Serial.println( (long)(f*100) );
  Serial.println( (long)(f*1000) % 1000 );
  }

void loop(){}
which results in the following:
Code: Select all | TOGGLE FULL SIZE
64839.60
0.60
601.56
6483960
600


--Dave
astronomerdave
 
Posts: 15
Joined: Thu Dec 06, 2012 3:17 am

Re: Adafruit_GPS parsing example

by adafruit_support_bill on Thu Dec 20, 2012 8:01 am

Thanks for finding that. Floating point can give you a misleading sense of precision & accuracy. I avoid it whenever possible. I spent the better part of a week once explaining to a delegation of German scientists that their calculations were in error because there is no such thing a floating point 0.1. (In binary, it is a repeating fraction. So by definition, even a quadruple-precision representation will have round-off error in the mantissa.)

Not sure what is causing the loss of the newline. I suppose it could be missing a character due to cumulative timing errors, but it seems like too much of a coincidence that it would be the last one in the message. Does your fix affect the output?
User avatar
adafruit_support_bill
 
Posts: 32643
Joined: Sat Feb 07, 2009 10:11 am

Re: Adafruit_GPS parsing example

by astronomerdave on Fri Dec 21, 2012 4:49 pm

I spent the better part of a week once explaining to a delegation of German scientists that their calculations were in error because there is no such thing a floating point 0.1

get them to write binary 13... 1101, great. Now write 13.1 in binary! :)

Not sure what is causing the loss of the newline. I suppose it could be missing a character due to cumulative timing errors, but it seems like too much of a coincidence that it would be the last one in the message.

I thought so too, but came to the same conclusion; it's missing only the newline, never any other part of the sentence. Actually, I haven't been working on this particular problem the last couple of days. I'm instead focussing on the outlier rejection, which came up in this other post. I'll come back to it eventually. Meanwhile I'm simply polling and parsing when a valid sentence arrives,
Code: Select all | TOGGLE FULL SIZE
void loop() {
  char c = GPS.read();
  if (GPS.newNMEAreceived()) {
    // log to SD, etc.
    }
  }


Does your fix affect the output?

It does, yes. That's how I first noticed it. Since the GPS is writing at 5Hz the time stamps are all .0, .2, .4, .6, .8 but I was getting crazy values 0.189999, etc. I looked at the raw message and they were all 0.2 etc. so that's how I found the problem.
astronomerdave
 
Posts: 15
Joined: Thu Dec 06, 2012 3:17 am