uBlox Library

brtaylor

Well-known member
For an introduction of me, you can find my recent post.

I'd like to announce availability of a library for uBlox 7 and 8 series receivers. This library uses the UBX protocol and the UBX-NAV-PVT packet. You might ask, "Why UBX when plenty of NMEA parsers are available?". The short answer is that UBX is an efficient, binary protocol and the UBX-NAV-PVT packet provides much more data than is available via NMEA. The additional data include North, East, and Down (NED) velocities; estimates of accuracy for horizontal and vertical position and velocity; and position dilution of precision (pDOP). I hope this library is useful to others and I'd love any feedback or contributions.

Best,
Brian
 
I'm interested in using a GPS receiver to provide an frequency reference at an arbitrary RF frequency in the range of 7 to 22 MHz. I know some of the uBlox products can do this, but I'm not sure if the series 7 & 8 have that capability. Do you plan on supporting this function?

Thanks,
Michael
 
Hi Michael, it looks like a couple of the uBlox 7 and 8 series variants would support a pulse output. Are you looking to configure the pulse pin or to receive and parse a packet of the timing data? Right now the library is setup to only receive and parse the UBX packets with configuration done through the uBlox u-center software. I would like to add the ability to configure receivers through the library, but that is lower priority for me at the moment.

Brian
 
Brian,

I'm interested in using a uBlox as the local oscillator for a very stable amateur radio transceiver. This would require configuring it to output an arbitrary and changeable frequency in the amateur bands. I already use the 1pps output for a similar function, but having a GPS chip output the target frequency directly is more desirable.

Your library looks very useful. I hope you'll consider this frequency configuration function down the line.

Best regards,
Michael
 
Hi Michael,

Thanks for the additional information. That sounds really cool! I'll definitely keep your use case in mind when I get to adding configuration messages to the library.

Brian
 
Very nice!
Created something like this myself for a Windsurf GPS project.
For high speed logging we also use NAV-SAT and NAV-DOP
 
Awesome! Are you coupling the pseudo ranges with an IMU (i.e. using an extended Kalman filter to get position, velocity, and orientation) or just computing location from the pseudo ranges?

Our use case has been coupling the GPS data (position and speed at a rate of 1-10 Hz) with IMU data (linear acceleration and rotational velocity at a rate of 50 - 150 Hz) using a loosely coupled EKF to get the inertial position, velocity, and orientation. The advantage is that you get the full inertial solution at the rate that you run the EKF at, in this case 50 to 150 Hz, but its drift is limited by the lower rate GPS data. Some grad students have been working on using pseudo ranges to more tightly couple the EKF and potentially add RTK with a second GPS receiver. Can't wait to get the Teensy 3.5/3.6 to try running the EKF code on the Teensy! I tried this with the Teensy 3.2, but could only manage to compute the solutions at a rate of 20 Hz or so...
 
I'm just computing speed and distance
Here is my project : https://forum.pjrc.com/threads/36306-Project-GPS-Teensy-3-2-High-Speed-GPS-logger

I tried your code and it is really slow in my use case...
Searched on the forum and found this : https://forum.pjrc.com/threads/3111...ion-floating-point-constants?highlight=double

I prefer the double you are using instead of my single float, as i think the double offers a better accuracy when using lat/lon.
But when driving around my hometown the 2 units I use for testing are not usable.
It updates speed, but to slow, and distance is very erratic...

When I use single float, the display can update 2 times a second without to much issue.

Is the double really that slow or am I using it in the wrong way?
 
I don't see a large computational difference in my uBlox library between double and float. The UBX-NAV-PVT packet is sent as longs for latitude, longitude, height, velocities, etc. Within the library, I take those longs and scale by 1E-7 (latitude and longitude) or 1E-3 (height and velocity). Using micros() to do some timing, I see about a 10 microsecond difference between using doubles and floats for computing latitude, longitude, height, and velocities.

Incidentally, the difference in precision between using a float and a double in latitude / longitude would result in about a 2 foot error.
 
Brian,

Are there any other settings that need to changed on the NEO-7M in U-Center?
Do all other Serial1 messages need to be turned off other than UBX-NAV-PVT?

I am using a UBLOX NEO-7M on Teensy 3.1 Serial1 (RX=Pin 0, TX=Pin 1)

I've turned on the UBX output and set the UART1 Baudrate to 115200.
I have set these in the EEPROM so it powers on correctly.

If I use my Teensy 3.1 as a serial pass through SERIAL1 <-> USB_Serial, I am able to see the UBX-NAV-PVT messages update from within U-Command.
However, when I run the uBlox library (set to SERIAL1@115200) I do not get any output from the uBloxData routine.

Here is my SERIAL1 <-> USB_SERIAL Passthrough code:
Code:
int BAUD = 115200;

void setup() {
 
    Serial.begin(BAUD); 
    Serial1.begin(BAUD);
    delay(1000);
    Serial.print("Serial <=> Serial1 Passthru Ready at ");
    Serial.print(BAUD);
    Serial.println(" BAUD");
}

void loop() {

    // Send bytes from UBLOX -> Teensy to Computer
    if ( Serial1.available() ) {
         Serial.write( Serial1.read() );       
    }

    // Send bytes from Computer -> Teensy back to UBLOX
    if ( Serial.available() ) {
        Serial1.write( Serial.read() ); 
    }
}

Here is a screencapture of the UBX-NAV-PVT messages window from U-Center
UBX_Messages.png

I wonder what I am doing wrong?
Thanks -- Wozzy
 
Last edited:
Here's a little more information:
This is a chunk of output of the U-Control packet console:
Code:
21:56:23  R -> UBX 03-09,  Size 128,  'Unknown'
21:56:23  R -> UBX 03-0A,  Size 1304,  'Unknown'
21:56:25  R -> UBX NAV-SOL,  Size  60,  'Navigation Solution'
21:56:24  R -> [B][COLOR="#FF0000"]UBX NAV-PVT[/COLOR][/B],  Size  92,  'Navigation PVT Solution'
21:56:24  R -> UBX NAV-SVINFO,  Size 244,  'Satellite Status and Information'
21:56:24  R -> UBX NAV-CLOCK,  Size  28,  'Clock Status'
21:56:24  R -> UBX NAV-AOPSTATUS,  Size  28,  'AOP Status'
21:56:24  R -> UBX 0C-31,  Size 640,  'Unknown'
21:56:24  R -> UBX 0C-10,  Size 652,  'Unknown'
21:56:24  R -> UBX MON,  Size 395,  'Monitor'
21:56:24  R -> UBX MON-MSGPP,  Size 128,  'MSGPP Message Parse & Process'
21:56:24  R -> UBX MON-IO,  Size 128,  'IO System'
21:56:24  R -> UBX MON,  Size 336,  'Monitor'
21:56:24  R -> UBX 03-09,  Size 128,  'Unknown'
21:56:24  R -> UBX 03-0A,  Size 1304,  'Unknown'
21:56:26  R -> UBX NAV-SOL,  Size  60,  'Navigation Solution'
21:56:25  R -> UBX NAV-PVT,  Size  92,  'Navigation PVT Solution'
21:56:25  R -> UBX NAV-SVINFO,  Size 244,  'Satellite Status and Information'
21:56:25  R -> UBX NAV-CLOCK,  Size  28,  'Clock Status'
21:56:25  R -> UBX NAV-AOPSTATUS,  Size  28,  'AOP Status'
21:56:25  R -> UBX 0C-31,  Size 640,  'Unknown'
21:56:25  R -> UBX 0C-10,  Size 652,  'Unknown'
21:56:25  R -> UBX 03-0F,  Size  61,  'Unknown'
21:56:25  R -> UBX 03-0F,  Size  61,  'Unknown'

And here is what I believe is the UBX-NAV-PVT Block from the Binary Console:
Code:
          04A0  00 00 00 00 00 00 00 C0 67 FF 00 00 00 00 00 00  .......Àgÿ......
          04B0  00 00 00 00 00 00 00 00 00 11 11 00 00 00 00 02  ................
          04C0  00 00 00 00 FF FF 00 00 00 00 00 00 20 00 05 04  ....ÿÿ...... ...
          04D0  00 03 01 00 00 00 BF 70                          ......¿p.
          
22:00:05  0000  B5 62 01 06 34 00 08 F9 9E 23 07 67 04 00 78 07  µb..4..ù.#.g..x.
          0010  03 DD C7 12 5F 07 A0 CB BE E3 50 29 43 18 17 08  .ÝÇ._.*˾ãP)C...
          0020  00 00 02 00 00 00 F5 FF FF FF 04 00 00 00 1D 00  ......õÿÿÿ......
          0030  00 00 23 02 03 06 84 D3 01 00 A7 18              ..#....Ó..§..
          
22:00:04  0000  [B][COLOR="#FF0000"]B5 62 01 07[/COLOR][/B] 54 00 08 F9 9E 23 E0 07 09 03 16 00  µb..T..ù.#à.....
          0010  04 07 2D 00 00 00 05 67 04 00 03 01 0B 06 77 3A  ..-....g......w:
          0020  12 D3 A2 1B CA 17 21 F2 00 00 FB 78 01 00 6D 16  .Ó¢.Ê.!ò..ûx..m.
          0030  00 00 B9 4D 00 00 D7 FF FF FF F7 FF FF FF 97 FF  ..¹M..×ÿÿÿ÷ÿÿÿ.ÿ
          0040  FF FF 2A 00 00 00 5F 1E 44 01 20 01 00 00 D5 86  ÿÿ*..._.D. ...Õ.
          0050  54 00 23 02 43 18 84 D3 11 00 28 A7              T.#.C..Ó..(§.
          
22:00:04  0000  B5 62 01 30 E0 00 08 F9 9E 23 12 03 00 00 05 02  µb.0à..ù.#......
          0010  0D 04 1D 24 72 00 C9 FE FF FF 00 05 0D 07 21 3B  ...$r.Éþÿÿ....!;
          0020  2C 00 91 FF FF FF 0E 07 04 04 0F 06 2A 00 00 00  ,..ÿÿÿ......*...
          0030  00 00 02 0D 0D 07 24 3C 8C 00 CC FF FF FF 06 0F  ......$<..Ìÿÿÿ..
          0040  0D 07 24 2A C1 00 4C FF FF FF 0F 12 04 01 00 06  ..$*Á.Lÿÿÿ......
          0050  00 01 00 00 00 00 03 14 0D 07 22 36 15 01 A9 00  .........."6..©.
          0060  00 00 0A 15 04 04 12 15 30 01 00 00 00 00 08 19  ........0.......
          0070  0C 01 00 01 EC 00 00 00 00 00 0D 1A 04 01 00 01  ....ì...........
          0080  3D 01 00 00 00 00 01 1D 0D 07 1F 3B 06 01 06 01  =..........;....
          0090  00 00 0C 1E 04 04 15 0A 43 00 00 00 00 00 04 85  ........C.......
          00A0  14 01 00 24 D4 00 00 00 00 00 0B 87 14 01 00 10  ...$Ô...........
          00B0  F8 00 00 00 00 00 07 8A 14 01 00 21 E0 00 00 00  ø..........!à...
          00C0  00 00 10 C1 10 01 00 A5 00 00 00 00 00 00 11 C4  ...Á...¥.......Ä
          00D0  10 01 00 A5 00 00 00 00 00 00 09 C5 10 01 00 A5  ...¥.......Å...¥
          00E0  00 00 00 00 00 00 80 E2                          .......â.
          
22:00:04  0000  B5 62 01 22 14 00 08 F9 9E 23 FA 4E 0E 00 D2 07  µb."...ù.#úN..Ò.
          0010  00 00 2D 00 00 00 84 03 00 00 DC 04              ..-.......Ü..
          
22:00:04  0000  B5 62 01 60 14 00 08 F9 9E 23 00 00 00 00 00 00  µb.`...ù.#......
          0010  00 00 00 00 00 00 00 00 00 00 37 FA              ..........7ú.
          
22:00:04  0000  B5 62 0C 31 78 02 00 03 98 02 0A 58 04 00 0A 08  µb.1x......X....
          0010  05 00 10 38 04 00 98 90 8A 0A 58 90 CA 05 98 91  ...8......X.Ê...
          0020  8A 14 D8 90 0A 02 F8 90 8A 19 48 91 0A 10 10 00  ..Ø...ø...H.....
          0030  00 00 10 00 00 00 18 11 CA 13 10 00 00 00 38 91  ........Ê.....8.
          0040  CA 12 68 90 4A 10 C8 90 4A 14 28 90 CA 05 D8 91  Ê.h.J.È.J.(.Ê.Ø.
 
I pulled a UBLOX NEO 8M out of another project and hooked it to my Teensy 3.6
It is working with the uBlox Library.

I discovered that the UBX NAV-PVT Packet Size = 100 bytes on the UBLOX NEO 8M,
and is equal to 92 bytes on the UBLOX NEO 7M
 
Hi Wozzy!

Thanks so much for digging into this and sorry for the delay (some time in a cabin away from internet and work deadlines, somehow September flew by...). I'll try to see about making this more robust as I have time, probably using the message size field.

Brian
 
Brian, I'm currently running an Adafruit GPS using the neoGPS library, but am thinking of getting a uBlox so I can try out your library and the UBX parsing and velocity measurement capability. I'm guessing I should get a UBLOX NEO 8M, but is there a particular variant you'd recommend? My ultimate goal is to (loosely or tightly) integrate it with an IMU and the Teensy 3.6. I'm testing out your 7-State uNavAHRS right now as well. Thanks! Don
 
Brian, I'm currently running an Adafruit GPS using the neoGPS library, but am thinking of getting a uBlox so I can try out your library and the UBX parsing and velocity measurement capability. I'm guessing I should get a UBLOX NEO 8M, but is there a particular variant you'd recommend? My ultimate goal is to (loosely or tightly) integrate it with an IMU and the Teensy 3.6. I'm testing out your 7-State uNavAHRS right now as well. Thanks! Don

Hi Don,

I'm currently using this one:
https://hobbyking.com/en_us/ublox-micro-m8n-gps-compass-module-1pc.html

But I would recommend buying this:
https://store.mrobotics.io/mRo-GPS-u-Blox-Neo-M8N-HMC5983-Compass-p/mro-gps003-mr.htm

It includes a micro USB, which makes setting up the thing in uCenter much easier, plus it uses JST-GH connectors. The boards that I'm developing also use these connectors and they're quickly becoming standard in the drone and robotics space.

Brian
 
This is just a quick and easy (well): ugly) fix to get this library cooperate with UBLOX NEO 7M and UBLOX NEO 6M.

All you have to do is replace UBLOX.H with my edited UBLOX.H.

Note: this is set up for UBLOX NEO 7M and UBLOX NEO 6M, you can change that in #### Select GPS module ####
UBLOX.H
Code:
/*
UBLOX.h
Brian R Taylor
brian.taylor@bolderflight.com
2016-11-03

Copyright (c) 2016 Bolder Flight Systems

Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
and associated documentation files (the "Software"), to deal in the Software without restriction, 
including without limitation the rights to use, copy, modify, merge, publish, distribute, 
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or 
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// #### Select GPS module ####
// UBX-NAV-PVT (0x01 0x07) MSG. offset by 4 bytes 
//#define UBLOX8 // 96, UBX-NAV-PVT u-blox8 MSG lenght 92 bytes
#define UBLOX7 // 88, UBX-NAV-PVT u-blox6 & 7 MSG lenght 84 bytes

#ifndef UBLOX_h
#define UBLOX_h

#include "Arduino.h"				

struct gpsData {
  unsigned long   iTOW;			  ///< [ms], GPS time of the navigation epoch
  unsigned short  utcYear;		  ///< [year], Year (UTC)
  unsigned char   utcMonth;		  ///< [month], Month, range 1..12 (UTC)
  unsigned char   utcDay;		  ///< [day], Day of month, range 1..31 (UTC)
  unsigned char   utcHour;		  ///< [hour], Hour of day, range 0..23 (UTC)
  unsigned char   utcMin;		  ///< [min], Minute of hour, range 0..59 (UTC)
  unsigned char   utcSec;		  ///< [s], Seconds of minute, range 0..60 (UTC)
  unsigned char   valid;		  ///< [ND], Validity flags
  unsigned long   tAcc;			  ///< [ns], Time accuracy estimate (UTC)
  long            utcNano;		  ///< [ns], Fraction of second, range -1e9 .. 1e9 (UTC)
  unsigned char   fixType;		  ///< [ND], GNSSfix Type: 0: no fix, 1: dead reckoning only, 2: 2D-fix, 3: 3D-fix, 4: GNSS + dead reckoning combined, 5: time only fix
  unsigned char   flags;		  ///< [ND], Fix status flags
  unsigned char   flags2;		  ///< [ND], Additional flags
  unsigned char   numSV;		  ///< [ND], Number of satellites used in Nav Solution
  double          lon;			  ///< [deg], Longitude
  double          lat;			  ///< [deg], Latitude
  double          height;		  ///< [m], Height above ellipsoid 
  double          hMSL;			  ///< [m], Height above mean sea level
  double          hAcc;			  ///< [m], Horizontal accuracy estimate
  double          vAcc;			  ///< [m], Vertical accuracy estimate
  double          velN;			  ///< [m/s], NED north velocity
  double          velE;			  ///< [m/s], NED east velocity
  double          velD;			  ///< [m/s], NED down velocity
  double          gSpeed;		  ///< [m/s], Ground Speed (2-D)
  double          heading;		  ///< [deg], Heading of motion (2-D)
  double          sAcc;			  ///< [m/s], Speed accuracy estimate
  double          headingAcc;	  ///< [deg], Heading accuracy estimate (both motion and vehicle)
  double          pDOP;			  ///< [ND], Position DOP
  double		  headVeh;		  ///< [deg], Heading of vehicle (2-D)   #### NOTE: u-blox8 only ####
};

class UBLOX{
  public:
    UBLOX();
    UBLOX(uint8_t bus);
    void configure(uint8_t bus);
    void begin(int baud);
    bool read(gpsData *gpsData_ptr);
  private:
  	uint8_t _bus;
  	uint8_t _fpos;
	
	// #### Select GPS module ####
	#if defined UBLOX8
  	static const uint8_t _payloadSize = 96; //  UBX-NAV-PVT (0x01 0x07) offset by 4 bytes, u-blox8 msg lenght 92 bytes, u-blox6 and 7 msg lenght 84 bytes
	#endif
	#if defined UBLOX7 // and UBLOX6
	static const uint8_t _payloadSize = 88;
	#endif
	
  	uint8_t _gpsPayload[_payloadSize];
  	HardwareSerial* _port;
	bool parse();
	void calcChecksum(unsigned char* CK, unsigned char* payload, uint8_t length);
};

#endif
 
Update #2
So I was messing with this for 2 days and i managed to:
Fix the checksum calculations, now it's based on the received message payload.
This fixes the compatibility issues between NEO-M8 and NEO-M7-6 GPS series.

Changes:
Identify the packet Class in receive message
Identify the packet Message ID in receive message
Identify the packet 16Bit Message payloadSize

uBlox now supports:
UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution, Msg Length[28]
UBX-NAV-PVT (0x01 0x07) Navigation Position Velocity Time Solution, Msg Length U-Blox7=[84], U-Blox8=[92]

Work in progress
# high level commands, for the user #
No need to use U‑center to change GPS Port Configuration
CFG-PRT (0x06 0x00) Get/Set Port Configuration for UART
SetGPSbaud460800(); //Set UBLOX GPS Port Configuration to 460800Baud
SetGPSbaud9600(); //Set UBLOX GPS Port Configuration to 9600Baud

UBLOX.cpp
Code:
/*
  UBLOX.cpp
  Brian R Taylor
  brian.taylor@bolderflight.com
  2016-11-03

  Copyright (c) 2016 Bolder Flight Systems

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  and associated documentation files (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge, publish, distribute,
  sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all copies or
  substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  v1.0.1
  Chris O.
  2018-02-26 Changes:
  Identify the packet Class in receive message
  Identify the packet Message ID in receive message
  Identify the packet 16Bit Message payloadSize
  Fixed the checksum calculations, now it's based on*the*received message payload.
  This fixes the compatibility issues between NEO-M8 and NEO-M7-6 GPS series.

  Addition:
  UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution
    high level commands, for the user
  CFG-PRT (0x06 0x00) Get/Set Port Configuration for UART
  SetGPSbaud460800(); //Set UBLOX GPS Port Configuration to 460800Baud
  SetGPSbaud9600(); //Set UBLOX GPS Port Configuration to 9600Baud
*/

// Teensy 3.0 || Teensy 3.1/3.2 || Teensy 3.5 || Teensy 3.6 || Teensy LC
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || \
  defined(__MK66FX1M0__) || defined(__MKL26Z64__)

#include "Arduino.h"
#include "UBLOX.h"

/* default constructor */
UBLOX::UBLOX() {}

/* uBlox object, input the serial bus */
UBLOX::UBLOX(uint8_t bus) {
  _bus = bus; // serial bus
}

void UBLOX::configure(uint8_t bus) {
  _bus = bus; // serial bus
}

/* starts the serial communication */
void UBLOX::begin(int baud) {

  // initialize parsing state
  _fpos = 0;

  // select the serial port
#if defined(__MK20DX128__) || defined(__MK20DX256__) ||  defined(__MKL26Z64__) // Teensy 3.0 || Teensy 3.1/3.2 || Teensy LC

  if (_bus == 3) {
    _port = &Serial3;
  }
  else if (_bus == 2) {
    _port = &Serial2;
  }
  else {
    _port = &Serial1;
  }

#endif

#if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Teensy 3.5 || Teensy 3.6

  if (_bus == 6) {
    _port = &Serial6;
  }
  else if (_bus == 5) {
    _port = &Serial5;
  }
  else if (_bus == 4) {
    _port = &Serial4;
  }
  else if (_bus == 3) {
    _port = &Serial3;
  }
  else if (_bus == 2) {
    _port = &Serial2;
  }
  else {
    _port = &Serial1;
  }

#endif

  // begin the serial port for uBlox
  _port->begin(baud);
}

/* write the uBlox data */
/******************************************/
/**  high level commands, for the user    */
/******************************************/
void UBLOX::SetGPSbaud460800()
{
  _port->write(SET460800B_CFG_PRT, sizeof(SET460800B_CFG_PRT));
}
void UBLOX::SetGPSbaud9600()
{
  _port->write(SET9600B_CFG_PRT, sizeof(SET9600B_CFG_PRT));
}

/* read the uBlox data */
bool UBLOX::read(gpsData *gpsData_ptr) {

  const double mm2m = 1.0e-3;
  const double en7 = 1.0e-7;
  const double en5 = 1.0e-5;
  const double en2 = 1.0e-2;

  union {
    unsigned long val;
    uint8_t b[4];
  } iTOW;

  union {
    unsigned short val;
    uint8_t b[2];
  } utcYear;

  union {
    unsigned long val;
    uint8_t b[4];
  } tAcc;

  union {
    long val;
    uint8_t b[4];
  } utcNano;

  union {
    long val; // I4
    uint8_t b[4];
  } lon;

  union {
    long val;
    uint8_t b[4];
  } lat;

  union {
    long val;
    uint8_t b[4];
  } height;

  union {
    long val;
    uint8_t b[4];
  } hMSL;

  union {
    unsigned long val;
    uint8_t b[4];
  } hAcc;

  union {
    unsigned long val;
    uint8_t b[4];
  } vAcc;

  union {
    long val;
    uint8_t b[4];
  } velN;

  union {
    long val;
    uint8_t b[4];
  } velE;

  union {
    long val;
    uint8_t b[4];
  } velD;

  union {
    long val;
    uint8_t b[4];
  } gSpeed;

  union {
    long val;
    uint8_t b[4];
  } heading;

  union {
    unsigned long val;
    uint8_t b[4];
  } sAcc;

  union {
    unsigned long val;
    uint8_t b[4];
  } headingAcc;

  union {
    unsigned short val;
    uint8_t b[2];
  } pDOP;

  union {
    unsigned long val;
    uint8_t b[4];
  } headVeh;

  union {
    I2 val;
    uint8_t b[2];
  } magDec;

  union {
    U2 val;
    uint8_t b[2];
  } magAcc;

  // parse the uBlox packet
  if (parse()) {

    // UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution
    if (_UBX_NAV_POSLLH_ID == true) {
      iTOW.b[0] = _gpsPayload[4]; // GPS time of week of the navigation epoch.
      iTOW.b[1] = _gpsPayload[5];
      iTOW.b[2] = _gpsPayload[6];
      iTOW.b[3] = _gpsPayload[7];
      gpsData_ptr->iTOW = iTOW.val;

      lon.b[0] = _gpsPayload[8]; // Longitude
      lon.b[1] = _gpsPayload[9];
      lon.b[2] = _gpsPayload[10];
      lon.b[3] = _gpsPayload[11];
      gpsData_ptr->lon = lon.val * en7;

      lat.b[0] = _gpsPayload[12]; // Latitude
      lat.b[1] = _gpsPayload[13];
      lat.b[2] = _gpsPayload[14];
      lat.b[3] = _gpsPayload[15];
      gpsData_ptr->lat = lat.val * en7;

      height.b[0] = _gpsPayload[16]; // Height above ellipsoid
      height.b[1] = _gpsPayload[17];
      height.b[2] = _gpsPayload[18];
      height.b[3] = _gpsPayload[19];
      gpsData_ptr->height = height.val * mm2m;

      hMSL.b[0] = _gpsPayload[20]; // Height above mean sea level
      hMSL.b[1] = _gpsPayload[21];
      hMSL.b[2] = _gpsPayload[22];
      hMSL.b[3] = _gpsPayload[23];
      gpsData_ptr->hMSL = hMSL.val * mm2m;

      hAcc.b[0] = _gpsPayload[24]; // Horizontal accuracy estimate
      hAcc.b[1] = _gpsPayload[25];
      hAcc.b[2] = _gpsPayload[26];
      hAcc.b[3] = _gpsPayload[27];
      gpsData_ptr->hAcc = hAcc.val * mm2m;
      _UBX_NAV_POSLLH_ID = false;
    }

    // UBX-NAV-PVT (0x01 0x07) Navigation Position Velocity Time Solution
    if (_UBX_NAV_PVT_ID == true) {
      iTOW.b[0] = _gpsPayload[4];
      iTOW.b[1] = _gpsPayload[5];
      iTOW.b[2] = _gpsPayload[6];
      iTOW.b[3] = _gpsPayload[7];
      gpsData_ptr->iTOW = iTOW.val;

      utcYear.b[0] = _gpsPayload[8];
      utcYear.b[1] = _gpsPayload[9];
      gpsData_ptr->utcYear = utcYear.val;

      gpsData_ptr->utcMonth = _gpsPayload[10];
      gpsData_ptr->utcDay = _gpsPayload[11];
      gpsData_ptr->utcHour = _gpsPayload[12];
      gpsData_ptr->utcMin = _gpsPayload[13];
      gpsData_ptr->utcSec = _gpsPayload[14];
      gpsData_ptr->valid = _gpsPayload[15];

      tAcc.b[0] = _gpsPayload[16];
      tAcc.b[1] = _gpsPayload[17];
      tAcc.b[2] = _gpsPayload[18];
      tAcc.b[3] = _gpsPayload[19];
      gpsData_ptr->tAcc = tAcc.val;

      utcNano.b[0] = _gpsPayload[20];
      utcNano.b[1] = _gpsPayload[21];
      utcNano.b[2] = _gpsPayload[22];
      utcNano.b[3] = _gpsPayload[23];
      gpsData_ptr->utcNano = utcNano.val;

      gpsData_ptr->fixType = _gpsPayload[24];
      gpsData_ptr->flags = _gpsPayload[25];
      gpsData_ptr->flags2 = _gpsPayload[26];
      gpsData_ptr->numSV = _gpsPayload[27];

      lon.b[0] = _gpsPayload[28];
      lon.b[1] = _gpsPayload[29];
      lon.b[2] = _gpsPayload[30];
      lon.b[3] = _gpsPayload[31];
      gpsData_ptr->lon = lon.val * en7;

      lat.b[0] = _gpsPayload[32];
      lat.b[1] = _gpsPayload[33];
      lat.b[2] = _gpsPayload[34];
      lat.b[3] = _gpsPayload[35];
      gpsData_ptr->lat = lat.val * en7;

      height.b[0] = _gpsPayload[36];
      height.b[1] = _gpsPayload[37];
      height.b[2] = _gpsPayload[38];
      height.b[3] = _gpsPayload[39];
      gpsData_ptr->height = height.val * mm2m;

      hMSL.b[0] = _gpsPayload[40];
      hMSL.b[1] = _gpsPayload[41];
      hMSL.b[2] = _gpsPayload[42];
      hMSL.b[3] = _gpsPayload[43];
      gpsData_ptr->hMSL = hMSL.val * mm2m;

      hAcc.b[0] = _gpsPayload[44];
      hAcc.b[1] = _gpsPayload[45];
      hAcc.b[2] = _gpsPayload[46];
      hAcc.b[3] = _gpsPayload[47];
      gpsData_ptr->hAcc = hAcc.val * mm2m;

      vAcc.b[0] = _gpsPayload[48];
      vAcc.b[1] = _gpsPayload[49];
      vAcc.b[2] = _gpsPayload[50];
      vAcc.b[3] = _gpsPayload[51];
      gpsData_ptr->vAcc = vAcc.val * mm2m;

      velN.b[0] = _gpsPayload[52];
      velN.b[1] = _gpsPayload[53];
      velN.b[2] = _gpsPayload[54];
      velN.b[3] = _gpsPayload[55];
      gpsData_ptr->velN = velN.val * mm2m;

      velE.b[0] = _gpsPayload[56];
      velE.b[1] = _gpsPayload[57];
      velE.b[2] = _gpsPayload[58];
      velE.b[3] = _gpsPayload[59];
      gpsData_ptr->velE = velE.val * mm2m;

      velD.b[0] = _gpsPayload[60];
      velD.b[1] = _gpsPayload[61];
      velD.b[2] = _gpsPayload[62];
      velD.b[3] = _gpsPayload[63];
      gpsData_ptr->velD = velD.val * mm2m;

      gSpeed.b[0] = _gpsPayload[64];
      gSpeed.b[1] = _gpsPayload[65];
      gSpeed.b[2] = _gpsPayload[66];
      gSpeed.b[3] = _gpsPayload[67];
      gpsData_ptr->gSpeed = gSpeed.val * mm2m;

      heading.b[0] = _gpsPayload[68];
      heading.b[1] = _gpsPayload[69];
      heading.b[2] = _gpsPayload[70];
      heading.b[3] = _gpsPayload[71];
      gpsData_ptr->heading = heading.val * en5;

      sAcc.b[0] = _gpsPayload[72];
      sAcc.b[1] = _gpsPayload[73];
      sAcc.b[2] = _gpsPayload[74];
      sAcc.b[3] = _gpsPayload[75];
      gpsData_ptr->sAcc = sAcc.val  * mm2m;

      headingAcc.b[0] = _gpsPayload[76];
      headingAcc.b[1] = _gpsPayload[77];
      headingAcc.b[2] = _gpsPayload[78];
      headingAcc.b[3] = _gpsPayload[79];
      gpsData_ptr->headingAcc = headingAcc.val * en5;

      pDOP.b[0] = _gpsPayload[80];
      pDOP.b[1] = _gpsPayload[81];
      gpsData_ptr->pDOP = pDOP.val * 0.01L;

      headVeh.b[0] = _gpsPayload[88];
      headVeh.b[1] = _gpsPayload[89];
      headVeh.b[2] = _gpsPayload[90];
      headVeh.b[3] = _gpsPayload[91];
      gpsData_ptr->headVeh = headVeh.val * en5;

      // Edit - 2018 2 25 magDec, magAcc
      magDec.b[0] = _gpsPayload[92];
      magDec.b[0] = _gpsPayload[93];
      gpsData_ptr->magDec = magDec.val * en2;

      magAcc.b[0] = _gpsPayload[94];
      magAcc.b[0] = _gpsPayload[95];
      gpsData_ptr->magDec = magAcc.val * en2;
      _UBX_NAV_PVT_ID = false;
    }
    // return true on receiving a full packet
    return true;
  }
  else {
    // return false if a full packet is not received
    return false;
  }
}

/* parse the uBlox data */
bool UBLOX::parse() {
  // uBlox UBX header definition
  uint8_t const UBX_HEADER[] = { 0xB5, 0x62 };

  // uBlox Message   (Class, ID)    # Description #
  // UBX-NAV-PVT     (0x01 0x07)    Navigation Position Velocity Time Solution
  // UBX-NAV-POSLLH  (0x01 0x02)    Geodetic Position Solution

  // checksum calculation
  static unsigned char checksum[2];

  // read a byte from the serial port
  while ( _port->available() ) {
    uint8_t c = _port->read();

    // identify the packet header
    if ( _fpos < 2 ) {
      if ( c == UBX_HEADER[_fpos] ) {
        // Serial.print("UBX_HEADER :"), Serial.println(c, HEX); // USB debug print
        _fpos++;
      }
      else {
        _fpos = 0;
      }
    }
    // Identify the packet Class
    else if (_fpos == 2) {
      switch (c) {    // ### Name Class Description ###
        case 0x01:    // NAV 0x01 Navigation Results: Position, Speed, Time, Acc, Heading, DOP, SVs used
          _CurrentClass = c; // Save for Message ID.
          ((unsigned char*)_gpsPayload)[_fpos - 2] = c; // grab the payload, need for checksum
          // Serial.print("UBX_NAV_CLASS:0x0"), Serial.println(_CurrentClass, HEX); // USB debug print
          _fpos++;
          break;
        case 0x02:    // RXM 0x02 Receiver Manager Messages: Satellite Status, RTC Status
          _CurrentClass = c;
          Serial.print("UBX_RXM_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _fpos = 0;
          break;
        case 0x04:    // INF 0x04 Information Messages: Printf-Style Messages, with IDs such as Error, Warning, Notice
          _CurrentClass = c;
          Serial.print("UBX_INF_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _fpos = 0;
          break;
        case 0x05:    // ACK 0x05 Ack/Nack Messages: as replies to CFG Input Messages
          //Serial.print("UBX_ACK_CLASS:0x0"), Serial.print(c, HEX), Serial.println(); // USB debug print
          _CurrentClass = c;
          ((unsigned char*)_gpsPayload)[_fpos - 2] = c; // grab the payload, need for checksum
          _fpos++;
          break;
        case 0x06:    // CFG 0x06 Configuration Input Messages: Set Dynamic Model, Set DOP Mask, Set Baud Rate, etc.
          Serial.print("UBX_CFG_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _CurrentClass = c;
          _fpos = 0;
          break;
        case 0x0A:    // MON 0x0A Monitoring Messages: Comunication Status, CPU Load, Stack Usage, Task Status
          Serial.print("UBX_MON_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _CurrentClass = c;
          _fpos = 0;
          break;
        case 0x0B:    // AID 0x0B AssistNow Aiding Messages: Ephemeris, Almanac, other A-GPS data input
          Serial.print("UBX_AID_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _CurrentClass = c;
          _fpos = 0;
          break;
        case 0x0D:    // TIM 0x0D Timing Messages: Time Pulse Output, Timemark Results
          Serial.print("UBX_TIM_CLASS:0x0"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _CurrentClass = c;
          _fpos = 0;
          break;
        case 0x21:    // LOG 0x21 Logging Messages: Log creation, deletion, info and retrieval
          Serial.print("UBX_LOG_CLASS:0x"), Serial.print(c, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
          _CurrentClass = c;
          _fpos = 0;
          break;
        default:
          // 0x03 and 0x0E to 0x20 Not Supported
          _CurrentClass = c;
          _fpos = 0;
          Serial.print("Unknown packet Class:");
          if (c > 16) {
            Serial.println("0x");
          } else {
            Serial.println("0x0");
          }
          Serial.println(c, HEX), Serial.println(); // USB debug print
          break;
      }
    }
    // Identify the packet Message ID
    else if (_fpos == 3 ) {
      switch (_CurrentClass) {
        case 0x01:    // NAV 0x01
          NAV_IDs(c); // Go grab the uBlox NAV - ID
          break;
        case 0x02:    // RXM 0x02
          _fpos = 0;
          break;
        case 0x04:    // INF 0x04
          _fpos = 0;
          break;
        case 0x05:    // ACK 0x05
          ACK_IDs(c); // Go grab the uBlox ACK - ID
          break;
        case 0x06:    // CFG 0x06
          _fpos = 0;
          break;
        case 0x0A:    // MON 0x0A
          _fpos = 0;
          break;
        case 0x0B:    // AID 0x0B
          _fpos = 0;
          break;
        case 0x0D:    // TIM 0x0D
          _fpos = 0;
          break;
        case 0x21:    // LOG 0x21
          _fpos = 0;
          break;
        default:
          // 0x03 and 0x0E to 0x20 Not Supported
          _fpos = 0;
          Serial.println("Unknown packet ID:"), Serial.println(c, HEX), Serial.println(); // USB debug print
          break;
      }
      _CurrentClass = 0x00;
    }
    // Identify the packet 16Bit Message payloadSize
    // It does not include 16Bit-Sync header, 8Bit-Class, 8Bit-ID, 16Bit-Length Field, and 16Bit-CRC fields
    else if (_fpos == 4 ) {
      ((unsigned char*)_gpsPayload)[_fpos - 2] = c; // least significant byte (LSB)
      _fpos++;
    }
    else if (_fpos == 5 ) {
      ((unsigned char*)_gpsPayload)[_fpos - 2] = c; // most significant byte (MSB)
      _MSGpayloadSize = (_gpsPayload[3] << 8) | _gpsPayload[2]; // Combining two uint8_t as uint16_t
      // Serial.print("_MSGpayloadSize DEC:"), Serial.println(_MSGpayloadSize, DEC); // USB debug print
      _MSGpayloadSize = _MSGpayloadSize + 4; // Fix for (CRC), Add 8Bit-Class, 8Bit-ID, 16Bit-Length Field
      _fpos++;
    }
    else {
      // grab the payload
      if ( (_fpos - 2) < _MSGpayloadSize )
        ((unsigned char*)_gpsPayload)[_fpos - 2] = c;
      _fpos++;

      // The checksum is calculated over the packet, starting and including the
      // CLASS field, ID and payload, up until, but excluding, the Checksum Field
      // (CRC) Compute checksum
      if ( (_fpos - 2) == _MSGpayloadSize ) {
        calcChecksum(checksum, _gpsPayload, _MSGpayloadSize);
      }
      else if ( (_fpos - 2) == (_MSGpayloadSize + 1) ) {
        if ( c != checksum[0] )
          _fpos = 0;
      }
      else if ( (_fpos - 2) == (_MSGpayloadSize + 2) ) {
        _fpos = 0;
        if ( c == checksum[1] ) {
          return true;
        }
      }
      else if ( _fpos > (_MSGpayloadSize + 4) ) {
        _fpos = 0;
      }
    }
  }
  return false;
}

/* uBlox checksum */
void UBLOX::calcChecksum(unsigned char* CK, unsigned char* payload, uint8_t length) {
  CK[0] = 0;
  CK[1] = 0;
  for (uint8_t i = 0; i < length; i++) {
    CK[0] += payload[i]; // CK[0] = CK[0] + payload[i];
    CK[1] += CK[0];
  }
}

/* uBlox ACK - IDs */
void UBLOX::ACK_IDs(uint8_t SERc) {
  switch (SERc) {    // ### Name ACK-Class & IDs Description, Msg Length ###
    case 0x00:          //  UBX-ACK-NAK (0x05 0x00) Message Not-Acknowledged, len[2]
      Serial.print("UBX_ACK_NAK ID:"), Serial.print(SERc, HEX), Serial.println(" Message Not-Acknowledged"), Serial.println(); // USB debug print
      //_UBX_ACK_NAK_ID = true; //TODO
      ((unsigned char*)_gpsPayload)[_fpos - 2] = SERc; // grab the payload, need for checksum
      _fpos++;
      break;
    case 0x01:          //  UBX-ACK-ACK (0x05 0x01) Message Acknowledged, len[2]
      Serial.print("UBX_ACK_ACK ID:"), Serial.print(SERc, HEX), Serial.println(" Message Acknowledged"), Serial.println(); // USB debug print
      //_UBX_ACK_ACK_ID = true; //TODO
      ((unsigned char*)_gpsPayload)[_fpos - 2] = SERc; // grab the payload, need for checksum
      _fpos++;
      break;
    default:
      // Not Supported
      _fpos = 0;
      Serial.println("Unknown packet Class ACK ID:"), Serial.println(SERc, HEX), Serial.println(); // USB debug print
      break;
  }
}

/* uBlox NAV - IDs */
void UBLOX::NAV_IDs(uint8_t SERIALc) {

  switch (SERIALc) {    // ### Name NAV-Class & IDs Description, Msg Length ###
    case 0x01:          // NAV-POSECEF 0x01 0x01 Position Solution in ECEF, U-Blox7 = len[20]
      Serial.print("UBX_NAV_POSECEF ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x02:          // NAV-POSLLH 0x01 0x02 Geodetic Position Solution, U-Blox7 = len[28]
      // Serial.print("UBX_NAV_POSLLH ID:"), Serial.println(SERIALc, HEX);; // USB print
      _UBX_NAV_POSLLH_ID = true;
      ((unsigned char*)_gpsPayload)[_fpos - 2] = SERIALc; // grab the payload, need for checksum
      _fpos++;
      break;
    case 0x03:          // NAV-STATUS 0x01 0x03 Receiver Navigation Status, U-Blox7 = len[16]
      Serial.print("UBX_NAV_STATUS ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x04:         // NAV-DOP 0x01 0x04 Dilution of precision, U-Blox7 = len[18]
      Serial.print("UBX_NAV_DOP ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x06:         // NAV-SOL 0x01 0x06 Navigation Solution Information, U-Blox7 = len[52]
      Serial.print("UBX_NAV_SOL ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x07:        // NAV-PVT 0x01 0x07 Navigation Position Velocity Time Solution, U-Blox7=len[84], U-Blox8=len[92]
      // Serial.print("UBX_NAV_PVT ID:"), Serial.println(SERIALc, HEX);; // USB debug print
      _UBX_NAV_PVT_ID = true;
      ((unsigned char*)_gpsPayload)[_fpos - 2] = SERIALc; // grab the payload, need for checksum
      _fpos++;
      break;
    case 0x11:        // NAV-VELECEF 0x01 0x11 Velocity Solution in ECEF, U-Blox7 = len[20]
      Serial.print("UBX_NAV_VELECEF ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x12:        // NAV-VELNED 0x01 0x12 Velocity Solution in NED, U-Blox7 = len[36]
      Serial.print("UBX_NAV_VELNED ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x20:        // NAV-TIMEGPS 0x01 0x20 16 Periodic/Polled GPS Time Solution, U-Blox7 = len[16]
      Serial.print("UBX_NAV_TIMEGPS ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x21:        // NAV-TIMEUTC 0x01 0x21 UTC Time Solution, U-Blox7 = len[20]
      Serial.print("UBX_NAV_TIMEUTC ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x22:       // NAV-CLOCK 0x01 0x22 Clock Solution, U-Blox7 = len[20]
      Serial.print("UBX_NAV_CLOCK ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x30:       // NAV-SVINFO U-Blox7 = len[8 + 12*numCh]
      Serial.print("UBX_NAV_SVINFO ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x31:       // NAV-DGPS 0x01 0x31 DGPS Data Used for NAV, U-Blox7 = len[16 + 12*numCh]
      Serial.print("UBX_NAV_DGPS ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x32:       // NAV-SBAS 0x01 0x32 12 + 12*cnt Periodic/Polled SBAS Status Data
      Serial.print("UBX_NAV_SBAS ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    case 0x60:       // NAV-AOPSTATUS 0x01 0x60 AssistNow Autonomous Status, U-Blox7 = len[20]
      Serial.print("UBX_NAV_AOPSTATUS ID:"), Serial.print(SERIALc, HEX), Serial.println(" Not implemented"), Serial.println(); // USB debug print
      _fpos = 0;
      break;
    default:
      // Not Supported
      _fpos = 0;
      Serial.println("Unknown packet Class NAV ID:"), Serial.println(SERIALc, HEX), Serial.println(); // USB debug print
      break;
  }
}
#endif

UBLOX.h
Code:
/*
  UBLOX.h
  Brian R Taylor
  brian.taylor@bolderflight.com
  2016-11-03

  Copyright (c) 2016 Bolder Flight Systems

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  and associated documentation files (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge, publish, distribute,
  sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all copies or
  substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  v1.0.1
  Chris O.
  2018-02-26 Changes:
  Identify the packet Class in receive message
  Identify the packet Message ID in receive message
  Identify the packet 16Bit Message payloadSize
  Fixed the checksum calculations, now it's based on*the*received message payload.
  This fixes the compatibility issues between NEO-M8 and NEO-M7-6 GPS series.

  Addition:
  UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution
   high level commands, for the user
  CFG-PRT (0x06 0x00) Get/Set Port Configuration for UART
  SetGPSbaud460800(); //Set UBLOX GPS Port Configuration to 460800Baud
  SetGPSbaud9600(); //Set UBLOX GPS Port Configuration to 9600Baud
*/

// NOTE: U-blox8, U-blox6 and 7
// UBX-NAV-PVT (0x01 0x07) offset by 4 bytes, U-blox8 msg lenght 92 bytes, U-blox6 and 7 msg lenght 84 bytes

//   ### UBX Protocol ###
// Class NAV 0x01, ID 0x02, Message Naming
// UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution
// iTOW;                      ///< [ms], GPS time of the navigation epoch
// lon;                          ///< [deg], Longitude
// lat;                          ///< [deg], Latitude
// height;                    ///< [m], Height above ellipsoid
// hMSL;                    ///< [m], Height above mean sea level
// hAcc;                      ///< [m], Horizontal accuracy estimate

// Class NAV 0x01, ID 0x07
// UBX-NAV-PVT (0x01 0x07) Navigation Position Velocity Time Solution
// iTOW;                ///< [ms], GPS time of the navigation epoch
// utcYear;             ///< [year], Year (UTC)
// utcMonth;          ///< [month], Month, range 1..12 (UTC)
// utcDay;              ///< [day], Day of month, range 1..31 (UTC)
// utcHour;            ///< [hour], Hour of day, range 0..23 (UTC)
// utcMin;              ///< [min], Minute of hour, range 0..59 (UTC)
// utcSec;             ///< [s], Seconds of minute, range 0..60 (UTC)
// valid;                 ///< [ND], Validity flags
// tAcc;                 ///< [ns], Time accuracy estimate (UTC)
// utcNano;           ///< [ns], Fraction of second, range -1e9 .. 1e9 (UTC)
// fixType;             ///< [ND], GNSSfix Type: 0: no fix, 1: dead reckoning only, 2: 2D-fix, 3: 3D-fix, 4: GNSS + dead reckoning combined, 5: time only fix
// flags;                 ///< [ND], Fix status flags
// flags2;               ///< [ND], Additional flags
// numSV;             ///< [ND], Number of satellites used in Nav Solution
// lon;                    ///< [deg], Longitude
// lat;                     ///< [deg], Latitude
// height;               ///< [m], Height above ellipsoid
// hMSL;               ///< [m], Height above mean sea level
// hAcc;                ///< [m], Horizontal accuracy estimate
// vAcc;                ///< [m], Vertical accuracy estimate
// velN;                 ///< [m/s], NED north velocity
// velE;                 ///< [m/s], NED east velocity
// velD;                 ///< [m/s], NED down velocity
// gSpeed;                  ///< [m/s], Ground Speed (2-D)
// heading;                 ///< [deg], Heading of motion (2-D)
// sAcc;                ///< [m/s], Speed accuracy estimate
// headingAcc;                ///< [deg], Heading accuracy estimate (both motion and vehicle)
// pDOP;                ///< [ND], Position DOP
// headVeh;                 ///< [deg], Heading of vehicle (2-D)               #### NOTE: u-blox8 only ####
// --- Edit - 2018-2-25 magDec, magAcc --- TODO TEST
// magDec;                    ///< [deg], Magnetic declination                 #### NOTE: u-blox8 only ####
// magAcc;                    ///< [deg], Magnetic declination accuracy        #### NOTE: u-blox8 only ####

// Variable Type Definitions, For future UBX Protocol implementation (the data sheets are using these weird variable types)
#define U1 unsigned char    // Unsigned Char 1 0..255 1
#define RU1_3 unsigned char // Unsigned Char 1, binary floating point with 3 bit exponent, eeeb bbbb, (Value & 0x1F) << (Value>> 5), 0..(31*2^7)non-continuous, ~ 2^(Value >> 5)
#define I1 signed Char      // Signed Char 1, 2's complement -128..127 1
#define X1 uint8_t          // Bitfield 1, n/a n/a
#define U2 unsigned short   // Unsigned Short 2, 0..65535 1
#define I2 signed short     // Signed Short 2, 2's complement -32768..32767 1
#define X2 uint16_t         // Bitfield 2, n/a n/a
#define U4 unsigned long    // Unsigned Long 4, 0..4 '294'967'295 1
#define I4 double           // Signed Long 4, 2's complement -2'147'483'648 .. 2'147'483'647, 1
#define X4 uint32_t         // Bitfield 4, n/a n/a
#define R4 int32_t          // IEEE 754 Single Precision 4, -1*2^+127 .. 2^+127, ~ Value * 2^-24
#define R8 int64_t          // IEEE 754 Double Precision 8, -1*2^+1023 .. 2^+1023, ~ Value * 2^-53
#define CH char             // ASCII / ISO 8859.1 Encoding, 1

#ifndef UBLOX_h
#define UBLOX_h

#include "Arduino.h"

struct gpsData {
  unsigned long   iTOW;       // U4 /< [ms], GPS time of the navigation epoch
  unsigned short  utcYear;      // U2 /< [year], Year (UTC)
  unsigned char   utcMonth;     // U1 /< [month], Month, range 1..12 (UTC)
  unsigned char   utcDay;     // U1 /< [day], Day of month, range 1..31 (UTC)
  unsigned char   utcHour;      // U1 /< [hour], Hour of day, range 0..23 (UTC)
  unsigned char   utcMin;     // U1 /< [min], Minute of hour, range 0..59 (UTC)
  unsigned char   utcSec;     // U1 /< [s], Seconds of minute, range 0..60 (UTC)
  unsigned char   valid;      // X1 /< [ND], Validity flags
  unsigned long   tAcc;       // U4 /< [ns], Time accuracy estimate (UTC)
  long            utcNano;      // I4 /< [ns], Fraction of second, range -1e9 .. 1e9 (UTC)
  unsigned char   fixType;      // U1 /< [ND], GNSSfix Type: 0: no fix, 1: dead reckoning only, 2: 2D-fix, 3: 3D-fix, 4: GNSS + dead reckoning combined, 5: time only fix
  unsigned char   flags;      // X1 /< [ND], Fix status flags
  unsigned char   flags2;     // X1 /< [ND], Additional flags
  unsigned char   numSV;      // U1 /< [ND], Number of satellites used in Nav Solution
  double          lon;        // I4 /< [deg], Longitude
  double          lat;        // I4 /< [deg], Latitude
  double          height;     // I4 /< [m], Height above ellipsoid
  double          hMSL;       // I4 /< [m], Height above mean sea level
  double          hAcc;       // U4 /< [m], Horizontal accuracy estimate
  double          vAcc;       // U4 /< [m], Vertical accuracy estimate
  double          velN;       // I4 /< [m/s], NED north velocity
  double          velE;       // I4 /< [m/s], NED east velocity
  double          velD;       // I4 /< [m/s], NED down velocity
  double          gSpeed;     // I4 /< [m/s], Ground Speed (2-D)
  double          heading;      // I4 /< [deg], Heading of motion (2-D)
  double          sAcc;       // U4 /< [m/s], Speed accuracy estimate
  double          headingAcc;   // U4 /< [deg], Heading accuracy estimate (both motion and vehicle)
  double          pDOP;       // U2 /< [ND], Position DOP
  double          headVeh;      // I4 /< [deg], Heading of vehicle (2-D)         #### NOTE: u-blox8 only ####
  // Edit - 2018-2-25 magDec, magAcc
  I2 magDec;                      // I2/< [deg], Magnetic declination            #### NOTE: u-blox8 only ####
  U2 magAcc;                      // U2/< [deg], Magnetic declination accuracy   #### NOTE: u-blox8 only ####
};

class UBLOX {
  public:
    UBLOX();
    UBLOX(uint8_t bus);
    void configure(uint8_t bus);
    void begin(int baud);
    bool read(gpsData *gpsData_ptr);

    /******************************************/
    /**  high level commands, for the user    */
    /******************************************/
    void SetGPSbaud460800(); //Set UBLOX GPS Port Configuration to 460800Baud
    void SetGPSbaud9600(); //Set UBLOX GPS Port Configuration to 9600Baud

  private:
    uint8_t _bus;
    uint8_t _fpos;
    uint8_t _CurrentClass;
    bool _UBX_NAV_PVT_ID = false;
    bool _UBX_NAV_POSLLH_ID = false;
    uint16_t _MSGpayloadSize; // Message payloadSize

    // CFG-PRT (0x06 0x00) Get/Set Port Configuration for UART
    uint8_t const SET9600B_CFG_PRT[28]   = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xB5};
    uint8_t const SET460800B_CFG_PRT[28] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x08, 0x07, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xBC};

    static const uint8_t _payloadSize = 96;
    uint8_t _gpsPayload[_payloadSize];
    HardwareSerial* _port;
    bool parse();
    void calcChecksum(unsigned char* CK, unsigned char* payload, uint8_t length);
    void ACK_IDs(uint8_t SERc);
    void NAV_IDs(uint8_t SERIALc);
};
#endif
 
Hi Chris O.

Nice work on updating the ublox without ucenter. If you want more of a challenge there are a couple of other parameters you might want to look at setting if you are interested in doing more work :) Let me know and I will make a list (there is a max of 4 I think that I am always looking at changing).

Mike
 
Hi Mike

Sounds good give me the list while i'm digging in the library.

Currently i'm working on:

// ### Polling Commands Mechanism, MSG REQUEST ###
// All messages that are output by the receiver in a periodic manner (i.e. messages in classes MON, NAV and RXM) can also be polled.
// CFG-PRT (0x06 0x00) Polls the configuration for one I/O Port, (I/O Target # MSG) 0x01=UART1
uint8_t const Poll_CFG_PRT[9] = {0xB5, 0x62, 0x06, 0x00, 0x01, 0x00, 0x01, 0x08, 0x22};
// UBX-NAV-POSLLH (0x01 0x02) Geodetic Position Solution, pull message request
uint8_t const Poll_NAV_POSLLH[8] = {0xB5, 0x62, 0x01, 0x02, 0x00, 0x00, 0x03, 0x0A};
// UBX-NAV-PVT (0x01 0x07) Navigation Position Velocity Time Solution, pull message request
uint8_t const Poll_NAV_PVT[8] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x08, 0x19};

// ### Periodic auto update ON,OFF command ###
uint8_t const Dis_NAV_PVT[11] = {0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x07, 0x00, 0x12, 0x50}; // Disable periodic auto update NAV_PVT
uint8_t const Ena_NAV_PVT[11] = {0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x07, 0x01, 0x13, 0x51}; // Enable periodic auto update NAV_PVT
 
Chris O.

Ok. First you might want to check this site out, it has some good info on what messages does what, https://ukhas.org.uk/guides:falcom_fsa03. Anyway, a couple things that I tend to change more often is the sample rate and the nav5 mode. Those two would be good. Don't know if the below helps.

Code:
# Sample rate 1 Hz for setup
!UBX CFG-RATE 1000 1 1

# Configure GPS and GLONASS satellites
!UBX CFG-GNSS 0 32 32 1 0 10 32 0 65537
!UBX CFG-GNSS 0 32 32 1 6 8 16 0 65537

# Set up raw data out for UART and USB
!UBX CFG-MSG 3 15 0 1 0 1 0 0
!UBX CFG-MSG 3 16 0 1 0 1 0 0
!UBX CFG-MSG 1 32 0 1 0 1 0 0

# change NAV5 mode to pedestrian
!UBX CFG-NAV5 1 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

# turn off extra messages default messages
# NMEA GGA
!UBX CFG-MSG 240 0 0 0 0 0 0 0
# NMEA GLL
!UBX CFG-MSG 240 1 0 0 0 0 0 0
# NMEA GSA
!UBX CFG-MSG 240 2 0 0 0 0 0 0
# NMEA GSV
!UBX CFG-MSG 240 3 0 0 0 0 0 0
# NMEA RMC
!UBX CFG-MSG 240 4 0 0 0 0 0 0
# NMEA VTG
!UBX CFG-MSG 240 5 0 0 0 0 0 0
# NMEA ZDA + others
!UBX CFG-MSG 240 8 0 0 0 0 0 0
!UBX CFG-MSG 1 3 0 0 0 0 0 0
!UBX CFG-MSG 1 3 0 0 0 0 0 0
!UBX CFG-MSG 1 6 0 0 0 0 0 0
!UBX CFG-MSG 1 18 0 0 0 0 0 0
!UBX CFG-MSG 1 34 0 0 0 0 0 0
!UBX CFG-MSG 1 48 0 0 0 0 0 0

# Sample rate 5 Hz for data collection
!UBX CFG-RATE 200 1 1


@
!UBX CFG-RATE 1000 1 1

In one of my sketches I had it do specific configurations for the Ublox modules. These were the functions I used, don't know if they would help:
Code:
  void configureUblox(byte *settingsArrayPointer) {
  byte gpsSetSuccess = 0;
  //Serial.println("Configuring u-Blox GPS initial state...");

  //Generate the configuration string for loading from i2c eeprom
  byte setCFG[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x1E,
  0xB4 };
  calcChecksum(&setCFG[2], sizeof(setCFG) - 4);

  delay(2500);

  gpsSetSuccess = 0;
  while(gpsSetSuccess < 3) {
    //Serial.print("Loading permanent configuration... ");
    sendUBX(&setCFG[0], sizeof(setCFG));  //Send UBX Packet
    gpsSetSuccess += getUBX_ACK(&setCFG[2]); 
               //Passes Class ID and Message ID to the ACK Receive function      
    if (gpsSetSuccess == 10) gpsStatus[1] = true;
    if (gpsSetSuccess == 5 | gpsSetSuccess == 6) gpsSetSuccess -= 4;
  }
  if (gpsSetSuccess == 3) Serial.println("Config update failed.");
  gpsSetSuccess = 0;
  }

  void calcChecksum(byte *checksumPayload, byte payloadSize) {
    byte CK_A = 0, CK_B = 0;
    for (int i = 0; i < payloadSize ;i++) {
      CK_A = CK_A + *checksumPayload;
      CK_B = CK_B + CK_A;
      checksumPayload++;
    }
    *checksumPayload = CK_A;
    checksumPayload++;
    *checksumPayload = CK_B;
  }

  void sendUBX(byte *UBXmsg, byte msgLength) {
    for(int i = 0; i < msgLength; i++) {
      gpsSerial.write(UBXmsg[i]);
      gpsSerial.flush();
    }
    gpsSerial.println();
    gpsSerial.flush();
  }


  byte getUBX_ACK(byte *msgID) {
    byte CK_A = 0, CK_B = 0;
    byte incoming_char;
    boolean headerReceived = false;
    unsigned long ackWait = millis();
    byte ackPacket[10] = {0xB5, 0x62, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    int i = 0;
    while (1) {
      if (gpsSerial.available()) {
        incoming_char = gpsSerial.read();
        if (incoming_char == ackPacket[i]) {
          i++;
        }
      else if (i > 2) {
        ackPacket[i] = incoming_char;
        i++;
      }
    }
    if (i > 9) break;
    if ((millis() - ackWait) > 1500) {
      //Serial.println("ACK Timeout");
      return 5;
      }
    if (i == 4 && ackPacket[3] == 0x00) {
      //Serial.println("NAK Received");
      return 1;
      }
    }

    for (i = 2; i < 8 ;i++) {
      CK_A = CK_A + ackPacket[i];
      CK_B = CK_B + CK_A;
    }
  
    if (msgID[0] == ackPacket[6] && msgID[1] == ackPacket[7] && CK_A == ackPacket[8] && CK_B == ackPacket[9]) {
      //Serial.println("Success!");
      //Serial.print("ACK Received! ");
      //printHex(ackPacket, sizeof(ackPacket));
      return 10;
    }
    else {
      //Serial.print("ACK Checksum Failure: ");
      //printHex(ackPacket, sizeof(ackPacket));
      delay(1000);
      return 1;
    }
  }

Cheers
Mike
 
Woah lots of good info in the links you provided.

Unfortunately life is getting in the way, so far i only got the CFG-PRT (0x06 0x00) implemented properly.

Thanks.
 
Back
Top