AN-002: RTCM SC-104 Frame Structure & Message Type Reference
Document Scope
AN-002 is a field-level RTCM SC-104 / RTCM 3.x reference for engineers writing or validating RTCM binary parsers. It focuses on frame synchronization, CRC-24Q verification, non-byte-aligned payload extraction, MSM field layout, message type lookup, and base-station interoperability messages. The naming follows the RTCM SC-104 convention maintained by the Radio Technical Commission for Maritime Services.
This document is written as a dictionary-style Application Note. For RTK correction strategy, MSM selection, and field-debugging workflow, read RTCM Unpacked. For receiver-side ASCII output parsing, use AN-001 — NMEA 0183 Sentence Dictionary.
Part 1 — Protocol Architecture & Parsing Fundamentals
1.1 Data Type Conventions
RTCM 3.x payloads are encoded as a continuous, non-byte-aligned bit stream in big-endian (MSB first) order. All field positions and widths in this document are specified in bits, not bytes. Developers must use bit-level extraction functions (see Appendix A) to read fields that span byte boundaries.
RTCM 3.x payloads use non-byte-aligned bit streams constructed from three base binary data types:
| Type | Name | Description |
|---|---|---|
bit(n) |
Bit String | Raw bit pattern of n bits. No numeric interpretation. Used for masks and indicators. |
uint(n) |
Unsigned Integer | Unsigned integer of n bits. Range: 0 to 2n−1. |
int(n) |
Signed Integer | Two's Complement signed integer of n bits. Range: −2n−1 to 2n−1−1. |
Scale Factor Rule: Many DF fields encode physical quantities as scaled integers. The conversion is:
Physical Value = Extracted Integer × Resolution
Resolution values are specified in the Notes column of each field table throughout this document.
Invalid Value Convention: Certain DF fields use a designated bit pattern (typically all-ones or a specific hex value) to indicate that the observation is missing or invalid. These are marked in the Notes column of each field table (e.g., 255 = invalid, 0x4000 = invalid).
1.2 General Frame Structure
Every RTCM 3.x message is encapsulated in a fixed-format binary frame. Unlike NMEA 0183's ASCII sentences delimited by $ and <CR><LF>, RTCM frames have no human-readable delimiters. Frame boundaries must be established programmatically through preamble detection, length parsing, and CRC verification.
An RTCM 3.x frame has a fixed byte-level wrapper around a variable-length payload; a parser should validate the preamble, declared payload length, and CRC before dispatching the message type:
|
Preamble
8 bits
0xD3 |
Reserved
6 bits
000000 |
Length
10 bits
0–1023
|
Payload
Variable (0–1023 bytes)
First 12 bits = DF002
|
CRC-24Q
24 bits
3 bytes
|
The byte-level RTCM 3.x frame wrapper consists of the following fields:
| Field | Bits | Description |
|---|---|---|
| Preamble | 8 | Fixed sync byte: 0xD3 (binary 11010011). Scan the incoming byte stream for this value to begin frame synchronization. |
| Reserved | 6 | Reserved bits. Must be 000000. If non-zero after a 0xD3 byte, this is a false preamble — resume scanning. |
| Length | 10 | Payload length in bytes (0–1023). Maximum frame size: 3 (header) + 1023 (payload) + 3 (CRC) = 1029 bytes. |
| Payload | Variable | Message content. The first 12 bits of the payload always contain DF002 (Message Type number, 0–4095). |
| CRC-24Q | 24 | 24-bit CRC computed over all bytes from the Preamble through the end of the Payload (not including the CRC itself). |
1.3 CRC-24Q Algorithm
The CRC-24Q is computed over all bytes from Preamble through the end of Payload. If the computed CRC does not match the 3-byte CRC field appended to the frame, the entire frame must be discarded.
RTCM 3.x uses CRC-24Q to protect the frame header and payload; parser implementations should reject the frame before decoding any message fields when the CRC check fails.
| Parameter | Value |
|---|---|
| Polynomial | 0x1864CFB |
| Initial Value | 0x000000 |
| Input Range | Byte 0 (Preamble) through last byte of Payload |
| Output | 24-bit unsigned integer, transmitted MSB first |
C language reference implementation:
#define CRC24Q_POLY 0x1864CFB
unsigned int crc24q(const unsigned char *buf, int len) {
unsigned int crc = 0;
int i, j;
for (i = 0; i < len; i++) {
crc ^= (unsigned int)buf[i] << 16;
for (j = 0; j < 8; j++) {
crc <<= 1;
if (crc & 0x1000000) crc ^= CRC24Q_POLY;
}
}
return crc & 0xFFFFFF;
}
1.4 Frame Synchronization
RTCM 3.x frames contain no line-ending characters or inter-frame delimiters. When reading from a TCP socket or serial port, incoming bytes arrive as an arbitrary continuous stream. The parser must implement a state machine to locate frame boundaries:
A streaming RTCM parser should be implemented as a byte-level state machine because TCP, USB, and UART reads do not preserve RTCM frame boundaries:
| State | Action |
|---|---|
SYNC_PREAMBLE |
Scan byte-by-byte for 0xD3. On match, advance to next state. |
READ_HEADER |
Read next 2 bytes. Verify Reserved bits (upper 6 bits of byte 1) are zero. Extract 10-bit Length field. If Reserved ≠ 0, return to SYNC. |
READ_PAYLOAD |
Buffer exactly Length bytes of payload data. |
VERIFY_CRC |
Read 3 CRC bytes. Compute CRC-24Q over header + payload (3 + Length bytes). If mismatch, return to SYNC at the byte following the original preamble. |
DISPATCH |
Extract DF002 (first 12 bits of payload) to determine Message Type. Route to the appropriate decoder. |
*Remark: On CRC failure, do not skip the entire frame — the 0xD3 byte that triggered this attempt may have been a coincidental data byte, and a real preamble may exist within the bytes already consumed. Robust implementations rescan from preamble_position + 1.
TCP/Serial Buffer Handling: TCP delivers data in arbitrarily-sized chunks. A recv() call may return a partial frame, multiple frames, or a frame split across two calls. The parser must maintain a persistent ring buffer or byte queue across read operations and never assume a single read contains a complete frame.
Part 2 — Multiple Signal Messages (MSM), MT1071–MT1137
2.1 Constellation Assignment
MSM messages use a systematic numbering scheme: each constellation is assigned a block of 7 message types (MSM1 through MSM7). RTCM 3 Multiple Signal Messages group satellite observations by constellation, mapping message types MT1071 through MT1137 to specific GNSS signal families and time epochs:
| MT Range | Constellation | Epoch Time Basis | Notes |
|---|---|---|---|
1071–1077 |
GPS | Milliseconds of GPS week | DF004, uint(30)
|
1081–1087 |
GLONASS | Milliseconds of day (UTC+3) | DF034, uint(27). 3-bit day-of-week prepended. |
1091–1097 |
Galileo | Milliseconds of Galileo week | DF248, uint(30)
|
1101–1107 |
SBAS | Milliseconds of GPS week | DF004, uint(30)
|
1111–1117 |
QZSS | Milliseconds of GPS week | DF004, uint(30)
|
1121–1127 |
BeiDou | Milliseconds of BDS week | DF427, uint(30). BDT epoch: Jan 1, 2006. |
1131–1137 |
NavIC/IRNSS | Milliseconds of GPS week | V3.3+ |
2.2 MSM Type Definitions (Last Digit → Content)
RTCM MSM message numbers use the last digit to identify the observation content level, while the first three digits identify the constellation family:
| Last Digit | MSM Type | Observation Fields Included | Parser Implication |
|---|---|---|---|
| 1 | MSM1 | Compact pseudorange observations. | Decode pseudorange cells only; no carrier phase. |
| 2 | MSM2 | Compact carrier-phase observations. | Decode phase cells without pseudorange pairing. |
| 3 | MSM3 | Compact pseudorange and carrier-phase observations. | Use both fine pseudorange and fine phase-range fields. |
| 4 | MSM4 | Pseudorange, phase range, lock time, half-cycle flag, and CNR. | Common full observation payload without Doppler. |
| 5 | MSM5 | MSM4 fields plus rough and fine phase-range rate. | Add Doppler/range-rate field extraction. |
| 6 | MSM6 | High-resolution MSM4-style observations. | Use high-resolution pseudorange, phase, and CNR scaling. |
| 7 | MSM7 | High-resolution MSM5-style observations. | Decode high-resolution observation fields plus Doppler/range-rate. |
*Reference note: this table identifies payload content for parser implementation. For deployment-level MSM selection, see RTCM Unpacked.
2.3 MSM Payload Architecture
Every MSM message payload is organized in three consecutive segments. The Header is fixed-structure (though Cell Mask length varies). The Satellite Data and Signal Data segments have lengths determined dynamically by the mask values parsed from the Header.
An MSM payload is decoded in layers: fixed header fields first, then satellite and signal masks, then variable-length observation data derived from the active cell mask:
|
MSM Header
(fixed + masks)
|
Satellite Data × N_sat
(per-satellite block)
|
Signal Data × N_cell
(per-cell block)
|
The number of Satellite Data blocks (N_sat) and Signal Data blocks (N_cell) cannot be known until the three mask fields in the Header have been read and decoded. See Section 2.5 for the dynamic length calculation algorithm.
2.4 MSM Header (DF Field Reference)
The MSM header is a fixed field sequence followed by variable masks. Start Bit values in this table are measured from the first bit of the RTCM message payload, starting at DF002, not from the outer 0xD3 frame header:
| DF# | Field Name | Type | Bits | Start Bit | Notes |
|---|---|---|---|---|---|
| DF002 | Message Number | uint(12) |
12 | 0 |
e.g., 1077 = GPS MSM7, 1124 = BDS MSM4. |
| DF003 | Reference Station ID | uint(12) |
12 | 12 |
0–4095. Matches Station ID in MT1005/1006. |
| DF004 * | GNSS Epoch Time | uint(30) |
30 | 24 |
Resolution: 1 ms. Time basis varies by constellation (see Section 2.1). * DF number varies by constellation. |
| DF393 | Multiple Message Bit | bit(1) |
1 | 54 |
1 = additional MSM messages follow for the same epoch. 0 = last (or only) MSM for this epoch. |
| DF409 | IODS (Issue of Data Station) | uint(3) |
3 | 55 |
Incremented when station parameters change. |
| — | Reserved | bit(7) |
7 | 58 |
Reserved for future use. |
| DF411 | Clock Steering Indicator | uint(2) |
2 | 65 |
0=unknown, 1=not applied, 2=applied, 3=unknown. |
| DF412 | External Clock Indicator | uint(2) |
2 | 67 |
0=internal, 1=external locked, 2=external unlocked, 3=unknown. |
| DF417 | Divergence-free Smoothing | bit(1) |
1 | 69 |
0=not used, 1=divergence-free smoothing applied. |
| DF418 | Smoothing Interval | uint(3) |
3 | 70 |
0=no smoothing. Values 1–7 map to intervals from <30s to unlimited. |
| — | Satellite Mask | bit(64) |
64 | 73 |
1 bit per satellite slot. Bit position maps to satellite PRN/slot number. See Section 2.5. |
| — | Signal Mask | bit(32) |
32 | 137 |
1 bit per signal type. Signal-mask index maps to a signal code (see Section 2.8). See Section 2.5. |
| — | Cell Mask | bit(N_sat × N_sig) |
var | 169 |
Presence matrix. Cell[i][j] = 1 → satellite i has observation data for signal j. See Section 2.5. |
*Remark: Start Bit values in this table are measured from the first bit of the RTCM message payload, starting at DF002, not from the outer 0xD3 frame header. The fixed MSM header portion from DF002 through DF418 is 73 bits. After the 64-bit Satellite Mask and 32-bit Signal Mask, the Cell Mask begins at bit 169. Total MSM header length is 169 + (N_sat × N_sig) bits.
2.5 The Mask Matrix & Dynamic Length Calculation
The three mask fields in the MSM Header form a two-dimensional matrix that determines how many data blocks follow, and in what order. Incorrect mask handling is the primary source of buffer overrun and data misalignment bugs in RTCM parsers.
Step-by-step algorithm:
// Step 1: Count satellites with data in this frame
N_sat = popcount(Satellite_Mask); // number of set bits in 64-bit mask
// Step 2: Count signal types present in this frame
N_sig = popcount(Signal_Mask); // number of set bits in 32-bit mask
// Step 3: Read the Cell Mask (variable length)
Cell_Mask = read_bits(N_sat * N_sig);
// Step 4: Count actual observation cells
N_cell = popcount(Cell_Mask); // total signal data blocks to parse
// Satellite Data segment: N_sat blocks, each containing per-satellite fields
// Signal Data segment: N_cell blocks, each containing per-cell fields
Data ordering rules:
- Satellite Data blocks are ordered by the set-bit positions in the Satellite Mask, from the lowest bit index to the highest.
- Signal Data blocks are ordered in satellite-major order: iterate through each satellite (in Satellite Mask bit order), and within each satellite, iterate through its signal types (in Signal Mask bit order), including only cells where the corresponding Cell Mask bit is 1.
Worked example:
Satellite Mask: bits 2, 5, 11 are set → N_sat = 3 (PRN 3, 6, 12)
Signal Mask: bits 1, 4 are set → N_sig = 2 (e.g., L1 C/A, L2C)
Cell Mask (3 × 2 = 6 bits): read in transmission order →
MSM payload length is data-dependent because the Cell Mask determines how many satellite-signal observation cells are present in the message.
| Cell Index | Satellite | Signal | Mask Bit | Result |
|---|---|---|---|---|
| 0 | PRN 3 (Sat Mask bit 2) | L1 C/A (Sig Mask bit 1) | 1 |
→ Signal Data Cell 1 |
| 1 | PRN 3 (Sat Mask bit 2) | L2C (Sig Mask bit 4) | 1 |
→ Signal Data Cell 2 |
| 2 | PRN 6 (Sat Mask bit 5) | L1 C/A (Sig Mask bit 1) | 1 |
→ Signal Data Cell 3 |
| 3 | PRN 6 (Sat Mask bit 5) | L2C (Sig Mask bit 4) | 0 |
— skipped (no observation) |
| 4 | PRN 12 (Sat Mask bit 11) | L1 C/A (Sig Mask bit 1) | 1 |
→ Signal Data Cell 4 |
| 5 | PRN 12 (Sat Mask bit 11) | L2C (Sig Mask bit 4) | 1 |
→ Signal Data Cell 5 |
N_cell = popcount(Cell_Mask) = 5
Signal Data contains 5 blocks, transmitted in the Cell order shown above.
*Critical Limit: The RTCM standard dictates that N_sat × N_sig ≤ 64. If the product of set bits in the Satellite Mask and Signal Mask exceeds 64, the Cell Mask is invalid and the frame must be discarded. Parsers must check this boundary before attempting to allocate memory or read the Cell Mask to prevent severe buffer overflows.
2.6 MSM Satellite Data Fields
One block of Satellite Data fields is transmitted per satellite (repeated N_sat times). Not all fields are present in every MSM type — the "Present in" column indicates which MSM types include each field.
*Important: Within each MSM frame, all values for a given DF are transmitted contiguously before the next DF begins. For example, in MSM7 with N_sat = 3: all three DF397 values are sent first, then all three DF398, then all three DF399, then all three DF404.
Satellite data fields are transmitted once per active satellite and provide rough range, phase range, and range-rate information before per-cell signal observations are decoded.
| DF# | Field Name | Type | Bits | Present in | Notes |
|---|---|---|---|---|---|
| DF397 | Rough Range Integer | uint(8) |
8 | MSM1–7 | Integer milliseconds of rough satellite range. Maximum representable distance ≈ 76,000 km (sufficient for MEO/GEO orbits). 255 = invalid (satellite excluded). |
| DF398 | Extended Satellite Info | uint(4) |
4 | MSM5, MSM7 | Extended satellite information (constellation-specific). For GLONASS: frequency channel number offset. |
| DF399 | Rough Range Modulo | uint(10) |
10 | MSM1–7 | Fractional milliseconds of rough range. Resolution: 2−10 ms ≈ 0.977 μs. Full rough range = DF397 + DF399 × 2−10 ms. |
| DF404 | Rough PhaseRange Rate | int(14) |
14 | MSM5, MSM7 |
(Satellite-level, rough). Resolution: 1 m/s. Range: ±8191 m/s. 0x2000 = invalid. See Section 2.7 for the signal-level fine Doppler (also labeled DF404, but int(15)). |
2.7 MSM Signal Data Fields
One block of Signal Data fields is transmitted per observation cell (repeated N_cell times). As with Satellite Data, all values for a given DF are transmitted contiguously across all cells before the next DF begins.
Signal data fields are transmitted per active observation cell and carry the fine pseudorange, fine carrier phase, lock time, half-cycle, and CNR values used by a rover decoder.
| DF# | Field Name | Type | Bits | Present in | Notes |
|---|---|---|---|---|---|
| DF400 | Fine Pseudorange | int(15) |
15 | MSM1, 3, 4, 5 | Resolution: 2−24 ms ≈ 17.9 mm in range domain. 0x4000 = invalid. |
| DF405 | Fine Pseudorange (High-Res) | int(20) |
20 | MSM6, MSM7 | Resolution: 2−29 ms ≈ 0.558 mm in range domain. 0x80000 = invalid. |
| DF401 | Fine PhaseRange | int(22) |
22 | MSM2, 3, 4, 5 | Resolution: 2−29 ms ≈ 0.558 mm in range domain. 0x200000 = invalid. |
| DF406 | Fine PhaseRange (High-Res) | int(24) |
24 | MSM6, MSM7 | Resolution: 2−31 ms ≈ 0.140 mm in range domain. 0x800000 = invalid. |
| DF402 | Lock Time Indicator | uint(4) |
4 | MSM1–5 | Phase lock duration indicator. 0 = <24 ms to 15 = ≥524 s. Non-linear scale. |
| DF407 | Lock Time Indicator (Extended) | uint(10) |
10 | MSM6, MSM7 | Extended lock time with finer resolution. 10-bit encoding. |
| DF420 | Half-cycle Ambiguity | bit(1) |
1 | MSM2–7 | 0 = resolved, 1 = unresolved. Unresolved half-cycle will degrade RTK fix quality. |
| DF403 | CNR (Carrier-to-Noise) | uint(6) |
6 | MSM4, MSM5 | dB-Hz. Resolution: 1 dB-Hz. Range: 0–63. |
| DF408 | CNR (High-Res) | uint(10) |
10 | MSM6, MSM7 | dB-Hz. Resolution: 0.0625 dB-Hz (1/16). Range: 0–63.9375. |
| DF404 | Fine PhaseRange Rate | int(15) |
15 | MSM5, MSM7 |
(Signal-level, fine.) Resolution: 0.0001 m/s. 0x4000 = invalid. See Section 2.6 for the satellite-level rough Doppler (also labeled DF404, but int(14)). |
*Remark on DF404: The RTCM standard reuses the DF404 identifier for two distinct Doppler fields at different data layers. In the Satellite Data segment (Section 2.6), DF404 is a 14-bit rough estimate at 1 m/s resolution. In the Signal Data segment (this section), DF404 is a 15-bit fine-resolution field at 0.0001 m/s. Parser implementations must extract the correct bit width depending on the payload segment currently being decoded.
2.8 Common MSM Signal IDs (RTKLIB Index & Mask Position)
Each MSM Signal Mask entry maps to a constellation-specific signal code. To avoid off-by-one ambiguity, the tables below show both the zero-based RTKLIB C array index and the equivalent one-based Signal Mask position:
*Indexing note: these compact tables follow the RTKLIB current-master msm_sig_*[32] arrays as an open-source implementation reference. RTKLIB Index is the zero-based C array slot used by decoder code; 1-Based Mask Position is RTKLIB Index + 1. The RTCM SC-104 standard remains the normative source for production implementations.
*Signal naming note: this section maps common RTCM MSM signal IDs to RTKLIB/RINEX-style observation codes. For the GNSS frequency background behind L1/L2/L5, E1/E5, B1/B2, QZSS, NavIC, and SBAS signals, see GNSS Signals Explained.
GPS Signal Codes
| RTKLIB Index 0-based |
1-Based Mask Position |
Code | Signal | Frequency | Notes |
|---|---|---|---|---|---|
| 1 | 2 | 1C |
L1 C/A | 1575.42 MHz | Primary civil signal. |
| 2 | 3 | 1P |
L1 P | 1575.42 MHz | Precise code tracking where available. |
| 3 | 4 | 1W |
L1 P(Y) | 1575.42 MHz | Semi-codeless P(Y) tracking. |
| 14 | 15 | 2S |
L2C M | 1227.60 MHz | Modernized L2 civil medium-length code. |
| 15 | 16 | 2L |
L2C L | 1227.60 MHz | Modernized L2 civil long code. |
| 16 | 17 | 2X |
L2C M+L | 1227.60 MHz | Combined L2C tracking. |
| 21 | 22 | 5I |
L5 I | 1176.45 MHz | L5 in-phase component. |
| 22 | 23 | 5Q |
L5 Q | 1176.45 MHz | L5 quadrature component. |
| 23 | 24 | 5X |
L5 I+Q | 1176.45 MHz | Combined L5 tracking. |
| 29 | 30 | 1S |
L1C D | 1575.42 MHz | Modernized L1C data component. |
| 30 | 31 | 1L |
L1C P | 1575.42 MHz | Modernized L1C pilot component. |
| 31 | 32 | 1X |
L1C D+P | 1575.42 MHz | Combined L1C tracking. |
GLONASS Signal Codes
| RTKLIB Index 0-based |
1-Based Mask Position |
Code | Signal | Frequency | Notes |
|---|---|---|---|---|---|
| 1 | 2 | 1C |
G1 C/A | 1602 + k×0.5625 MHz | FDMA civil signal; k is satellite channel number. |
| 2 | 3 | 1P |
G1 P | 1602 + k×0.5625 MHz | FDMA P-code. |
| 7 | 8 | 2C |
G2 C/A | 1246 + k×0.4375 MHz | FDMA L2 civil signal. |
| 8 | 9 | 2P |
G2 P | 1246 + k×0.4375 MHz | FDMA L2 P-code. |
| 10 | 11 | 3I |
G3 L3OC I | 1202.025 MHz | CDMA GLONASS-K signal; decoder support is implementation-dependent. |
| 11 | 12 | 3Q |
G3 L3OC Q | 1202.025 MHz | CDMA GLONASS-K signal; decoder support is implementation-dependent. |
| 12 | 13 | 3X |
G3 L3OC I+Q | 1202.025 MHz | Combined L3OC tracking. |
*GLONASS note: G1/G2 use FDMA, so each satellite transmits on a channel-dependent frequency. MT1230 Code-Phase Biases (Part 3.4) help rovers handle inter-manufacturer bias differences on FDMA signals.
Galileo Signal Codes
| RTKLIB Index 0-based |
1-Based Mask Position |
Code | Signal | Frequency | Notes |
|---|---|---|---|---|---|
| 1 | 2 | 1C |
E1 C | 1575.42 MHz | E1 Open Service pilot. |
| 2 | 3 | 1A |
E1 A | 1575.42 MHz | PRS / restricted signal component. |
| 3 | 4 | 1B |
E1 B | 1575.42 MHz | E1 Open Service data. |
| 4 | 5 | 1X |
E1 B+C | 1575.42 MHz | Combined E1 data+pilot tracking. |
| 5 | 6 | 1Z |
E1 A+B+C | 1575.42 MHz | Combined E1 tracking mode. |
| 7 | 8 | 6C |
E6 C | 1278.75 MHz | E6 pilot/ranging component. |
| 8 | 9 | 6A |
E6 A | 1278.75 MHz | E6 restricted component. |
| 9 | 10 | 6B |
E6 B | 1278.75 MHz | E6 data component. |
| 10 | 11 | 6X |
E6 B+C | 1278.75 MHz | Combined E6 tracking. |
| 11 | 12 | 6Z |
E6 A+B+C | 1278.75 MHz | Combined E6 tracking mode. |
| 13 | 14 | 7I |
E5b I | 1207.14 MHz | E5b in-phase. |
| 14 | 15 | 7Q |
E5b Q | 1207.14 MHz | E5b quadrature. |
| 15 | 16 | 7X |
E5b I+Q | 1207.14 MHz | Combined E5b tracking. |
| 17 | 18 | 8I |
E5 AltBOC I | 1191.795 MHz | Wideband E5 AltBOC in-phase. |
| 18 | 19 | 8Q |
E5 AltBOC Q | 1191.795 MHz | Wideband E5 AltBOC quadrature. |
| 19 | 20 | 8X |
E5 AltBOC I+Q | 1191.795 MHz | Combined E5 AltBOC tracking. |
| 21 | 22 | 5I |
E5a I | 1176.45 MHz | E5a in-phase. |
| 22 | 23 | 5Q |
E5a Q | 1176.45 MHz | E5a quadrature. |
| 23 | 24 | 5X |
E5a I+Q | 1176.45 MHz | Combined E5a tracking. |
BeiDou Signal Codes
| RTKLIB Index 0-based |
1-Based Mask Position |
Code | Signal | Frequency | Notes |
|---|---|---|---|---|---|
| 1 | 2 | 1I |
B1I I | 1561.098 MHz | RTKLIB current-master BeiDou B1I mapping. |
| 2 | 3 | 1Q |
B1I Q | 1561.098 MHz | RTKLIB current-master BeiDou B1I mapping. |
| 3 | 4 | 1X |
B1I I+Q | 1561.098 MHz | RTKLIB current-master BeiDou B1I mapping. |
| 7 | 8 | 6I |
B3I I | 1268.52 MHz | BeiDou B3 in-phase. |
| 8 | 9 | 6Q |
B3I Q | 1268.52 MHz | BeiDou B3 quadrature. |
| 9 | 10 | 6X |
B3I I+Q | 1268.52 MHz | Combined B3 tracking. |
| 13 | 14 | 7I |
B2I I | 1207.14 MHz | BeiDou B2 in-phase. Same RF band as Galileo E5b. |
| 14 | 15 | 7Q |
B2I Q | 1207.14 MHz | BeiDou B2 quadrature. |
| 15 | 16 | 7X |
B2I I+Q | 1207.14 MHz | Combined B2 tracking. |
*BeiDou mapping note: this compact table follows RTKLIB current-master msm_sig_cmp[] for parser validation. BeiDou signal-code naming can differ across RTCM/RINEX revisions and decoder implementations; use the RTCM SC-104 revision required by your product as the normative source.
QZSS, NavIC, and SBAS MSM Families
QZSS, NavIC/IRNSS, and SBAS use dedicated MSM message ranges in RTCM 3.3+. In parser code, treat them as separate constellation families with their own epoch rules and signal-mask interpretation, even when some signal names overlap with GPS-style L1/L2/L5 notation.
| Constellation | MSM Range | Epoch Field | Implementation Note |
|---|---|---|---|
| SBAS | MT1101–MT1107 |
DF004, GPS-week milliseconds |
Use the SBAS constellation branch; do not merge it into GPS solely because the epoch basis is GPS time. |
| QZSS | MT1111–MT1117 |
DF004, GPS-week milliseconds |
QZSS shares several GPS-like signal names, but the message type range and satellite ID mapping are distinct. |
| NavIC / IRNSS | MT1131–MT1137 |
RTCM 3.3+ constellation-specific epoch field | Implement as a separate MSM family. Older decoder code may not support NavIC MSM messages. |
Part 3 — Base Station & Interoperability Messages
3.1 Station Coordinates — MT1005
Transmits the Antenna Reference Point (ARP) position in ECEF coordinates. This message is mandatory for RTK operation — without base station coordinates, the rover cannot compute a baseline vector and will never achieve a fixed solution regardless of observation data quality.
MT1005 defines the reference station coordinate payload; these ECEF fields establish the base-station position used by downstream RTK solvers.
| DF# | Field Name | Type | Bits | Start Bit | Notes |
|---|---|---|---|---|---|
| DF002 | Message Number | uint(12) |
12 | 0 |
1005 |
| DF003 | Reference Station ID | uint(12) |
12 | 12 |
0–4095. |
| DF021 | ITRF Realization Year | uint(6) |
6 | 24 |
ITRF year indicator. 0 = unspecified. When zero, the rover's output coordinates inherit reference frame ambiguity — downstream systems cannot determine the correct frame for datum transformations. See Kalmix GNSS Handbook Vol. 4 (GCS) for frame alignment guidance. |
| DF022 | GPS Indicator | bit(1) |
1 | 30 |
1 = station provides GPS observations. |
| DF023 | GLONASS Indicator | bit(1) |
1 | 31 |
1 = station provides GLONASS observations. |
| DF024 | Galileo Indicator | bit(1) |
1 | 32 |
1 = station provides Galileo observations. |
| — | Reference Station Indicator | bit(1) |
1 | 33 |
0 = real/physical reference station. 1 = non-physical (virtual/network-generated). |
| DF025 | ARP ECEF-X | int(38) |
38 | 34 |
Resolution: 0.0001 m. Physical value = integer × 10−4 meters. |
| — | Single Receiver Oscillator | bit(1) |
1 | 72 |
0 = separate oscillators for L1/L2. 1 = single oscillator. |
| — | Reserved | bit(1) |
1 | 73 |
Reserved. |
| DF026 | ARP ECEF-Y | int(38) |
38 | 74 |
Resolution: 0.0001 m. |
| — | Quarter Cycle Indicator | bit(2) |
2 | 112 |
Quarter-cycle alignment status for GLONASS. |
| DF027 | ARP ECEF-Z | int(38) |
38 | 114 |
Resolution: 0.0001 m. |
*Remark: Total payload: 152 bits (19 bytes). The three ECEF coordinate fields (DF025/026/027) together consume 114 of 152 bits — 75% of the message is coordinate data. The int(38) type provides a range of approximately ±13,744 km from the Earth's center, sufficient for any terrestrial antenna position.
3.2 Station Coordinates + Antenna Height — MT1006
Identical to MT1005 with one additional field appended:
MT1006 extends MT1005 by adding antenna height, allowing a rover or post-processing tool to reconstruct the antenna reference point more precisely.
| DF# | Field Name | Type | Bits | Start Bit | Notes |
|---|---|---|---|---|---|
| All MT1005 fields (DF002 through DF027) as defined in Section 3.1, followed by: | |||||
| DF028 | Antenna Height | uint(16) |
16 | 152 |
Height of antenna above the ARP marker. Resolution: 0.0001 m. Range: 0–6.5535 m. |
*Remark: Total payload: 168 bits (21 bytes). MT1006 is preferred over MT1005 when the antenna is mounted on a known survey marker and the height tie is measured.
3.3 Receiver & Antenna Descriptors — MT1033
Carries human-readable text descriptions of the base station's receiver and antenna hardware. This message uses variable-length ASCII string fields, each preceded by a length indicator.
Field sequence (in transmission order):
MT1033 describes receiver, antenna, and serial-number metadata; it is useful for logging, traceability, and mixed-equipment base-station identification.
| # | Field | Parsing Rule |
|---|---|---|
| 1 | Message Number (DF002) |
uint(12) = 1033. |
| 2 | Reference Station ID (DF003) |
uint(12). |
| 3 | Antenna Descriptor Length |
uint(8). Number of ASCII characters to follow (0–31). |
| 4 | Antenna Descriptor |
char(n). Read n bytes as ASCII. Matches IGS antenna naming convention (e.g., LEIAR25.R4 LEIT). |
| 5 | Antenna Setup ID |
uint(8). 0 = default. |
| 6 | Antenna Serial Number Length |
uint(8). |
| 7 | Antenna Serial Number |
char(n). ASCII string. |
| 8 | Receiver Type Descriptor Length |
uint(8). |
| 9 | Receiver Type Descriptor |
char(n). ASCII string (e.g., TRIMBLE ALLOY). |
| 10 | Receiver Firmware Version Length |
uint(8). |
| 11 | Receiver Firmware Version |
char(n). ASCII string. |
| 12 | Receiver Serial Number Length |
uint(8). |
| 13 | Receiver Serial Number |
char(n). ASCII string. |
*Remark: The Antenna Descriptor string provides the antenna model name needed for Phase Center Offset (PCO) corrections. However, the rover must have a built-in antenna calibration database (e.g., derived from NGS/IGS calibration files) to act on this information. Low-cost GNSS modules without such a database will receive MT1033 but cannot apply PCO corrections from it.
3.4 GLONASS Code-Phase Biases — MT1230
GLONASS uses Frequency Division Multiple Access (FDMA), causing inter-frequency code-phase biases between receivers from different manufacturers. MT1230 provides bias correction values that enable the rover to compensate for these hardware differences.
MT1230 carries GLONASS code-phase bias information; decoders should parse it separately from MSM observations because it affects GLONASS interoperability rather than observation-cell layout.
| DF# | Field Name | Type | Bits | Start Bit | Notes |
|---|---|---|---|---|---|
| DF002 | Message Number | uint(12) |
12 | 0 |
1230 |
| DF003 | Reference Station ID | uint(12) |
12 | 12 |
|
| DF421 | Code-Phase Bias Indicator | bit(1) |
1 | 24 |
0 = default biases (zero). 1 = explicit bias values follow. |
| — | Reserved | bit(3) |
3 | 25 |
Reserved. |
| DF423 | FDMA Signals Mask | bit(4) |
4 | 28 |
Bit 0 = L1 C/A, Bit 1 = L1 P, Bit 2 = L2 C/A, Bit 3 = L2 P. Each set bit indicates a bias value follows. |
| DF424–427 | Code-Phase Bias Values |
int(16) × N |
16 each | 32 |
Resolution: 0.02 m. N = popcount(DF423). One value per signal flagged in DF423, transmitted in mask bit order. |
*Critical: Without MT1230 in the correction stream, the rover will observe GLONASS satellites but exclude them from the RTK solution without an explicit parser error because the inter-frequency biases cannot be resolved. This often appears in mixed-vendor base-rover combinations. Verify MT1230 whenever GLONASS satellites are visible but do not contribute to the fixed solution.
*Output validation note: if GLONASS satellites are visible but not used in the RTK solution, check both the RTCM MT1230 input and the receiver's NMEA output fields. For SCOUT PRO output behavior, see the SCOUT PRO protocol knowledge base.
Part 4 — Quick Reference Tables
4.1 Legacy Observation Messages (MT1001–MT1012)
Legacy observation messages predate the MSM framework and are limited to GPS and GLONASS on L1/L2 bands only. No message type numbers exist for Galileo or BeiDou in the Legacy scheme — this is a structural limitation of the standard, not a versioning gap, and is the fundamental reason MSM was created.
| MT | Constellation | Bands | Content | Notes |
|---|---|---|---|---|
| 1001 | GPS | L1 | Code + Phase | Minimal. Rarely used standalone. |
| 1002 | GPS | L1 | Code + Phase + Ambiguity + CNR | Most common Legacy GPS single-freq. |
| 1003 | GPS | L1+L2 | Code + Phase (dual-freq) | Dual-frequency without CNR. |
| 1004 | GPS | L1+L2 | Code + Phase + Ambiguity + CNR | Full Legacy GPS. Recommended minimum for Legacy RTK. |
| 1009 | GLONASS | L1 | Code + Phase | Minimal. |
| 1010 | GLONASS | L1 | Code + Phase + Ambiguity + CNR | Common single-freq GLONASS. |
| 1011 | GLONASS | L1+L2 | Code + Phase (dual-freq) | Dual-frequency without CNR. |
| 1012 | GLONASS | L1+L2 | Code + Phase + Ambiguity + CNR | Full Legacy GLONASS. |
*Remark: Legacy messages are still encountered in production, particularly from state CORS networks and older base stations. Some correction services (e.g., Swift Navigation Skylark) provide RTCM 3.1 fallback mountpoints using MT1004/1012. However, new deployments should use MSM (Part 2) for multi-constellation support.
4.2 Ephemeris Messages
Broadcast ephemeris data relayed from the base station's GNSS receiver. These messages duplicate information the rover can acquire directly from satellite signals.
Ephemeris messages carry satellite orbit and clock data; many real-time RTK links rely on rover-side navigation data, but parsers should still dispatch these message types correctly.
| MT | Constellation | Introduced | Notes |
|---|---|---|---|
| 1019 | GPS | V3.1 | GPS broadcast ephemeris parameters. |
| 1020 | GLONASS | V3.1 | GLONASS broadcast ephemeris parameters. |
| 1042 | BeiDou | V3.3 | BDS broadcast ephemeris parameters. |
| 1044 | QZSS | V3.2 | QZSS broadcast ephemeris parameters. |
| 1045 | Galileo (F/NAV) | V3.2 | E5a-based navigation message. |
| 1046 | Galileo (I/NAV) | V3.3 | E1-based navigation message. |
*Remark: Ephemeris decoding is handled by the GNSS receiver's firmware. Developers typically do not need to parse these messages manually. Primary use cases: reducing Time-To-First-Fix (TTFF) for cold-start rovers, and providing ephemeris data in raw RTCM logs for Post-Processed Kinematic (PPK) workflows.
4.3 SSR Messages (Overview)
State Space Representation (SSR) messages carry modeled error components for Precise Point Positioning (PPP) and PPP-RTK. These are not used in standard Observation Space Representation (OSR) RTK workflows.
SSR message types are used for state-space correction workflows rather than conventional OSR RTK streams, so AN-002 lists them only as a parser-recognition reference.
| MT Range | Category |
|---|---|
1057–1062 |
GPS SSR: orbit corrections, clock corrections, code biases. |
1063–1068 |
GLONASS SSR: orbit, clock, and code bias corrections. |
1240–1270 |
Multi-GNSS SSR: Galileo, QZSS, SBAS, BeiDou orbit/clock/bias corrections. |
1264 |
VTEC (Vertical Total Electron Content): ionospheric model for single-frequency PPP. |
*Remark: Standard OSR RTK correction streams (e.g., from CORS networks or services like Point One Polaris, Swift Skylark) do not contain SSR messages. SSR data is delivered via dedicated channels, often using the SPARTN format (e.g., u-blox PointPerfect) rather than RTCM encoding.
4.4 Common RTCM Stream Profiles for Parser Validation
Common RTCM stream profiles are listed here for parser test coverage and log inspection, not for selecting a production RTK mountpoint. These profiles often appear in NTRIP mountpoints and base-station output streams; deployment choices such as MSM4 versus MSM7 should be handled in RTCM Unpacked:
| Scenario | Required Messages | Optional |
|---|---|---|
| GPS-only RTK |
1074 or 1075 (GPS MSM4/5) + 1005
|
1019, 1033
|
| GPS + GLONASS RTK | + 1084/1085 (GLO MSM4/5) + 1230
|
1020 |
| 4-constellation RTK | + 1094/1095 (GAL) + 1124/1125 (BDS) + 1005 + 1230
|
1042, 1045/1046, 1033
|
| PPK Logging | MSM7 all constellations + 1005 + ephemeris (1019/1020/1042/1045) |
1230, 1033
|
| Sub-meter DGNSS |
1071/1081 (MSM1) + 1005
|
— |
*Critical: MT1230 is marked as required (not optional) whenever GLONASS MSM messages are included. Omitting it will cause the rover to discard GLONASS observations from the RTK solution without an explicit parser error.
4.5 Bandwidth Estimation
Approximate data rates for common message configurations at 1 Hz update rate. Actual values vary with satellite count and signal count.
RTCM stream bandwidth depends on constellation count, signal count, message rate, and MSM level; the table below gives practical parser/logging expectations rather than fixed protocol limits.
| Configuration | Approx. Rate | 9600 bps Feasible? |
|---|---|---|
| GPS-only MSM4 @ 1 Hz (~12 sats) | ~250 B/s | Yes |
| GPS + GLONASS MSM4 @ 1 Hz | ~450 B/s | Yes |
| 4-constellation MSM4 @ 1 Hz | ~900 B/s | Marginal (960 B/s limit) |
| 4-constellation MSM7 @ 1 Hz | ~1,300 B/s | No — requires ≥19200 bps |
| + ephemeris + MT1005 + MT1230 overhead | +50–100 B/s | — |
*Remark: 9600 bps (common for UHF radio links) provides a theoretical maximum throughput of 960 bytes/second, factoring in 8-N-1 serial framing overhead (10 bits per byte on the wire). In practice, protocol overhead reduces usable bandwidth to approximately 900 B/s. For 9600 baud deployments, GPS + GLONASS MSM4 is the practical upper limit.
4.6 RTCM ↔ RINEX 3
RTCM 3.x and RINEX 3 encode the same observation data in different representations: RTCM is a compact binary format designed for real-time streaming, while RINEX 3 is a human-readable text format designed for offline archival and post-processing.
MSM message structure was designed for conversion to and from RINEX 3 observation records. The signal-mask indices listed in Section 2.8 map to RTKLIB/RINEX-style observation codes, and the RTKLIB convbin utility performs this conversion.
*Remark: RINEX 2 does not support multi-constellation observation encoding and cannot represent the full range of MSM signal types. Always use RINEX 3.0x or later when converting from MSM data. A standard PPK workflow is: record raw RTCM stream → convert to RINEX 3 via convbin → process with PPK engine (e.g., RTKLIB rnx2rtkp).
4.7 Open-Source Reference Implementations
The following RTKLIB components serve as the open-source implementation references for RTCM 3.x parsing and are useful for parser comparison or debugging:
Open-source RTCM implementations are useful for cross-checking parser behavior, CRC handling, and field scaling against known decoder code paths.
| Tool / Source | Type | Notes |
|---|---|---|
rtcm.c / rtcm3.c
|
C source | Core RTCM decoder/encoder. getbitu() and getbits() (see Appendix A) are the bit extraction primitives underlying all DF field parsing in this codebase. |
convbin |
CLI tool | RTCM ↔ RINEX 3 format conversion. Essential for PPK workflows. Validates MSM-to-RINEX signal code mapping. |
str2str |
CLI tool | NTRIP client and stream relay (serial/TCP/UDP). Primary tool for real-time RTCM stream monitoring and debugging. |
*Remark: RTKLIB's MSM decoding logic is concentrated in decode_msm_head() (mask parsing, Section 2.5) and decode_msm4() through decode_msm7() (observation extraction, Sections 2.6/2.7). Reading these functions is the most direct path to understanding how the Mask Matrix operates in practice.
*Reference-stream note: when comparing your parser against rtcm.c, capture repeatable binary input from known hardware before changing decoder logic. For module-level testing, see the Kalmix GUIDE Module.
Appendix A — Bit-Level Extraction Reference (C Language)
RTCM 3.x payloads are non-byte-aligned bit streams. The following two functions extract unsigned and signed integers of arbitrary bit width from any bit offset in a byte buffer. These are adapted from the RTKLIB open-source implementation and serve as the low-level primitives for all DF field extraction described in this document.
*Note: These functions handle byte-boundary crossing internally. Callers should not perform additional endianness conversions — the big-endian bit ordering of the RTCM stream is resolved within the extraction logic.
/* Extract unsigned integer of 'len' bits starting at bit position 'pos' */
unsigned int getbitu(const unsigned char *buff, int pos, int len) {
unsigned int bits = 0;
int i;
for (i = pos; i < pos + len; i++) {
bits = (bits << 1) + ((buff[i / 8] >> (7 - i % 8)) & 1u);
}
return bits;
}
/* Extract signed integer (Two's Complement) of 'len' bits */
int getbits(const unsigned char *buff, int pos, int len) {
unsigned int bits = getbitu(buff, pos, len);
if (len <= 0 || 32 <= len || !(bits & (1u << (len - 1)))) {
return (int)bits;
}
return (int)(bits | (~0u << len)); /* sign extension */
}
Usage example — extracting the Message Type (DF002) from a frame payload:
/* payload points to the first byte of the RTCM payload (after the 3-byte header) */
unsigned int msg_type = getbitu(payload, 0, 12); /* DF002: bits 0-11 */
unsigned int sta_id = getbitu(payload, 12, 12); /* DF003: bits 12-23 */
unsigned int epoch = getbitu(payload, 24, 30); /* DF004: bits 24-53 */
Part 5 — Frequently Asked Questions
Can I read RTCM 3.x messages using a standard serial terminal or text logger?
No. Unlike NMEA 0183, RTCM 3.x is a binary protocol, not a line-based ASCII protocol. A serial monitor may show unreadable characters because RTCM frames do not use printable delimiters or newline-separated sentences. Capture the raw byte stream to a .bin file, inspect 0xD3 preambles with a hex viewer, or feed the stream into a binary RTCM decoder.
After a CRC-24Q failure, how should an RTCM parser resynchronize?
Do not trust the declared frame length after a CRC failure. A robust parser should continue byte-wise scanning for the next plausible 0xD3 preamble, then validate the reserved bits, payload length, and CRC before accepting the next frame. Some implementations resume at preamble + 1; the key rule is to resynchronize by validation, not by blindly skipping the corrupted length.
Why can't an RTCM 3.x decoder use C structs to parse the payload?
RTCM payload fields are packed by bit offset, and many fields cross byte boundaries. A C struct depends on compiler layout, padding, endianness, and byte alignment, so it cannot safely represent RTCM payloads. Use explicit bit extraction functions such as getbitu() and getbits() with verified start offsets and field widths.
How do MSM masks locate one satellite-signal observation cell?
The Satellite Mask selects active satellites, the Signal Mask selects active signal types, and the Cell Mask marks valid satellite-signal pairs. Cells are decoded in satellite-major order: for each selected satellite, iterate through selected signals and consume one Cell Mask bit for each pair. If N_sat × N_sig exceeds 64, the frame should be rejected by implementations that follow the standard MSM cell-mask limit.
You Might Also Like