Problem receiving latitude data from tinyGPS to a float variable

I suspect this is a silly error on my part so I would really appreciate some help to spot the problem.

I am trying to capture latitude coordinates to 5 decimal places using the tinyGPS++ library. According to the tinyGPS documentation, gps.location.lat returns the latitude as a double with a default of 2 decimal places. However, the number of decimal places returned can be changed by adding a parameter.

The code below is a hack of one of the examples supplied with tinyGPS++. The Arduino board has a uBlox GPS module attached to the soft.serial pins 3 & 4 and is streaming NMEA data.

Here’s the problem: In the example shown below, the line: gpslat=(gps.location.lat(),5); results into gpslat taking the value 5.00 whereas the line: Serial.print(gps.location.lat(),5); results in the desired answer with the latitude to 5 decimal places printed via the serial monitor. If I change the gpslat line to remove the 5 parameter the result is latitude to 2 decimal places (the default).
It seems to me that the decimal parameter is not being sent when I try to populate my variable but it is being sent when I serial print.

Any idea why that is?

Mike

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

static const int RXPin = 4, TXPin = 3; // set software serial pins
static const uint32_t GPSBaud = 9600; // set gps baud rate
double gpslat;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  Serial.begin(115200); //open the standard serial port
  ss.begin(GPSBaud); //open the soft serial port
}

void loop()
{
  
  gpslat=(gps.location.lat(),5);
  Serial.print(gpslat);
  Serial.println();
  Serial.print(gps.location.lat(),5);
  Serial.println(); // print line break
  
  smartDelay(1000); // short delay to get the latest gps coords

}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

By doing

gpslat=(gps.location.lat(),5);

You're setting gpslat to 5.0.

There's no need to set the amount of decimal points, as it will be done automatically depending on the value and size of the type. Does gps.location.lat() return a float or a double (a double is a double-precision floating point number).?

Passing 5 as the second parameter to Serial.print() is a special case for printing floating point numbers, and the second parameter refers to the number of decimal points to display, similar to doing printf("%.5f", gpslat);

Brilliant thanks - I knew it would be something silly :-))

My brain was stuck in a loop thinking that the parameter should be acting on tinyGPS whereas it was really acting on the variable and print statements!

tinyGPS returns a double so I just need to work out how to get five decimal places in my double as it seems to default to 2.

Thanks

Serial.print on floating point variables defaults to two decimal places. The extra precision is there, you just need to ask to have it displayed.

It shouldn't be able to default to 2 unless it's rounding it down, which I don't think it should be. How are you finding that it is rounded to 2? If you're just doing Serial.print() it's going to display 2, but the default precision is 6 I believe. Your variable is holding full precision.

Thank you both - it's me misinterpreting the output!

I'm using serial.print as a debugging tool to monitor the values and I can see it's serial.print that's limiting the output to 2 digits not the variable itself. Just added the 5 parameter to Serial.print and I see the right answer.

That's a really good learning point - thanks.

The next stage in my program is to create a Lat and Lon string in the format "123.45678,-111.22222" I was going to use sprintf for that.

Is that a good choice?

Yeah, sprintf is a good solution for it. You'd likely want to do something like

char latlon[24]; // A better estimate for length than 24 would be better
sprintf("%.5f,%.5f", gpslat, gpslon);

As long as the length is greater than what you can expect the max to be, you're fine. I'd assume 24 is safe, since you should be around 10 (5 for each sets of decimals) + 2 (decimal points) + 3 (separator and possible negative symbols) + 6 (3 for each set of leading digits)

You can mess with those numbers, though.

.

DivinityArcane:
Yeah, sprintf is a good solution for it. You'd likely want to do something like

char latlon[24]; // A better estimate for length than 24 would be better

sprintf("%.5f,%.5f", gpslat, gpslon);




As long as the length is greater than what you can expect the max to be, you're fine. I'd assume 24 is safe, since you should be around 10 (5 for each sets of decimals) + 2 (decimal points) + 3 (separator and possible negative symbols) + 6 (3 for each set of leading digits)

You can mess with those numbers, though.

No, sprintf doesn't support floating point.

Use dtostrf.

Excellent thanks - I think I've got it cracked.

dtostrf(gpslat,8,5,latstr);

This gives me the right answer with a string result in latstr and a minimum width of 8 characters and 5 decimal places.

Thanks for all your help - saved me a lot of time and I've learnt some stuff along the way :slight_smile:

Mike

Just to finish-off, I used sprintf to create the desired output string, i.e. lat and lon in the form "123.12345,123.12345"

sprintf(locateString,%s,%s, latstr, lonstr);

You could've done the same with two dtostrf and a little cunning.
(uncompiled, untested)

char* p = dtostrf(gpslat,8,5,latlonstr);
int len = strlen (p);
strcat (p, ",");
dtostrf(gpslon,8,5,latlonstr + len + 1);

Yep -that worked - thanks.

For the sake of anyone else reading this thread, here is the problem and both solutions:

Problem: How to use tinyGPS++ to parse a stream of NMEA words from a GPS module and create a string containing lat and lon to five decimal places, comma separated.

Solution 1:

  char* p = dtostrf((gps.location.lat()),8,5,locateString);
  int len = strlen (p);
  strcat (p, ",");
  dtostrf((gps.location.lng()),8,5,locateString + len + 1);

Solution 2:

  dtostrf((gps.location.lat()),8,5,latstr); // Get lat from tinyGPS++ and convert to string with 5 decimal places
  dtostrf((gps.location.lng()),8,5,lonstr); // Get lon and covert as above
  sprintf(locateString,"%s,%s", latstr, lonstr); // combine the strings with a , separator into locateString

There is another problem, and that is that Arduino supports only single precision floating point numbers, which have typically 6 to 7 digits of precision total. That means that in your example of 123.12345, the digits 4 and 5 (especially the 5) are random and not meaningful. Don't rely on them for accurate positioning!

Internally, TinyGPS stores locations as long integers, so the basic precision of the GPS system is retained.

AWOL:
.No, sprintf doesn’t support floating point.

Should clarify that you are referring to the sprintf Arduino has packaged, and not the function itself. Arduino seems to be using a simplified version, I’m guessing because of size concerns.

I am trying to capture latitude coordinates to 5 decimal places using the tinyGPS++ library. According to the tinyGPS documentation, gps.location.lat returns the latitude as a double with a default of 2 decimal places. However, the number of decimal places returned can be changed by adding a parameter.

I'd like for you to cite this documentation. The gos.location.lat is a float. It does NOT have a fixed number of decimal places, and I can not believe that the documentation claims that it does. Nor can I believe that the documentation claims that adding a parameter can change that number of decimal places.

What I suspect is that you are referring to PRINTING the value, which does default to printing (not storing) two decimal places.