ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
rtcm3.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 rtcm3.h
6 * @brief RTCM 3.x framing + station-reference message codec (PROTOCORE_ENABLE_NTRIP_CASTER).
7 *
8 * The pure, zero-heap, host-testable core of the GNSS RTK base / NTRIP caster: the RTCM3 transport frame
9 * (0xD3 preamble, 6 reserved + 10-bit length, payload, 24-bit CRC-24Q) and the MSB-first bit I/O every
10 * RTCM3 message is packed with, plus encode/decode of the Stationary Antenna Reference Point messages
11 * 1005 (no height) and 1006 (with antenna height) that advertise the base's surveyed position.
12 *
13 * RTCM 10403.x numbers every field (DFxxx) and packs them big-endian, MSB-first, with no byte alignment
14 * inside the payload; this codec follows that layout exactly and is verified byte-for-byte against pyrtcm
15 * / RTKLIB. ECEF coordinates use the standard 0.0001 m (0.1 mm) resolution as 38-bit signed integers, so
16 * they are carried here in units of 0.1 mm.
17 *
18 * Observation messages (the MSM sets 1074/1077/1084/... that actually let a rover fix ambiguities) are
19 * built from a receiver's raw carrier-phase / pseudorange measurements and are added when a raw-capable
20 * receiver (u-blox RXM-RAWX: F9P / M8T class) drives the base; this file is the framing + reference-point
21 * foundation they and the caster share.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_RTCM3_H
28#define PROTOCORE_RTCM3_H
29
30#include "protocore_config.h" // the entry point: protocore_types.h for the widths
31
32#if PROTOCORE_ENABLE_NTRIP_CASTER
33
35
36#define RTCM3_PREAMBLE 0xD3u ///< frame start byte
37#define RTCM3_HDR_LEN 3 ///< preamble (1) + 6 reserved bits + 10-bit length (2)
38#define RTCM3_CRC_LEN 3 ///< trailing CRC-24Q
39#define RTCM3_MAX_PAYLOAD 1023
40#define RTCM3_MAX_FRAME (RTCM3_HDR_LEN + RTCM3_MAX_PAYLOAD + RTCM3_CRC_LEN)
41
42// ---------------------------------------------------------------------------------------------
43// CRC-24Q (the RTCM3 / Qualcomm CRC): polynomial 0x1864CFB, initial value 0, no reflection.
44// ---------------------------------------------------------------------------------------------
45
46/** @brief CRC-24Q over @p len bytes of @p data (returned in the low 24 bits). */
47uint32_t protocore_rtcm3_crc24q(const uint8_t *data, size_t len);
48
49// ---------------------------------------------------------------------------------------------
50// MSB-first bit I/O (RTCM3 packs fields back to back, most-significant bit first, up to 64 bits).
51// ---------------------------------------------------------------------------------------------
52
53/// A bit cursor over a byte buffer for writing MSB-first fields. Pre-zero the buffer before writing.
54typedef struct
55{
56 uint8_t *buf;
57 size_t cap_bits; ///< capacity in bits
58 size_t pos; ///< current bit offset
59 proto_bool ok; ///< cleared if a write would overflow
60} RtcmBitWriter;
61
62/** @brief Start a writer over @p buf (@p cap bytes). The buffer must already be zeroed. */
63void protocore_rtcm_bw_init(RtcmBitWriter *w, uint8_t *buf, size_t cap);
64/** @brief Append @p nbits (1..64) of @p val, MSB-first (unsigned). */
65void protocore_rtcm_bw_u(RtcmBitWriter *w, uint64_t val, uint8_t nbits);
66/** @brief Append @p nbits (1..64) of @p val as two's-complement (signed). */
67void protocore_rtcm_bw_s(RtcmBitWriter *w, int64_t val, uint8_t nbits);
68
69/** @brief Read @p nbits (1..64) unsigned, MSB-first, advancing @p pos (bit offset). */
70uint64_t protocore_rtcm_br_u(const uint8_t *buf, size_t *pos, uint8_t nbits);
71/** @brief Read @p nbits (1..64) as a sign-extended two's-complement value, advancing @p pos. */
72int64_t protocore_rtcm_br_s(const uint8_t *buf, size_t *pos, uint8_t nbits);
73
74// ---------------------------------------------------------------------------------------------
75// Transport frame.
76// ---------------------------------------------------------------------------------------------
77
78/// A parsed RTCM3 frame view (payload points into the caller's buffer).
79typedef struct
80{
81 uint16_t msg_type; ///< DF002 message number (first 12 payload bits)
82 const uint8_t *payload; ///< start of the payload inside the parsed buffer
83 uint16_t payload_len; ///< payload length in bytes
84 proto_bool crc_ok; ///< the trailing CRC-24Q matched
85} Rtcm3Frame;
86
87/**
88 * @brief Parse one RTCM3 frame beginning at @p buf[0] (which must be the preamble).
89 *
90 * @return the total frame length (header + payload + CRC) when a whole frame is buffered - with
91 * @p out->crc_ok reflecting the CRC check - or 0 when @p buf does not yet hold the full frame.
92 * Use protocore_rtcm3_sync() first to align @p buf to a preamble in a byte stream.
93 */
94size_t protocore_rtcm3_frame_parse(const uint8_t *buf, size_t len, Rtcm3Frame *out);
95
96/** @brief Index of the next 0xD3 preamble in @p buf, or @p len if there is none. */
97size_t protocore_rtcm3_sync(const uint8_t *buf, size_t len);
98
99/**
100 * @brief Wrap @p payload (@p payload_len bytes) in a full frame: preamble + length + payload + CRC-24Q.
101 * @return the total frame length written to @p out, or 0 if @p cap is too small or payload_len > 1023.
102 */
103size_t protocore_rtcm3_frame_build(uint8_t *out, size_t cap, const uint8_t *payload, uint16_t payload_len);
104
105// ---------------------------------------------------------------------------------------------
106// Message 1005 / 1006 - Stationary Antenna Reference Point. Coordinates are ECEF in 0.1 mm units.
107// ---------------------------------------------------------------------------------------------
108
109/// Decoded 1005 / 1006 antenna reference point.
110typedef struct
111{
112 uint16_t station_id;
113 int64_t ecef_x_01mm; ///< ECEF X in 0.1 mm (RTCM3 0.0001 m resolution)
114 int64_t ecef_y_01mm;
115 int64_t ecef_z_01mm;
116 uint16_t antenna_height_01mm; ///< 1006 only (0 for 1005)
117 proto_bool has_height; ///< true for 1006
118} Rtcm3StationArp;
119
120/** @brief Build a full 1005 frame (ARP, no antenna height). @return frame length or 0. */
121size_t protocore_rtcm3_build_1005(uint8_t *out, size_t cap, uint16_t station_id, int64_t ecef_x_01mm,
122 int64_t ecef_y_01mm, int64_t ecef_z_01mm);
123
124/** @brief Build a full 1006 frame (ARP with antenna height). @return frame length or 0. */
125size_t protocore_rtcm3_build_1006(uint8_t *out, size_t cap, uint16_t station_id, int64_t ecef_x_01mm,
126 int64_t ecef_y_01mm, int64_t ecef_z_01mm, uint16_t antenna_height_01mm);
127
128/**
129 * @brief Decode a 1005 / 1006 payload (not the framed message) into @p out.
130 * @return true if @p payload is a well-formed 1005 or 1006; false otherwise.
131 */
132proto_bool protocore_rtcm3_parse_1005(const uint8_t *payload, uint16_t payload_len, Rtcm3StationArp *out);
133
135
136#endif // PROTOCORE_ENABLE_NTRIP_CASTER
137
138#endif // PROTOCORE_RTCM3_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