TinyGps error decoding date

Hello everyone, I'm using this GPS Shield with Arduino Due.

I extract GPS data with this code.

#include "TinyGPS.h"

TinyGPS gps;

#define GPS_TX_DIGITAL_OUT_PIN 5
#define GPS_RX_DIGITAL_OUT_PIN 6

long startMillis;
long secondsToFirstLocation = 0;

#define DEBUG

float latitude = 0.0;
float longitude = 0.0;

int yeargps;
byte monthgps, daygps, hourgps, minutegps, secondgps, hundredths;
unsigned long age;

void setup()
{
  #ifdef DEBUG
  Serial.begin(19200);
  #endif
  
  // Serial1 is GPS
  Serial1.begin(9600);//38400//9600);
  
  // prevent controller pins 5 and 6 from interfering with the comms from GPS
  pinMode(GPS_TX_DIGITAL_OUT_PIN, INPUT);
  pinMode(GPS_RX_DIGITAL_OUT_PIN, INPUT);
  
  startMillis = millis();
  Serial.println("Starting");
}

void loop()
{
  readLocation();
}

//--------------------------------------------------------------------------------------------
void readLocation(){
  bool newData = false;
  unsigned long chars = 0;
  unsigned short sentences, failed;

  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (Serial1.available())
    {
      int c = Serial1.read();
      Serial.print((char)c); // if you uncomment this you will see the raw data from the GPS
      ++chars;
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;
      //else
        //Serial.println("Fixing position");
    }
  }
  
  if (newData)
  {
    // we have a location fix so output the lat / long and time to acquire
    if(secondsToFirstLocation == 0){
      secondsToFirstLocation = (millis() - startMillis) / 1000;
      Serial.print("Acquired in:");
      Serial.print(secondsToFirstLocation);
      Serial.println("s");
    }
    
    unsigned long age;
    gps.f_get_position(&latitude, &longitude, &age);
  
    if (age == TinyGPS::GPS_INVALID_AGE)
      Serial.println("No lock detected!");
    else
      Serial.println("GPS locked, data is valid !");
    
    latitude == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : latitude;
    longitude == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : longitude;
    
    Serial.print("Location: ");
    Serial.print(latitude, 6);
    Serial.print(" , ");
    Serial.print(longitude, 6);
    Serial.println("");
    
    gps.crack_datetime(&yeargps, &monthgps, &daygps, &hourgps, &minutegps, &secondgps, &hundredths, &age);
    
    if (age == TinyGPS::GPS_INVALID_AGE)
      Serial.println("Invalid datetime data");
    else
    {
      Serial.print("Year: ");
      Serial.print(yeargps);
      Serial.print(", month: ");
      Serial.print(monthgps);
      Serial.print(", Day: ");
      Serial.println(daygps);
      Serial.print(hourgps);
      Serial.print(":");
      Serial.print(minutegps);
      Serial.print(":");
      Serial.println(minutegps);
      Serial.print("Hundredths: ");
      Serial.println(hundredths);
    }
  }
  
  if (chars == 0){
    // if you haven't got any chars then likely a wiring issue
    Serial.println("Check wiring");
  }
  else if(secondsToFirstLocation == 0){
    // still working
  }
}

It is an example obtained from http://davidhoulding.blogspot.com.es/2014/06/gps-location-sensing-with-arduino-mega.html with a little modifications

The serial output obtained is:

Starting
$GPGGA,173156.000,3806.7776,N,00054.5494,W,1,15,0.7,37.8,M,50.5,M,,0000*77
$GNGSA,A,3,25,12,02,31,14,29,24,32,,,,,1.4,0.7,1.2*20
$GNGSA,A,3,79,88,78,82,80,81,70,,,,,,1.4,0.7,1.2*20
$GPGSV,3,1,10,25,66,357,51,12,37,060,49,02,25,067,48,31,28,313,33*75
$GPGSV,3,2,10,14,37,287,31,29,64,201,30,24,26,132,28,32,40,252,25*71
$GPGSV,3,3,10,06,04,033,,26,01,270,*7A
$GLGSV,3,1,09,79,59,005,44,88,40,039,38,78,47,081,35,82,23,189,31*66
$GLGSV,3,2,09,80,14,311,29,81,67,140,25,70,11,308,23,77,01,113,38*66
$GLGSV,3,3,09,69,08,256,*5A
$GNRMC,173156.000,A,3806.7776,N,00054.5494,W,0.00,311.25,080317,,,A*6D
$GNZDA,173157.000,08,03,2017,,*41
Acquired in:1s
GPS locked, data is valid !
Location: 38.112961 , -0.909157
Year: 2000, month: 0, Day: 0
17:31:31
Hundredths: 0

The last NMEA trace is ok, it contain correct day, month and year but the gps.crack_datetime function don't work properly and I can't find where is the error in this function.

Can any one help me to correct it?

Thanks, best regards.

  // prevent controller pins 5 and 6 from interfering with the comms from GPS

pinMode(GPS_TX_DIGITAL_OUT_PIN, INPUT);
  pinMode(GPS_RX_DIGITAL_OUT_PIN, INPUT);

Looks like a copy-paste error. Pins 5 and 6 have nothing to do with Serial1 on the Due.

When gps.encode() returns true, it means it has parsed a valid sentence. That may not be the $GPRMC sentence you were expecting. It could be another one. Make sure that you have the right sentence before unpacking the GPS time and date.

Hello MorganS,

Thank you very much for your advice.

I forgot to say that I had put a pair of wires between pins 5 and 6 to the Arduino Due pins 18 (Tx1) and 19(Rx1).

Yes, gps.encode() return true. In the line 12 of the Serial output: $GNZDA,173157.000,08,03,2017,,*41 I can see a valid date.

Time is correct but no the date :frowning: , both values are obtained with gps.crack_datetime() function.

I don't understand you when you say that I need a $GPRMC sentence. Is is to check if the GPS data is valid? I check if the data is valid with this sentence: if (age == TinyGPS::GPS_INVALID_AGE).

Thanks again and if you or someone can help me more I will be very grateful.

$GPRMC is usually where you get the date from. $GNZDA is not normally used. I've never actually seen a GPS which outputs this.

@MorganS is correct. The TinyGPS and TinyGPS++ libraries do not use the $GNZDA sentence. It's just discarded.

I'm pretty sure that at some point your device was configured (using the serial interface) to refrain from emitting the $GPRMC sentence. If you can figure out how to turn that back on your problem should be solved.

Hello! mikalhart and MorganS, thanks for your right answers. I go to explain more details about the problem in order to find a good solution.

I bought one Gps Shield V 1.1 from Itead Studio one year ago, in order to test it. It has a NEO-6M-0-001 GPS module as you can see in the next photo:

I develop my project with this GPS shield and it work fine. I received the correct $GPRMC sentence and If I test the code used to extract GPS data (exposed above) it work fine:

$GPRMC,220602.00,A,3806.7776,N,00054.5494,W,0.034,,100317,,,D*63
$GPVTG,,T,,M,0.034,N,0.063,K,D*24
$GPGGA,220602.00,3825.35559,N,00026.72032,W,2,09,1.26,93.1,M,48.5,M,,0000*74
$GPGSA,A,3,27,16,07,21,08,26,31,18,10,,,,2.06,1.26,1.64*00
$GPGSV,4,1,13,04,55,144,33,07,07,324,25,08,16,285,41,10,33,145,39*77
$GPGSV,4,2,13,16,80,306,35,18,42,104,33,20,11,041,,21,48,048,29*74
$GPGSV,4,3,13,26,67,156,43,27,50,305,51,29,02,086,28,31,06,188,35*70
$GPGSV,4,4,13,33,43,203,36*48
$GPGLL,3806.7776,N,00054.5494,,2620,,*9
GPS locked, data is valid !
Location: 38.112961 , -0.909157
Year: 2017, month: 3, Day: 10
22:6:6

I check it today, after reading your answers.

I receive the $GPRMC sentence and gps.crack_datetime() function extract all data fine, Year, month and day too.

Recently I bought another Gps Shield V1.1. from Itead Studio. I think that it will be the same shield, but when I receive it, I see that now it has a Royaltek REB-5216 GPS module.

With the last test I check that it don't send $GPRMC sentence and I don't know If I can change the operating mode in order to receive $GPRMC sentence.

Anyone know if I can change the operation mode and how?

Thanks, best regards.

Hello again,

I reading the the manual of Royaltek REB-5216 GPS modul it say this: "Default protocol GGA(1)" and it says that the protocol can be RMC. But I don't see how to change it >:(

Good to know that we found the cause. $GPRMC is the "recommended minimum" so it is unusual to find a GPS which doesn't output that by default.

For me, I would try to modify the library to use the other sentence. It can't be too hard. It would be an interesting project to try.

Hello,

I solve it and post the solution. Pherhaps someone have the same problem and can find the solution here.

I was reading the sentences and find that the $GPRMC sentence obtained from NEO-6M-0-001 GPS module is equal to the $GNRMC sentence obtained from the Royaltek REB-5216.

You only need change one character in the code of the library, in order to use this sentence ($GNRMC) that have the correct date instead the sentence GPGGA that is used by default by TinyGPS library when you don't have GPRMC sentence.

In the file TinyGPS.cpp, line 26 change

#define _GPRMC_TERM   "GPRMC"

by

#define _GPRMC_TERM   "GNRMC"

I prove it and it work fine now.

Thanks!

You might be interested in my NeoGPS library. It handles all these sentences correctly (even a mix). It is faster, smaller and more accurate than other libraries, and the example programs are structured correctly. You can configure it to parse only the fields that you really use. Here is your original sketch, modified to use NeoGPS:

#include <NMEAGPS.h>

NMEAGPS gps;
gps_fix fix;

#define gpsPort Serial1    // Serial1 is connected to the GPS
#define GPS_TX_DIGITAL_OUT_PIN 5
#define GPS_RX_DIGITAL_OUT_PIN 6

long startMillis;
long secondsToFirstLocation = 0;

#define DEBUG

void setup()
{
  #ifdef DEBUG
  Serial.begin(19200);
  #endif
  
  gpsPort.begin(9600);//38400//9600);
  
  // prevent controller pins 5 and 6 from interfering with the comms from GPS
  pinMode(GPS_TX_DIGITAL_OUT_PIN, INPUT);
  pinMode(GPS_RX_DIGITAL_OUT_PIN, INPUT);

  startMillis = millis();
  Serial.println("Starting");
}

void loop()
{
  readLocation();
}

//--------------------------------------------------------------------------------------------
void readLocation()
{

  // For one second, parse GPS data and report some key values
  unsigned long start = millis();
  while (millis() - start < 1200) // make sure the time window is a smidge bigger
  {
    if (gps.available( gpsPort ))
    {
      fix = gps.read();

      // we have a fix so output the time to acquire
      if(secondsToFirstLocation == 0){
        secondsToFirstLocation = (millis() - startMillis) / 1000;
        Serial.print("Acquired in:");
        Serial.print(secondsToFirstLocation);
        Serial.println('s');
      }

      //
      if (!fix.valid.location)
        Serial.println("No lock detected!");
      else {
        Serial.println("GPS locked, data is valid !");
        Serial.print("Location: ");
        Serial.print( fix.latitude(), 5 );
        Serial.print(" , ");
        Serial.print( fix.longitude(), 5 );
        Serial.println();
      }
      
      if (!fix.valid.date || !fix.valid.time)
        Serial.println("Invalid datetime data");
      else
      {
        Serial.print("Year: ");
        Serial.print(fix.dateTime.year); // or fix.dateTime.full_year()
        Serial.print(", month: ");
        Serial.print(fix.dateTime.month);
        Serial.print(", Day: ");
        Serial.println(fix.dateTime.date);
        Serial.print(fix.dateTime.hours);
        Serial.print(":");
        Serial.print(fix.dateTime.minutes);
        Serial.print(":");
        Serial.println(fix.dateTime.minutes);
        Serial.print("Hundredths: ");
        Serial.println(fix.dateTime_cs); // or fix.dateTime_ms() for milliseconds
      }
    }
  }
  
  if (gps.statistics.chars == 0){
    // if you haven't got any chars then likely a wiring issue
    Serial.println("Check wiring");
  }
  else if(secondsToFirstLocation == 0){
    // still working
  }

} // readLocation

Notice how it checks to see if each field is valid, and the date/time fields are just parts of the fix structure, ready to use. NeoGPS merges the information from all received sentences (e.g., GNRMA and the GPGGA) into the one coherent fix structure.

Instead of separate variables at the top, you can just use fix anywhere in your sketch. It contains all the fields that you have enabled. By default, this is lat/lon, date, time, satellite count, status, speed and heading.

If you want to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers,
/dev

Hello everybody! I change one symbol: #define _GPRMC_TERM "GPRMC" & #define _GPRMC_TERM "GNRMC"
And REB-5216 start to work. But very long time looking for position. REB-4216 can do it fast but REB-5216 - few minutes.