GPS logger works fine...as long as the serial data coming in isn't valid

Feeling so close yet so far. As the data comes in I am parsing it, pulling the latitude and longitude data out and converting it to a decimal format before printing it out to the serial port. Eventually it will be written to an sd card (as 1000's have done before me I'm sure). The problem is, as long as the data coming in is invalid, the program behaves as expected. As soon as the GPS spits out some valid data, the program processes it and then stops reading any additional data from the GPS. I know that the GPS is still transmitting because if I reset the micro controller I get one valid line of data before it freezes up again. I threw in a line of code at the top of my looping function just to see if the controller was freezing up completely and it doesn't, i.e. it still prints out the string at the top of the loop even though it isn't processing any more of the incoming data from the GPS. Can someone spot my problem because I am at a loss? My program is below:

#include <SoftwareSerial.h>

SoftwareSerial softSerial(4,5); //receive on pin 4 and transmit on pin 5

byte START_CMD[] = {
                    0xA0,
                    0xA2,
                    0x00,
                    0x18};
byte NMEA_SET[] = {
                  0x81,
                  0x02,
                  0x01, //GGA
                  0x01, //check sum
                  0x00, //GLL  
                  0x00, //check sum
                  0x00, //GSA
                  0x00, //check sum
                  0x00, //GSV
                  0x00, //check sum
                  0x01, //RMC
                  0x01, //check sum
                  0x00, //VTG
                  0x00, //check sum
                  0x00, //MSS
                  0x00, //check sum
                  0x00, //EPE
                  0x00, //check sum
                  0x00, //ZDA
                  0x00, //check sum
                  0x00, //unused
                  0x00, //unused
                  0x12, //baud in hex high byte
                  0xC0}; //baud in hex low byte

volatile int state = 0;
boolean sentenceBegins = false;
char buffer[90];
int index = 0;
const byte wakePin = 3;
const byte Control = 2;
const byte On_Off = 7;
char messageID[6];
char time[11];
char latit1[5];
char latit2[5];
char NS[2];
char longit1[6];
char longit2[5];
char EW[2];
char fixindicator[2];
char satsUsed[3];
char HDOP[4];
char MSLalt[10];
char Units[2];
char Geoid[6];
char GeoUnits[2];
char GPSstatus[2];
char GPSspeed[8];
char GPScourse[7];
char Date[7];
char Dummy[12];
boolean sdfileOK = false;





void setup()
{
  Serial.begin(115200);
  softSerial.begin(4800);
  
  pinMode(wakePin,INPUT);  //GPS Wakeup Pin
  pinMode(Control,INPUT);  //Control Button
  pinMode(On_Off,OUTPUT); //GPS ON/OFF Pin
  digitalWrite(On_Off,LOW);
  digitalWrite(Control,HIGH);
  
  ADCSRA = 0; //disable ADC as we don't need it
  
  //configure GPS to transmit GGA and RMC data only

  Serial.println("Waking GPS");
  //wake up GPS unless it is already awake
  while(!GPSisAwake())
  {    
    digitalWrite(On_Off, HIGH);
    delay(200);
    digitalWrite(On_Off, LOW);
  }
  
    Serial.println("GPS is awake!");
    Serial.println("Switching to Binary");
    //set to binary mode
    softSerial.println("$PSRF100,0,4800,8,1,0*0F");
    delay(100);
    
    //set back to NMEA mode with GGA and RMC messages active
    Serial.println("Switching to NMEA");
    for(int x=0; x<4; x++)
    {
      softSerial.write(START_CMD[x]);
    }
    
    for(int x = 0; x<24; x++)
    {
      softSerial.write(NMEA_SET[x]);
    }
    
    softSerial.write(highByte(calc_check()));
    softSerial.write(lowByte(calc_check()));  
    softSerial.write(0xB0);
    softSerial.write(0xB3);
      
}

void loop()
{
  if(checkforSentence())
  {
    //Serial.println("Sentence Found");
    //Serial.println(buffer);
    Process_message();

  }
}


boolean GPSisAwake()
{
  
  //check to see if GPS is already awake by purging serial data and then
  //checking to see if Serial data is still coming in
  while(softSerial.available()) softSerial.read();
  delay(1200);
  if(softSerial.available()) return true;
  return false;
}

boolean checkforSentence()
{
  char c;
  while(softSerial.available())
  {
    c = softSerial.read();
    
    if(sentenceBegins && c == '\r') //we have a full sentence
    {
      sentenceBegins = false;
      return true;
    }
    
    if(sentenceBegins) //store characters to buffer
    {
      buffer[index] = c;
      index++;
      buffer[index] = '\0';
    }
    
    if(c == '

and this is what the output looks like:

Waking GPS
GPS is awake!
Switching to Binary
Switching to NMEA
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
...lots more lines of 0.00000 here deleted for space
0.000000,0.000000
0.000000,0.000000
0.000000,0.000000
40.689670,-74.115737
Waking GPS             //This is where I tried a few resets with no luck
GPS is awake!
Switching to Binary
Switching to NMEA
40.689731,-74.115287
Waking GPS
GPS is awake!
Switching to Binary
Switching to NMEA
40.689811,-74.115303
Waking GPS
GPS is awake!
Switching to Binary
Switching to NMEA
40.689834,-74.115661
Waking GPS
GPS is awake!
Switching to Binary
Switching to NMEA
40.689792,-74.115745

) //beginning of sentence...start saving to buffer
    {
      sentenceBegins = true;
      index = 0;
    }
 
  }
  return false;
}

int calc_check()
{
  byte msglen = sizeof(NMEA_SET);
  byte index = 0;
  int checksum = 0;
  while(index<msglen)
  {
    checksum+=NMEA_SET[index++];
    checksum&=(0x7FFF);
  }
  return checksum;
}

const char* mytok(char* pDst, const char* pSrc, char sep = ',')
{
    while ( *pSrc )
    {
        if ( sep == *pSrc )
        {
            *pDst = '\0';

return ++pSrc;
        }

*pDst++ = *pSrc++;
    }

*pDst = '\0';

return NULL;
}

void Process_message()
{
  const char*    ptr;
 
  //check message ID to see what kind of message we got
  ptr = mytok(messageID, buffer);
 
  //if it is GGA, read in the data and write to SDCard if
  //the data is valid and an SD file has been created
  if(strcmp(messageID, "GPGGA") == 0)
  {
    ptr = mytok(time, ptr);
    ptr = mytok(latit1, ptr, '.'); //get the first half of latitude
    ptr = mytok(latit2, ptr); //get the second half of latitude
    ptr = mytok(NS, ptr);
    ptr = mytok(longit1, ptr, '.');
    ptr = mytok(longit2, ptr);
    ptr = mytok(EW, ptr);
    ptr = mytok(fixindicator, ptr);
    ptr = mytok(satsUsed, ptr);
    ptr = mytok(HDOP, ptr);
    ptr = mytok(MSLalt, ptr);
    ptr = mytok(Geoid, ptr);
    ptr = mytok(GeoUnits, ptr);
   
    float f, GPSdegrees, fminutes, fseconds, latdecCoords, longdecCoords;
   
    //convert latitude to GPS Decimal format
    f = atof(latit1);
    f /= 100.00;
    GPSdegrees = (int)f; //isolate degrees
    fminutes = (f - (int)f) * 100;
    //float fminutes = (int)(temp); //isolate minutes
    fseconds = atof(latit2)/100.00; //isolate seconds
    latdecCoords = GPSdegrees + (((fminutes * (60.0)) + fseconds)/3600.0);
    if(NS[0] == 'S') latdecCoords *= -1.0;
   
    //cconvert longitude to GPS decimal format
    f = atof(longit1);
    f /= 100.00;
    GPSdegrees = (int)f; //isolate degrees
    fminutes = (f - (int)f) * 100;
    fseconds = atof(longit2)/100.00; //isolate seconds
    longdecCoords = GPSdegrees + (((fminutes * (60.0)) + fseconds)/3600.0);
    if(EW[0] == 'W') longdecCoords *= -1.0;
   
   
   
    Serial.print(latdecCoords, 6);
    Serial.print(",");
    Serial.println(longdecCoords,6);
  }

}


and this is what the output looks like:

§DISCOURSE_HOISTED_CODE_1§

There's a fair bit of code there so tracking bugs down by inspection will be a slow process.

To get an idea where the problem is I suggest you selectively disable parts of the message processing until you get to the part that causes the problem.

If you simply print out and discard each sentence, does it keep working?

How about of you try and parse the message ID, then skip all the following processing?

My main area of suspicion is that code in mytok which seems to be copying a token from the front of the buffer. None of the calls to it seem to handle the null return case, and I also wonder what would happen if it ran off the end of the source string without finding a separator. It's used extensively and it would only need to fail once to cause mayhem and memory corruption.

I took your advice and started pairing back the instructions and found that it works fine if I just take out the last mytok call in this section of code:

if(strcmp(messageID, "GPGGA") == 0)
  {
    ptr = mytok(time, ptr);
    ptr = mytok(latit1, ptr, '.'); //get the first half of latitude
    ptr = mytok(latit2, ptr); //get the second half of latitude
    ptr = mytok(NS, ptr);
    ptr = mytok(longit1, ptr, '.');
    ptr = mytok(longit2, ptr);
    ptr = mytok(EW, ptr);
    ptr = mytok(fixindicator, ptr);
    ptr = mytok(satsUsed, ptr);
    ptr = mytok(HDOP, ptr);
    ptr = mytok(MSLalt, ptr);
    ptr = mytok(Geoid, ptr);
    ptr = mytok(GeoUnits, ptr); //works fine if I take this out

I also am handling the NULL pointer in the mytok function by just doing this:

ptr = mytok(time, ptr); if(ptr == NULL) {Serial.println("Out1"); return;}
    ptr = mytok(latit1, ptr, '.'); if(ptr == NULL) {Serial.println("Out2"); return;}//get the first half of latitude
    ptr = mytok(latit2, ptr); if(ptr == NULL) {Serial.println("Out3"); return;}//get the second half of latitude
    ptr = mytok(NS, ptr); if(ptr == NULL) {Serial.println("Out4"); return;}
    ptr = mytok(longit1, ptr, '.'); if(ptr == NULL) {Serial.println("Out5"); return;}
    ptr = mytok(longit2, ptr); if(ptr == NULL) {Serial.println("Out6"); return;}
    ptr = mytok(EW, ptr); if(ptr == NULL) {Serial.println("Out7"); return;}
    ptr = mytok(fixindicator, ptr); if(ptr == NULL) {Serial.println("Out8"); return;}
    ptr = mytok(satsUsed, ptr); if(ptr == NULL) {Serial.println("Out9"); return;}
    ptr = mytok(HDOP, ptr); if(ptr == NULL) {Serial.println("Out10"); return;}
    ptr = mytok(MSLalt, ptr); if(ptr == NULL) {Serial.println("Out11"); return;}
    ptr = mytok(Geoid, ptr); if(ptr == NULL) {Serial.println("Out12"); return;}
    //ptr = mytok(GeoUnits, ptr); if(ptr == NULL) {Serial.println("Out13"); return;}

Thanks for your help! Now if I can just figure out why the GPS is telling me that I am about 200 yards from my actual position and walking out in the middle of the water(at least according to google maps).

These are the coordinates from a short walk I took....uploaded them into google maps and apparently I can walk on water:

40.689712,-74.115570
40.689708,-74.115562
40.689701,-74.115539
40.689689,-74.115524
40.689689,-74.115516
40.689685,-74.115501
40.689682,-74.115486
40.689666,-74.115463
40.689659,-74.115447
40.689659,-74.115432
40.689655,-74.115417
40.689643,-74.115402
40.689640,-74.115379
40.689640,-74.115364
40.689640,-74.115348
40.689632,-74.115333
40.689628,-74.115310
40.689620,-74.115295
40.689609,-74.115272
40.689598,-74.115249
40.689586,-74.115226
40.689579,-74.115211
40.689571,-74.115196
40.689563,-74.115180
40.689559,-74.115165
40.689563,-74.115173
40.689579,-74.115196
40.689586,-74.115219
40.689594,-74.115242
40.689598,-74.115257
40.689605,-74.115264
40.689609,-74.115287
40.689613,-74.115303
40.689620,-74.115318
40.689628,-74.115333
40.689632,-74.115356
40.689636,-74.115379
40.689640,-74.115402
40.689647,-74.115432
40.689655,-74.115455
40.689659,-74.115478
40.689666,-74.115501
40.689682,-74.115509
40.689689,-74.115531
40.689697,-74.115554
40.689701,-74.115570
40.689704,-74.115600
40.689716,-74.115608
40.689723,-74.115623
40.689735,-74.115646
40.689743,-74.115661
40.689750,-74.115676
40.689762,-74.115684
40.689769,-74.115707
40.689777,-74.115722
40.689788,-74.115737
40.689796,-74.115760
40.689807,-74.115768
40.689815,-74.115791
40.689830,-74.115798
40.689838,-74.115806
40.689842,-74.115806
40.689842,-74.115791
40.689849,-74.115783
40.689853,-74.115776
40.689857,-74.115768
40.689865,-74.115760

It is ususual for the actual measurement error to be as large as 200 metres.

However, I have noticed that the measured GPS position sometimes wanders away from the correct position,
and then gets "stuck" maybe 200 metres away and it won't come back. Thats if I stand still. But then
if I walk around a bit, it will suddenly come good, maybe ten or 15 minutes later.

I used to get this often, when I got off a train. The GPS reading would overshoot the station, and stop
several hundred metres down the track. I was told that this was a "feature", to prevent the apparent
position of cars shifting while they were stopped at the traffic lights.

If you get a NULL pointer from mytok, it means that the rest of the data is missing or is crap. You can't call mytok again after getting a NULL pointer.

michinyon:
It is ususual for the actual measurement error to be as large as 200 metres.

However, I have noticed that the measured GPS position sometimes wanders away from the correct position,
and then gets "stuck" maybe 200 metres away and it won't come back. Thats if I stand still. But then
if I walk around a bit, it will suddenly come good, maybe ten or 15 minutes later.

I used to get this often, when I got off a train. The GPS reading would overshoot the station, and stop
several hundred metres down the track. I was told that this was a "feature", to prevent the apparent
position of cars shifting while they were stopped at the traffic lights.

I've got to figure this out. I took it out for a quick drive last night and got this course plot. Last I checked, my car isn't amphibious. I'm going to take it out for a while today and see if it can figure itself out. Interestingly enough, amidst all of that jumping around, I can make out segments of the course that I actually drove but it is offset from my actual position by a fixed amount at some points in the track but then it jumps to a different offset amount.


Screen Shot 2013-01-21 at 7.41.10 AM by jg1996business, on Flickr

I'm kinda thinking more and more that I have a defective unit. I drove that route again and got the exact same track on google maps, even the places where the offset between the indicated track and the actual track suddenly jumps. I'm logging the number of satellites used in the position calculation and it is never less than 7 which seems to me should provide a very precise location (within a few meters). I then took it out on the freeway to see if perhaps a better line of sight would give better results. The results are just as erratic in terms of the offset. Crazy thing is that when I am sitting outside of my house, it is absolutely sure of my position in that it doesn't jump around. I hooked it up wrong when I first started prototyping with it and for a short period of time was applying negative 1.7 volts to Vcc by accidently plugging the ground wire into the +5V arduino rail with Vcc hooked to the 3.3V arduino output. Given that it was putting out data after I hooked it up correctly, I kind of figured that I must not have done any damage to the GPS module but perhaps I did and that is causing the erratic behavior. Thoughts?

I'm not familiar with that hardware but it seems unlikely to me that an electrical failure would leave it basically working and with such a precise and consistent error in the results.

PeterH:
I'm not familiar with that hardware but it seems unlikely to me that an electrical failure would leave it basically working and with such a precise and consistent error in the results.

That would be my expectation as well, but I just can't figure out what may be causing this behavior. As far as I can tell, these modules are basically designed to be plug and play. :~

The module that I am using is the Maestro A2035-H.

http://www.richardsonrfpd.com/resources/RellDocuments/SYS_28/Maestro_A2035_H_data_v11.pdf

Does anyone else have experience with this module?

Your use of float may be generating some rounding errors. In addition, float's only good to 6-7 significant places - your output is providing more. Can you write a simpler sketch that just echoes what the GPS is sending? Then you'll be able to see if the device really is bad.

It does seem more likely that it's some kind of rounding error in your GPS coordinates. It could be that the X and Y offsets just round off at different points as you move and that makes it appear that you jump +/- the error in the x or y planes.

I'd be inclined to log all the values, both as your extracted and converted numerical values, against the raw string values, and see if there are any differences in the conversion between string > numeric.

You're doing quite a bit of arithmetic with those Floats. Even simple things like dividing by 100 or multiplying by -1 can have results that you don't expect, when working with Floats.

I think I've figured out what I was doing wrong, and as usual it is a face palm kind of problem. I was mistaking the format that the GPS was spitting out (4041.4335 as an example) as 40 degrees, 41 minutes, 43.35 seconds but it appears that this is actually 40 degrees, 41.4335 minutes. I am going to wait until I am driving around before I decide if this was truly the problem but in this format it does seem to put me within 20 yards or so of my house on google maps.

Yep that was the problem. I took it for a drive and it is quite amazing how the track on google maps follows exactly along with the route that I drove. Thanks everyone for helping me work through this problem!

@jerseyguy1996

Hi, I'm glad you did manage to have your A2035-H fully working in the end. Could you please share your final sketch? I am going to buy an A2035-H module soon, and I will be very thankful to you for it.

The reason for your position discrepancy, according to your code, is that you didn't read the instructions about how the lattitude and longitude are actually encoded.