GPS decimal precision via XBEE

Hi all,

Got a newBee question.

I am trying to send GPS latitude/longitude from one arduino to the other via Xbees.

The slave arduino with the GPS device prints out:

48.871820 & 2.355817

So I use Serial2.println(lon); and Serial.println(lat);

On the receiving master arduino, I'm able to receive the lat/lon data in form of chars.

How can I convert the chars to an int or float producing the exact same figures without it rounding up or down, ready for processing.

I tried playing around with;

char buffer[40];
buffer[0] = lat[1];
buffer[1] = lat[2];
buffer[2] = lat[4];
buffer[3] = lat[5];
buffer[4] = lat[6];
buffer[5] = lat[7];
buffer[6] = lat[8];
buffer[7] = lat[9];
buffer[8] = lat[10];
buffer[9] = lat[11];
buffer[10] = lat[12];
buffer[11] = lat[13];
buffer[12] = lat[14];
buffer[13] = lat[15];
buffer[14] = lat[16];
buffer[15] = lat[17];
buffer[16] = '\0';

int  n;
float m;

n = atoi(buffer);
m = n/1000.00;
  Serial.println(n/1000.00);
  Serial.println(m,10);
  Serial.println(m);

But i still do not get the required results..

My aim is to get the GPS lat/lon sent from the slave arduino to the master, and then get the master to calculate the distance between them.

Thanks All!

How can I convert the chars to an int or float producing the exact same figures without it rounding up or down, ready for processing.

You can't using float, because on the Arduino, floats are limited to between 6 and 7 digits of resolution.

It would be better just to send the two lat/lon character strings exactly as they come from the GPS module.

Note that you can't perform accurate distance calculations using float variables on the Arduino, for the same reason.

Delta_G:
See atoi to convert ascii to int and atof to convert ascii to float.

jremington:
You can't using float, because on the Arduino, floats are limited to between 6 and 7 digits of resolution.

It would be better just to send the two lat/lon character strings exactly as they come from the GPS module.

Note that you can't perform accurate distance calculations using float variables on the Arduino, for the same reason.

Ok, I was beginning to come to that conclusion. The GPS data from the slave arduino is sent/received as character strings on the master, but my problem is using the strings as they are alphanumeric so ther unworkable from a mathematical point of view. So my plan was to change them into something usable i.e int(Which wouldnt be great as they have no decimals) or Float.. Are there any other options? I am about to research atio-ascii ascii-float, hopefully not in vein...

Thanks again

Are there any other options?

Yes, convert the coordinates into signed long integers, in units of one millionth of a degree. You do not lose precision that way.

The TinyGPS library does this for accurate storage of coordinates, but unfortunately for calculating distances, TinyGPS converts them back into floats and so loses precision.

What do you want to do with the coordinates?

For short distance robot navigation, I use a set of routines that calculate distances and waypoints using long integer units, but they use the equirectangular approximation instead of the great circle and are therefore not useful for long range navigation.

jremington:
Yes, convert the coordinates into signed long integers, in units of one millionth of a degree. You do not lose precision that way.

Ok, so im doing a little research on signed long integers.

Well the scope of the project is to have two arduinos work out the distance between them. Thats all :slight_smile: Do you have a link that can point me in the right direction for these 'signed long integers'?

Not too sure where to start to be honest, all I have so far is one can send char lat/lon to the other (&vice versa)

Thanks again!

Of course latitude and longitude can be negative!

However, it depends on the convention used. GPS NMEA convention uses "W" and "E" to distinguish between the two sides of the prime meridian, and "N" and "S" to distinguish above and below the equator, but internally it is much more convenient to use signed values.

Here is some interrupt code to parse NMEA GGA sentences, store the lat/lon values as long integers and incidentally calculate bearings and waypoints using long integers. Precision is a few tens of cm, but GPS coordinates are rarely accurate to better than 3 meters.

The code is a bit too long to include with code tags, so it is attached as a zip file.

gps_gga.zip (5.16 KB)

Ahh nice! Emm...so how can I implement this code? Stuck it in my library but not sure on how to use it if my GPS is connected on Serial2. Thanks :slight_smile:

Stuck it in my library but not sure on how to use it if my GPS is connected on Serial2.

The example I posted is not a library and is not intended for general use. It would have to be included in your code. If you want a library intended for general use, I suggest to try TinyGPS++ or neoGPS.

The code I posted would also need to be modified for use with serial 2 as the interrupt vector is different. Or, just get a character from the GPS and call gps_parse(), checking every time whether a full NMEA sentence has been received.

Here is an example of how I use it:

//  setup 
   gps_parse_init(9600); //enable uart
//  end of setup.

// ... in main loop
 static signed long lat1,lon1,lat2,lon2; //can be global variables
 sei(); //(re)enable interrupts
 gps_parse_status = 0; //ignore old results
 gps_parse_enable(); //(re)enable parsing
 
// check for GPS fix:

 if (gps_parse_status == 1) {
 gps_parse_enable();

// get number of satellites and current location
 num_sats = gga_num_sats();
 gps_last_fix = millis(); //time of fix
 gps_latlon(&lat1, &lon1); //current position to lat1, lon1

// get bearing and distance to destination (lat2, lon2)
 gps_bearing = (int) (course_to(lat1, lon1, lat2, lon2, &distance)+0.5);
 dist_to_go = (int) (distance + 0.5); //in meters
 }

Sorry for the delay, been away for work..... Ok so I see what you've posted but I still cant work out how to implement it, I get that I can include it in my arduino IDE.

Im currently using tinygps to receive the NMEA data from the gps unit which works fine. My problem is sending that data from that arduino to the next and receiving it as a signed long so the data is usable for calculations, unlike chars.

How can I learn from/use your code to change received char's from an external arduino into a signed a long value? As thats still my problem. Once I manage to change the data into a signed long I can then use it for rough distance calculations :slight_smile:

Thanks again!

These two short functions convert NMEA latitude and longitude character strings into signed long values, in units of degrees*1000000. The second of the two functions expects two strings as well as N/S or E/W indicator characters to be present, so that the signs can be determined.

Example of use of the first function:

char *lat = "4305.2345";
long llat = parse_degrees(lat);

//
// decode individual NMEA lat/lon character strings in format DDDMM.MMMMM
// result is stored as long integer as degrees*10^6
// p is pointer to character string to be converted.
//
long parse_degrees(char* p) {
    char* minutes;
    char deg[]={0,0,0,0};
    double x;
    long d;

 if (strlen(p) == 0) return 0L;

    if ((minutes = strchr(p,'.')) == NULL) return 0L;  //'.' not found
    minutes -= 2; //back up pointer to include two digits of minutes+decimal fraction
    x = strtod(minutes,NULL); //make into double
    memcpy(deg,p,minutes-p); //copy degrees, works OK with zero length string
    d = strtol(deg,NULL,10)*1000000UL;  //degrees to long int
    d += (long)(x*16666.667 + 0.5); //*1.e6/60.  add in minutes

    return d;
}

// returns current latitude and longitude
// return value = data valid flag for the most recent sentence
// return 0 = not valid
// return 1 = recent valid fix

char gps_latlon(long* lat, long* lon) {
 long temp;
 char valid;
 temp = parse_degrees(gga_lat);
 if (gga_NS[0] == 'S') temp = -temp;
 *lat = temp;
   temp = parse_degrees(gga_long);
 if (gga_EW[0] == 'W') temp = -temp;
 *lon = temp;
 valid=0;
 if (gga_data_valid[0] > '0' && gga_data_valid[0] < '6' ) return 1;
 //valid = 1 for GPS, 2 for DGPS fix, etc.
 return 0;
}