DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
rtcm3.h
Go to the documentation of this file.
1// 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 (DETWS_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 DETERMINISTICESPASYNCWEBSERVER_RTCM3_H
28#define DETERMINISTICESPASYNCWEBSERVER_RTCM3_H
29
30#include "ServerConfig.h"
31
32#if DETWS_ENABLE_NTRIP_CASTER
33
34#include <stddef.h>
35#include <stdint.h>
36
37#define RTCM3_PREAMBLE 0xD3u ///< frame start byte
38#define RTCM3_HDR_LEN 3 ///< preamble (1) + 6 reserved bits + 10-bit length (2)
39#define RTCM3_CRC_LEN 3 ///< trailing CRC-24Q
40#define RTCM3_MAX_PAYLOAD 1023
41#define RTCM3_MAX_FRAME (RTCM3_HDR_LEN + RTCM3_MAX_PAYLOAD + RTCM3_CRC_LEN)
42
43// ---------------------------------------------------------------------------------------------
44// CRC-24Q (the RTCM3 / Qualcomm CRC): polynomial 0x1864CFB, initial value 0, no reflection.
45// ---------------------------------------------------------------------------------------------
46
47/** @brief CRC-24Q over @p len bytes of @p data (returned in the low 24 bits). */
48uint32_t rtcm3_crc24q(const uint8_t *data, size_t len);
49
50// ---------------------------------------------------------------------------------------------
51// MSB-first bit I/O (RTCM3 packs fields back to back, most-significant bit first, up to 64 bits).
52// ---------------------------------------------------------------------------------------------
53
54/// A bit cursor over a byte buffer for writing MSB-first fields. Pre-zero the buffer before writing.
55struct RtcmBitWriter
56{
57 uint8_t *buf;
58 size_t cap_bits; ///< capacity in bits
59 size_t pos; ///< current bit offset
60 bool ok; ///< cleared if a write would overflow
61};
62
63/** @brief Start a writer over @p buf (@p cap bytes). The buffer must already be zeroed. */
64void rtcm_bw_init(RtcmBitWriter *w, uint8_t *buf, size_t cap);
65/** @brief Append @p nbits (1..64) of @p val, MSB-first (unsigned). */
66void rtcm_bw_u(RtcmBitWriter *w, uint64_t val, uint8_t nbits);
67/** @brief Append @p nbits (1..64) of @p val as two's-complement (signed). */
68void rtcm_bw_s(RtcmBitWriter *w, int64_t val, uint8_t nbits);
69
70/** @brief Read @p nbits (1..64) unsigned, MSB-first, advancing @p pos (bit offset). */
71uint64_t rtcm_br_u(const uint8_t *buf, size_t *pos, uint8_t nbits);
72/** @brief Read @p nbits (1..64) as a sign-extended two's-complement value, advancing @p pos. */
73int64_t rtcm_br_s(const uint8_t *buf, size_t *pos, uint8_t nbits);
74
75// ---------------------------------------------------------------------------------------------
76// Transport frame.
77// ---------------------------------------------------------------------------------------------
78
79/// A parsed RTCM3 frame view (payload points into the caller's buffer).
80struct Rtcm3Frame
81{
82 uint16_t msg_type; ///< DF002 message number (first 12 payload bits)
83 const uint8_t *payload; ///< start of the payload inside the parsed buffer
84 uint16_t payload_len; ///< payload length in bytes
85 bool crc_ok; ///< the trailing CRC-24Q matched
86};
87
88/**
89 * @brief Parse one RTCM3 frame beginning at @p buf[0] (which must be the preamble).
90 *
91 * @return the total frame length (header + payload + CRC) when a whole frame is buffered - with
92 * @p out->crc_ok reflecting the CRC check - or 0 when @p buf does not yet hold the full frame.
93 * Use rtcm3_sync() first to align @p buf to a preamble in a byte stream.
94 */
95size_t rtcm3_frame_parse(const uint8_t *buf, size_t len, Rtcm3Frame *out);
96
97/** @brief Index of the next 0xD3 preamble in @p buf, or @p len if there is none. */
98size_t rtcm3_sync(const uint8_t *buf, size_t len);
99
100/**
101 * @brief Wrap @p payload (@p payload_len bytes) in a full frame: preamble + length + payload + CRC-24Q.
102 * @return the total frame length written to @p out, or 0 if @p cap is too small or payload_len > 1023.
103 */
104size_t rtcm3_frame_build(uint8_t *out, size_t cap, const uint8_t *payload, uint16_t payload_len);
105
106// ---------------------------------------------------------------------------------------------
107// Message 1005 / 1006 - Stationary Antenna Reference Point. Coordinates are ECEF in 0.1 mm units.
108// ---------------------------------------------------------------------------------------------
109
110/// Decoded 1005 / 1006 antenna reference point.
111struct Rtcm3StationArp
112{
113 uint16_t station_id;
114 int64_t ecef_x_01mm; ///< ECEF X in 0.1 mm (RTCM3 0.0001 m resolution)
115 int64_t ecef_y_01mm;
116 int64_t ecef_z_01mm;
117 uint16_t antenna_height_01mm; ///< 1006 only (0 for 1005)
118 bool has_height; ///< true for 1006
119};
120
121/** @brief Build a full 1005 frame (ARP, no antenna height). @return frame length or 0. */
122size_t rtcm3_build_1005(uint8_t *out, size_t cap, uint16_t station_id, int64_t ecef_x_01mm, int64_t ecef_y_01mm,
123 int64_t ecef_z_01mm);
124
125/** @brief Build a full 1006 frame (ARP with antenna height). @return frame length or 0. */
126size_t rtcm3_build_1006(uint8_t *out, size_t cap, uint16_t station_id, int64_t ecef_x_01mm, int64_t ecef_y_01mm,
127 int64_t ecef_z_01mm, uint16_t antenna_height_01mm);
128
129/**
130 * @brief Decode a 1005 / 1006 payload (not the framed message) into @p out.
131 * @return true if @p payload is a well-formed 1005 or 1006; false otherwise.
132 */
133bool rtcm3_parse_1005(const uint8_t *payload, uint16_t payload_len, Rtcm3StationArp *out);
134
135#endif // DETWS_ENABLE_NTRIP_CASTER
136
137#endif // DETERMINISTICESPASYNCWEBSERVER_RTCM3_H
User-facing configuration for DeterministicESPAsyncWebServer.