RTCM 3.x differential-GNSS stream decoding.
RTCM 10403.x is the dominant wire format for real-time GNSS correction and
observation streams. This module is a thin wrapper over the sidereon-core
rtcm sans-I/O decoder: a forgiving frame layer that syncs on the 0xD3
preamble and verifies the CRC-24Q, and a canonical message decoder.
decode_messages/1 scans a whole byte buffer and returns every CRC-valid
message; decode_frame/1 decodes the single frame at the start of a buffer.
Message shapes
Each decoded message is a {type, fields} pair where type is one of
:station_coordinates (1005/1006), :antenna_descriptor (1007/1008/1033),
:gps_ephemeris (1019), :glonass_ephemeris (1020), :msm (MSM4/MSM7
observations), or :unsupported (any other number, preserved verbatim). The
fields map carries the raw transmitted integer fields; station coordinates
additionally carry the scaled :x_m / :y_m / :z_m / :antenna_height_m
values.
Summary
Functions
Decode every CRC-valid RTCM 3 frame in a byte buffer.
Decode the single RTCM 3 frame that begins at the start of bytes.
Decode one RTCM message body.
Decode every CRC-valid RTCM 3 frame in a byte buffer.
Decode every CRC-valid RTCM 3 frame and return messages plus stream diagnostics.
Derive the RINEX LLI digit for one signal cell.
Construct a supported RTCM 3 message and return its message body.
Construct a supported RTCM 3 message and return its complete frame.
Construct a supported RTCM 3 message from a {type, fields} pair and encode it
into a complete transport frame (preamble, length, body, CRC-24Q).
Return the RINEX LLI bit constants used by the RTCM MSM derivation helpers.
Return the message number from one RTCM message body.
Minimum continuous-lock time in milliseconds for an MSM lock indicator.
Elapsed milliseconds between two raw MSM epoch-time fields for one system.
Derive per-cell LLI rows by running a core lock-time tracker over MSM maps.
RINEX observation-code suffix for an MSM signal id, or nil if reserved.
Types
@type message() :: {message_type(), map()}
@type message_type() ::
:station_coordinates
| :antenna_descriptor
| :gps_ephemeris
| :glonass_ephemeris
| :msm
| :unsupported
Functions
Decode every CRC-valid RTCM 3 frame in a byte buffer.
@spec decode_frame(binary()) :: {:ok, %{message_number: integer(), frame_len: integer(), body: binary()}} | {:error, term()}
Decode the single RTCM 3 frame that begins at the start of bytes.
Verifies the preamble and the CRC-24Q. Returns
{:ok, %{message_number: n, frame_len: bytes, body: binary}} or
{:error, reason} for a missing preamble, a truncated buffer, or a CRC
mismatch. The body can be fed back through decode_messages/1 after re-framing.
Decode one RTCM message body.
Decode every CRC-valid RTCM 3 frame in a byte buffer.
Frames whose CRC fails or whose body cannot be decoded are skipped, and the
scan resynchronizes on the next preamble (the forgiving stream contract for a
noisy feed). Returns {:ok, [{type, fields}, ...]} in stream order, or
{:error, reason}.
@spec decode_stream(binary()) :: {:ok, %{ messages: [message()], diagnostics: %{resync_bytes: non_neg_integer(), skipped_frames: [map()]} }} | {:error, term()}
Decode every CRC-valid RTCM 3 frame and return messages plus stream diagnostics.
The message list matches decode_messages/1. Diagnostics include skipped
resynchronization bytes and CRC-valid frames whose typed body decode failed.
@spec derive_lli(integer() | nil, integer() | nil, integer() | nil, boolean()) :: non_neg_integer()
Derive the RINEX LLI digit for one signal cell.
Pass elapsed_ms as nil for the first observation. When elapsed_ms is an
integer, previous_min_lock_time_ms may be nil to represent a previous
reserved indicator.
Construct a supported RTCM 3 message and return its message body.
Construct a supported RTCM 3 message and return its complete frame.
Construct a supported RTCM 3 message from a {type, fields} pair and encode it
into a complete transport frame (preamble, length, body, CRC-24Q).
type is one of the supported message_type/0 atoms (:unsupported cannot be
constructed). fields is a map of the raw transmitted fields, the same shape
decode_messages/1 produces for that type (the derived :x_m/:y_m/:z_m
station outputs are ignored when present, so a decoded map round-trips
directly). Returns {:ok, frame_binary} or {:error, reason}.
The output frame feeds back through decode_messages/1, so
construct -> encode -> decode reproduces the same message fields.
@spec lli_bits() :: %{loss_of_lock: 1, half_cycle: 2}
Return the RINEX LLI bit constants used by the RTCM MSM derivation helpers.
Return the message number from one RTCM message body.
@spec minimum_lock_time_ms(String.t(), integer()) :: {:ok, non_neg_integer() | nil} | {:error, term()}
Minimum continuous-lock time in milliseconds for an MSM lock indicator.
kind is "msm4" or "msm7".
@spec msm_epoch_dt_ms(String.t(), integer(), integer()) :: {:ok, non_neg_integer()} | {:error, term()}
Elapsed milliseconds between two raw MSM epoch-time fields for one system.
Derive per-cell LLI rows by running a core lock-time tracker over MSM maps.
The input is a list of decoded MSM field maps, in stream order. The result is
one list of %{satellite_id, signal_id, lli, min_lock_time_ms} maps per input
message.
RINEX observation-code suffix for an MSM signal id, or nil if reserved.