How to handle 6 decimal place GPS coordinates?

Hi, I am reading GPS RMC data at 10hz with my UNO r3 using the tiny GPS library and turning it into lat,lon,speed and time to save to an sd card using SdFat library. The issue is that I don't know how to convert what TinyGPS spits out lat and lon values to something more standard so I can pass it along to GPS Visualizer without having to post process.

The data from tinyGPS comes through with the decimal point 6 over so it doesnt have to deal with float. When I convert them in the sketch to a float moving the decimal it seems to drop off 4 of the 6 digits. Example: tinyGPS gives me lat as 37456267 and when I try to convert it changes to 37.450000. Is there any clean way to pass these variables into usable numbers? Its also ov concern because I would like to convert thr lat and lon to Cartesian to do some on the fly geofence calculations.

Hopefully that makes sense but if I need to add more information please let me know.

Thanks!

If you've got a couple digits in front of the decimal the you can't have six behind it with any accuracy. A float is only good to 6 or maybe 7 digits precision all together. Consider sticking with the fixed point math and use unsigned long. There you can have 9 digits of precision.

I was under the impression unsigned long did the same as float in the arduino ide but ill give it a shot! Is there a good way to handle the negative longitude value?

I'll have to do more research on fixed point math and how I can apply it.

Thanks for the speedy reply!

I was under the impression unsigned long did the same as float in the arduino ide but ill give it a shot!

An arithmetical impossibility. They occupy the same number of bits, that's all.

Is there a good way to handle the negative longitude value?

Use a signed data type.

Example: tinyGPS gives me lat as 37456267 and when I try to convert it changes to 37.450000

This happens because you have not specified the number of places past the decimal point in “println”, which defaults to 2.

I’ll have to do more research on fixed point math and how I can apply it.

No need to do that. Store the coordinates as signed long integers, in units of degrees*106 (as done in TinyGPS).

For course and distance calculations, convert back to float. With some care and thought, you can preserve full nine-digit accuracy.

Yes, use signed integer coordinates. That's what my library NeoGPS does, for the same reason. It's also ~2x faster and uses less RAM than other libraries, especially when configured to parse only the information you actually use. All other messages and fields are skipped, saving even more RAM and CPU time.

If you've really got TinyGPS parsing 10Hz updates and saving them to SD, without losing any GPS data, you're doing pretty well. The quantity of printing and the stupid smartDelay in the examples usually stymies most beginners. Maybe you're already aware of the issues; if not, here's some good info.

Cheers,
/dev

jremington:
This happens because you have not specified the number of places past the decimal point in “println”, which defaults to 2.
No need to do that. Store the coordinates as signed long integers, in units of degrees*106 (as done in TinyGPS).

For course and distance calculations, convert back to float. With some care and thought, you can preserve full nine-digit accuracy.

Can you clue me in on what a “signed integer” is? When I search I get int and unsigned in. Int doesn’t appear to be able to hold enough based on the arduino reference page.

Can you also elaborate on the “care and thought” needed for 9digit accuracy? Sorry I am still relatively new to this.

Dev, it might be time to dive back into neoGPS. I do have it currently sending 10hz rmc data to the sd card but its really doing nothing else. I haven’t noticed any dropped data and my timestamps in my .txt file follow 10 updates a second.

char is a signed integer.
short is a signed integer (the same size as int on the AVR)
long is a signed integer, as is long long.

Shift314:
Hi, I am reading GPS RMC data at 10hz with my UNO r3 using the tiny GPS library and turning it into lat,lon,speed and time to save to an sd card using SdFat library. The issue is that I don't know how to convert what TinyGPS spits out lat and lon values to something more standard so I can pass it along to GPS Visualizer without having to post process.
The data from tinyGPS comes through with the decimal point 6 over so it doesnt have to deal with float. When I convert them in the sketch to a float moving the decimal it seems to drop off 4 of the 6 digits. Example: tinyGPS gives me lat as 37456267 and when I try to convert it changes to 37.450000. Is there any clean way to pass these variables into usable numbers?

You don't show any single line of code, but my guess ist, that ALL your problems arise because the usage of "float" type numbers in your program.

The accuracy of 'float' is 6-7 significant digits (counting starts at the first non-zero digit). So if you have 2 digits before the decimal point, with "float" you can have at most 5 accurate digits after the decimal point.

If you want accurate numbers and calculations, use "int and "long /unsigned long" numeric types only.

Exampe:

  • store "whole degrees" as an 'int'
  • store "fractions of a degree" as an 'unsigned long' in units of one millionth of a degree.

So instead of trying to store
float lat= 37.456267;
you better do:
int latDegrees=37;
unsigned long latFraction=456267;

Thank you all for your replies! Sorry I haven’t posted any code in this thread. I was posting everything from a mobile device with no access to my code. I was just trying to make sense of a precision issue. I did some testing and here is what i have found.

First thanks to jRemmington for pointing out that print defaults to 2 decimal places! That solved it for me when i was able to give it a shot in my code.

I am printing to the Serial Monitor for debugging currently(tracking a random NMEA setting issue) and here were my results.

Declaring my CorrectedLat and CorrectedLon values as “FLOAT” and using Serial.print(((float)CorrectedLat),6); allows me to see all the decimal places. When i logfile.print(((float)CorrectedLat),6): allows me to print it to the SD card.

When i used unsigned long it was ok to print the Lat to SD, but not the negative Lon value. This did not work in the Serial Monitor. I tried int, long, char etc with varying results but none of them worked.

Below is my code. I am new to this and this is a very basic sketch that took me a lot of head scratching and late nights to figure out. No real checks or intelligence in the sketch, just a basic brute setup that allows me to take my UP501 GPS module, Sparkfun uSD shield and my Uno r3 and write GPS RMC data at 10hz to my uSD card. I have a long list of things i want/will be adding to it but I kept it simple as I am stepping through it. I have SoftwareSerial.h commented out and I am using gSoftSerial right now. I did increase the buffer size of SS to 128. The sketch works with both SoftwareSerial and gSoftSerial with the same results as far as I can tell, just have to change the gSoftSerial gpsSerial(2,3) to SoftwareSerial gpsSerial(2,3).

If someone finds this and decides to use it(and happens to have the same GPS module) there are a few things that need to be clarified. The UP501 is a 3.3v module not 5v. It is wired up using the 3.3v to both VDD and the battery backup for now. TX goes to dig pin2 and RX goes to dig pin3 via a 10k Resistor Divider so you don’t send it 5v when sending commands.

#include <SPI.h>
#include <SdFat.h>
#include <TinyGPS.h>
//#include <SoftwareSerial.h>
#include <gSoftSerial.h>

long lat, lon;
unsigned long fix_age, date, time, speed, altitude, course;
float CorrectedSpeed, CorrectedLon, CorrectedLat;
const int chipSelect = 8;

//#define PMTK_SET_NMEA_BAUDRATE_38400 "$PMTK251,38400*1F" // Set Baud Rate to 38400
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"
#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"     // only RMC data

File logfile;

gSoftSerial gpsSerial (2,3); // create gps sensor connection
TinyGPS gps;                 // create gps object
SdFat SD;                    // create SD object

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  gpsSerial.begin(9600);
  gpsSerial.println(PMTK_SET_NMEA_OUTPUT_RMCONLY);            // turn on only RMC
  gpsSerial.println(PMTK_SET_NMEA_UPDATE_10HZ);               // turn on 10Hz
  
  while (!Serial) {
    ; // wait for serial port to connect. 
  }
  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  char filename[15];
 strcpy(filename, "data_00.txt");
 for (uint8_t i=0; i<100; i++) {
  filename[5] = '0' + i/10;
  filename[6] = '0' + i%10;
  if (!SD.exists(filename)) {
    break;
  }
 }
logfile = SD.open(filename, FILE_WRITE);
  if(!logfile) {
    Serial.print("Couldnt create "); 
    Serial.println(filename);
    while (1) {}
  }
  Serial.print("Writing to "); 
  Serial.println(filename);
  logfile.println("latitude, longitude, speed, time");
 

}

void loop() {
  
  while(gpsSerial.available()>0){                                // check for gps data
 //   char c = gpsSerial.read();                                 // read gps data into char c
 //   Serial.print(c);                                           // pring NMEA RMC strings to the Serial Monitor, this is here to figure if its passing all NMEA sentences or just RMC for debug
    if (gps.encode(gpsSerial.read())){
      gps.get_position(&lat, &lon, &fix_age);                    // get GPS lat, lon and fix age variables
      gps.get_datetime(&date, &time, &fix_age);                  // get GPS date, time and fix age, when i tried to omit the date variable i had bad results
      speed = gps.speed();                                       // get speed value in 100th of a knot from TinyGPS    
      CorrectedSpeed = speed * 0.0115;                           // convert speed to mph
      CorrectedLon = lon * 1E-6;                                 // Convert Longitude to a normal value for GPS Visualizer
      CorrectedLat = lat * 1E-6;                                 // Convert Latitude to a normal value for GPS Visualizer
//      Serial.print(((float)CorrectedLat),6);Serial.print(",");Serial.print(((float)CorrectedLon), 6);Serial.print(",");Serial.println(time); // print data to serial monitor for debug
      logfile.print(((float)CorrectedLat),6);logfile.print(","); // print CorrectedLat to SD with 6 decimal places
      logfile.print(((float)CorrectedLon),6);logfile.print(","); // print CorrectedLon to SD with 6 decimal places
      logfile.print(CorrectedSpeed);logfile.print(",");          // print CorrectedSpeed to SD
      logfile.println(time);                                     // print time string from RMC, no conversion yet
      logfile.flush();                                           // flush to push the data to the SD without having to close it
    }
    }
    }

I posted my GPS code on your other thread. I just found this thread.