TinyGPS++ and $GNRMC message issues?

I just purchased a NEO M8N GPS because I would like to have 10Hz updates rather than the maximum of 5Hz on my NEO 6M GPS.

I have disabled all but the RMC sentence and my output looks like this:

17:42:18.066 -> $GNRMC,214xxx.xxx,A,42xx.xxxxx,N,071xx.xxxxx,W,0.00,10.92,030824,,,A,V2A
17:42:18.154 -> $GPTXT,01,01,01,ANTENNA OK
35
17:42:18.154 -> $GNRMC,214xxx.xxx,A,42xx.xxxxx,N,071xx.xxxxx,W,0.00,10.92,030824,,,A,V25
17:42:18.239 -> $GPTXT,01,01,01,ANTENNA OK
35
17:42:18.288 -> $GNRMC,214xxx.xxx,A,42xx.xxxxx,N,071xx.xxxxx,W,0.00,10.92,030824,,,A,V24
17:42:18.371 -> $GPTXT,01,01,01,ANTENNA OK
35

The GPS seems to have a fix, based on the lat and long being populated, however the "speed over ground" parameter always is 0.00.

This seems like it must be a TinyGPS++ parsing issue but from what I have read it seems like it should be able to handle $GNRMC just as well as $GPRMC as it has code that conflates them.

Any ideas on how I can get the "speed over ground" to be populated with data? Bonus points if you also know how to get rid of the ANTENNA OK message spam :slight_smile:

Code:

#define rxPin 5
#define txPin 4
SoftwareSerial neogps(rxPin, txPin);
TinyGPSPlus gps;

  neogps.begin(9600);
  delay(50);
  // for ublox NEO M8N: https://gis.stackexchange.com/questions/198846/enabling-disabling-nmea-sentences-on-u-blox-gps-receiver
  byte GGAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x24, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x00, 0xf9, 0x11};  //GGA OFF
  byte GLLoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2b, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x01, 0xfa, 0x12};  //GLL OFF
  byte GSAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x02, 0xfb, 0x13};  //GSA OFF
  byte GSVoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x03, 0xfc, 0x14};  //GSV OFF
  byte VTGoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x05, 0xfe, 0x16};  //VTG OFF
  byte ZDAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5b};  //ZDA OFF
  byte disableGLONASS[] = {0xB5, 0x62, 0x06, 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x8F, 0xB2}; // GLONASS satellites ignored

  neogps.write(GGAoff, sizeof(GGAoff));
  neogps.write(GLLoff, sizeof(GLLoff));
  neogps.write(GSAoff, sizeof(GSAoff));
  neogps.write(GSVoff, sizeof(GSVoff));
  neogps.write(VTGoff, sizeof(VTGoff));
  neogps.write(ZDAoff, sizeof(ZDAoff));
  neogps.write(disableGLONASS, sizeof(disableGLONASS));
// end NEO M8N


  delay(50);
  neogps.end();
  delay(50);
   sendUBX( ubxRate10Hz, sizeof(ubxRate10Hz) ); // 10hz updates, for NEO M8N

void feedGPS() {
  while (neogps.available()) {
    int c = neogps.read();
    Serial.write(c); 
  }
}

The code posted does not appear to be doing any parsing. What is being printed are the raw GNRMC sentences and in the examples posted, the ground speed field is zero.

Please post all the code.

What is the point of this call?

  neogps.write(disableGLONASS, sizeof(disableGLONASS));

In order to get speed over ground, the GPS has to be in motion.

The code posted does not appear to be doing any parsing. What is being printed are the raw GNRMC sentences and in the examples posted, the ground speed field is zero.

Sorry, this may just be my inability to correctly understand and describe programming terms. Maybe it's better to say that TinyGPS++ isn't calculating the speed? In the printed text output, the latitude and longitude do change a bit on some readings just due to GPS margin of error. I assume that normally TinyGPS++ would do some math on those coordinates and calculate a speed, and then it would get printed out in the RMC sentence as well. And that's how it works with $GPRMC and the NEO 6M unit.

The issue here seems to be that TinyGPS++ does have the data points to be able to calculate a speed, but it's either not doing it or not writing it in the $GNRMC sentence; it's simply writing 0.00 every time and I cannot figure out why.

What is the point of this call?

  neogps.write(disableGLONASS, sizeof(disableGLONASS));

I had read somewhere that NEO M8N GPS units are set to use GLONASS by default rather than GPS. I believe this is what leads to the fix generating a $GNRMC sentence rather than a $GPRMC sentence. So I thought if I disabled the ability to use GLONASS, it would use GPS and print a GPRMC sentence, and with other GPS units that sentence seemed to include the speed without any issues. However, this command did not seem to work properly so it is probably a red herring here.

In order to get speed over ground, the GPS has to be in motion.

Due to the margin of error of a few meters on any given signal, the GPS data does show location changes, and thus calculates a speed over ground, even if the unit is stationary on a desk. I have seen this happen with an Adafruit Ultimate GPS breakout and a NEO 6M GPS and I am sure it would with the NEO M8N as well, since that is showing location changes.

On a fortunate side note, I did find that a NEO 6M will actually do 10Hz updates, even though the data sheet says it can only do up to 5Hz. I am only using the speed figure from its $GPRMC sentence, so maybe things break down if you demand more at 10Hz, but for that limited purpose it is working perfectly.

You have not provided any evidence that TinyGPS++ is even being used.

POST THE CODE. All of it.

I had read somewhere that NEO M8N GPS units are set to use GLONASS by default rather than GPS. I believe this is what leads to the fix generating a $GNRMC sentence rather than a $GPRMC sentence.

Sorry, this makes no sense. The M8N manual is extensive, and is the only completely reliable source of information.

Maybe it's smart enough to know that a speed inside the margin of error is fake so prints 0. Until you put it in a car it's a guess. I would remove that guess.

As @jremington said, your code snippet appears to be echoing what is received from the GPS before TinyGPS++ does anything with it. So the GPS is reporting a 0 speed over ground. Even if you have code that you haven't shown that does parse the GPS output, all it can do it report what the GPS outputs: in this case, a speed over ground of 0.

2 Likes

For information on how to customize the data that TinyGPS++ collects and interprets, see this page: TinyGPS++ | Arduiniana

Maybe it's smart enough to know that a speed inside the margin of error is fake so prints 0. Until you put it in a car it's a guess. I would remove that guess.

That would be a really frustrating feature, but I suppose it might be plausible. This whole debacle started because I noticed the Adafruit Ultimate GPS was "smoothing" the speed readings it sent out. For use in a car speedometer, this led to extremely slow responsiveness when accelerating to say 40mph from a stop; the GPS would start giving a 40mph reading about 5 to 7 seconds after the car actually hit 40mph.

your code snippet appears to be echoing what is received from the GPS before TinyGPS++ does anything with it. So the GPS is reporting a 0 speed over ground. Even if you have code that you haven't shown that does parse the GPS output, all it can do it report what the GPS outputs: in this case, a speed over ground of 0.

OK now I understand this statement better. Yes that make sense, the GPS sends the sentence with the "0.00" speed, so perhaps this is a GPS issue rather than a TinyGPS++ issue.

For information on how to customize the data that TinyGPS++ collects and interprets, see this page: TinyGPS++ | Arduiniana

Thanks, this was an interesting read but I didn't spot anything in there that seemed like an explanation for what I am seeing. Full code is below though, in case it unlocks any other ideas or thoughts. Thanks again for taking the time to read/respond!

// MODE: CHOOSE WHICH GPS IS CONNECTED 
//#define Adafruit
//#define NEO6M
#define NEOM8N

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

#define rxPin 5
#define txPin 4
SoftwareSerial neogps(rxPin, txPin);
TinyGPSPlus gps;

int currentSpeed = 0;
int accuracyAdjustment = 0;
uint8_t speedSmoothingFactor = 3;  // 0 = no speed smoothing. non-zero indicates the most recent data point is weighted by (1 / speedSmoothingFactor)
int GPSknots;
float trailingAverageKnots;
bool newGPSdata = false;

// need this funky code method to send 5hz command to the NEO GPS; there seems to be no $PUBX way to do it
const unsigned char ubxRate5Hz[] PROGMEM = { 0x06, 0x08, 0x06, 0x00, 200, 0x00, 0x01, 0x00, 0x01, 0x00 };
const unsigned char ubxRate10Hz[] PROGMEM = { 0x06, 0x08, 0x06, 0x00, 100, 0x00, 0x01, 0x00, 0x01, 0x00 };  // totally guessing based on the above

void setup() {

#ifdef Adafruit
  Serial.begin(57600);
#endif
#ifdef NEO6M
  Serial.begin(38400);
#endif
#ifdef NEOM8N
  Serial.begin(38400);
#endif


  // SPEEDOMETER GPS data feed

  neogps.begin(9600);
  delay(50);

#ifdef Adafruit
  neogps.println("$PMTK251,57600*2C"); //set baud rate to 57600
  //neogps.println("$PMTK251,115200*1F"); //set baud rate to 115200
  //neogps.println("$PMTK251,74880*2B"); //set baud rate to 74880
  //neogps.println("$PMTK251,38400*27"); //set baud rate to 38400
#endif
#ifdef NEO6M
  //neogps.println("$PUBX,41,1,3,3,38400,0*38\r\n"); // 38400 baud rate  
#endif
#ifdef NEOM8N
  // defaults to 9600 and I don't yet know how to change it
#endif

  delay(50);
  neogps.end();
  delay(50);

#ifdef Adafruit
  // for Adafruit GPS
  neogps.begin(57600);
  neogps.println("$PMTK220,100,0,0,0,0*2F"); //NMEA update 10hz
  //neogps.println("$PMTK220,1000,0,0,0,0*1F"); //NMEA update 1hz
  //neogps.println("$PMTK300,200,0,0,0,0*2F"); //NMEA update 5hz
  neogps.println("$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"); // I think this should print out only RMC sentences
#endif

#ifdef NEO6M
  neogps.begin(9600);
  // for ublox NEO 6M: https://gist.github.com/laroo/346674840c25db9d86e532c33e255cc3
  neogps.println("$PUBX,40,GLL,0,0,0,0*5C\r\n"); // Disable GLL
  neogps.println("$PUBX,40,ZDA,0,0,0,0*44\r\n"); // Disable ZDA
  neogps.println("$PUBX,40,VTG,0,0,0,0*5E\r\n"); // Disable VTG
  neogps.println("$PUBX,40,GSV,0,0,0,0*59\r\n"); // Disable GSV
  neogps.println("$PUBX,40,GSA,0,0,0,0*4E\r\n"); // Disable GSA
  neogps.println("$PUBX,40,GGA,0,0,0,0*5A\r\n"); // Disable GGA
  //sendUBX( ubxRate5Hz, sizeof(ubxRate5Hz) ); // 5hz updates - supposed to be the max
  sendUBX( ubxRate10Hz, sizeof(ubxRate10Hz) ); // 10hz updates - ACTUALLY WORKS!!!
#endif

#ifdef NEOM8N
  neogps.begin(9600);
  // for ublox NEO M8N: https://gis.stackexchange.com/questions/198846/enabling-disabling-nmea-sentences-on-u-blox-gps-receiver
  byte GGAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x24, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x00, 0xf9, 0x11};  //GGA OFF
  byte GLLoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2b, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x01, 0xfa, 0x12};  //GLL OFF
  byte GSAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x02, 0xfb, 0x13};  //GSA OFF
  byte GSVoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x03, 0xfc, 0x14};  //GSV OFF
  byte VTGoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47, 0xb5, 0x62, 0x06, 0x01, 0x02, 0x00, 0xf0, 0x05, 0xfe, 0x16};  //VTG OFF
  byte ZDAoff[] = {0xb5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xf0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5b};  //ZDA OFF
  byte disableGLONASS[] = {0xB5, 0x62, 0x06, 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x8F, 0xB2}; // GLONASS satellites ignored
  neogps.write(GGAoff, sizeof(GGAoff));
  neogps.write(GLLoff, sizeof(GLLoff));
  neogps.write(GSAoff, sizeof(GSAoff));
  neogps.write(GSVoff, sizeof(GSVoff));
  neogps.write(VTGoff, sizeof(VTGoff));
  neogps.write(ZDAoff, sizeof(ZDAoff));
  neogps.write(disableGLONASS, sizeof(disableGLONASS));
  // end NEO M8N
#endif

}


void loop() {
  feedGPS();
}

void feedGPS() {
  while (neogps.available()) {
    int c = neogps.read();
    Serial.write(c);  
    if (gps.encode(c)) {
      newGPSdata = true;
    }
  }
  if (newGPSdata == true) {
    newGPSdata = false;
    if (gps.location.isValid()) {
      if (gps.speed.isUpdated()) {
        GPSknots = gps.speed.knots();
      }
    }
    if (speedSmoothingFactor == 0) {
      currentSpeed = gps.speed.mph();
    }
    else {
      trailingAverageKnots = (trailingAverageKnots * (speedSmoothingFactor - 1)) + gps.speed.knots();
      trailingAverageKnots = trailingAverageKnots / speedSmoothingFactor;
      currentSpeed = (trailingAverageKnots * 115) / 100 + accuracyAdjustment;
    }
    if (currentSpeed <= 1) {
      currentSpeed = 0;
    }
    else {
      currentSpeed = currentSpeed + accuracyAdjustment;
    }
  }
}

void sendUBX( const unsigned char *progmemBytes, size_t len )
{
  neogps.write( 0xB5 ); // SYNC1
  neogps.write( 0x62 ); // SYNC2

  uint8_t a = 0, b = 0;
  while (len-- > 0) {
    uint8_t c = pgm_read_byte( progmemBytes++ );
    a += c;
    b += a;
    neogps.write( c );
  }

  neogps.write( a ); // CHECKSUM A
  neogps.write( b ); // CHECKSUM B

}

That is what we've been trying to tell you. If the GPS reports speed over ground 0.00, that is what TinyGPS++ reads and reports.

The code you posted has the capability of smoothing the data, however the code is poorly written and may not work as expected.

As has been mentioned, TinyGPS++ is just reporting the speed over ground that the GPS is reporting, no calculation involved.

Most GPSs will measure the Doppler shift of the incoming GPS signals carrier, so even if the reported position of a stationary GPS is shifting a bit, the GPS can sense if its actually moving.

The code you posted has the capability of smoothing the data, however the code is poorly written and may not work as expected.

Thanks, I am sure some of my code is far worse than "poorly" written! For what it's worth, it does work as I expected. I have been able to test it live in a car with the NEO 6M and it works quite well. It's a good mix between adding a little smoothing to eliminate outlier readings that would make the speedometer jumpy, but not too much of a long average that it is super sluggish to respond to going from a stop to say 35mph.

I guess my next step is to try the NEO M8N in a car with some speeds well above stationary variance.