AN-002: RTCM SC-104 Frame Structure & Message Type Reference

APPLICATION NOTES Reading time
On this page

Document Scope

AN-002 is a practical RTCM 3.x reference for reading binary frames, decoding MSM observations and checking reference-station records. Start with the task index, then use the field tables and complete Hex examples to compare your parser’s output.

RTCM 3 uses a six-byte frame envelope around a variable-length payload: a three-byte header and a three-byte CRC-24Q. The payload starts with a 12-bit message number. The tables below use payload-relative bit offsets, so bit 0 begins immediately after the three-byte header.

For message selection, link capacity and receiver-side diagnosis, read RTCM Unpacked: message selection, bandwidth and diagnostics. Here, the worked examples cover MSM4/MSM7, MT1005/1006 and MT1230. Signal tables distinguish transmitted IDs from library indexes and identify the version basis for modern BeiDou signals.

RTCM Message and Parser Quick Index

Jump to the question you are trying to answer:

Table 1 — Task index for RTCM frame and message decoding
Task Message or field Go to
Find a frame 0xD3, length and CRC Frame envelope · stream synchronization
Decode observations MT1071–1137, MSM1–7 MSM content · complete MSM4 example
Resolve masks and signals DF394–396, constellation-specific IDs Mask ordering · signal tables
Handle missing measurements DF397–408, DF420 Satellite fields · cell fields and lock time
Read reference coordinates MT1005 / MT1006 ARP coordinates · antenna height
Check interoperability MT1033 / MT1230 Descriptors · GLONASS biases
Inspect other message groups Legacy, ephemeris and SSR Message reference and byte budgets
Test a decoder 64-bit extraction and known outputs C reader and test-vector checks

RTCM Frame Architecture and Parser Fundamentals

Data Type Conventions

Read bits most significant first within each byte. Fields often cross byte boundaries. In the MSM and coordinate tables, use these conventions:

Table 2 — Bit notation, signed extraction and scaling
Notation Interpretation Implementation detail
bit(n) An n-bit pattern Preserve the defined bit order when reading masks and flags
uint(n) Unsigned integer Range 0 to 2ⁿ−1; a reserved or unavailable code may occupy part of that range
int(n) Two’s-complement integer Sign-extend from the field’s own width before applying its scale
Physical value Raw integer × resolution Check the field’s unavailable code before converting to metres, milliseconds or dB-Hz

Read the signed fields tabulated here with two’s-complement extraction; check other message definitions for encodings such as sign-magnitude. RTCM payloads require explicit bit extraction: C struct layout, padding and alignment cannot represent arbitrary protocol bit offsets. A 38-bit ECEF coordinate needs a result wider than a 32-bit C int. The C reader handles fields up to 64 bits with bounds checks.

General Frame Structure

The payload length excludes the header and CRC. A message-number decoder needs at least two payload bytes even though the ten-bit length field can represent zero:

Table 3 — RTCM 3 frame envelope
Part Width Meaning
Preamble 8 bits 0xD3
Reserved 6 bits Zero in this frame format
Payload length L 10 bits 0–1023 bytes; complete frame size is L + 6
Payload L bytes First 12 bits are DF002, the message number
CRC-24Q 24 bits Computed over header plus payload; transmitted most significant byte first
Byte 0       : D3
Bytes 1–2    : six reserved bits + ten-bit payload length
Bytes 3..L+2 : payload; DF002 starts at the MSB of byte 3
Bytes L+3..L+5: CRC-24Q

L = ((frame[1] & 0x03) << 8) | frame[2]
message_type = (frame[3] << 4) | (frame[4] >> 4)

Capture RTCM as raw binary bytes. A text logger can alter non-printable values, line endings or character encodings and destroy frame integrity. Use Hex only as a readable representation of those bytes, as in the complete examples below.

CRC-24Q Algorithm

Validate the complete frame before dispatching its payload. The parameter set is:

Table 4 — CRC-24Q parameters and a reproducible check
Parameter Value
Polynomial 0x1864CFB including the x²⁴ term; 0x864CFB when that term is implicit
Initial value / final XOR 0 / 0
Bit reflection None on input or output
Input bytes Header and payload, excluding the final three CRC bytes
Check input ASCII bytes for 123456789 → 0xCDE703
#include <stddef.h>
#include <stdint.h>

static uint32_t crc24q(const uint8_t *data, size_t size)
{
    uint32_t crc = 0;
    for (size_t i = 0; i < size; ++i) {
        crc ^= (uint32_t)data[i] << 16;
        for (unsigned j = 0; j < 8; ++j) {
            crc <<= 1;
            if (crc & UINT32_C(0x1000000))
                crc ^= UINT32_C(0x1864CFB);
        }
    }
    return crc & UINT32_C(0xFFFFFF);
}

Frame Synchronization

A read operation is a transport boundary, not a message boundary. Preserve unfinished bytes between serial or socket reads and extract zero, one or several frames from each chunk:

Table 5 — Stream-parser states and recovery actions
State Action
Find candidate Scan for 0xD3; keep the candidate until its header is available
Read header Check reserved bits and extract L; reject a payload too short for the message number
Wait for body Wait for L + 6 complete bytes; track candidate age separately from CRC failures
Validate CRC On failure, resume searching one byte after the candidate preamble
Dispatch Decode the supported message; a valid unknown type can be logged or skipped by its frame length

A false candidate with a large length can delay processing. Set an application-specific incomplete-frame deadline and connection-reset policy, allowing enough time for a valid frame on the slowest supported link. Do not treat every embedded 0xD3 as a new frame. A ten-bit length cannot exceed 1023, so a length > 1023 condition does not detect false synchronization. The Python extractor in the companion guide demonstrates incremental buffering; its waiting behavior is explicit.

Multiple Signal Messages: MT1071–MT1137

Constellation Assignment

The message number supplies the constellation and MSM layout. The 30-bit epoch slot uses constellation-specific time conventions:

Table 6 — MSM constellation families and epoch interpretation
Constellation Message family Epoch handling
GPS 1071–1077 DF004: GPS time, milliseconds of week
GLONASS 1081–1087 DF416: three-bit day number; DF034: 27-bit milliseconds of day in GLONASS time
Galileo 1091–1097 DF248: milliseconds of Galileo week
SBAS 1101–1107 DF004: milliseconds of GPS week
QZSS 1111–1117 DF428: milliseconds of GPS week
BeiDou 1121–1127 DF427: milliseconds of BeiDou week; normalize before combining with GPS time
NavIC 1131–1137 DF546: milliseconds of week; apply the epoch definition supported by the receiver

Constellation-specific satellite IDs also need their own mapping. Do not prefix every set satellite-mask position with a GPS PRN label. Time normalization, week/day rollover and source association belong in the observation assembler, after frame decoding.

MSM Type Definitions: Content and Bit Cost

The last digit selects the observation layout. The following lengths exclude the common header and masks:

Table 7 — MSM1–7 content and per-object field lengths
MSM Satellite bits Cell bits Included observations
1 10 15 Compact code observations; no carrier phase
2 10 27 Compact carrier phase, lock and half-cycle flag
3 10 42 Compact code and carrier phase, lock and half-cycle flag
4 18 48 Full-range code and phase, lock, half-cycle flag and CNR
5 36 63 MSM4 content plus extended satellite information and phase-range rate
6 18 65 Higher-resolution code, phase, lock and CNR
7 36 80 MSM6 content plus extended satellite information and phase-range rate

MSM1 has fine pseudorange as well as rough-range information, but no phase observation; it cannot alone supply carrier-phase RTK. MSM2/3 use compact ranges that require ambiguity reconstruction. MSM4 and MSM7 below provide complete worked layouts. Higher field resolution describes quantization, not the accuracy of a resulting position.

MSM Payload Architecture

MSM transmits arrays one field at a time. Read a field for every selected satellite before advancing to the next satellite field. Then do the same for every active cell. The wire format is not a sequence of complete satellite objects.

Common header → satellite mask → signal mask → cell mask
DF397[all satellites] → DF398[all satellites]
DF400[all active cells] → DF401[all active cells]
DF402[all active cells] → DF420[all active cells]
DF403[all active cells] → zero padding to the next byte
In a GPS MSM4 example, cell mask 111011 selects five cells from three satellites and two signals. Satellite and cell data are transmitted in separate arrays, one field at a time.
Figure 1 — The zero at satellite 6 and signal ID 17 occupies a mask bit. The five selected cells retain the same order in each cell-field array. The satellite arrays precede the cell arrays.

MSM5/7 insert a four-bit extended-information array after DF397 and the DF399 rough-rate array after DF398. MSM7 then uses DF405, DF406, DF407, DF420, DF408 and DF404 cell arrays in that order. Keep the on-wire arrays separate from the satellite/cell objects your application builds.

MSM Header: Fields and Payload Offsets

Offsets are measured from the start of the payload, not from 0xD3. The fixed portion ends at bit 169, immediately before the variable cell mask:

Table 8 — MSM header through the satellite and signal masks
Field Offset Bits Interpretation
DF002 0 12 Message number
DF003 12 12 Reference Station ID
Epoch 24 30 Constellation-specific time; DF004 for the GPS example
DF393 54 1 Multiple Message Bit: 1 means more MSM messages follow for the observation epoch; 0 marks the last
DF409 55 3 Issue Of Data Station; reserved for site-data association in the referenced layout, not an epoch counter
Reserved 58 7 Preserve alignment; do not treat as observations
DF411 65 2 Clock Steering Indicator
DF412 67 2 External Clock Indicator
DF417 69 1 Divergence-Free Smoothing Indicator
DF418 70 3 Smoothing Interval indicator; encoded interval, not raw seconds
DF394 73 64 Satellite mask; most significant bit is ID 1
DF395 137 32 Signal mask; most significant bit is Signal ID 1
DF396 169 Nsat × Nsig Cell mask over the selected satellite/signal matrix

Keep the clock and smoothing indicators in diagnostic output, using the selected protocol revision for their enumerations. Group observations by source, station and normalized epoch. The multiple-message bit helps assemble an epoch; it is not a transport sequence number or proof that a complete epoch arrived.

Satellite, Signal and Cell Masks

Count set bits in the satellite and signal masks to obtain Nsat and Nsig. The cell mask contains Nsat × Nsig bits, including zero entries. Enumerate those bits in increasing satellite-ID order, then increasing Signal ID within each satellite. Ncell is the number of set bits in that matrix.

For GPS satellites 3, 6 and 12 with Signal IDs 2 and 17, the six-bit mask 111011 selects five cells:

Table 9 — Mask enumeration for the shared MSM example
Satellite ID 2: L1 C/A ID 17: L2C M+L Active cell sequence
3 1 1 Cell 1 → (3,2); cell 2 → (3,17)
6 1 0 Cell 3 → (6,2); no cell data for (6,17)
12 1 1 Cell 4 → (12,2); cell 5 → (12,17)

The zero entry still occupies a mask bit. It does not occupy an entry in the later measurement arrays. Conversely, a selected cell can contain an unavailable measurement code: consume the encoded fields, then mark that measurement unavailable. Skipping its bits would corrupt every following field.

For the RTCM 3.2/3.3 MSM layout used here, keep the cell-mask matrix within 64 bits; split a larger set across messages. The constraint is Nsat × Nsig, not just Ncell. RTKLIB’s MSM encoder is a useful comparison for field order and mask handling.

H = 169 + Nsat * Nsig
MSM4 payload bits = H + 18 * Nsat + 48 * Ncell
MSM7 payload bits = H + 36 * Nsat + 80 * Ncell
Payload bytes = (payload_bits + 7) // 8
Frame bytes = payload_bytes + 6

Example: Nsat=3, Nsig=2, Ncell=5
MSM4: 469 bits + 3 padding bits = 59 payload bytes; 65 frame bytes
MSM7: 683 bits + 5 padding bits = 86 payload bytes; 92 frame bytes

MSM Satellite Data Fields

The rough range is shared by a satellite’s cells. These fields are transmitted as separate arrays in the order shown:

Table 10 — Satellite fields for MSM4–7, including unavailable values
Field Type / layout Resolution Unavailable code and use
DF397 uint8; MSM4–7 1 ms Rough Range Integer. 255 (0xFF) is unavailable; otherwise supplies the integer milliseconds
Extended info bit4; MSM5/7 Constellation-specific GLONASS uses it for frequency-channel information; do not apply that meaning to GPS
DF398 uint10; MSM4–7 2⁻¹⁰ ms Rough Range Modulo. 1023 is a valid fraction. Combine with a valid DF397; do not use 0x3FF as an invalid sentinel
DF399 int14; MSM5/7 1 m/s Rough Phase-Range Rate. −8192 (raw 0x2000) is unavailable; rough phase-range rate

One millisecond of light travel is 299792.458 metres. With valid fields, rough_range_ms = DF397 + DF398/1024. A missing rough range prevents reconstruction of the dependent full range, even if the fine-range field itself is valid.

MSM Cell Fields, Unavailable Values and Lock Time

Field IDs matter: DF400 is fine pseudorange, DF401 is fine phase range, and DF402 is the four-bit lock indicator. The following table keeps raw unavailable patterns beside their signed values:

Table 11 — Cell observations and unavailable encodings
Field Type / layout Resolution Unavailable code or interpretation
DF400 int15; MSM1/3/4/5 2⁻²⁴ ms Fine Pseudorange. −16384; raw 0x4000. Fine pseudorange
DF401 int22; MSM2/3/4/5 2⁻²⁹ ms Fine Phase Range. −2097152; raw 0x200000. Fine phase range
DF402 uint4; MSM2–5 Encoded interval Lock Time Indicator. Lock duration range; see table below
DF420 bit1; MSM2–7 Flag Half-Cycle Ambiguity. 1 indicates half-cycle ambiguity; retain with the phase observation
DF403 uint6; MSM4/5 1 dB-Hz CNR. 0 means CNR unavailable; other observables can remain valid
DF404 int15; MSM5/7 0.0001 m/s Fine Phase-Range Rate. −16384; raw 0x4000. Fine phase-range rate
DF405 int20; MSM6/7 2⁻²⁹ ms Extended Fine Pseudorange. −524288; raw 0x80000. Fine pseudorange
DF406 int24; MSM6/7 2⁻³¹ ms Extended Fine Phase Range. −8388608; raw 0x800000. Fine phase range
DF407 uint10; MSM6/7 Encoded interval Extended Lock Time Indicator. Extended lock duration; do not interpret as seconds
DF408 uint10; MSM6/7 2⁻⁴ dB-Hz Extended CNR. 0 means CNR unavailable; no automatic invalidation of code or phase

For MSM4/5, valid full pseudorange is (rough_range_ms + DF400 × 2⁻²⁴) × 299792.458 metres. Replace DF400 and its scale with DF401 × 2⁻²⁹ for phase range. MSM6/7 use DF405 × 2⁻²⁹ and DF406 × 2⁻³¹. MSM5/7 phase-range rate is DF399 + DF404 × 0.0001 m/s when both rate fields are valid. Converting phase range to cycles also requires the correct signal wavelength; GLONASS FDMA depends on channel number.

DF402 reports a duration bin, not an exact duration or a cycle-slip counter. All interval limits below are milliseconds:

Table 12 — DF402 four-bit lock-time intervals
Indicator Minimum lock time Upper bound, exclusive
0 0 32
1 32 64
2 64 128
3 128 256
4 256 512
5 512 1024
6 1024 2048
7 2048 4096
8 4096 8192
9 8192 16384
10 16384 32768
11 32768 65536
12 65536 131072
13 131072 262144
14 262144 524288
15 524288 No finite upper bound

For DF407, indicators 0–63 have one-millisecond bins. For 64–703, let b = floor(i/32) − 1 and step = 2ᵇ ms: the lower bound is (i − 32b) × step, and the next bound is one step higher. Indicator 704 represents at least 67108864 ms; values above it are reserved in this mapping. Thus i=100 represents 144–148 ms, not 100 seconds. Track the same signal across epochs and keep the half-cycle flag when deriving loss-of-lock information.

Worked MSM4 Frame: Three Satellites, Two Signals, Five Cells

This constructed decoder test includes negative fine ranges, a missing cell and a half-cycle flag. The complete MT1074 frame is:

D3 00 3B 43 20 2A 52 41 21 00 00 00 12 08 00 00
00 00 00 00 20 00 40 00 76 8C 8E 91 00 20 18 00
3E 8F 06 00 00 00 FA 7D 12 01 38 83 F8 AD 00 09
C4 00 61 A8 FF 15 A1 9E 26 89 6D 74 5F 50 A0 21
25

The header decodes to Station ID 42 and GPS time-of-week 345000000 ms. All clock/smoothing flags, IODS and the multiple-message bit are zero. Satellite mask = 0x2410000000000000; signal mask = 0x40008000; cell mask = 111011. CRC-24Q is 0xA02125.

Read each array from left to right. The start offsets below refer to the payload; add 24 bits only if indexing from the first frame byte:

Table 13 — MSM4 array offsets and expected raw integers
Array Start bit Bits / entry Values in satellite or active-cell order
DF397 175 8 70, 71, 72
DF398 199 10 512, 256, 768
DF400 229 15 1000, -2000, 0, 500, -750
DF401 304 22 20000, -30000, 10000, 25000, -15000
DF402 414 4 6, 7, 8, 9, 10
DF420 434 1 0, 0, 1, 0, 0
DF403 439 6 45, 43, 40, 47, 42

The first two arrays have three entries; every later array has five. Data ends at bit 468 inclusive, followed by three zero padding bits. The corresponding reconstructed ranges are:

Table 14 — Expected MSM4 ranges for the five active cells
Cell Satellite / signal Pseudorange, m Phase range, m
1 GPS 3 / 1C 21135386.158023 21135379.457139
2 GPS 3 / 2X 21135332.550954 21135351.536791
3 GPS 6 / 1C 21360212.632500 21360218.216570
4 GPS 12 / 1C 21809910.254011 21809915.279674
5 GPS 12 / 2X 21809887.917733 21809892.943395

For cell 1, the shared rough range is 70 + 512/1024 = 70.5 ms. Its fine pseudorange adds 1000 × 2⁻²⁴ ms before conversion to metres. Cell 3 has a half-cycle flag of 1; the table shows its decoded phase range while retaining that ambiguity flag for downstream processing.

MSM Signal IDs: On-Wire Positions and Observation Codes

Use the one-based Signal ID transmitted by the 32-bit signal mask. A library may index its lookup array from zero, so array index 1 can correspond to Signal ID 2. A RINEX observation suffix such as 1C describes band and tracking code; it is not a mask position. Select the constellation before looking up any ID.

The tables list common signals, not every reserved or future allocation. The modern BeiDou entries use the RTCM 3.3 Amendment 2 mapping; parser cross-checks use pyrtcm 1.1.9, with the B2b entries also checked against MRTKLIB 0.7.10. Record the implemented revision and library release when comparing receivers. Preserve an unknown ID in diagnostic output rather than assigning it a guessed frequency.

GPS Signal Codes

These are one-based IDs in the GPS signal mask:

Table 15 — GPS Signal Codes by transmitted Signal ID
Signal ID Observation suffix Signal Frequency
2 1C L1 C/A 1575.42 MHz
3 1P L1 P 1575.42 MHz
4 1W L1 P(Y) 1575.42 MHz
8 2C L2 C/A 1227.60 MHz
9 2P L2 P 1227.60 MHz
10 2W L2 P(Y) 1227.60 MHz
15 2S L2C M 1227.60 MHz
16 2L L2C L 1227.60 MHz
17 2X L2C M+L 1227.60 MHz
22 5I L5 I 1176.45 MHz
23 5Q L5 Q 1176.45 MHz
24 5X L5 I+Q 1176.45 MHz
30 1S L1C D 1575.42 MHz
31 1L L1C P 1575.42 MHz
32 1X L1C D+P 1575.42 MHz

GLONASS Signal Codes

These are one-based IDs in the GLONASS signal mask:

Table 16 — GLONASS Signal Codes by transmitted Signal ID
Signal ID Observation suffix Signal Frequency
2 1C G1 C/A 1602 + k×0.5625 MHz
3 1P G1 P 1602 + k×0.5625 MHz
8 2C G2 C/A 1246 + k×0.4375 MHz
9 2P G2 P 1246 + k×0.4375 MHz

Galileo Signal Codes

These are one-based IDs in the Galileo signal mask:

Table 17 — Galileo Signal Codes by transmitted Signal ID
Signal ID Observation suffix Signal Frequency
2 1C E1 C 1575.42 MHz
3 1A E1 A 1575.42 MHz
4 1B E1 B 1575.42 MHz
5 1X E1 B+C 1575.42 MHz
6 1Z E1 A+B+C 1575.42 MHz
8 6C E6 C 1278.75 MHz
9 6A E6 A 1278.75 MHz
10 6B E6 B 1278.75 MHz
11 6X E6 B+C 1278.75 MHz
12 6Z E6 A+B+C 1278.75 MHz
14 7I E5b I 1207.14 MHz
15 7Q E5b Q 1207.14 MHz
16 7X E5b I+Q 1207.14 MHz
18 8I E5 AltBOC I 1191.795 MHz
19 8Q E5 AltBOC Q 1191.795 MHz
20 8X E5 AltBOC I+Q 1191.795 MHz
22 5I E5a I 1176.45 MHz
23 5Q E5a Q 1176.45 MHz
24 5X E5a I+Q 1176.45 MHz

BeiDou Signal Codes and Version Compatibility

BeiDou B1I uses modern RINEX suffixes 2I/2Q/2X. Older codebases may label those same legacy B1I signals 1I/1Q/1X. That naming difference is separate from the addition of B1C, B2a and B2b mask positions:

Table 18 — Common BeiDou Signal IDs with modern observation suffixes
Signal ID Suffix Signal Frequency and compatibility
2 / 3 / 4 2I / 2Q / 2X B1I I / Q / I+Q 1561.098 MHz; older libraries may use 1I/1Q/1X labels
8 / 9 / 10 6I / 6Q / 6X B3I I / Q / I+Q 1268.52 MHz
14 / 15 / 16 7I / 7Q / 7X B2I I / Q / I+Q 1207.14 MHz
22 / 23 / 24 5D / 5P / 5X B2a data / pilot / combined 1176.45 MHz; check modern-signal decoder support
25 / 26 / 27 7D / 7P / 7Z B2b data / pilot / combined 1207.14 MHz; a library can support only a subset
30 / 31 / 32 1D / 1P / 1X B1C data / pilot / combined 1575.42 MHz; these are not the old B1I labels

A concrete compatibility example: MRTKLIB 0.7.10 restored B2b IDs 25–27. In pyrtcm 1.1.9, the B2b lookup contains ID 25 but does not supply all three mappings. Inspect the decoded constellation, transmitted Signal ID and observation suffix together when testing B1C, B2a and B2b input.

QZSS, NavIC and SBAS MSM Families

These families use the same MSM framing and array rules, with their own satellite and signal assignments:

Table 19 — Additional MSM families and common signal examples
Family Message range Common Signal IDs / suffixes Decoder check
QZSS 1111–1117 2=1C; 15/16/17=2S/2L/2X; 22/23/24=5I/5Q/5X Apply the QZSS satellite-ID mapping; do not display the mask position as a GPS PRN
SBAS 1101–1107 2=1C; 22/23/24=5I/5Q/5X Map satellite-mask positions to the appropriate SBAS PRNs
NavIC 1131–1137 22=5A in the referenced common mapping Confirm modern signal and satellite coverage against the implemented revision

Base-Station Coordinates and Interoperability Messages

Station Coordinates: MT1005

MT1005 and MT1006 carry the reference antenna’s ARP coordinates in Earth-Centered, Earth-Fixed (ECEF) form. The X, Y and Z fields are signed 38-bit integers with 0.0001 m resolution. They are not the ground-marker coordinates and not the rover’s solution.

MT1005 has a 152-bit, 19-byte payload. The following offsets start at DF002:

Table 20 — MT1005 fields and payload-relative offsets
Field Offset Type Meaning
DF002 0 uint12 1005
DF003 12 uint12 Reference Station ID
DF021 24 uint6 Reserved for ITRF realization information; do not infer a complete coordinate reference system from this field
DF022 / 023 / 024 30 / 31 / 32 bit1 each GPS / GLONASS / Galileo indicators
DF141 33 bit1 Reference Station Indicator
DF025 34 int38 ARP ECEF X, 0.0001 m
DF142 72 bit1 Single Receiver Oscillator Indicator
Reserved 73 bit1 Reserved
DF026 74 int38 ARP ECEF Y, 0.0001 m
DF364 112 bit2 Quarter-cycle alignment indicator for GPS observations; not a GLONASS-only flag
DF027 114 int38 ARP ECEF Z, 0.0001 m

For transformations or map alignment, obtain the reference frame, realization and coordinate epoch from the base operator or correction service. The coordinate-reference guide explains why coordinates alone do not identify all of those choices.

D3 00 13 3E D0 2A 03 B9 B6 AC AD 2E 8A 03 00 04
2E 08 F6 C7 61 01 F6 32 B5

Expected output: Station ID 42; X = −2700000.1234 m, Y = 4300000.5678 m, Z = 3850000.0001 m. The complete frame is 25 bytes and its CRC is 0xF632B5. The negative X value exercises 38-bit sign extension.

Station Coordinates and Antenna Height: MT1006

MT1006 uses the same coordinate fields with DF002 set to 1006, then appends antenna height. The ECEF position still refers to the ARP:

Table 21 — MT1006 additional field
Field Offset Type Meaning
DF028 152 uint16 Vertical antenna height from the reference marker to the ARP; 0.0001 m resolution, range 0–6.5535 m
MT1005 and MT1006 both report ECEF coordinates at the antenna reference point (ARP). MT1006 also reports the vertical height from the ground reference marker to the ARP in field DF028.
Figure 2 — The amber dimension shows DF028 along the local vertical, from the reference marker up to the ARP. The ECEF X, Y and Z in both messages already locate the ARP.

The ECEF coordinates already locate the ARP. Applying the marker-to-ARP height again displaces that position a second time. Recovering a marker coordinate requires the intended vertical direction and frame convention. Antenna phase-center modeling is a separate operation.

D3 00 15 3E E0 2A 03 B9 B6 AC AD 2E 8A 03 00 04
2E 08 F6 C7 61 01 3B 6A 97 4F 61

This constructed frame retains the MT1005 example coordinates and adds height 1.5210 m. It is 27 bytes long with CRC 0x974F61. Use it to confirm that the extra height does not alter the parsed X, Y or Z values.

Receiver and Antenna Descriptors: MT1033

MT1033 combines station identification with antenna and receiver strings. The strings are counted byte sequences, not zero-terminated C strings:

Table 22 — MT1033 counted fields in transmission order
Order Field Parsing rule
1 DF002 / DF003 Read uint12 message number 1033 and uint12 Station ID
2 DF029 / DF030 Read uint8 antenna-descriptor count, then that many character bytes
3 DF031 Read uint8 Antenna Setup ID
4 DF032 / DF033 Read uint8 antenna-serial count, then the counted bytes
5 DF227 / DF228 Read uint8 receiver-type count, then the counted bytes
6 DF229 / DF230 Read uint8 firmware-version count, then the counted bytes
7 DF231 / DF232 Read uint8 receiver-serial count, then the counted bytes

Validate each count against the remaining payload before reading or allocating storage. Keep empty strings and padding spaces distinct. Descriptor text can support antenna-model selection and diagnostics, but the string alone does not prove that a matching calibration was applied.

GLONASS Code-Phase Biases: MT1230

MT1230 describes GLONASS FDMA code-phase biases. DF421 indicates whether pseudorange and phase-range observations have been aligned; DF422 selects which bias fields are present. These are separate decisions. Read the bias fields selected by the mask even when DF421 is zero.

Bias values appear in this mask order, beginning with the most significant mask bit:

Table 23 — MT1230 fields, mask weights and unavailable bias codes
Field Offset / presence Type Meaning
DF002 / DF003 0 / 12 uint12 each Message number 1230 and Station ID
DF421 24 bit1 0: code and phase observations not aligned; 1: aligned
Reserved 25 bit3 Reserved
DF422 28 bit4 Mask weights 8, 4, 2, 1 select the four fields below
DF423 Mask weight 8 int16 L1 C/A bias, 0.02 m; −32768 / raw 0x8000 unavailable
DF424 Mask weight 4 int16 L1 P bias, same scale and unavailable value
DF425 Mask weight 2 int16 L2 C/A bias, same scale and unavailable value
DF426 Mask weight 1 int16 L2 P bias, same scale and unavailable value

The first selected bias begins at payload bit 32; each present bias advances the cursor by 16 bits. Payload size is 4 + 2 × popcount(DF422) bytes. A missing field is not an explicit zero bias. Likewise, an unavailable code is not a large negative correction.

D3 00 08 4C E0 2A 0A 00 0A FF EC 29 2F 7B

Expected output: Station ID 42, DF421 = 0, mask 1010, DF423 = 10 × 0.02 = +0.20 m, DF425 = −20 × 0.02 = −0.40 m. DF424 and DF426 are absent. The frame is 14 bytes with CRC 0x292F7B. This example checks both signed decoding and field presence independently of the alignment indicator.

For applicable GLONASS FDMA observations, compare MT1230 input with the receiver’s calibration or bias-estimation support. Record the supplied bias fields and the observations accepted by the engine to establish how that base-to-rover combination handles interoperability.

Message Groups, Stream Checks and Conversion

Legacy Observation Messages: MT1001–MT1012

The Legacy observation families remain relevant when maintaining older receivers and services. Station records between those number ranges are separate messages:

Table 24 — Legacy observation layouts and neighboring station records
Messages Content Compatibility check
1001 / 1002 GPS L1; basic / extended 1002 adds full-range ambiguity and CNR information
1003 / 1004 GPS L1/L2; basic / extended Check tracking-code support and ambiguity reconstruction
1009 / 1010 GLONASS L1; basic / extended Retain frequency-channel information for FDMA
1011 / 1012 GLONASS L1/L2; basic / extended Check applicable bias treatment and antenna environment
1005 / 1006 Station ARP coordinates Coordinate records, not observation messages
1007 / 1008 Antenna descriptor / descriptor plus serial Metadata, not observation messages

Ephemeris Messages

Navigation data can come from the receiver’s own decoding or an external log. These message numbers identify common broadcast-ephemeris records:

Table 25 — Common broadcast-ephemeris message numbers
MT Constellation Navigation record
1019 GPS GPS broadcast ephemeris
1020 GLONASS GLONASS broadcast ephemeris
1042 BeiDou BeiDou broadcast ephemeris
1044 QZSS QZSS broadcast ephemeris
1045 Galileo F/NAV ephemeris
1046 Galileo I/NAV ephemeris

Match the navigation record and epoch to the observations used by the processor.

SSR Message Overview

SSR messages describe correction components such as orbit, clock and signal biases. They are not drop-in replacements for reference-station observations in a conventional OSR rover:

Table 26 — Established RTCM SSR message groups
Message range System Contents
1057–1062 GPS Orbit, clock, code bias, combined orbit/clock, URA and high-rate clock
1063–1068 GLONASS The same six correction categories
1240–1245 Galileo The same six correction categories
1246–1251 QZSS The same six correction categories
1252–1257 SBAS The same six correction categories
1258–1263 BeiDou The same six correction categories
1264 Ionosphere VTEC model coefficients

For later SSR extensions and provider-specific messages, match the service specification to the receiving positioning engine.

Build validation sets around the behavior you need to exercise, then compare them with real receiver captures:

Table 27 — Test sets and the behavior each one checks
Test set Include Expected check
Frame extraction Valid, corrupt, split and concatenated frames No dispatch before CRC passes; later valid frames recover after corrupt candidates
Coordinates 1005 and 1006 with positive and negative ECEF 38-bit signed extraction; antenna height remains separate
MSM arrays 1074 and 1077 with unequal cell coverage Correct masks, field-major ordering, scales and signed values
Missing observations Selected cells carrying unavailable codes Cursor advances correctly while availability is tracked per measurement
GLONASS biases 1230 with partial masks and either DF421 value Field presence follows DF422; signed bias and unavailable values handled
Live session Raw RTCM plus NMEA and connection events Separate arriving data, forwarded data and receiver-reported solution state

With a USB receiver such as SCOUT or SCOUT PRO, TRACE can record raw RTCM, NMEA and session events and show correction receive/forward counters. Those records let you compare delivery with receiver-reported state and correction age. Decode the captured RTCM separately for CRC, message IDs, masks and field values; TRACE’s connection status is not a field-level validation result.

Bandwidth Estimation from the Actual Masks

Use the frame-length equations above for each constellation, multiply by message rate, and add coordinate, bias and other traffic. Under the same masks, MSM7 is larger than MSM4:

Table 28 — Calculated frame sizes with explicit satellite and cell counts
Mask configuration MSM4 frame MSM7 frame
3 satellites × 2 signals; 5 active cells 65 bytes 92 bytes
12 satellites × 2 signals; 24 active cells 202 bytes 325 bytes

Four messages of the second size per second require 808 B/s for MSM4 or 1300 B/s for MSM7, before auxiliary messages. An 8N1 UART at 9600 bit/s provides 960 B/s ideally. Measure both average load and bursts; a radio air rate, UART rate and internet throughput are different quantities.

RTCM to RINEX 3 and PPK Inputs

RTCM observations can be converted to RINEX when the converter supports the message layouts, signal mappings and required metadata. RINEX observation types add a measurement prefix: for GPS suffix 1C, C1C is code, L1C phase, D1C Doppler and S1C signal strength. Preserve wavelength, time system, lock and half-cycle information when translating fields.

Use a converter appropriate to the source format, such as RTKLIB convbin for supported binary inputs. Verify the output header, observation codes, time span, epoch count and missing-data handling; a file that opens successfully can still have lost unsupported signals. The PPK logging guide covers the full dataset: rover raw observations, overlapping base/CORS observations and navigation data. Logging incoming corrections alone cannot recover reference data lost during a link outage.

Versioned References and Decoder Cross-Checks

Separate the specification from implementation evidence. The RTCM publication catalog lists RTCM 10403.4 with Amendment 1 dated November 2024. The MSM examples here deliberately use established 3.2/3.3 layouts; the BeiDou signal table identifies the Amendment 2 mapping rather than assuming that every older decoder has it.

The following checks make the examples reproducible:

Table 29 — Reference roles used for this article
Reference Role What to compare
RTCM 10403 series Protocol authority Edition, amendments, message definitions and receiver conformance requirements
pyrtcm 1.1.9 Independent example decoder The five constructed frames below: IDs, raw/scaled values, masks and cell association
RTKLIB Implementation comparison MSM array order, signed extraction, conversion behavior and documented encoder issues
MRTKLIB 0.7.10 Modern BeiDou compatibility example Restored B2b IDs 25–27; demonstrates why release identification matters

The five vectors were independently decoded with pyrtcm 1.1.9. They exercise signed values, cell association, array order and reference records. The troubleshooting table below adds failure cases to check.

Parser Troubleshooting

Locate the first wrong value before changing later scale factors:

Table 30 — Observed decoder failures and the first checks to make
Symptom Likely check Concrete test
CRC fails on every frame Input region, binary transport or CRC parameters Run 123456789 → CDE703, then a complete Hex frame below
Parser waits despite later bytes Incomplete candidate with plausible length Feed a false D3/1023-byte header; inspect the application deadline and reset behavior
Correct MSM header, wrong observations Mask length or field-major array order Compare payload offsets 175, 199 and 229 in the MSM4 example
GPS L2 signal labeled incorrectly Signal ID mixed with a zero-based array index Confirm ID 17 maps to 2X, not ID 4
Coordinates wrap or change sign 32-bit truncation or missing sign extension Read the negative 38-bit X field from the MT1005 vector
Constant height discrepancy ARP, marker and antenna-height handling Confirm DF028 was not added to coordinates already describing the ARP
GLONASS bias fields shift DF421 used as presence flag or wrong mask direction Use mask 1010 with DF421=0; only DF423 and DF425 follow
One invalid cell breaks all later cells Bits skipped before consuming the encoded arrays Insert an unavailable fine range and confirm subsequent offsets stay unchanged

Appendix: 64-Bit Extraction and Decoder Test Vectors

The following C functions take a pointer, available byte count, bit offset and field width. A false return means the request is invalid or outside the supplied buffer; the caller must not use an output value from a failed call.

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/* Bit 0 is the MSB of buf[0]. Width is 1..64. */
static bool read_u64(const uint8_t *buf, size_t size,
                     size_t pos, unsigned width, uint64_t *out)
{
    if (!buf || !out || width == 0 || width > 64 || size > SIZE_MAX / 8)
        return false;
    size_t total = size * 8;
    if (pos > total || width > total - pos)
        return false;
    uint64_t value = 0;
    for (unsigned i = 0; i < width; ++i) {
        size_t bit = pos + i;
        value = (value << 1) | ((buf[bit / 8] >> (7 - bit % 8)) & 1u);
    }
    *out = value;
    return true;
}

/* Two's-complement fields, including 38-bit ECEF coordinates. */
static bool read_i64(const uint8_t *buf, size_t size,
                     size_t pos, unsigned width, int64_t *out)
{
    uint64_t value;
    if (!out || !read_u64(buf, size, pos, width, &value))
        return false;
    if (value & (UINT64_C(1) << (width - 1))) {
        uint64_t mask = width == 64 ? UINT64_MAX :
                        (UINT64_C(1) << width) - 1;
        *out = -1 - (int64_t)(mask - value);
    } else {
        *out = (int64_t)value;
    }
    return true;
}

After checking the envelope and CRC, point the reader at the payload. For an MT1005 or MT1006 payload of length L:

int64_t x_raw, y_raw, z_raw;
if (!read_i64(payload, L, 34, 38, &x_raw) ||
    !read_i64(payload, L, 74, 38, &y_raw) ||
    !read_i64(payload, L, 114, 38, &z_raw)) {
    /* Reject the truncated payload. */
    return false;
}
double x_m = (double)x_raw * 0.0001;
double y_m = (double)y_raw * 0.0001;
double z_m = (double)z_raw * 0.0001;

For MSM7, use this second constructed observation frame. It retains the MSM4 example’s station, epoch, satellite mask, signal mask and five-cell ordering, while exercising higher-resolution fields and phase-range rate:

D3 00 56 43 50 2A 52 41 21 00 00 00 12 08 00 00
00 00 00 00 20 00 40 00 76 8C 8E 90 00 10 02 01
80 7C E0 03 20 1F 40 4E 21 F6 3C 00 00 00 02 71
1F C5 68 06 1A 81 F6 D8 40 03 0D 40 07 A1 21 FB
6C 20 32 12 C6 41 F4 96 12 D0 AC 28 0B C2 A0 00
C9 FC E0 00 00 03 2F F6 A0 BA 24 64

Its expected raw arrays are listed below. Each start offset plus index × field width identifies one array element:

Table 31 — MSM7 expected raw arrays and payload offsets
Array Start bit Bits / entry Raw integers
DF397 175 8 70, 71, 72
ExtSatInfo 199 4 0, 0, 0
DF398 211 10 512, 256, 768
DF399 241 14 -400, 100, 250
DF405 283 20 10000, -20000, 0, 5000, -7500
DF406 383 24 200000, -300000, 100000, 250000, -150000
DF407 503 10 100, 150, 200, 250, 300
DF420 553 1 0, 0, 1, 0, 0
DF408 558 10 720, 688, 640, 752, 672
DF404 608 15 100, -200, 0, 50, -75

For the first cell, pseudorange = (70.5 + 10000 × 2⁻²⁹) × 299792.458 = 21135373.873070 m. Phase range = (70.5 + 200000 × 2⁻³¹) × 299792.458 = 21135396.209348 m. Phase-range rate = −400 + 100 × 0.0001 = −399.9900 m/s; CNR = 720/16 = 45 dB-Hz. These are decoder outputs from constructed input values.

All examples include the header and CRC. Check their lengths and checksums before comparing field values:

Table 32 — Complete constructed-frame test set
Message Payload / frame bytes CRC-24Q Expected distinguishing result
1074 59 / 65 A02125 Five cells; signed fine ranges; one half-cycle flag
1077 86 / 92 BA2464 Extended fields and phase-range rates
1005 19 / 25 F632B5 Negative 38-bit ECEF X
1006 21 / 27 974F61 Same ECEF coordinates; separate 1.5210 m height
1230 8 / 14 292F7B DF421=0 with two present signed biases

The reference reader was checked against every named field in these vectors, including signed 38-bit coordinates, and with invalid widths, truncated buffers and the signed 64-bit minimum. The frame extractor was checked at all 228 two-part split positions across the five frames, with byte-at-a-time input, concatenation and corrupt-CRC recovery. Add receiver captures and unavailable-value mutations to a decoder’s own tests before relying on it for the full supported message set.

Frequently Asked Questions

After a CRC-24Q failure, how should an RTCM parser resynchronize?

Resume byte-wise searching one byte after the candidate preamble. Check each new 0xD3 candidate’s reserved bits, payload length and complete CRC before dispatch. Preserve incomplete bytes across reads and apply an application-specific deadline to stalled candidates. This recovers synchronization by validation while retaining valid frames that may follow a corrupted candidate.

How do MSM masks locate one satellite-signal observation cell?

The satellite and signal masks select IDs in increasing order. Traverse their cell matrix by satellite, then signal, and retain set entries in the active-cell list. Read observation arrays field by field over that list. In the worked example, mask 111011 selects five cells from three satellites and two signals.

What is the difference between MT1005 and MT1006?

Both carry reference-station ARP coordinates as signed 38-bit ECEF values with 0.0001 metre resolution. MT1006 appends antenna height from the reference marker to the ARP. That height is separate from the ECEF position, which already describes the ARP. Obtain the coordinate reference frame and epoch from the service or base operator.

Does DF421 decide whether MT1230 bias values are present?

No. DF421 indicates whether code and phase observations are aligned. DF422 is the four-bit signal mask that selects the bias fields DF423 through DF426. Read selected values in mask order, starting with L1 C/A. Each is a signed 16-bit integer scaled by 0.02 metre; raw 0x8000 means the bias is unavailable.

Why can two RTCM decoders label BeiDou signals differently?

They may implement different signal-table revisions or use different observation-code labels. Older libraries can label B1I as 1I where modern RINEX uses 2I, and some omit newer B1C, B2a or B2b entries. Compare the transmitted one-based Signal ID, constellation, protocol revision and library release before comparing decoded signal names.

Does a valid RTCM CRC mean the observations are usable for RTK?

A matching CRC establishes frame integrity against that check, not observation usability. Decode the message and inspect supported signals, station association, time, unavailable-value codes and lock information. The receiving engine also needs its own compatible observations. Byte delivery, successful field decoding and an RTK solution are separate results that should be logged separately.

Can I read RTCM 3.x messages using a standard serial terminal or text logger?

Capture RTCM as raw binary bytes and inspect it with a Hex viewer or RTCM decoder. A serial terminal’s text display can show non-printable characters, while text logging can change byte values or line endings. Preserve a binary file for CRC and field checks; use NMEA output for readable receiver status.

Why can’t an RTCM 3.x decoder use C structs to parse the payload?

RTCM payload fields use protocol-defined bit offsets and often cross byte boundaries. C structs follow compiler layout, padding and alignment rules. Read each field explicitly by offset and width, then apply signed interpretation, unavailable-value handling and scaling. The C example supports widths up to 64 bits, including the 38-bit ECEF coordinates.

Continue exploring

Use SCOUT for a receiver-side correction test, and compare recorded input with its reported solution state.

Zero Jiang, Founder of Kalmix

Zero Jiang

Founder, Kalmix

Dedicated to making high-precision GNSS positioning accessible and reliable for global developers building autonomous systems and robust RTK hardware.