ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ubx.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ubx.h
6 * @brief u-blox UBX binary protocol codec (PROTOCORE_ENABLE_UBX) - the GNSS receiver control/nav protocol.
7 *
8 * A UBX frame is `B5 62 <class> <id> <len-LE:2> <payload...> <CK_A> <CK_B>`: two sync chars, a
9 * class/id message selector, a little-endian payload length, the payload, and an 8-bit Fletcher
10 * checksum computed over everything between the sync chars and the checksum (class..payload end).
11 * This codec builds a frame (adding the sync chars, length, and checksum), builds a zero-length
12 * poll request (how UBX asks a receiver to emit a message), parses one complete frame (validating
13 * the length and checksum), and - because a u-blox receiver multiplexes UBX with ASCII NMEA on the
14 * same UART - provides a streaming demultiplexer that pulls UBX frames out of a byte stream and
15 * hands every non-UBX byte back to the caller (for an NMEA line assembler). Little-endian payload
16 * readers and an ACK helper decode the common replies. Pure codec, host-tested; the UART is the
17 * application's (a plain HardwareSerial link, commonly 9600 baud).
18 *
19 * @author Douglas Quigg (dstroy0)
20 * @date 2026
21 */
22
23#ifndef PROTOCORE_UBX_H
24#define PROTOCORE_UBX_H
25
26#include "protocore_config.h" // the entry point: protocore_types.h for the widths
27
28#if PROTOCORE_ENABLE_UBX
29
31
32#define PROTOCORE_UBX_SYNC1 0xB5u ///< first sync char
33#define PROTOCORE_UBX_SYNC2 0x62u ///< second sync char
34
35/** @brief A parsed UBX frame. @p payload aliases the caller's / stream's buffer (@p len octets). */
36typedef struct
37{
38 uint8_t cls; ///< message class
39 uint8_t id; ///< message id
40 uint16_t len; ///< payload length
41 const uint8_t *payload; ///< payload (len octets); references an external buffer
42} protocore_ubx;
43
44/**
45 * @brief 8-bit Fletcher checksum over @p body (the class..payload-end span - everything the frame
46 * checksums). Writes @p ck_a / @p ck_b.
47 */
48void protocore_ubx_checksum(const uint8_t *body, size_t len, uint8_t *ck_a, uint8_t *ck_b);
49
50/**
51 * @brief Build a UBX frame into @p buf: `B5 62 cls id len(LE) payload CK_A CK_B`.
52 * @return total frame length (8 + @p len) or 0 on overflow / bad args (@p payload may be NULL
53 * only when @p len is 0).
54 */
55size_t protocore_ubx_build(uint8_t *buf, size_t cap, uint8_t cls, uint8_t id, const uint8_t *payload, uint16_t len);
56
57/** @brief Build a zero-length poll request for (@p cls, @p id) - how UBX asks for a message. */
58size_t protocore_ubx_build_poll(uint8_t *buf, size_t cap, uint8_t cls, uint8_t id);
59
60/**
61 * @brief Parse exactly one complete UBX frame at the front of @p s. Validates the sync chars, the
62 * declared length against @p len, and the checksum; fills @p out (payload aliases @p s). Returns
63 * false on a short / malformed / bad-checksum frame.
64 */
65proto_bool protocore_ubx_parse(const uint8_t *s, size_t len, protocore_ubx *out);
66
67/**
68 * @brief ACK helper. Returns 1 for UBX-ACK-ACK, 0 for UBX-ACK-NAK, -1 if @p m is not an ACK frame.
69 * When it is an ACK, writes the acknowledged class/id (the payload's two octets) to @p acked_cls /
70 * @p acked_id.
71 */
72int protocore_ubx_ack(const protocore_ubx *m, uint8_t *acked_cls, uint8_t *acked_id);
73
74/** @brief Little-endian readers for a payload at byte offset @p off (caller bounds-checks @p off). */
75uint16_t protocore_ubx_u16(const uint8_t *p, size_t off);
76uint32_t protocore_ubx_u32(const uint8_t *p, size_t off);
77int16_t protocore_ubx_i16(const uint8_t *p, size_t off);
78int32_t protocore_ubx_i32(const uint8_t *p, size_t off);
79
80// -- NAV-PVT: the u-blox all-in-one navigation solution (position / velocity / time) --
81
82#define PROTOCORE_UBX_CLASS_NAV 0x01 ///< navigation-results message class
83#define PROTOCORE_UBX_NAV_PVT 0x07 ///< NAV-PVT message id (class NAV)
84#define PROTOCORE_UBX_NAV_PVT_LEN 92 ///< NAV-PVT payload length (u-blox 8 / M8)
85#define PROTOCORE_UBX_PVT_FIX_OK 0x01u ///< NAV-PVT flags bit 0: a valid fix (within DOP / accuracy masks)
86
87/** @brief NAV-PVT fixType values. */
88enum protocore_ubx_fix_type
89{
90 PROTOCORE_UBX_FIX_NONE = 0, ///< no fix
91 PROTOCORE_UBX_FIX_DR = 1, ///< dead-reckoning only
92 PROTOCORE_UBX_FIX_2D = 2, ///< 2D fix
93 PROTOCORE_UBX_FIX_3D = 3, ///< 3D fix
94 PROTOCORE_UBX_FIX_GNSS_DR = 4, ///< GNSS + dead reckoning
95 PROTOCORE_UBX_FIX_TIME = 5 ///< time-only fix
96};
97
98/** @brief Decoded UBX-NAV-PVT payload (the fields an application usually needs; native integer scales). */
99typedef struct // NOSONAR(cpp:S1820): UBX-NAV-PVT is one fixed protocol message; its ~30 fields mirror the
100 // wire layout, so splitting the struct would be artificial, not clearer
101{
102 uint32_t itow_ms; ///< GPS time of week of the solution (ms)
103 uint16_t year; ///< UTC year
104 uint8_t month; ///< UTC month (1..12)
105 uint8_t day; ///< UTC day (1..31)
106 uint8_t hour; ///< UTC hour (0..23)
107 uint8_t minute; ///< UTC minute (0..59)
108 uint8_t second; ///< UTC second (0..60)
109 uint8_t valid; ///< validity flags (validDate / validTime / fullyResolved / validMag)
110 int32_t nano; ///< fraction of second, -1e9..1e9 (ns)
111 uint32_t time_acc_ns; ///< time accuracy estimate (ns)
112 uint8_t fix_type; ///< @ref protocore_ubx_fix_type
113 uint8_t flags; ///< fix status flags (bit 0 = @ref PROTOCORE_UBX_PVT_FIX_OK)
114 uint8_t num_sv; ///< number of satellites used in the solution
115 int32_t lon_1e7; ///< longitude (1e-7 deg)
116 int32_t lat_1e7; ///< latitude (1e-7 deg)
117 int32_t height_mm; ///< height above the ellipsoid (mm)
118 int32_t hmsl_mm; ///< height above mean sea level (mm)
119 uint32_t h_acc_mm; ///< horizontal accuracy estimate (mm)
120 uint32_t v_acc_mm; ///< vertical accuracy estimate (mm)
121 int32_t vel_n_mm_s; ///< NED north velocity (mm/s)
122 int32_t vel_e_mm_s; ///< NED east velocity (mm/s)
123 int32_t vel_d_mm_s; ///< NED down velocity (mm/s)
124 int32_t gspeed_mm_s; ///< 2-D ground speed (mm/s)
125 int32_t head_mot_1e5; ///< heading of motion (1e-5 deg)
126 uint32_t s_acc_mm_s; ///< speed accuracy estimate (mm/s)
127 uint32_t head_acc_1e5; ///< heading accuracy estimate (1e-5 deg)
128 uint16_t pdop_1e2; ///< position DOP (0.01)
129} protocore_ubx_nav_pvt;
130
131/**
132 * @brief Decode a UBX-NAV-PVT frame into @p out (per the u-blox interface description).
133 * @return true iff @p m is a NAV-PVT frame (class 0x01 / id 0x07) of at least @ref PROTOCORE_UBX_NAV_PVT_LEN
134 * octets; false (and @p out untouched) otherwise.
135 */
136proto_bool protocore_ubx_parse_nav_pvt(const protocore_ubx *m, protocore_ubx_nav_pvt *out);
137
138// -- NAV-SAT: per-satellite signal + usage info (variable length) --
139
140#define PROTOCORE_UBX_NAV_SAT 0x35 ///< NAV-SAT message id (class NAV)
141#define PROTOCORE_UBX_NAV_SAT_HDR_LEN 8 ///< NAV-SAT fixed header (iTOW + version + numSvs + reserved)
142#define PROTOCORE_UBX_NAV_SAT_ENTRY_LEN 12 ///< NAV-SAT per-satellite block length
143#define PROTOCORE_UBX_SAT_QUALITY_MASK 0x07u ///< flags bits 0..2: signal quality indicator
144#define PROTOCORE_UBX_SAT_USED 0x08u ///< flags bit 3: this satellite is used in the navigation solution
145
146/** @brief NAV-SAT fixed header. */
147typedef struct
148{
149 uint32_t itow_ms; ///< GPS time of week (ms)
150 uint8_t version; ///< message version (1)
151 uint8_t num_svs; ///< number of satellite blocks that follow
152} protocore_ubx_nav_sat_hdr;
153
154/** @brief One NAV-SAT satellite block. */
155typedef struct
156{
157 uint8_t gnss_id; ///< GNSS identifier (0 GPS, 2 Galileo, 3 BeiDou, 5 QZSS, 6 GLONASS, ...)
158 uint8_t sv_id; ///< satellite identifier within the GNSS
159 uint8_t cno_dbhz; ///< carrier-to-noise density ratio (dB-Hz)
160 int8_t elev_deg; ///< elevation (deg, -90..90; out of range if unknown)
161 int16_t azim_deg; ///< azimuth (deg, 0..360)
162 int16_t pr_res_01m; ///< pseudorange residual (0.1 m)
163 uint32_t
164 flags; ///< bitfield: quality (@ref PROTOCORE_UBX_SAT_QUALITY_MASK), used (@ref PROTOCORE_UBX_SAT_USED), ...
165} protocore_ubx_sat;
166
167/**
168 * @brief Decode a UBX-NAV-SAT frame's fixed header (per the u-blox interface description).
169 * @return true iff @p m is a NAV-SAT frame (class 0x01 / id 0x35) whose declared length holds the header
170 * plus its @c num_svs blocks; false otherwise. Walk the blocks with @ref protocore_ubx_nav_sat_get.
171 */
172proto_bool protocore_ubx_parse_nav_sat(const protocore_ubx *m, protocore_ubx_nav_sat_hdr *out);
173
174/**
175 * @brief Decode satellite block @p index (0-based) from a NAV-SAT frame into @p out.
176 * @return true on success, false if @p index is out of range or @p m is not a valid NAV-SAT frame.
177 */
178proto_bool protocore_ubx_nav_sat_get(const protocore_ubx *m, uint8_t index, protocore_ubx_sat *out);
179
180// -- NAV-TIMEUTC: the validated UTC time solution (for a GNSS time source) --
181
182#define PROTOCORE_UBX_NAV_TIMEUTC 0x21 ///< NAV-TIMEUTC message id (class NAV)
183#define PROTOCORE_UBX_NAV_TIMEUTC_LEN 20 ///< NAV-TIMEUTC payload length
184#define PROTOCORE_UBX_TIMEUTC_VALID_TOW 0x01u ///< valid flags bit 0: the time of week is valid
185#define PROTOCORE_UBX_TIMEUTC_VALID_WKN 0x02u ///< valid flags bit 1: the week number is valid
186#define PROTOCORE_UBX_TIMEUTC_VALID_UTC 0x04u ///< valid flags bit 2: the UTC time is valid (leap seconds known)
187
188/** @brief Decoded UBX-NAV-TIMEUTC payload. */
189typedef struct
190{
191 uint32_t itow_ms; ///< GPS time of week (ms)
192 uint32_t time_acc_ns; ///< time accuracy estimate (ns)
193 int32_t nano; ///< fraction of second, -1e9..1e9 (ns)
194 uint16_t year; ///< UTC year (1999..2099)
195 uint8_t month; ///< UTC month (1..12)
196 uint8_t day; ///< UTC day (1..31)
197 uint8_t hour; ///< UTC hour (0..23)
198 uint8_t minute; ///< UTC minute (0..59)
199 uint8_t second; ///< UTC second (0..60)
200 uint8_t valid; ///< validity flags (@ref PROTOCORE_UBX_TIMEUTC_VALID_TOW / _WKN / _UTC + utcStandard)
201 proto_bool utc_valid; ///< convenience: the UTC-valid bit is set (leap seconds resolved)
202} protocore_ubx_nav_time_utc;
203
204/**
205 * @brief Decode a UBX-NAV-TIMEUTC frame into @p out (the receiver's UTC time, for a GNSS time source).
206 * @return true iff @p m is a NAV-TIMEUTC frame (class 0x01 / id 0x21) of at least 20 octets; false otherwise.
207 */
208proto_bool protocore_ubx_parse_nav_timeutc(const protocore_ubx *m, protocore_ubx_nav_time_utc *out);
209
210// -- CFG: configure the receiver (which messages to emit, and how fast) --
211
212#define PROTOCORE_UBX_CLASS_CFG 0x06 ///< configuration-input message class
213#define PROTOCORE_UBX_CFG_MSG 0x01 ///< CFG-MSG: set a message's output rate
214#define PROTOCORE_UBX_CFG_RATE 0x08 ///< CFG-RATE: set the measurement / navigation rate
215#define PROTOCORE_UBX_TIME_REF_UTC 0 ///< CFG-RATE timeRef: align measurements to UTC
216#define PROTOCORE_UBX_TIME_REF_GPS 1 ///< CFG-RATE timeRef: align measurements to GPS time
217
218/**
219 * @brief Build a CFG-MSG that sets how often (@p cls, @p id) is emitted on the current port.
220 *
221 * @p rate is in navigation solutions: 0 disables the message, 1 emits it every solution, N every Nth.
222 * This is the short (3-octet) form that targets the port the command arrives on.
223 * @return the frame length (11) or 0 on overflow / a null buffer.
224 */
225size_t protocore_ubx_build_cfg_msg(uint8_t *buf, size_t cap, uint8_t cls, uint8_t id, uint8_t rate);
226
227/**
228 * @brief Build a CFG-RATE that sets the measurement + navigation rate.
229 * @param meas_rate_ms the measurement period in ms (e.g. 200 for 5 Hz).
230 * @param nav_rate navigation solutions per measurement cycle (usually 1).
231 * @param time_ref the reference the measurements align to (@ref PROTOCORE_UBX_TIME_REF_UTC / _GPS).
232 * @return the frame length (14) or 0 on overflow / a null buffer.
233 */
234size_t protocore_ubx_build_cfg_rate(uint8_t *buf, size_t cap, uint16_t meas_rate_ms, uint16_t nav_rate,
235 uint16_t time_ref);
236
237/** @brief Result of feeding one byte to the streaming demultiplexer. */
238enum protocore_ubx_feed
239{
240 PROTOCORE_UBX_NONE = 0, ///< byte consumed inside a (partial or discarded) UBX frame
241 PROTOCORE_UBX_FRAME = 1, ///< a complete, checksum-valid frame is in @p out
242 PROTOCORE_UBX_PASSTHROUGH = 2, ///< byte is not part of any UBX frame (returned in @p passthrough)
243 PROTOCORE_UBX_OVERFLOW = 3 ///< a frame whose declared length exceeds PROTOCORE_UBX_MAX_PAYLOAD; skipped
244};
245
246/** @brief Streaming demultiplexer state for a mixed NMEA + UBX byte stream. Zero-initialize / init. */
247typedef struct
248{
249 uint8_t state; ///< internal parser state
250 uint8_t cls; ///< class of the frame in progress
251 uint8_t id; ///< id of the frame in progress
252 uint16_t len; ///< declared payload length of the frame in progress
253 uint16_t idx; ///< payload bytes received so far
254 uint32_t skip; ///< bytes left to discard for an over-long frame
255 uint8_t ck_a; ///< running checksum A
256 uint8_t ck_b; ///< running checksum B
257 uint8_t rx_ck_a; ///< received checksum A (awaiting B to compare)
258 uint8_t buf[PROTOCORE_UBX_MAX_PAYLOAD]; ///< payload accumulator
259} protocore_ubx_stream;
260
261/** @brief Reset a demux to the idle (hunting-for-sync) state. */
262void protocore_ubx_stream_init(protocore_ubx_stream *st);
263
264/**
265 * @brief Feed one byte. Returns a ::protocore_ubx_feed code: PROTOCORE_UBX_FRAME (@p out filled, payload aliases
266 * the stream buffer, valid until the next feed), PROTOCORE_UBX_PASSTHROUGH (@p passthrough set to a
267 * non-UBX byte - hand it to your NMEA assembler), PROTOCORE_UBX_OVERFLOW (an over-long frame was skipped),
268 * or PROTOCORE_UBX_NONE.
269 */
270int protocore_ubx_stream_feed(protocore_ubx_stream *st, uint8_t b, protocore_ubx *out, uint8_t *passthrough);
271
273
274#endif // PROTOCORE_ENABLE_UBX
275
276#endif // PROTOCORE_UBX_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97