Parsing a string (badly)

I’m having trouble parseing some navigation data out of a string. Take a look at this snippet…

char my_lat[8+1];
char my_lon[9+1];
char my_utc_time[6+1];
char my_pres_alt[4+1];
char navLine[100];

void getNavFix(char * line)
{
  if (line[0]=='X')
  {
    // split the line into individual records. start with time and pressure - they don't seem to cause problems
    strncpy(my_utc_time,line+1,6);    // Starting from position 1, copy 6 characters into the 'time' string
    strncpy(my_pres_alt,line+26,4);   // Starting from position 26, copy 4 characters into the 'altitude' string
    
    // now extract latitude and longitude
    strncpy(my_lat,line+7,8);
    strncpy(my_lon,line+15,9);
    
  }
  else
  {
    my_utc_time[0]=0;
    my_lat[0]=0;
    my_lon[0]=0;
    my_pres_alt[0]=0;
  }
  
  Serial.print("line=<");Serial.print(line);Serial.println(">");
  Serial.print("time=<");Serial.print(my_utc_time);Serial.println(">");
  Serial.print("my_lat=<");Serial.print(my_lat);Serial.println(">");
  Serial.print("my_lon=<");Serial.print(my_lon);Serial.println(">");
  Serial.print("my_pres_alt=<");Serial.print(my_pres_alt);Serial.println(">");
}

I test it with these lines…

    strcpy(navLine,"X1259125122406N00106516WA00793008210070041167812356045-012");
    getNavFix(navLine);                  // Split the line into lat, lon, time and alt
    strcpy(navLine,"X1259205122502N00106353WA00788008130070041052911489049-022");
    getNavFix(navLine);                  // Split the line into lat, lon, time and alt

And I get results like this…

line=<X1259125122406N00106516WA00793008210070041167812356045-012>
time=<125912>
my_lat=<5122406N>
my_lon=<00106516W>
my_pres_alt=<0793>
line=<X1259205122502N00106353WA00788008130070041052911489049-022>
time=<125920>
my_lat=<5122502N>
my_lon=<00106353W>
my_pres_alt=<0788>

This produces the expected results, I’ve got 4 separate strings for time, latitude, longitude and altitude.

However, I want my values for latitude and longitude to START with the point of the compass, not end with it. So I changed my code to look like this…

    strncpy(my_lat,line+14,1);       // Copy just one character (N or S) into the latitude string
    strncpy(my_lon,line+23,1);       // Copy just one character (E or W) into the longitude string
    strncat(my_lat,line+7,7);        // Append the seven numeric characters of the latitude onto the current single character.
    strncat(my_lon,line+15,8);       // Append the eight numeric characters of the longitude onto the current single character.

(code for time and altitude remains unchanged)
My results now look like this…

line=<X1259125122406N00106516WA00793008210070041167812356045-012>
time=<125912>
my_lat=<N5122406>
my_lon=<W00106516>
my_pres_alt=<0793>
line=<X1259205122502N00106353WA00788008130070041052911489049-022>
time=<6353>
my_lat=<N5122406512250200106353>
my_lon=<12250200106353>
my_pres_alt=<0788>

On the first pass everything looks ok, but when processing the 2nd line it all starts to go a bit squiffy, where am I going wrong? I thought maybe I’d misunderstood how strncat() works, but it seems to work ok on the first pass.

(working on mega with version 1.0)

thanks

I’m having trouble parseing some navigation data out of a string.

Where is this data coming from?

Where is this data coming from?

In this example it's just a hardcoded string......

strcpy(navLine,"X1259125122406N00106516WA00793008210070041167812356045-012");

In practice it will be coming from a file. (the error I'm trying to fix does manifest itself in the hardcoded example above - I've just trimmed it down a bit for simplicity)

First, I can't think of any reason why longitude should have more digits than latitude except in certain locations. I think you should write your code to look for the marker letters rather than assume a length.

However I don't think that's what's causing your problem. It looks as if "line" isn't pointing to the correct location the second time or else as if your string lengths change.

I would put in a few Serial.print statements to follow the calculations step by step.

...R

Robin2: First, I can't think of any reason why longitude should have more digits than latitude except in certain locations. I think you should write your code to look for the marker letters rather than assume a length.

Simple case of longitude is in a range of 0-180 and latitude is in a range of 0-90. Notice that additional digit... (You want to drive a GPS unit insane, take it to latitude 90S. Technically at that point (and at 90N) you are at [u]all[/u] longitudes...) ;)

However I don't think that's what's causing your problem. It looks as if "line" isn't pointing to the correct location the second time or else as if your string lengths change.

I would put in a few Serial.print statements to follow the calculations step by step.

...R

This looks to me like the strings (even though the OP stated they are read from a file) originally came from a GPS unit. GPS units (in my experience) all output data in a very strict format and never trim leading or lagging zeros. Thus a character's position defines it's meaning. (That said, you should probably have at least some minor checks to make sure there wasn't a lost character in data transmission somewhere. This can be a simple as a check on line length before parsing, or as complicated as writing a CRC check routine. I expect last few characters of each line is the line's CRC generated by the GPS, specifically for receivers of the data to verify there weren't any transmission errors.)

@OhMyCod (btw, I love the handle) :D

I'd probably subtly change your parsing methods and add two more variables; my_latDir and my_lonDir (or whatever you want to call them). Use these to store the N/S and E/W characters, and just put the latitude and longitude digits into your my_lat and my_lon character arrays. Then you can output in any order you want, and leaves the my_lat and my_lon character arrays pure digits to convert to numbers if you need to down the line.

Also, as Robin2 points out, liberal use of Serial.print statements while troubleshooting to follow variable values can be invaluable.

I can't think of any reason why longitude should have more digits than latitude except in certain locations

latitude goes ranges from -90 to + 90 (i.e from the equator to the poles) longitude ranges from -180 to +180 (i.e. from the Greenwich meridian east and west)

rather than assume a length

The file this data is ultimately coming from uses positional formating, so they should always be the same length

I would put in a few Serial.print statements to follow the calculations step by step.

I'm doing this, I'll shortly be posting a full working example.

Thanks

Ok guys, here’s a cut-out-n-keep example of a working (actually, failing) sketch. Just paste it and upload it to see what I’m going on about…

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"                       
#else
#include "WProgram.h"                      
#endif
 
char my_lat[8+1];
char my_lon[9+1];
char my_utc_time[6+1];
char my_pres_alt[4+1];
int test=0;
char navLine[100]; //one line of a file

void setup()
{
  Serial.begin(115200);//Communication speed for the display
  
  Serial.println("####################     START     ###############################");
  
  test = 0;
  if (test == 0)
  {
    Serial.print("######## TEST ");Serial.print(test);Serial.println(" #########");
    Serial.println("Testing the parsing of a single line of Nav data");
    
    strcpy(navLine,"X1259125122406N00106516WA00793008210070041167812356045-012");
    getNavFix(navLine);                  // Split the line into lat, lon, time and alt
    strcpy(navLine,"X1259205122502N00106353WA00788008130070041052911489049-022");
    getNavFix(navLine);                  // Split the line into lat, lon, time and alt
    strcpy(navLine,"X1259445122754N00105812WA00759007860070041065411559057-002");
    getNavFix(navLine);                  // Split the line into lat, lon, time and alt
  }
}
  

void loop()
{
  Serial.print(".");
  delay (5000);
}



// Converts a full line from a nav file into a set of fixes.
void getNavFix(char * line)
{
  if (line[0]=='X')
  {
    // split the line into individual records. start with time and pressure - they don't seem to cause problems
    strncpy(my_utc_time,(char *)(line+1),6);    // Starting from position 1, copy 6 characters into the 'time' string
    strncpy(my_pres_alt,(char *)(line+26),4);   // Starting from position 26, copy 4 characters into the 'altitude' string
    
    // now extract latitude and longitude
    //strncpy(my_lat,line+7,8);
    //strncpy(my_lon,line+15,9);
    
    // NOTE - in the NAV file the compass cardinal (N,S,E or W) comes at the end of the string, it needs to be moved to the start
    strncpy(my_lat,(char *)(line+14),1);       // Copy just one character (N or S) into the latitude string
    strncpy(my_lon,(char *)(line+23),1);       // Copy just one character (E or W) into the longitude string
    strncat(my_lat,(char *)(line+7),7);        // Append the seven numeric characters of the latitude onto the current single character.
    strncat(my_lon,(char *)(line+15),8);       // Append the eight numeric characters of the longitude onto the current single character.
  }
  else
  {
    Serial.println("ooops");
    my_utc_time[0]=NULL;
    my_lat[0]=NULL;
    my_lon[0]=NULL;
    my_pres_alt[0]=NULL;
  }
  
  Serial.print("line=<");Serial.print(line);Serial.println(">");
  Serial.print("time=<");Serial.print(my_utc_time);Serial.println(">");
  Serial.print("my_lat=<");Serial.print(my_lat);Serial.println(">");
  Serial.print("my_lon=<");Serial.print(my_lon);Serial.println(">");
  Serial.print("my_pres_alt=<");Serial.print(my_pres_alt);Serial.println(">");
}
add two more variables; my_latDir and my_lonDir (or whatever you want to call them). Use these to store the N/S and E/W characters

Good idea. I've done this. I think I've narrowed down the issue to the strcat() function.

If I use strcat() inside getNavFix() then the weird shit happens. If use strcat() outside of getNavFix() then it seems to work. But I'd still like to know why!

Oops ...

...R

Sembazuru: Simple case of longitude is in a range of 0-180 and latitude is in a range of 0-90. Notice that additional digit...

OhMyCod: Ok guys, here's a cut-out-n-keep example of a working (actually, failing) sketch. Just paste it and upload it to see what I'm going on about.....

http://www.cplusplus.com/reference/cstring/strncpy/

No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

You need be explicitly null terminating the strings after the strncpy().

You need be explicitly null terminating the strings after the strncpy().

Thanks!

I knew that strcat() and strcpy() DO add a null terminator, so I naively assumed that the 'n' versions did as well!

Thanks for the fix, I'd spent half the night pulling my hair out over that one!