Docs directory
Table of contents
MicoAir GPS Module — UBX Protocol Parsing Reference
MicoAir GPS modules ship with the UBX protocol, NAV-PVT message, and 115200 baud enabled by default.
For protocol details, refer to the official manual. The M10 (M10050) and M9 (M9140) protocols are almost identical, apart from a few specific differences.
The following reference implementation configures a u-blox module and parses NAV-PVT messages.
ublox.h
#ifndef __UBLOX_H
#define __UBLOX_H
#include <stdint.h>
#define UBLOX_SYNC1 0xB5
#define UBLOX_SYNC2 0x62
#define UBLOX_NAV_CLASS 0x01
#define UBLOX_RXM_CLASS 0x02
#define UBLOX_ACK_CLASS 0x05
#define UBLOX_CFG_CLASS 0x06
#define UBLOX_MON_CLASS 0x0a
#define UBLOX_AID_CLASS 0x0b
#define UBLOX_TIM_CLASS 0x0d
#define UBLOX_NAV_POSLLH 0x02
#define UBLOX_NAV_DOP 0x04
#define UBLOX_NAV_SOL 0x06
#define UBLOX_NAV_PVT 0x07
#define UBLOX_NAV_VALNED 0x12
#define UBLOX_NAV_TIMEUTC 0x21
#define UBLOX_NAV_SBAS 0x32
#define UBLOX_NAV_SVINFO 0x30
#define UBLOX_ACK_NAK 0x00
#define UBLOX_ACK_ACK 0x01
#define UBLOX_AID_REQ 0x00
#define UBLOX_RXM_RAW 0x10
#define UBLOX_RXM_SFRB 0x11
#define UBLOX_MON_VER 0x04
#define UBLOX_MON_HW 0x09
#define UBLOX_TIM_TP 0x01
#define UBLOX_CFG_PRT 0x00
#define UBLOX_CFG_MSG 0x01
#define UBLOX_CFG_TP 0x07
#define UBLOX_CFG_RTATE 0x08
#define UBLOX_CFG_CFG 0x09
#define UBLOX_CFG_SBAS 0x16
#define UBLOX_CFG_NAV5 0x24
#define UBLOX_MAX_PAYLOAD 256
typedef struct {
float time; // Time
double latitude; // Latitude
double longitude; // Longitude
float altitude; // Altitude
float velN; // North velocity
float velE; // East velocity
float velD; // Down velocity
float speed; // Ground speed
float heading; // Heading
float hAcc; // Horizontal position accuracy
float vAcc; // Vertical position accuracy
float sAcc; // Speed accuracy
float cAcc; // Heading accuracy
uint8_t fixed; // Fix status
uint8_t numSV; // Number of satellites
} GPS_t;
enum
{
UBLOX_WAIT_SYNC1 = 0,
UBLOX_WAIT_SYNC2,
UBLOX_WAIT_CLASS,
UBLOX_WAIT_ID,
UBLOX_WAIT_LEN1,
UBLOX_WAIT_LEN2,
UBLOX_PAYLOAD,
UBLOX_CHECK1,
UBLOX_CHECK2
};
enum
{
PORTABLE = 0,
STATIONARY = 2,
PEDESTRIAN = 3,
AUTOMOTIVE = 4,
SEA = 5,
AIRBORNE_1G = 6,
AIRBORNE_2G = 7,
AIRBORNE_4G = 8
};
#pragma pack (1)
typedef struct {
uint32_t iTOW; //GPS Millisecond Time of Week ms
uint16_t year; //Year, range 1999..2099 (UTC)
uint8_t month; //Month, range 1..12 (UTC)
uint8_t day; //Day of Month, range 1..31 (UTC)
uint8_t hour; //Hour of Day, range 0..23 (UTC)
uint8_t min; //Minute of Hour, range 0..59 (UTC)
uint8_t sec; //Seconds of Minute, range 0..59 (UTC)
uint8_t valid; //Validity Flags
uint32_t tAcc; //Time Accuracy Estimate
int32_t nano; //Fraction of second, range -1e9 .. 1e9 (UTC)
uint8_t gpsFix; //GPSfix Type, range 0..5 0: no fix 1: dead reckoning only 2: 2D-fix 3: 3D-fix 4: GNSS + dead reckoning combined 5: time only fix
uint8_t flags; //Fix status flags
uint8_t flags2; //Additional flags
uint8_t numSV; //Number of SVs used in Nav Solution
int32_t lon; //Longitude
int32_t lat; //Latitude
int32_t height; //Height above Ellipsoid
int32_t hMSL; //Height above mean sea level
uint32_t hAcc; //Horizontal Accuracy Estimate
uint32_t vAcc; //Vertical Accuracy Estimate
int32_t velN; //NED north velocity cm/s
int32_t velE; //NED east velocity cm/s
int32_t velD; //NED down velocity cm/s
int32_t gSpeed; //Ground Speed (2-D) cm/s
int32_t heading; //Heading 2-D deg
uint32_t sAcc; //Speed Accuracy Estimate cm/s
uint32_t cAcc; //Course / Heading Accuracy Estimate deg
uint16_t pDOP; //Position DOP
uint8_t res1; //reserved
int32_t headVeh; //Heading of vehicle (2-D)
uint8_t res2; //reserved
} UBLOX_PVT_t;
typedef struct {
union
{
UBLOX_PVT_t pvt;
char other[UBLOX_MAX_PAYLOAD];
} payload;
uint8_t state;
uint16_t count;
uint8_t class;
uint8_t id;
uint16_t length;
uint16_t checksumErrors;
uint8_t ubloxRxCK_A;
uint8_t ubloxRxCK_B;
uint8_t ubloxTxCK_A;
uint8_t ubloxTxCK_B;
} UBLOX_RAW_t;
typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
} UTC_TIME_t;
#pragma pack ()
void ublox_init(void);
#endif
ublox.c
#include "ublox.h"
// Default GPS baud rate
#define GPS_DEFAULT_BAUDRATE 115200
GPS_t gps_data;
UTC_TIME_t time;
static void ublox_parse_char(uint8_t data);
static void ublox_enable_message(uint8_t class, uint8_t id, uint8_t rate);
static void ublox_cfg_rate(uint16_t rate);
static void ublox_cfg_port(uint32_t baudrate);
static void ublox_cfg_nav5(void);
// Send data over the serial port; implement this function for the target platform
void ublox_send_message(uint8_t *data, uint8_t length)
{
// Implement serial transmission for the target platform
}
// u-blox data decoder; call it from the serial interrupt or serial-data handler and pass in each received byte
void ublox_decode(uint8_t data)
{
ublox_parse_char(data);
}
// Millisecond delay; implement this function for the target platform
void delay_ms(uint32_t ms)
{
}
/**********************************************************************************************************
* Function: ublox_init
* Description: Initialize the u-blox configuration
* Parameters: None
* Return value: None
**********************************************************************************************************/
void ublox_init(void)
{
// Implement serial-port initialization for the target platform; the MG90X default baud rate is 115200
// usart_init(GPS_DEFAULT_BAUDRATE);
// Wait 300 ms after powering the GPS module before configuring it
delay_ms(300);
// Configure the u-blox UART
// Baud rate: 115200
// Input protocols: NMEA + UBX + RTCM
// Output protocol: UBX
ublox_cfg_port(GPS_DEFAULT_BAUDRATE);
delay_ms(5);
// Configure the output rate
// The input parameter is the interval in ms
// At output rates >=10 Hz, the u-blox M9140 (MG-902/903) limits navigation to 16 satellites
// (the 16 satellites with the best signal quality among those visible)
// To use the maximum number of satellites for navigation (32 on M9), set the output rate to no more than 8 Hz
ublox_cfg_rate(125); // 125 ms output interval
delay_ms(5);
// Configure navigation parameters
// AIRBORNE_2G mode (recommended for drones)
ublox_cfg_nav5();
delay_ms(5);
// Enable PVT message output
// The PVT message contains latitude, longitude, time, velocity, and the other data required for navigation,
// so outputting and parsing this message alone is sufficient
ublox_enable_message(UBLOX_NAV_CLASS, UBLOX_NAV_PVT, 1);
}
/**********************************************************************************************************
* Function: ublox_payload_decode
* Description: Decode the u-blox payload
* Parameters: Input data
* Return value: None
**********************************************************************************************************/
static void ublox_payload_decode(UBLOX_RAW_t raw_data)
{
if(raw_data.class == UBLOX_NAV_CLASS)
{
switch(raw_data.id)
{
case UBLOX_NAV_PVT:
gps_data.time = (float)raw_data.payload.pvt.iTOW / 1000;
gps_data.latitude = (double)raw_data.payload.pvt.lat * (double)1e-7; // Unit: deg
gps_data.longitude = (double)raw_data.payload.pvt.lon * (double)1e-7; // Unit: deg
gps_data.altitude = (float)raw_data.payload.pvt.hMSL * 0.001f; // Unit: m
gps_data.hAcc = (float)raw_data.payload.pvt.hAcc * 0.001f; // Unit: m
gps_data.vAcc = (float)raw_data.payload.pvt.vAcc * 0.001f; // Unit: m
gps_data.velN = (float)raw_data.payload.pvt.velN * 0.001f; // Unit: m/s
gps_data.velE = (float)raw_data.payload.pvt.velE * 0.001f; // Unit: m/s
gps_data.velD = (float)raw_data.payload.pvt.velD * 0.001f; // Unit: m/s
gps_data.speed = (float)raw_data.payload.pvt.gSpeed * 0.001f; // Unit: m/s
gps_data.heading = (float)raw_data.payload.pvt.heading * 1e-5f; // Unit: deg
gps_data.sAcc = (float)raw_data.payload.pvt.sAcc * 0.001f; // Unit: m/s
gps_data.cAcc = (float)raw_data.payload.pvt.cAcc * 1e-5f; // Unit: deg
gps_data.numSV = raw_data.payload.pvt.numSV;
gps_data.fixed = raw_data.payload.pvt.gpsFix;
time.year = raw_data.payload.pvt.year;
time.month = raw_data.payload.pvt.month;
time.day = raw_data.payload.pvt.day;
time.hour = raw_data.payload.pvt.hour;
time.min = raw_data.payload.pvt.min;
time.sec = raw_data.payload.pvt.sec;
break;
default:
break;
}
}
else if(raw_data.class == UBLOX_ACK_CLASS)
{
if(raw_data.id == UBLOX_ACK_ACK)
{
}
}
}
/**********************************************************************************************************
* Function: ublox_parse_char
* Description: Parse a u-blox protocol byte stream
* Parameters: Input data
* Return value: None
**********************************************************************************************************/
static void ublox_parse_char(uint8_t data)
{
static UBLOX_RAW_t ublox_raw;
switch (ublox_raw.state)
{
/* Frame header 1 */
case UBLOX_WAIT_SYNC1:
if (data == UBLOX_SYNC1)
ublox_raw.state = UBLOX_WAIT_SYNC2;
break;
/* Frame header 2 */
case UBLOX_WAIT_SYNC2:
if (data == UBLOX_SYNC2)
ublox_raw.state = UBLOX_WAIT_CLASS;
else
ublox_raw.state = UBLOX_WAIT_SYNC1;
break;
/* Message class */
case UBLOX_WAIT_CLASS:
ublox_raw.class = data;
// Initialize the checksum
ublox_raw.ubloxRxCK_A = 0;
ublox_raw.ubloxRxCK_B = 0;
// Calculate the checksum
ublox_raw.ubloxRxCK_A += data;
ublox_raw.ubloxRxCK_B += ublox_raw.ubloxRxCK_A;
ublox_raw.state = UBLOX_WAIT_ID;
break;
/* Message ID */
case UBLOX_WAIT_ID:
ublox_raw.id = data;
// Calculate the checksum
ublox_raw.ubloxRxCK_A += data;
ublox_raw.ubloxRxCK_B += ublox_raw.ubloxRxCK_A;
ublox_raw.state = UBLOX_WAIT_LEN1;
break;
/* Low 8 bits of the message length */
case UBLOX_WAIT_LEN1:
ublox_raw.length = data;
// Calculate the checksum
ublox_raw.ubloxRxCK_A += data;
ublox_raw.ubloxRxCK_B += ublox_raw.ubloxRxCK_A;
ublox_raw.state = UBLOX_WAIT_LEN2;
break;
/* High 8 bits of the message length */
case UBLOX_WAIT_LEN2:
ublox_raw.length += (data << 8);
// Calculate the checksum
ublox_raw.ubloxRxCK_A += data;
ublox_raw.ubloxRxCK_B += ublox_raw.ubloxRxCK_A;
if (ublox_raw.length >= (UBLOX_MAX_PAYLOAD-1))
{
ublox_raw.length = 0;
ublox_raw.state = UBLOX_WAIT_SYNC1;
}
else if (ublox_raw.length > 0)
{
ublox_raw.count = 0;
ublox_raw.state = UBLOX_PAYLOAD;
}
else
{
ublox_raw.state = UBLOX_CHECK1;
}
break;
/* Message payload */
case UBLOX_PAYLOAD:
*((char *)(&ublox_raw.payload) + ublox_raw.count) = data;
if (++ublox_raw.count == ublox_raw.length)
{
ublox_raw.state = UBLOX_CHECK1;
}
// Calculate the checksum
ublox_raw.ubloxRxCK_A += data;
ublox_raw.ubloxRxCK_B += ublox_raw.ubloxRxCK_A;
break;
/* Compare the CKA checksum byte */
case UBLOX_CHECK1:
if (data == ublox_raw.ubloxRxCK_A)
{
ublox_raw.state = UBLOX_CHECK2;
}
else
{
ublox_raw.state = UBLOX_WAIT_SYNC1;
ublox_raw.checksumErrors++;
}
break;
/* Compare the CKB checksum byte */
case UBLOX_CHECK2:
ublox_raw.state = UBLOX_WAIT_SYNC1;
if (data == ublox_raw.ubloxRxCK_B)
{
// Reception is complete; decode the payload
ublox_payload_decode(ublox_raw);
}
else
{
ublox_raw.checksumErrors++;
}
break;
default:
break;
}
}
/**********************************************************************************************************
* Function: ublox_enable_message
* Description: Enable u-blox message output
* Parameters: Message class, message ID, and output rate
* (0 disables output; 1 outputs once per cycle, the fastest setting; larger values reduce the output rate)
* Return value: None
**********************************************************************************************************/
static void ublox_enable_message(uint8_t class, uint8_t id, uint8_t rate)
{
uint8_t data[11];
uint8_t msg_len = 11;
uint8_t ck_a = 0, ck_b = 0;
data[0] = UBLOX_SYNC1; // Frame header 1
data[1] = UBLOX_SYNC2; // Frame header 2
data[2] = UBLOX_CFG_CLASS; // Message class
data[3] = UBLOX_CFG_MSG; // Message ID
data[4] = 0x03; // Low 8 bits of the payload length
data[5] = 0x00; // High 8 bits of the payload length
data[6] = class; // Set the message class
data[7] = id; // Set the message ID
data[8] = rate; // Set the message output rate
for(uint8_t i=2; i<msg_len-2; i++)
{
ck_a += data[i];
ck_b += ck_a;
}
data[9] = ck_a;
data[10] = ck_b;
ublox_send_message(data, msg_len);
}
/**********************************************************************************************************
* Function: ublox_cfg_rate
* Description: Set the u-blox output rate
* Parameters: Output interval (unit: ms)
* Return value: None
**********************************************************************************************************/
static void ublox_cfg_rate(uint16_t rate)
{
uint8_t data[14];
uint8_t msg_len = 14;
uint8_t ck_a = 0, ck_b = 0;
data[0] = UBLOX_SYNC1; // Frame header 1
data[1] = UBLOX_SYNC2; // Frame header 2
data[2] = UBLOX_CFG_CLASS; // Message class
data[3] = UBLOX_CFG_RTATE; // Message ID
data[4] = 0x06; // Low 8 bits of the payload length
data[5] = 0x00; // High 8 bits of the payload length
data[6] = (uint8_t)rate; // Low eight bits of the output interval
data[7] = (uint8_t)(rate >> 8); // High eight bits of the output interval
data[8] = 0x01; // Low eight bits of the navigation-cycle count
data[9] = 0x00; // High eight bits of the navigation-cycle count
data[10] = 0x01; //timeRef 0:UTC, 1:GPS time
data[11] = 0x00; // High eight bits
for(uint8_t i=2; i<msg_len-2; i++)
{
ck_a += data[i];
ck_b += ck_a;
}
data[12] = ck_a;
data[13] = ck_b;
ublox_send_message(data, msg_len);
}
/**********************************************************************************************************
* Function: ublox_cfg_port
* Description: Configure u-blox output
* Parameters: Baud rate
* Return value: None
**********************************************************************************************************/
static void ublox_cfg_port(uint32_t baudrate)
{
uint8_t data[28];
uint8_t msg_len = 28;
uint8_t ck_a = 0, ck_b = 0;
data[0] = UBLOX_SYNC1; // Frame header 1
data[1] = UBLOX_SYNC2; // Frame header 2
data[2] = UBLOX_CFG_CLASS; // Message class
data[3] = UBLOX_CFG_PRT; // Message ID
data[4] = 0x14; // Low 8 bits of the payload length
data[5] = 0x00; // High 8 bits of the payload length
data[6] = 0x01; // Port number, 1: UART1
data[7] = 0x00; // Reserved
data[8] = 0x00; // High eight bits of TX Ready
data[9] = 0x00; // Low eight bits of TX Ready
data[10] = (uint8_t)(0x08D0); // Serial-port configuration
data[11] = (uint8_t)(0x08D0 >> 8); // 8 data bits
data[12] = (uint8_t)(0x08D0 >> 16); // 1 stop bit
data[13] = (uint8_t)(0x08D0 >> 24); // No parity
data[14] = (uint8_t)(baudrate); // Serial baud rate
data[15] = (uint8_t)(baudrate >> 8); //
data[16] = (uint8_t)(baudrate >> 16); //
data[17] = (uint8_t)(baudrate >> 24); //
data[18] = 0x07; // Input-protocol configuration
data[19] = 0x00; //ubx+nmea+rtcm
data[20] = 0x01; // Output-protocol configuration
data[21] = 0x00; //ubx
data[22] = 0x00; // Low eight bits of flags
data[23] = 0x00; // High eight bits of flags
data[24] = 0x00; //reserved
data[25] = 0x00; //reserved
for(uint8_t i=2; i<msg_len-2; i++)
{
ck_a += data[i];
ck_b += ck_a;
}
data[26] = ck_a;
data[27] = ck_b;
ublox_send_message(data, msg_len);
}
/**********************************************************************************************************
* Function: ublox_cfg_nav5
* Description: Configure navigation parameters
* Parameters: None
* Return value: None
**********************************************************************************************************/
static void ublox_cfg_nav5(void)
{
uint8_t data[44];
uint8_t msg_len = 44;
uint8_t ck_a = 0, ck_b = 0;
data[0] = UBLOX_SYNC1; // Frame header 1
data[1] = UBLOX_SYNC2; // Frame header 2
data[2] = UBLOX_CFG_CLASS; // Message class
data[3] = UBLOX_CFG_NAV5; // Message ID
data[4] = 0x24; // Low 8 bits of the payload length
data[5] = 0x00; // High 8 bits of the payload length
data[6] = 0xFF; // Mask
data[7] = 0xFF; // Mask
data[8] = AIRBORNE_2G; // Dynamic platform model; AIRBORNE_2G is recommended for drones
data[9] = 0x03; // position fixing mode 3=auto 2D/3D
data[10] = (uint8_t)(0x00); // Fixed altitude(mean sea level) for 2D fix mode
data[11] = (uint8_t)(0x00 >> 8);
data[12] = (uint8_t)(0x00 >> 16);
data[13] = (uint8_t)(0x00 >> 24);
data[14] = (uint8_t)(10000); // Fixed altitude variance for 2D mode
data[15] = (uint8_t)(10000 >> 8);
data[16] = (uint8_t)(10000 >> 16);
data[17] = (uint8_t)(10000 >> 24);
data[18] = 0x0A; // Minimum satellite elevation: 10°
data[19] = 0x00; // drLimit
data[20] = (uint8_t)(250); // pDop
data[21] = (uint8_t)(250 >> 8);
data[22] = (uint8_t)(250); // tDop
data[23] = (uint8_t)(250 >> 8);
data[24] = (uint8_t)(100); // pAcc Mask
data[25] = (uint8_t)(100 >> 8);
data[26] = (uint8_t)(350); // tAcc Mask
data[27] = (uint8_t)(350 >> 8);
data[28] = 0; // Static hold threshold
data[29] = 60; // DGNSS timeout
data[30] = 0; // cn0ThreshNumSVs
data[31] = 25; // Minimum C/N0 required for a satellite to participate in navigation
data[32] = 0; // pAccAdr
data[33] = 0;
data[34] = 0; // staticHoldMaxDist
data[35] = 0;
data[36] = 0; // utcStandard
data[37] = 0; // reserved
data[38] = 0;
data[39] = 0;
data[40] = 0;
data[41] = 0;
for(uint8_t i=2; i<msg_len-2; i++)
{
ck_a += data[i];
ck_b += ck_a;
}
data[42] = ck_a;
data[43] = ck_b;
ublox_send_message(data, msg_len);
}

