GPS: set update frequency

Hi guys

I'm quite new to arduino (started with the blink example yesterday) so please have patience if this seems totally noob.

I've got a Skylab skm53 GPS hooked up to a Pro micro. Running the example code for TinyGPS (changed to hardware serial) I've got the GPS working. But it's only updating the long-/latitude every 3-5 minutes. I won't brag about it, but I'm able to move quite some distance between those updates!
The code is printing out data every second and I see the timestamp is updating every second, but my speed, direction and position remains the same for 3-5 minutes. When those data change they might do 3-5 updates within 3-5 seconds, then they reminds the same for another 3-5 minutes.

If I manage to read the datasheet right the gps should be able to do 10 updates per second. So how can I make this thing update position more frequently?

To test I went out on a field, far from any possible disturbance, to no avail. The gps never reported it had contact with less than 9 satellites.

Code:
https://github.com/awaxa/arduino/blob/master/libraries/TinyGPS/Examples/test_with_gps_device/test_with_gps_device.pde

Datasheet
http://www.webtronico.com/documentos/SKM53_DS_030609.pdf

Did you let the GPS run for a while?
Do you have clear line of sight to the sky?

Sounds like maybe just bad reception?

It ran for about 6 hours.
The only place I can think of with a better sight than out in the field would be the top of a mountain.

During those 6 hours it was indoors half the time - my laptop needed to charge. The circuit is powered from the laptops usb. Indoors it had connection to 9 satellites. Outdoors it had between 9 and 12.

Running the example code for TinyGPS (changed to hardware serial)

So, you posted a link to some other code. Would you like a link to some other answer?

I though it would be good practice for the forum to use a link rather than posting the code. A link to the solution would be fine though.
Here's the modified code: (replaced "nss" with "Serial1", commented out softwareserial)

//#include <SoftwareSerial.h>

#include <TinyGPS.h>

/* This sample code demonstrates the normal use of a TinyGPS object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 3(rx) and 4(tx).
*/

TinyGPS gps;
//SoftwareSerial nss(3, 4);

static void gpsdump(TinyGPS &gps);
static bool feedgps();
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);

void setup()
{
  Serial.begin(115200);
  Serial1.begin(9600);
  
  Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS));
  Serial.println();
  Serial.println("Sats HDOP Latitude Longitude Fix  Date       Time       Date Alt     Course Speed Card  Distance Course Card  Chars Sentences Checksum");
  Serial.println("          (deg)    (deg)     Age                        Age  (m)     --- from GPS ----  ---- to London  ----  RX    RX        Fail");
  Serial.println("--------------------------------------------------------------------------------------------------------------------------------------");
}

void loop()
{
  bool newdata = false;
  unsigned long start = millis();
  
  // Every second we print an update
  while (millis() - start < 1000)
  {
    if (feedgps())
      newdata = true;
  }
  
  gpsdump(gps);
}

static void gpsdump(TinyGPS &gps)
{
  float flat, flon;
  unsigned long age, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;
  static const float LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
  
  print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  print_int(gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  gps.f_get_position(&flat, &flon, &age);
  print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 9, 5);
  print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 10, 5);
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);

  print_date(gps);

  print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 8, 2);
  print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_float(gps.f_speed_kmph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  print_int(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(flat, flon, LONDON_LAT, LONDON_LON) / 1000, 0xFFFFFFFF, 9);
  print_float(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(flat, flon, LONDON_LAT, LONDON_LON), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(flat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(flat, flon, LONDON_LAT, LONDON_LON)), 6);

  gps.stats(&chars, &sentences, &failed);
  print_int(chars, 0xFFFFFFFF, 6);
  print_int(sentences, 0xFFFFFFFF, 10);
  print_int(failed, 0xFFFFFFFF, 9);
  Serial.println();
}

static void print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  feedgps();
}

static void print_float(float val, float invalid, int len, int prec)
{
  char sz[32];
  if (val == invalid)
  {
    strcpy(sz, "*******");
    sz[len] = 0;
        if (len > 0) 
          sz[len-1] = ' ';
    for (int i=7; i<len; ++i)
        sz[i] = ' ';
    Serial.print(sz);
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1);
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(" ");
  }
  feedgps();
}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
    Serial.print("*******    *******    ");
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d   ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
  feedgps();
}

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  feedgps();
}

static bool feedgps()
{
  while (Serial1.available())
  {
    if (gps.encode(Serial1.read()))
      return true;
  }
  return false;
}

petterg:
I though it would be good practice for the forum to use a link rather than posting the code.

Good practice is explained in detail in the sticky threads... Its there to facilitate rapid
and accurate solutions to peoples problems and reduce the to-ing and fro-ing of questions
and answers and wasted effort in answering a different problem from the actual problem :slight_smile:

  // Every second we print an update
  while (millis() - start < 1000)
  {
    if (feedgps())
      newdata = true;
  }

That code does not do what the comment says.

Try this:

if(feedgps())
   gosdump(gps);

Nothing else in loop().

That helped quite a bit!
Now it's printing position about 3 times pr second, continuously updating for a while (30-60 secondes) - then it stop updating for a couple of minutes. Then it starts updating again for a another 30-60 seconds. ...And so on.

Could it be that the serial read comes out of sync with the data stream? Does the serial buffer need to be read continuously?
Could the solution be to flush buffer and wait for the start of a new sentence in order to start a new read? If so, how would the pro micro recognize the start of a new sentence?

(I don't fully understand every part of the sample code yet.)

Have you thought of buffering an entire NMEA sentence before passing to the library? Then
it won't matter if the libraries slow in parsing it.

Could the solution be to flush buffer

No.

What is the purpose of sending data TO the GPS? What do you expect it to do with it?

MarkT:
Have you thought of buffering an entire NMEA sentence before passing to the library? Then
it won't matter if the libraries slow in parsing it.

That could be an idea. How would I start/stop the buffering?
How does the buffer work? Will it automatically throw away old data when full, or does it drop new data when full? Does it receive data in the background independently of what code the mpu is currently executing?

PaulS:

Could the solution be to flush buffer

No.

What is the purpose of sending data TO the GPS? What do you expect it to do with it?

The purpose would be to say "Hey, I'd like some data now. Send me a sentence."
Then I would know that what is in the buffer is one full sentence ready for parsing/decoding.

Then I would know that what is in the buffer is one full sentence ready for parsing/decoding.

You know that the GPS has sent a complete sentence because feedgps() returned true.

PaulS:

Then I would know that what is in the buffer is one full sentence ready for parsing/decoding.

You know that the GPS has sent a complete sentence because feedgps() returned true.

Ah! That is useful knowledge!

I found this comment about the tinygps library:

NEMA format GPS data is usually transmitted at 4800 baud, in 8N1 format. The resulting maximum incoming speed is 480 bytes per second. By default, Teensyduino's UART Serial buffers up to 64 bytes, or 0.133 seconds of incoming data.

If your program must perform some operation for more than 0.13 seconds, it should check for new data and give it to TinyGPS with gps.encode() within that time period.

This gps run at 9600 baud. That would make the buffer fill every 0.0665 second.
As the position is printed to pc about 3 times pr second after PaulS' suggested modification to loop {}, the gps.encode() seems to need 0.3 seconds to run. That is 4.5 times longer than the buffer fill. I guess that would be the cause of trouble.

So, if flushing buffer is not the way to handle this, then what is?

PaulS:

Could the solution be to flush buffer

No.

As the position is printed to pc about 3 times pr second after PaulS' suggested modification to loop {}, the gps.encode() seems to need 0.3 seconds to run. That is 4.5 times longer than the buffer fill. I guess that would be the cause of trouble.

How are you measuring that? The gps.encode() function get called only when a complete sentence has arrived. Since that happens roughly once a second, it should not cause the buffer to overflow, since the GPS will be quiet for a while after the end of the sentence has arrived, before it starts sending the next one.

Doubling the baud rate halves the time it takes to receive the data, not doubles it.

petterg:
Here's the modified code: (replaced "nss" with "Serial1", commented out softwareserial)

You'd be much better off looking at the example code provided with TinyGPS as opposed to someone else's modified version.

Check out the examples included at TinyGPS | Arduiniana.

FWIW,

Brad
KF7FER

PaulS:
How are you measuring that?

date/time is part of the printed information. I'm counting 3 lines with the same seconds value, hence I know the printing is performed about 3 times per second.

PaulS:
The gps.encode() function get called only when a complete sentence has arrived. Since that happens roughly once a second, it should not cause the buffer to overflow, since the GPS will be quiet for a while after the end of the sentence has arrived, before it starts sending the next one.

How do you know it's sending data only once per second?
As far as I understand the datasheet it's sending 10 times per second.

PaulS:
Doubling the baud rate halves the time it takes to receive the data, not doubles it.

I think 0.0665 is closer to half than double of 0.13.

Thanks. I didn't know there was an official site for the library.

As far as I understand the datasheet it's sending 10 times per second.

Where did you get that? The link in your first post says, on the middle of page 2:

Navigation Data Update Rate 1Hz

That's once a second.

PaulS:

As far as I understand the datasheet it's sending 10 times per second.

Where did you get that? The link in your first post says, on the middle of page 2:

Navigation Data Update Rate 1Hz

That's once a second.

I read 10Hz somewhere. Might have been some other place.

Thanks to that link I found the library TinyGPS+. Thing speeded up quite a bit using that. Printing is now 7-10 times per second. Position updates are still lagging a bit thou. If I walk, stop, walk, stop, the printout shows exactly that I've walked, stopped, walked, stopped. It has a delay of about 15-20 seconds. I didn't get to test this very well yet as the weather is not equipment friendly at the moment (windy and rain). How can I figure out whats causing the delay? Is it the gps, the buffer or something else?