Real-Time CLAS PPP-RTK via mrtk run¶
MRTKLIB supports real-time CLAS PPP-RTK positioning via mrtk run (the unified-CLI entry point that supersedes the legacy rtkrcv binary). Input streams can be any RTKLIB-supported type — serial, TCP client/server, NTRIP, or file replay. Both single-channel and dual-channel L6 configurations are supported. This document describes the setup, usage, and performance characteristics, with file-stream replay as the primary example.
Overview¶
mrtk run processes GNSS observations and CLAS L6D corrections in real time (or accelerated file replay) to produce centimetre-level PPP-RTK solutions. The same CLAS engine used by mrtk post (post-processing) runs inside the rtksvr thread, with corrections decoded from a separate L6 input stream.
Supported Input Combinations¶
Single-channel L6:
| Stream 1 (obs) | Stream 3 (corrections) | Format |
|---|---|---|
BINEX (.bnx) | CLAS L6D (.l6) | binex + clas |
SBF (.sbf) | CLAS L6D (.l6) | sbf + clas |
RTCM3 (.rtcm) | UBX L6 (.ubx) | rtcm3 + ubx |
Dual-channel L6:
| Stream 1 (obs) | Stream 2 (L6 ch2) | Stream 3 (L6 ch1) | Format |
|---|---|---|---|
BINEX (.bnx) | CLAS L6D ch2 | CLAS L6D ch1 | binex + clas + clas |
Stream 2 (internal index 1, the base-station slot unused in PPP-RTK) is repurposed for L6 ch2. Channel mapping: ch = (index == 1) ? 1 : 0.
For file replay, all streams require .tag time-tag files and the ::T::xN suffix for synchronised playback (e.g. ::T::x10 for 10x speed).
Quick Start (File Replay)¶
1. Generate Time Tags¶
If your data files do not have .tag files, generate them:
# BINEX observation tag (parses epoch timestamps from 0x7F-05 records)
python3 scripts/tools/gen_bnx_tag.py <file.bnx>
# L6 correction tag (synchronized to the BINEX master tag)
python3 scripts/tools/gen_l6_tag.py <file.l6> --sync-tag <file.bnx.tag>
2. Configure¶
Use conf/claslib/rtkrcv.toml (single-channel) or rtkrcv_2ch.toml (dual-channel) as a template. Key settings:
[positioning]
mode = "ppp-rtk"
frequency = "l1+2" # CLAS does not provide E5b bias; use nf=2
satellite_ephemeris = "brdc+ssrapc"
constellations = 25 # GPS + Galileo + QZSS
# Single-channel L6
[streams.input.rover]
path = "./path/to/obs.bnx::T::x10"
format = "binex"
[streams.input.correction]
path = "./path/to/corrections.l6::T::x10"
format = "clas"
# --- OR dual-channel L6 ---
[streams.input.base] # repurposed for L6 ch2
path = "./path/to/ch2.l6::T::x10"
format = "clas"
[streams.input.correction] # L6 ch1
path = "./path/to/ch1.l6::T::x10"
format = "clas"
[files]
cssr_grid = "./tests/data/claslib/clas_grid.def"
ocean_loading = "./tests/data/claslib/clas_grid.blq"
eop = "./tests/data/claslib/igu00p01.erp"
receiver_atx = "./tests/data/claslib/igs14_L5copy.atx"
isb_table = "./tests/data/claslib/isb.tbl"
phase_cycle = "./tests/data/claslib/l2csft.tbl"
Important: Use
frequency = "l1+2"(nf=2) for CLAS. CLAS does not provide Galileo E5b bias corrections; using"l1+2+3"(nf=3) adds the E5b slot without valid bias, causing false geometry-free cycle slip detection that destroys Galileo ambiguities and severely degrades AR fix rate.
3. Run¶
# Single-channel
./build/mrtk run -s --port 52005 --config conf/claslib/rtkrcv.toml
# Dual-channel
./build/mrtk run -s --port 52005 --config conf/claslib/rtkrcv_2ch.toml
Flags: - -s : auto-start streaming on launch - -p / --port PORT : telnet console port - -t / --trace N : trace level (0-5) - -o / --config FILE : processing options / configuration file - -d / --device DEV : terminal device for the interactive console - -h / --help : show full help
Performance: Post-Processing vs Real-Time¶
Both modes use the same CLAS PPP-RTK engine (mrtk_ppp_rtk.c) and the same CLAS correction decoder.
Single-channel (2019/239 dataset)¶
0627 station, 2019-08-27 16:00-17:00 UTC, Trimble NetR9.
| Metric | Post-Processing (mrtk post) | Real-Time (mrtk run) |
|---|---|---|
| Total epochs | 3,580 | 3,599 |
| Fix (Q=4) | 3,575 (99.86%) | 3,517 (97.72%) |
| Float (Q=5) | 5 (0.14%) | 5 (0.14%) |
| SPP (Q=1) | 0 (0.00%) | 77 (2.14%) |
The 77 Q=1 epochs correspond to the initial convergence period (~77 seconds). After convergence, the steady-state fix rate is identical: ~99.86%.
Dual-channel (2025/157 dataset)¶
0627 station, 2025-06-06 20:00-21:00 UTC, Trimble NetR9.
| Metric | Post-Processing (mrtk post) | Real-Time (mrtk run) |
|---|---|---|
| Fix (Q=4) | 3,579 (99.4%) | ~3,335 (92.6%) |
| Float (Q=5) | ~21 (0.6%) | ~192 (5.3%) |
| SPP (Q=1) | 0 (0.0%) | ~73 (2.0%) |
RT fix rate varies slightly between runs due to stream timing, but consistently exceeds 90%. The RT-PP gap is primarily from initial convergence and stream synchronisation latency.
Analysis¶
- After convergence, the steady-state fix rate is near-identical between PP and RT.
- The slight epoch count differences arise from the real-time server processing more observation boundaries at stream startup.
- nf=2 is required for CLAS — Using nf=3 degrades the 2ch fix rate to ~67% due to false L1-L5 GF cycle slips on Galileo (see Troubleshooting).
Architecture¶
Server Thread Flow¶
strread(obs) ──> decoderaw(0) ──> rtkpos() ──> writesol()
strread(l6_ch2) ──> decoderaw(1) ──> clas_decode_msg(ch=1) [2ch only]
strread(l6_ch1) ──> decoderaw(2) ──> clas_decode_msg(ch=0)
──> clas_bank_get_close()
──> clas_update_global() ──> nav.ssr[]
The rtksvr main loop processes all input streams in each cycle:
- Read bytes from all streams (
strread) - Decode observations (stream 0) and corrections (stream 1/2)
- Channel mapping:
ch = (index == 1) ? 1 : 0— stream 2 → ch0, stream 1 → ch1 - When a new observation epoch is available, call
rtkpos()which accesses CLAS corrections vianav->clas_ctx
L6 Rate Limiter¶
A rate limiter prevents the L6 correction stream from overrunning the observation stream during file replay. When L6 time exceeds observation time by more than 60 seconds, L6 processing pauses until observations catch up. This protects the CLAS bank ring buffer (32 entries, ~16 minutes of corrections) from being overwritten before the positioning engine can use the data.
Time-Tag Synchronisation¶
File replay with ::T::xN requires .tag files for both streams. The tag file maps wall-clock time (tick_n in milliseconds) to file positions, allowing strsync() to release data at the correct rate.
- Master stream (obs):
tick_fsets the base clock - Slave stream (L6):
offset = master_tick_f - slave_tick_fdetermines when data starts relative to the master
Tag File Generation¶
BINEX Tag (gen_bnx_tag.py)¶
Parses BINEX 0x7F-05 observation records to extract epoch timestamps (minutes since GPS epoch + milliseconds). Each epoch creates one tag entry mapping its tick_n (ms since first epoch) to its file offset.
L6 Tag (gen_l6_tag.py)¶
L6 data is transmitted at 250 bytes/s (one frame per second). The tag generator creates one entry per frame at 1-second intervals.
The L6 filename encodes UTC start time (session letter A–X maps to hours 0–23). Since gen_bnx_tag.py stores GPST-basis timestamps, gen_l6_tag.py applies GPS-UTC leap seconds (+18 s as of 2017) to match the same time basis.
With --sync-tag, it aligns the L6 tag to a master tag file: - Reads the master's tick_f and base time (GPST basis) - Converts L6 UTC start time to GPST (adds leap seconds) - Computes the GNSS-time offset between L6 start and master start - Sets tick_f so the offset produces correct strsync() alignment - Matches tick_n scaling to the master tag's timing (handles both real-time and non-real-time recorded masters)
CTest Integration¶
Two CLAS real-time tests are registered in CMakeLists.txt:
| Test | Config | Dataset | Wall time |
|---|---|---|---|
rtkrcv_rt_clas | rtkrcv.toml | 2019/239 (1ch) | ~370s at x10 |
rtkrcv_rt_clas_2ch | rtkrcv_2ch.toml | 2025/157 (2ch) | ~372s at x10 |
Each test: 1. Extracts test data from claslib_testdata.tar.gz (fixture) 2. Patches the TOML config with a temporary output path 3. Runs mrtk run at x10 speed 4. Compares output line count against reference (>=90% threshold)
Both tests use RESOURCE_LOCK rtkrcv_port to prevent parallel execution conflicts with the MADOCA rtkrcv_rt test.
Configuration Reference¶
Key TOML Options for CLAS PPP-RTK¶
| TOML Key | Value | Description |
|---|---|---|
positioning.mode | "ppp-rtk" | CLAS PPP-RTK positioning mode |
positioning.frequency | "l1+2" | Must be nf=2 (CLAS has no E5b bias) |
positioning.satellite_ephemeris | "brdc+ssrapc" | Broadcast + SSR (antenna phase centre) |
positioning.constellations | 25 | GPS(1) + Galileo(8) + QZSS(16) |
positioning.dynamics | true | Enable kinematic dynamics model |
positioning.atmosphere.ionosphere | "est-adaptive" | Adaptive ionosphere estimation |
positioning.clas.grid_selection_radius | 1000 | CLAS grid selection radius (m) |
ambiguity_resolution.mode | "fix-and-hold" | Ambiguity resolution mode |
receiver.isb | true | Inter-system bias correction |
files.cssr_grid | "clas_grid.def" | CLAS grid definition (required) |
server.time_interpolation | true | Interpolate corrections between epochs |
server.max_obs_loss | 90.0 | Max epochs without obs before reset |
server.float_count | 15 | Float epochs before fix attempt |
Required Support Files¶
| File | TOML Key | Description |
|---|---|---|
clas_grid.def | files.cssr_grid | CLAS correction grid point definitions |
clas_grid.blq | files.ocean_loading | Ocean tide loading at grid points |
igu00p01.erp | files.eop | Earth rotation parameters |
igs14_L5copy.atx | files.receiver_atx | Satellite/receiver antenna phase centres |
isb.tbl | files.isb_table | Inter-system bias correction table |
l2csft.tbl | files.phase_cycle | L2C 1/4-cycle phase shift correction |
Troubleshooting¶
Low fix rate with Galileo satellites¶
Symptom: Fix rate drops to ~67% (2ch) or noticeably below expected levels, with frequent ambiguity resets on Galileo satellites.
Cause: frequency = "l1+2+3" (nf=3) in the config. CLAS does not provide Galileo E5b phase bias corrections. With nf=3, the E5b frequency slot has no valid bias, causing the geometry-free cycle slip detector (detslp_gf() in mrtk_ppp_rtk.c) to fire false slips at every epoch. This destroys Galileo ambiguities and prevents AR convergence.
Fix: Set frequency = "l1+2" (nf=2). This skips the L1-L5 GF slip detector entirely and uses only L1+L2 observations, which have valid CLAS bias corrections.
All solutions are Q=1 (SPP)¶
-
Missing
clas_grid.def: This is the most common cause. Ensurefiles.cssr_gridpoints to a valid grid definition file. Without it, the CLAS engine cannot select atmospheric correction grids. -
Missing
.tagfiles: Without time tags, both streams dump data at maximum speed with no synchronisation. Generate tags using the scripts inscripts/tools/. -
Stream format mismatch: Verify stream format settings match the actual file formats.
clas grid file error in output¶
The grid definition file path is invalid or the file doesn't exist. Check files.cssr_grid in the config.
Error: vt is NULL¶
Harmless warning from the rtkrcv console subsystem inside mrtk run when running without an interactive terminal (e.g. from scripts). Does not affect positioning.