RTCM Unpacked: The Binary Protocol Your RTK Fix Actually Depends On
AUTHOR: Zero Jiang | TITLE: Founder, Kalmix
If you've built an RTK system, you likely already speak NMEAโthat readable ASCII stream telling you where you are. You've probably configured an NTRIP client to pull correction data over the internet. But have you ever looked inside the binary payload NTRIP is actually carrying?
That payload is RTCM. It contains the raw satellite observations from a reference station, encoded into a compact binary format. Your receiver consumes this data to jump from meter-level standalone positioning to centimeter-level RTK. If NMEA is the final answer your receiver gives you, RTCM is the cheat sheet it needs to get the answer right.
In this volume of the Kalmix GNSS Handbook, we crack open the RTCM binary stream. We'll cover what's inside it, which messages actually matter, how to choose between MSM4 and MSM7, and what to check when your rover stubbornly refuses to Fix.
- MSM (Multiple Signal Messages): The modern RTCM 3.2+ message family supporting all GNSS constellations.
- OSR (Observation Space Representation): Corrections sending raw base station observations.
- SSR (State Space Representation): Corrections sending modeled error components.
- ARP (Antenna Reference Point): The base station's physical ECEF coordinates, broadcast in MT1005/1006.
Where RTCM Fits in the RTK Data Chain
Every RTK system relies on two data flows moving in opposite directions:
- RTCM flows IN: Carrying correction data from the reference station.
- NMEA flows OUT: Carrying the computed position to your host controller.
NTRIP is just the delivery truck; RTCM is the cargo. Your receiver takes the base station's RTCM observations, differences them against its own, and resolves carrier-phase ambiguities to produce centimeter-level coordinates.
The quality of your RTK solution is fundamentally bounded by the RTCM input. You can parse GGA perfectly, but if the RTCM stream is misconfigured or dropping packets, your Fix Quality (GGA Field 6) will never reach 4. If you can't Fix, start debugging upstream.
RTCM vs. NMEA: Inputs and Outputs
If you read Vol. 2 of this handbook, you know NMEA inside and out. Here is how RTCM compares:
| Property | RTCM 3.x | NMEA 0183 |
|---|---|---|
| Direction | Input (Corrections โ Receiver) | Output (Receiver โ Host) |
| Encoding | Binary (Compact) | ASCII (Human-readable) |
| Error Check | CRC-24Q (24-bit) | XOR Checksum (8-bit) |
| Frame Delimiter | Preamble 0xD3 + Length Field |
$ start, * checksum, CR LF
|
RTCM's binary encoding is roughly 5โ10x more bandwidth-efficient than ASCII. This is why it survives on constrained serial radio links where every byte counts.
A Brief History of RTCM 3
| Version | Standard No. | Official Title | Year |
|---|---|---|---|
| RTCM V1 | 10401 | Differential GPS Services | 1985 |
| RTCM V2.0 | 10402.0 | Differential GPS Services โ Version 2 | 1990 |
| RTCM V2.3 | 10402.3 | Differential GPS Services โ Version 2 (Final V2) | 2001 |
| RTCM V3.0 | 10403.0 | Differential GNSS Services โ Version 3 | 2004 |
| RTCM V3.1 | 10403.1 | Differential GNSS Services โ Version 3 | 2006 |
| RTCM V3.2 | 10403.2 | Differential GNSS Services โ Version 3 | 2013 |
| RTCM V3.3 | 10403.3 | Differential GNSS Services โ Version 3 (incl. Amendments) | 2016 |
| RTCM V4.0 | 10403.4 | Differential GNSS Services โ Version 4 | 2023 |
Versions 1 and 2 were built in the 80s and 90s for maritime DGPS. They used rigid, 30-bit words unsuited for the high data volumes of modern RTK. Forget them.
The modern era began with Version 3 (2004), featuring variable-length frames and robust 24-bit CRCs. But the true landmark was V3.2 (2013), which introduced Multiple Signal Messages (MSM).
Before MSM, legacy messages (MT1001โ1012) were hard-coded strictly for GPS and GLONASS. The standard literally had no numbering space for Galileo or BeiDou. MSM fixed this architectural dead-end with a constellation-agnostic matrix design.
The bottom line: If you are still using Legacy messages in 2025, you are artificially handicapping your receiver's multi-constellation capabilities.
Inside the Packet: Frame Structure
Every RTCM 3.x frame follows the exact same envelope:
โโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโ
โ Preamble โ Reserved โ Length โ Payload โ CRC-24Q โ
โ 8 bit โ 6 bit โ 10 bit โ Variable โ 24 bit โ
โ 0xD3 โ 000000 โ 0-1023 โ (binary) โ โ
โโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโ
Unlike NMEA, there are no newline characters. You cannot just "read a line." You must synchronize on 0xD3, read the length field, extract exactly that many payload bytes, and verify the CRC.
Here is the canonical pattern in Python:
"""
RTCM 3.x frame sync & extraction from a TCP/serial stream.
CRITICAL: TCP delivers arbitrary byte chunks. Never assume
one recv() = one complete RTCM frame. Always buffer.
"""
import struct
CRC24Q_POLY = 0x1864CFB
def crc24q(data: bytes) -> int:
crc = 0
for byte in data:
crc ^= byte << 16
for _ in range(8):
crc <<= 1
if crc & 0x1000000:
crc ^= CRC24Q_POLY
return crc & 0xFFFFFF
def extract_frames(buf: bytearray) -> list[bytes]:
frames = []
while True:
idx = buf.find(0xD3) # Step 1: Find preamble
if idx < 0 or idx + 3 > len(buf): break
length = ((buf[idx+1] & 0x03) << 8) | buf[idx+2] # Step 2: Read 10-bit length
frame_len = 3 + length + 3 # header + payload + CRC
if idx + frame_len > len(buf): break # Wait for more data
frame = bytes(buf[idx : idx + frame_len])
if crc24q(frame[:-3]) == struct.unpack('>I', b'\x00' + frame[-3:])[0]: # Step 3: Verify CRC
msg_type = (frame[3] << 4) | (frame[4] >> 4)
frames.append(frame)
del buf[:idx + frame_len]
return frames
The Message Groups & The Two "Silent Killers"
RTCM 3.x defines hundreds of message types, but RTK only requires a handful:
| Group | Typical MTs | Purpose | RTK Required? |
|---|---|---|---|
| Observations (MSM) | 1071โ1127 | All constellations, multi-signal | โ Yes |
| Station Coordinates | 1005 / 1006 | Base station ARP (ECEF) | โ Mandatory |
| GLONASS Bias | 1230 | FDMA inter-frequency bias | โ If using GLO |
| Observations (Legacy) | 1001โ1012 | GPS/GLONASS only | Legacy only |
| Antenna / Ephemeris | 1033 / 1019+ | Receiver info, broadcast ephemeris | Optional |
While observations (MSM) are obvious, developers constantly trip over two "silent killers":
- MT1005/1006 (Base Station Identity): These carry the base station's physical coordinates. Without them, your rover cannot compute a baseline vector. No station coordinates = no baseline = no Fix.
- MT1230 (GLONASS Interoperability): GLONASS uses FDMA (Frequency Division Multiple Access), creating hardware biases between different receiver brands. MT1230 carries the code-phase bias compensation. Without it, your rover will see GLONASS satellites in the sky, but silently refuse to use them in the RTK math.
The MSM Dilemma: Which Version?
MSM numbers are logical. The prefix identifies the constellation (e.g., 107x for GPS, 112x for BeiDou). The last digit (1โ7) indicates the payload richness.
| Scenario | Recommend | Why |
|---|---|---|
| UHF Radio / 9600 baud | MSM4 | Full carrier phase + CNR. No Doppler. Low bandwidth. |
| Standard 4G / WiFi | MSM5 | Adds Doppler for velocity and cycle-slip detection. |
| Max Precision / PPK | MSM7 | Full high-resolution data. High bandwidth. |
| Sub-meter IoT | MSM1 | Pseudorange only. Extreme bandwidth efficiency. |
MSM4 and MSM5 are the industry standard. While MSM7 is the "safe default" for mixed hardware fleets, do not run four-constellation MSM7 over a 9600 bps serial radio. It generates ~1,300 bytes/second on a link that only holds ~960 bytes/second. You will choke the serial buffer.
The Format Landscape: RTCM vs. The Rest
You will likely encounter three alternatives to RTCM in the field:
- CMRx (Trimble Proprietary): Created to save bandwidth, CMRx is ~30% smaller than RTCM MSM. The catch: It locks you into Trimble hardware. For cross-manufacturer interoperability, RTCM is the only choice.
- SPARTN (Openly Specified): Built for mass-market PPP-RTK and low-bandwidth satellite broadcasts (e.g., u-blox PointPerfect). While RTCM (OSR) sends raw observations for local differencing, SPARTN (SSR) sends modeled error components (orbit, clock, atmosphere) for the rover to reconstruct.
- RINEX (Offline Archival): RINEX and RTCM carry the exact same data. RTCM is compact binary for real-time streams; RINEX is text for offline post-processing.
Log your raw RTCM stream to an SD card during fieldwork. If the real-time link drops, you can seamlessly convert that raw log to RINEX using open-source tools like RTKLIB's convbin to secure a reliable PPK fallback.
The Developer's Debugging Checklist
When your rover refuses to Fix, use RTKLIB's str2str to inspect the incoming stream.
- GGA Fix Quality stuck at 1 You are missing MT1005/1006. The rover cannot find the base station.
- GLONASS visible (GSV) but not used (GSA) You are missing MT1230. Switch to a mountpoint that includes it, or disable GLONASS to save processing cycles.
- Correction Age climbing steadily The TCP connection dropped. Implement exponential backoff reconnection.
- Fix is very slow / Correction Age jumps erratically You are bandwidth-choked. You are likely pushing full-constellation MSM7 over a slow radio link. Drop to MSM4, reduce constellations, or lower the update rate to 0.5 Hz.
- Position offset by exactly ~2 meters Frame mismatch (e.g., ITRF2020 vs. NAD83). See Vol. 4 (GCS) to resolve.
Conclusion
RTCM is not a protocol you need to "learn" to write; it is a data stream you need to correctly route.
- Verify your receiver supports RTCM 3.x MSM.
- Select a mountpoint broadcasting MSM + MT1005/1006 + MT1230.
- Monitor your GGA Correction Age. If it climbs, the problem is your input chain.
For a complete field-by-field breakdown of the binary structure, the CRC algorithm, and the MSM mask matrix, see our companion deep-dive: AN-002: RTCM SC-104 v3.3 โ Frame Structure & Message Type Reference.
Frequently Asked Questions
What is the difference between RTCM and NMEA?
RTCM carries correction data into the receiver (binary, CRC-24Q protected). NMEA carries position results out (ASCII, XOR checksum). They flow in opposite directions in the RTK data chain โ RTCM is the input your receiver needs to compute a fix, NMEA is the output your application reads.
What RTCM version should I use?
RTCM 3.x with MSM messages (MSM4 or MSM5). This is the current industry standard, supported by all major correction services including Point One Polaris, Swift Skylark, HxGN SmartNet, and state CORS networks. Legacy messages (MT1001โ1012) cannot support Galileo or BeiDou.
What is MT1230 and why does it matter?
MT1230 carries GLONASS code-phase biases needed for cross-manufacturer interoperability. Because GLONASS uses FDMA, different receiver brands have different hardware biases. Without MT1230, your rover will see GLONASS satellites but silently refuse to include them in the RTK solution โ reducing satellite count, geometric strength, and fix reliability.
Can I mix RTCM messages from different base stations?
No. The rover forms a baseline to a single base station identified by the Station ID in the RTCM header. Mixing observations from Station A with coordinates from Station B โ different IDs, different clock references โ will produce an incorrect or completely failed solution. All messages in a correction stream must originate from the same reference station.