GPS logging parsing

Okay, this code parses the data for a proper message, then gets only the lines starting with that message. This was relatively messy, especially in C/C++. Note that the function filterForMessage doesn't pass a value really, but instead just modifies a global variable. I tried messing with pointers, but I don't think Arduino's code can handle them.



#define rxPin 7
#define txPin 8
#define ledPin 13

//definitions for 4800 baud communication
#define bit4800Delay 188 
#define halfBit4800Delay 94 

//The type of message we want. Edit this for other forms of GPS data.
char wantedMessage[7] = "$GPRMC";

// thisMessage is a global variable -- I didn't want to deal with returning
// strings, as properly object-oriented it would be. 
char thisMessage[100];
boolean ledPinOn;

void setup()  {
  // define pin modes for tx, rx, led pins:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(txPin,  HIGH); //set transmit pin to high
  digitalWrite(ledPin,  HIGH); //turn on debug led  
  ledPinOn = true;
  
  // set the data rate for the SoftwareSerial port
  Serial.begin(4800); //4800 is the baud rate for EM306
}

int SWread()
{
  byte val = 0;
  
  while (digitalRead(rxPin));
  //wait for start bit
  
  
  if (digitalRead(rxPin) == LOW) {  
  //okay, so now that it's low
    delayMicroseconds(halfBit4800Delay);
    //wait for a tiny bit, then get data
    for (int offset = 0; offset < 8; offset++) {
     //iterate through bit mask.
     //Or, in other words, each bit is sent after a bit4800delay,
     //from MSB to LSB. get the first most significant bit, shift it left, etc.
     delayMicroseconds(bit4800Delay);
     val |= digitalRead(rxPin) << offset; 
    }
    //wait for stop bit + extra
    delayMicroseconds(bit4800Delay); 
    delayMicroseconds(bit4800Delay);
    return val;
  }

}

boolean filterForMessage()
{
  int tempVal;
  int counter;
  char tempString[100]; //max string length is 100.
  
  tempVal = SWread(); //convert byte from GPS to character
  if(tempVal == '$') //if byte is a start of a new line 
  {
    counter = 1;
    tempString[0] = '$'; //add the dollar sign
    
    //while we're reading input, and the input isn't a newline (10 is ascii newline)
    while((tempVal = SWread()) && (tempVal != 10)) {
      tempString[counter++] = tempVal; //add current character to temporary string
    }
    tempString[counter] = '\0'; //end string with null char
    
    //now, do we want this line? let's compare the first 6 chars with wantedMessage.
    if(strncmp(wantedMessage, tempString, 6) == 0) {
      //if we do want this line, then copy the message into the global variable
      //so we can 'pass' it to the callee of this function.
      strcpy(thisMessage, tempString); 
      return true;
    }
    else
      return false;
     
  }
 
}
        
      
void loop() {
  
  //if our message matches our wanted message, print it out!
  if(filterForMessage() == true) {
    Serial.println(thisMessage); 
  
    // toggle an LED so that we can see that it's working
    // the LED will go on with every other proper message match
    if(ledPinOn == true) ledPinOn = false;
    else ledPinOn = true;
    digitalWrite(ledPin,  ledPinOn);
  }

}

So, let's look at the data. An example line:
$GPRMC,083739.000,A,3922.17xx,N,07640.02xx,W,0.38,32.39,210507,,*26
Clearly I'm not here right now or anything.

The reference manual for the NMEA standard details the RMC format in page 16. This page has it as well.

A bit of data parsing in Processing led to this code. String manipulation in Java is more awkward than I remembered -- I can't seem to be able to use 'split' well.

 boolean parseRMCData(String raw) {
    
    Date tempDate;
    
    String [] values = raw.split(","); //split values around comma
    
    if(values[2].equals("V")) return false; //status is invalid!
    
    
    //convert values into date. GAH! there should be a better way,
    //but split wasn't working properly.
    int h; int m; int s; int y; int mon; int d;
    h = parseInt(values[1].substring(0, 2));
    m = parseInt(values[1].substring(2, 4));
    s = parseInt(values[1].substring(4, 6));
    d = parseInt(values[9].substring(0, 2));
    mon = parseInt(values[9].substring(2, 4));
    y = parseInt(values[9].substring(4, 6)) + 100;
    
    timeDate = new Date(y, mon, d, h, m, s);
    //100 + y is done because y is two-digits, and the Date class
    //automatically adds 1900 to the year value. Adding 100 corrects this all.
    
   
    //// CONFUSING TIMEZONE CONVERSION COMING RIGHT UP
    
    // configure a SimpleDateFormat to recognize our certain format
    SimpleDateFormat sdf = new SimpleDateFormat("y M d H m s");
    
    // configure a time zone that is EST and with dst built in.
    
    // Base GMT offset: -5:00
    // DST starts:      at 2:00am in standard time
    //                  on the second Sunday in March
    // DST ends:        at 2:00am in daylight time
    //                  on the first Sunday in November
    // Save:            1 hour
    SimpleTimeZone stz = new SimpleTimeZone(-18000000,
                              "EST",
                              Calendar.MARCH, 2, -Calendar.SUNDAY,
                              7200000,
                              Calendar.NOVEMBER, 1, -Calendar.SUNDAY,
                              7200000,
                              3600000);
    
    // configure our SimpleDateFormat to recieve GMT input
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
    
    // make our SimpleDateFormat parse our date as GMT
    try {
      tempDate = sdf.parse(y + " " + mon + " " + d + " " + h + " " + m + " " + s);
    }
    catch (Exception e) {
      tempDate = new Date();
    }
    
    // convert SimpleDateFormat to EST with EDT
    sdf.setTimeZone(stz);
    
    // change display pattern of our format 
    sdf.applyPattern("E M/W/y H:m:s z");
    
    // convert the date to EST with EDT, and in our certain pattern
   print(sdf.format(tempDate));

   print("  |  Lat : " + values[3] + values[4] + "  |  Long : " + values[5] + values[6]);
   
   float  vel = Float.parseFloat(values[7]) * 1.852;
   println("  |  Velocity : " + vel + " km/h in the direction " + values[8] + " degrees from north");
  
   return true; //status is valid, of course
    
  }

Posted by provolot on May 21, 2007 3:51 AM | | Comments (0)

TrackBack

TrackBack URL for this entry:
http://www.provolot.com/cgi-bin/mt5/mt-tb.cgi/57

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)