RTK-hosted GNSS/IMU tight coupling¶
Objective¶
The tight-coupling path keeps RTKProcessor as the estimator that owns the
baseline, single-difference ambiguity, ionosphere, lock, hold, and ambiguity
resolution state. IMU propagation replaces the kinematic path's unconditional
per-epoch SPP position reseed; it does not create a second ambiguity filter.
Every new behavior is opt-in. With all tight-coupling options disabled, the existing RTK solution stream must remain bit-identical.
State ownership¶
| State or responsibility | Owner |
|---|---|
| Baseline position and covariance | RTKProcessor |
| SD ambiguities, ionosphere states, lock/hold lifecycle, AR | RTKProcessor |
| IMU samples and interval selection | TightCouplingProcessor |
| Attitude, velocity, accelerometer bias, gyroscope bias | TightCouplingProcessor |
| IMU interval transition and accumulated process noise | ImuPreintegrator |
| ZUPT, NHC, lever arm | TightCouplingProcessor using existing fusion helpers |
The first three RTK states remain position. Velocity states, when introduced, are appended after the existing RTK state layout so ambiguity indices and the AR machinery do not move.
Epoch sequence¶
For GNSS epoch k:
- Start from the previous accepted RTK FLOAT posterior, never from the loose ESKF's absolute position.
- Integrate body-FLU IMU samples from epoch
k-1tokin the local ENU frame. Gyroscope input is already radians per second. - Transform the predicted antenna displacement and covariance to ECEF.
- Apply an RTK time update that preserves position-to-ambiguity covariance.
- Run the existing DD measurement construction, Kalman update, validation, and ambiguity resolution.
- Validate an integer candidate with the self-reference-free CP-vs-PR test before a fixed solution can be fed back.
- Re-anchor the private INS navigation state from the accepted RTK posterior, applying the configured body-FLU IMU-to-antenna lever arm.
M1 initially implements steps 1-4 with a private external INS update. M2 adds step 6 before M3 enables the complete closed loop.
Coordinate and covariance contract¶
- IMU body frame: FLU (Forward, Left, Up).
- Mechanization frame: local ENU with gravity along negative Up.
- RTK state frame: ECEF baseline relative to the configured base station.
- Lever arm: IMU origin to GNSS antenna, expressed in body FLU.
ImuPreintegratorreturns the predicted ENU nominal state, the accumulated 15-state transition matrix, and process-noise covariance for one interval.- ENU/ECEF rotation is owned by
TightCouplingProcessor; the preintegrator has no base-station or antenna knowledge. - M1 maps the position block into RTK without zeroing position covariance rows or columns. A configurable diagonal floor handles the regularization that the legacy wide reset previously supplied.
Invalid interval policy¶
An interval is invalid when it is uninitialized, contains non-finite IMU data, has non-monotonic timestamps, or contains a sample gap above the configured limit. Invalid intervals are never partially consumed by RTK. M1 falls back to the unchanged legacy SPP/trusted-position reseed for that epoch and re-anchors at the next accepted RTK posterior.
No stale time update may be reused on a later epoch.
Delivery order and gates¶
- M0, preintegration: no RTK behavior change; analytic mechanization, transition-composition, noise, reset, and invalid-input tests.
- M1, INS time update:
--tc-ins-time-update; fix rate within 0.5 pp of baseline, no p95 regression, and solver wall time within 5%. - M2, wrong-fix containment: CP-vs-PR innovation gate and DDPR-LS anchor, implemented before closed-loop feedback.
- M3, closed loop:
TightCouplingProcessorowns interval selection, re-anchoring, ZUPT/NHC, and fallback. - M4, velocity states: append three RTK velocity states and add an explicit force-active mask to the RTKLIB-compatible Kalman helper.
- M5, TDCP diagnostics: diagnostics first; measurement updates require a separate opt-in gate after validation.
Each milestone requires unit tests, a deterministic short run, and same-binary OFF/ON full runs on Tokyo run1, Tokyo run3, and Nagoya run1. Major milestones also run all five PPC datasets. Negative results remain documented and disabled by default.
M1 evaluation¶
The first 1e-4 m^2 process-noise floor was overconfident and reduced Tokyo
run3 fix rate by 4.63 percentage points. One bounded retune selected 25 m^2
after 2,000- and 6,000-epoch prefix comparisons against 900 m^2. Full-run
same-binary results for the selected value were:
| Dataset | Mode | Fix % | PPC 3D 50 cm % | p95 horizontal m | p95 abs up m | Wall s |
|---|---|---|---|---|---|---|
| Tokyo run1 | OFF | 76.67 | 72.11 | 6.83 | 31.33 | 538.4 |
| Tokyo run1 | M1 | 74.33 | 69.38 | 7.24 | 29.86 | 725.5 |
| Tokyo run3 | OFF | 74.63 | 78.95 | 8.73 | 13.31 | 1450.9 |
| Tokyo run3 | M1 | 78.20 | 82.22 | 4.23 | 5.89 | 1389.8 |
| Nagoya run1 | OFF | 77.49 | 71.54 | 9.44 | 17.02 | 443.4 |
| Nagoya run1 | M1 | 77.66 | 65.64 | 8.54 | 15.35 | 587.7 |
M1 is therefore a documented mixed-negative result: Tokyo run3 improves
strongly, but Tokyo run1 and Nagoya run1 fail the quality and wall-time gates.
The feature remains default-off. The selected 25 m^2 floor is only the
default after a caller explicitly enables the M1 path.
M2 evaluation¶
M2 validates a fixed integer candidate without using either candidate
position. Non-GLONASS pairs apply
|DD_PR - (DD_CP - fixed_DD_ambiguity)|; GLONASS FDMA is skipped because the
reference and target wavelengths differ. A first pass that rejected a whole
candidate for one pair above 10 m was too strict on Tokyo run1. The one allowed
retune kept the 10 m reference threshold and allowed one bad pair. Two
consecutive vetoes escalate to an independent DDPR-only LS anchor with
leave-one-out FDE. M2 exposes that anchor for M3 but does not inject it yet.
Same-binary full runs with the selected settings produced:
| Dataset | OFF/ON fixed epochs | OFF/ON 3D 50 cm % | Vetoed candidates | DDPR anchors |
|---|---|---|---|---|
| Tokyo run1 | 9082 / 9079 | 72.11 / 72.11 | 195 | 191 |
| Tokyo run3 | 11419 / 11419 | 78.95 / 78.95 | 10 | 9 |
| Nagoya run1 | 5848 / 5848 | 71.54 / 71.54 | 5 | 4 |
Official score and horizontal/vertical p95 were also identical within each OFF/ON pair. The gate remains default-off; enabling it uses a 10 m threshold, minimum four checked pairs, one allowed bad pair, and two-epoch escalation.
M3 evaluation¶
--tc-closed-loop moves IMU interval selection, private attitude/velocity/bias
propagation, lever-arm handling, re-anchoring, ZUPT/NHC, and invalid-interval
fallback into TightCouplingProcessor. It automatically enables the M2 gate.
The normal anchor is the accepted RTK FLOAT posterior; on an escalated veto,
the current DDPR-LS/FDE anchor is consumed instead. The loose ESKF is used only
to bootstrap the first private navigation state.
A deterministic 300-epoch Tokyo run3 check supplied 111 INS time updates from 112 anchors with no invalid intervals. Its OFF output was bit-identical to the pre-M3 OFF output. A forced 0.1 m CP-vs-PR threshold rejected all 281 evaluated candidates, produced and consumed 280 DDPR anchors, and completed without an invalid interval or runtime error.
Same-binary full runs produced:
| Dataset | Mode | Fix % | PPC 3D 50 cm % | p95 horizontal m | p95 abs up m | Wall s | Veto / DDPR anchor |
|---|---|---|---|---|---|---|---|
| Tokyo run1 | OFF | 76.67 | 72.11 | 6.83 | 31.33 | 620.9 | 0 / 0 |
| Tokyo run1 | M3 | 77.89 | 75.14 | 6.91 | 24.66 | 1067.1 | 51 / 47 |
| Tokyo run3 | OFF | 74.63 | 78.95 | 8.73 | 13.31 | 2684.6 | 0 / 0 |
| Tokyo run3 | M3 | 76.74 | 81.01 | 3.88 | 6.04 | 3084.1 | 0 / 0 |
| Nagoya run1 | OFF | 77.49 | 71.54 | 9.44 | 17.02 | 415.1 | 0 / 0 |
| Nagoya run1 | M3 | 72.51 | 72.19 | 8.73 | 13.71 | 665.2 | 300 / 296 |
M3 strongly improves Tokyo run3 position quality and improves most Tokyo run1 and Nagoya error percentiles, but it fails the acceptance gates: Tokyo run1 horizontal p95 regresses slightly, Nagoya fix rate falls by 4.98 percentage points, and wall time rises by 14.9--71.9%. This is a documented mixed-negative result and the feature remains default-off.
M4 evaluation¶
--tc-velocity-states requires the M3 closed loop and appends three ECEF
velocity states after the complete legacy RTK state vector. Position,
GLONASS-hardware-bias, ionosphere, and ambiguity indices are unchanged. The
INS time update supplies antenna velocity and a 6x6 position-velocity process
noise block. Position DD updates can therefore correct velocity through the
cross-covariance. A force-active mask in the shared Kalman and NIS paths keeps
valid zero-valued velocity states observable without changing the legacy
RTKLIB sparse-state rule for any other state.
The 300-epoch Tokyo run3 smoke completed with 300 valid solutions, 296 fixes, 111 velocity-state time updates, no invalid interval, and no stderr. With M4 disabled, both the all-features-OFF and M3-only smoke outputs were bit-identical to their pre-M4 counterparts. Full M4-OFF outputs were also SHA-256 identical to the saved M3-ON output on all three evaluation datasets.
Same-binary full M3/M4 runs produced:
| Dataset | Mode | Fix % | PPC 3D 50 cm % | p95 horizontal m | p95 abs up m | Wall s | Veto / DDPR anchor |
|---|---|---|---|---|---|---|---|
| Tokyo run1 | M3 | 77.89 | 75.14 | 6.91 | 24.66 | 1133.3 | 51 / 47 |
| Tokyo run1 | M4 | 77.36 | 75.75 | 7.53 | 23.32 | 1139.9 | 51 / 47 |
| Tokyo run3 | M3 | 76.74 | 81.01 | 3.88 | 6.04 | 1919.9 | 0 / 0 |
| Tokyo run3 | M4 | 78.52 | 79.22 | 4.69 | 6.22 | 1948.1 | 81 / 76 |
| Nagoya run1 | M3 | 72.51 | 72.19 | 8.73 | 13.71 | 721.9 | 300 / 296 |
| Nagoya run1 | M4 | 78.27 | 74.39 | 9.66 | 13.15 | 741.3 | 0 / 0 |
Wall-time overhead stays within 2.7%, and Nagoya fix rate recovers strongly. However, Tokyo run1 misses the fix-rate tolerance by 0.03 percentage points, and horizontal p95 regresses on every dataset (with additional Tokyo run3 50-cm and vertical-p95 regressions). M4 is therefore another documented mixed-negative result and remains default-off.
M5 evaluation¶
--tc-tdcp-diagnostics requires M4 and is deliberately measurement-neutral.
For each satellite/frequency it compares the single-difference carrier-phase
change with trapezoid-integrated single-difference Doppler range rate. It
classifies missing history, excessive gap, loss of lock, and invalid input,
then reports raw residual RMS and maximum. It never constructs a filter row,
changes a state/covariance, or affects ambiguity resolution.
The 300-epoch Tokyo run3 smoke produced 15,865 valid residuals from 15,959 candidates (94 first-epoch/history misses), with 1.02 cm RMS and 12.6 cm maximum. M5 OFF and ON position streams were bit-identical, and diagnostic wall overhead was 0.48%.
Final same-binary evaluation used the three milestone datasets plus Tokyo
run2 and Nagoya run2. Every full OFF/ON rtk.pos pair was SHA-256 identical:
| Dataset | Fix % | PPC 3D 50 cm % | p95 horizontal m | p95 abs up m | Official % | OFF/ON wall s |
|---|---|---|---|---|---|---|
| Tokyo run1 | 77.36 | 75.75 | 7.53 | 23.32 | 70.07 | 2312.3 / 2312.4 |
| Tokyo run2 | 77.58 | 82.39 | 2.95 | 4.86 | 84.03 | 1145.2 / 1146.1 |
| Tokyo run3 | 78.52 | 79.22 | 4.69 | 6.22 | 74.68 | 2355.2 / 2359.3 |
| Nagoya run1 | 78.27 | 74.39 | 9.66 | 13.15 | 55.46 | 1166.6 / 1171.3 |
| Nagoya run2 | 53.74 | 53.80 | 27.72 | 60.04 | 39.61 | 1059.5 / 1061.4 |
Raw TDCP diagnostics were:
| Dataset | Candidates | Residuals | Missing / gap / LLI / invalid | RMS m | Max abs m |
|---|---|---|---|---|---|
| Tokyo run1 | 336152 | 331816 | 4306 / 25 / 5 / 0 | 3122.69 | 299793 |
| Tokyo run2 | 310171 | 306370 | 3799 / 0 / 2 / 0 | 3592.74 | 299793 |
| Tokyo run3 | 551476 | 545598 | 5869 / 0 / 9 / 0 | 2029.36 | 299793 |
| Nagoya run1 | 242782 | 241048 | 1402 / 0 / 332 / 0 | 3764.10 | 299793 |
| Nagoya run2 | 290885 | 287508 | 2696 / 0 / 681 / 0 | 1.78 | 375.25 |
The repeated approximately 299,793 m jump is consistent with (but does not by itself prove) an unmodelled one-millisecond receiver-clock discontinuity. These raw results explicitly fail readiness for a TDCP measurement update. Any future update requires a separate opt-in switch and validation of clock jump, slip/outlier, and robust residual gates. M5 diagnostics remain default-off.