GPS U-Blox Neo M8N parser

Hey! I just realized you asked about the Neo-6M over here. Did you end up buying the M8?

FWIW, the M8 appears to be backward-compatible. That is, it appears to support all the messages that the 6M has, plus a few new ones. NeoGPS implements most of the NMEA messages (GGA, GLL, GSA, GST, GSV, RMC, VTG, ZDA, PUBX00, PUBX04) and UBX messages (STATUS, TIMEGPS, TIMEUTC, POSLLH, VELNED, SVINFO) that you would probably need.

Cheers, /dev

Hi dev, yes I asked info about 6M but then I compared it to the M8 and there were no chances.. The last one is better. Since it uses GLONASS I needed to write a custom parser for these messages : GNRMC , GNGGA Now it works, if you need it I can post the code.

Since it uses GLONASS I needed to write a custom parser for these messages : GNRMC , GNGGA

Ah, I see. After a little research, I see that this is the same as the GPRMC, except the “talker ID” is “Mixed GNSS”, not GPS alone. It’s also one of the things that the Linux gpsd watches for, and basically ignores (discussion here). I’ll investigate incorporating different talker IDs into NeoGPS. This explains (to me) why TinyGPS doesn’t work with this device.

For anyone else using this device, there is a way to make it always send GPxxx instead of choosing GPxxx / GNxxx / GBxxx / GLxxx / GAxxx messages (e.g., GPRMC, not GNRMC). These all contain the same information, they just have different talker IDs.

The ublox M8 device family has a configuration command called CFG_NMEA (see section 21.11.13.4, Extended NMEA protocol configuration V1). This command allows you to set the mainTalkerID to “GP” (value 1). If you also use the GPGSV message, you would also want to force the gsvTalkerID to use the mainTalkerID. I suspect you would also have to set the Beidou talker ID to “GP” (two characters).

I think the bytes to send during setup would be:

const char cfg_nmea[] __PROGMEM =
  {
    0xB5,     // sync 1
    0x62      // sync 2
    0x06,     // CFG class ID
    0x17,     // CFG_NMEA msg id
    20, 0,    // bytes to follow
    0,        // filters disabled
    0x40,     // NMEA version 4.0
    0,        // Number of satellites reported in GSV = unlimited
    0x02,     // compatibility disabled, consideration mode enabled, limit to 82 char maximum disabled
    0, 0, 0, 0, // no GNSS satellites filtered
    0,        // strict satellite numbering
    1,        // main talker ID = GP
    1,        // GSV talker ID = main talker ID
    1,        // CFG_NMEA message version
    'G', 'P', // Beidou talker ID = GP
    0,0,0,0,0,0,  // fill
    0x0D      // CK_A
    0xC1      // CK_B
  };

This would allow GPS-only NMEA parsers like TinyGPS to work with this device.

BTW, the UBX parser in NeoGPS (like any binary parser) is not affected by the talker ID; this is an NMEA-only “feature”.

if you need it I can post the code.

Of course! We all like code! :slight_smile:

Cheers,
/dev

Oopsies! The "bytes to follow" is a word length. I added a zero byte above and updated CK_B. Still untested, as I do not have that hardware, but it is more likely to work now. ;)

Cheers, /dev

P.S. After coding it up and sending it to the Neo 6M, I found that the version 4.0 field should have been 0x40, not 4. updated above...

I found a nice parser online which I modified in order to get data from the U-Blox Neo M8N
Create a file .ino and include the files attached in this post
EXAMPLE:

// Use this code with UBLOX NEO M8N
#include "Ublox.h"
#define SERIAL_BAUD 115200
#define GPS_BAUD 115200
#define N_GPS_DATA 4

Ublox M8_Gps;
// Altitude - Latitude - Longitude - N Satellites
float gpsArray[N_GPS_DATA] = {0, 0, 0, 0};

void setup() {
   Serial.begin(SERIAL_BAUD);
   Serial1.begin(GPS_BAUD);

}

void loop() {
   if(!Serial1.available())
		return;

  while(Serial1.available()){
        char c = Serial1.read();
        M8_Gps.encode(c);           
        gpsArray[0] = M8_Gps.altitude;
        gpsArray[1] = M8_Gps.latitude;
        gpsArray[2] = M8_Gps.longitude; 
        gpsArray[3] = M8_Gps.sats_in_use;
	}
}

Ublox.cpp (7.74 KB)

Ublox.h (1.81 KB)

Great, thanks for that! One thing I noticed... I would suggest changing your while loop from this

  while(Serial1.available()){
        char c = Serial1.read();
        M8_Gps.encode(c);           
        gpsArray[0] = M8_Gps.altitude;
        gpsArray[1] = M8_Gps.latitude;
        gpsArray[2] = M8_Gps.longitude; 
        gpsArray[3] = M8_Gps.sats_in_use;
    }

to this:

  while(Serial1.available()) {
        char c = Serial1.read();
        if (M8_Gps.encode(c)) {
          gpsArray[0] = M8_Gps.altitude;
          gpsArray[1] = M8_Gps.latitude;
          gpsArray[2] = M8_Gps.longitude; 
          gpsArray[3] = M8_Gps.sats_in_use;
        }
  }

Like many libraries, Gps.encode(c) will return true when a sentence has been completely received. There's no reason to copy the values when any character is received. Not a big deal, unless you're trying to do other things at the same time.

For anyone else landing here, you could also modify TinyGPS.cpp to accept these messages by changing these lines:

#define _GPRMC_TERM   "GNRMC"
#define _GPGGA_TERM   "GNGGA"

Other libraries have similar literals that could also be modified.

The Bad News: they will then ignore the normal GPRMC and GPGGA sentences. If the receiver is not tracking any GLONASS satellites, it would emit GPRMC instead. Adding a check for "GNRMC" or "GPRMC" is not very difficult.

Also, I have just finished implementing the NMEA Talker ID concept in NeoGPS, and it will now accept the "GNRMC" et al. This actually improved the performance by about 10%! Ah, skipping bytes is good... :)

Cheers, /dev

You're right! thank you for the tip. I forgot do say that there are more GPS info you can read with the library I attached. e.g. HDOP, PDOP, satellites in view, time . bye

After the usual convulsions, i figured out how to set the talker on my NEO M8N to GP instead of GN so that TinyGPS++ could read the sentences. Since Tiny reads only GPRMC and GPGGA, I also configured the M8N to emit only those sentences. I’m reading lat, lon, and time just fine but not altitude. I want altitude as well and I suppose now I’ll have to get into Tiny and see where it went. It does read using another chip, so this must be an anomaly of using the M8N. if it reads altitude ok on another chip but not on the M8N, the re must be some difference in the sentence words.

While I’m at it, it looks as though the frequency of sentence output is controllable but so far I haven’t been able to get it to do this. I also suspect that the usable navigation data rate may be affected negatively by using fewer sentences. it’d doesn’t look like the update rate is improved at all byu using just GPGGA and GPRMC instead of all of them.

Is that correct?

I’d like 10 Hz usable nav data rate. Is that nuts?

it'd doesn't look like the update rate is improved at all by using just GPGGA and GPRMC instead of all of them.

I think that's right. It seems like the update rate can't be changed to 10Hz for NMEA, only 5Hz. Have you changed the baud rate, too? u-center is a good way to test that setting.

The NeoGPS library I wrote can parse all talkers, not just GP, and it's a much smaller and faster NMEA parser than TinyGPS/TinyGPS++. It also implements the UBX binary protocol, so you could use it to send the configuration commands.

Depending on what else you're doing, 10Hz is possible. NeoGPS and/or NeoHWSerial could help.

Cheers, /dev

/dev

Thanks for the note. I reset the M8N to default, loaded up your code and have been having a pretty good time with it. I'm running the M8N at 9600, but will now go back to U-Center and try it at 57600 with serial output at 115200. And try to see if I can get the update rate higher. I can see I'll need to fuss with this a bit, but now that I'm getting reliable output I've got a good place to start.

This system will run on a nano and be part of a datalogging system which will go for rides in a small fixed-wing r/c aircraft to collect information the better to design very small autonomous aircraft sometime next year.

I have to say I like /dev. I wonder if /etc or /bin is taken yet

And cheers yourself. thanks for some vary capable code,

John

/bin further to the above. It looks like I might be able to skip nmea altogether, use the UBX messages pollh and one for time and i'd have what I need and might be able to get it at 10 Hz. And yes U-center looks like the place to play with this.

I could log this output as is, no transformations before recording and then convert it to something I'm used to looking at when i analyze it. the airborne logger won't having a display, but i'll plug a little oled terminal into a port on it when I fire it up to make sure it's recording reasonable data before I launch.

have you tried running only on UBX sentences?

john

I have to say I like /dev.

Everybody likes /dev! Except String-lovers. They've got no stars upon thars. ;)

The reason I asked about the baud rate is because the ublox knows when there's too much data to send at a particular baud rate. It will throw "frames" away if they can't get transmitted before the next update. If you're running at 9600, an 80-character NMEA sentence will take ~80ms. That's most of the update interval right there. A second sentence can't get sent before the next update interval has started. So to be sure the baud rate isn't the bottleneck, you must increase it.

Regarding polling for fixes instead of letting it send them at a fixed interval, I hope you realize it will increase the Nano CPU load, both in the number of interrupts it has to handle (TX), and in the time it takes to queue up the the request. I'm not sure what else the Nano is doing, but 10Hz updates with logging is a significant load for this processor. If you're careful, it's easily doable. Logging and printing is usually what trips people up, though.

Which brings me to this last suggestion. Many projects have trouble because so many libraries take too much time away from loop(). Even logging or printing takes time to execute, sometimes blocking until all the data gets logged or printed. GPS data keeps rolling in, and if you don't do the Serial.read() often enough (64ms @ 9600), you will miss receiving some characters (aka input buffer overflow). And if you increase the baud rate to 115200, that window gets even shorter: 6.1ms! That means that no single operation you perform can take longer than 6ms, or you will lose some GPS data.

The only solution is to use an interrupt-driven approach like in NMEAfused_isr.ino (which requires NeoHWSerial). Letting the characters be handled in an interrupt allows you to do your data logging without worrying about getting back to loop. The fixes are "magically" updated by the interrupts, and you never have to do Serial.available() and Serial.read().

have you tried running only on UBX sentences?

Yes. The ublox example turns off the NMEA sentences and runs in pure UBX binary protocol. BTW, if you switch back to one of the NMEA examples, they won't work until you power off the ublox.

Everything is more complicated because it's a two-way protocol: configuration commands have acknowledgements. And starting up takes a few seconds because you must request and receive the current GPS leap seconds and the current UTC time. You need both before you can convert the GPS time-of-week value (ms since midnight Sunday) to a UTC time (leap seconds + time-of-week + UTC midnight Sunday). There's a state machine in ublox.ino that shows how to do that.

I haven't seen an application yet where it's worth it to use the UBX protocol. It does save about 120 RX interrupts per update (only 40 characters instead of 160), and the binary numbers don't have to get parsed from ASCII decimal. It is more efficient, but unless you're trying to squeeze every last cycle out of the Nano, it may not be worth the effort. Are you planning on using an IMU? That does require frequent sampling and computation.

Cheers, /dev

Hi /dev I was using a nano to sneak up on it, mostly because i have a bunch of them. i quickly realized that when i get to the imu and controlling fixed wing flight, a nano won't do the job, so I was going to try a teensy 3.2. I may use two of them. ultimately, I'll build a custom board with the chips I need and nothing i don't but that is still two years away. This is not a commercial project so time is not of the essence.

Thanks for your thoughts. I need to think about what to try next.

best,

john

and don't worry if you see anything stupid in what i write. i don't know what I'm doing -- yet.

john maybe /tmp

Hi Dev,

Great work with NEOgps

I would like to use the ublox binary protocol using NEOgps and was wondering if it possible to parse an already aquired sentance.

My setup is a 4d systems screen and a spi connection to the arduino. The 4d systems screen receives the GPS data on a hardware serial port and does that job very well without any impact on the screen. I then send and receive an SPI packet where the GPS data is transferred to the arduino as byte arrays.

The reason for this, is the Arduino hardware serial is in use by a bluetooth adapter and all works well. The 4d systems screen display rate is very fluid.

Any Serial read on the arduino messes things up.

If I could pass the whole byte array to the parser as ublox binary I could maintain this very fast framerate.

If this function already exist’s in the library, could you please point me in the right direction.

Thanks

My setup is a 4d systems screen and a spi connection to the arduino.

Ok, Arduino uses SPI to talk to the 4D Systems display shield...

Arduino hardware serial is in use by a bluetooth adapter and all works well.

Ok...

Any Serial read on the arduino messes things up.

Whut? You just said all works well.

The 4d systems screen receives the GPS data on a hardware serial port and does that job very well without any impact on the screen. I then send and receive an SPI packet where the GPS data is transferred to the arduino as byte arrays.

...and this makes no sense to me. You send and receive an SPI packet? The screen receives the GPS data without any impact on the screen?

If I could pass the whole byte array to the parser as ublox binary...

I do not understand the description of your system, so I'll give you an answer to a question you probably did not ask:

If you have the entire message in a byte array, including the SYNC bytes, you should verify the checksum. It's just the Fletcher checksum over everything except the first two bytes (SYNC 1 and 2) and the last two bytes (CS A and B). If that passes, you can simply "map" the buffer onto a struct you define that matches the UBX message:

   ubxMgaGPSephMessageStruct *mgaGPSeph = &buffer[2];
   if (mgaGPSeph->uraIndex == 7) ...

If you don't have the SYNC bytes, map it starting at 0:

   ubxMgaGPSephMessageStruct *mgaGPSeph = &buffer[0]; // no SYNC bytes at the beginning
   if (mgaGPSeph->uraIndex == 7) ...

If the message is one of the messages supported by NeoGPS (NAV POSLLH, NAV VELNED, NAV STATUS, NAV TIMEGPS, NAV TIMEUTC or NAV SVINFO), you can use ubxGPS.h to get the message structure definitions:

#include "ubxGPS.h"
    .
    :

    ublox::nav_posllh_t *posllh = &buffer[2]; // map the bytes onto a struct

    // just access the posllh structure members
    display.print( posllh->lat ); // integer latitude * 10000000
    display.print( posllh->lon ); // integer longitude * 10000000

There is no parsing required if you already have all the bytes. Just map it onto C data types in a struct. ubxGPS.h is a C++ way to declare the struct so that it matches the ublox message spec.

Did that help?

Cheers, /dev

Hi /Dev,

Yes that helped a lot. I will give it a try later and let you know.

Sorry I didn't explain my system all that well.

The arduino (atmega1284P) is used for I2c sensor data collection because of it's floating point ability. The 4d screen can't handle that easily.

The 4d screen is an SPI master and will send SPI data and also retreive the sensor data in one function. If I have read GPS via the 4d serial port, it will send that at the same time. This is giving me about 18 frames per second on the screen.

The hardware Serial on the arduino is used for the bluetooth adapter and after receiving a 1 byte request for data, the arduino sends all available data to my phone via bluetooth. All very smoothly.

The second serial port on the 1248P is also int0 and int1 and I have a rotary encoder using int0/int1 so Software serial was the only option to connect the GPS to and that pauses data flow when a message is received. Reading the GPS on the 4d screen had no impact on screen framerate at all.

I am needing location and speed only from the GPS.

Many thanks

Paul

Hi /dev,

Managed to solve it. Using the 4d systems screen to take in data from the GPS was a bad idea, so abandoned that.

I connected the NEO6 to U center and switched off everything apart from speed and heading message and set my baud rate to 115200, at 4hz rate. At the moment I only really want the groundspeed.

I moved 1 encoder pin from int0 to int2 (the 1284p has 3 interrupt pins) and left the other encoder pin on int1 which is Serial1 TX.

I connected the GPS TX to Serial1 RX (int0) and wrote a routine to get speed from Serial1 GPS message.

Works a treat and I have a fast update rate. the converted to mph speed is then passed to 4d screen during it's spi data transfer.

I might do an interpolation to smooth out the 4hz rate so the needle on the screen moves smoothly between speed updates.

Very impressed with the NEO6 being able to get a fix indoors.

Thanks for your help.

Paul

/dev: For anyone else landing here, you could also modify TinyGPS.cpp to accept these messages by changing these lines:

#define _GPRMC_TERM   "GNRMC"
#define _GPGGA_TERM   "GNGGA"

Other libraries have similar literals that could also be modified.

The Bad News: they will then ignore the normal GPRMC and GPGGA sentences. If the receiver is not tracking any GLONASS satellites, it would emit GPRMC instead. Adding a check for "GNRMC" or "GPRMC" is not very difficult.

In order to accept BOTH GPS and GLONASS mesages, I think that the two fixes to TinyGPS.cpp are as follows:

#include "TinyGPS.h"

#define _GPRMC_TERM   "GPRMC"
#define _GPGGA_TERM   "GPGGA"

#define _GNRMC_TERM   "GNRMC" // *** added
#define _GNGGA_TERM   "GNGGA" // *** added

TinyGPS::TinyGPS()

and

  // the first term determines the sentence type
  if (_term_number == 0)
  {
    if (!gpsstrcmp(_term, _GPRMC_TERM) || !gpsstrcmp(_term, _GNRMC_TERM))      //modified
      _sentence_type = _GPS_SENTENCE_GPRMC; // *** modified
    else if (!gpsstrcmp(_term, _GPGGA_TERM) || !gpsstrcmp(_term, _GNGGA_TERM)) //modified
      _sentence_type = _GPS_SENTENCE_GPGGA; // *** modified
    else
      _sentence_type = _GPS_SENTENCE_OTHER;
    return false;
  }

greenonline: In order to accept BOTH GPS and GLONASS mesages, I think that the two fixes to TinyGPS.cpp are as follows:

#include "TinyGPS.h"

define _GPRMC_TERM  "GPRMC"

define _GPGGA_TERM  "GPGGA"

define _GNRMC_TERM  "GNRMC" // *** added

define _GNGGA_TERM  "GNGGA" // *** added

TinyGPS::TinyGPS()




and



  // the first term determines the sentence type   if (_term_number == 0)   {     if (!gpsstrcmp(_term, _GPRMC_TERM) || !gpsstrcmp(_term, _GNRMC_TERM))      //modified       _sentence_type = _GPS_SENTENCE_GPRMC; // *** modified     else if (!gpsstrcmp(_term, _GPGGA_TERM) || !gpsstrcmp(_term, _GNGGA_TERM)) //modified       _sentence_type = _GPS_SENTENCE_GPGGA; // *** modified     else       _sentence_type = _GPS_SENTENCE_OTHER;     return false;   } ```

I try not work bro