Skip to content

MADOCALIB Native Migration Ledger

This ledger is the active execution plan for issue #148. The older madoca_port_plan.md remains the detailed implementation history; when the two documents differ, this ledger describes the current migration state and exit criteria.

Target

Move the supported MADOCALIB behavior into native libgnss++ C++ while retaining MADOCALIB only as an opt-in test oracle. A normal configure, build, install, and runtime must not require a MADOCALIB checkout.

This is behavioral migration, not source-tree vendoring. Native code keeps the libgnss++ state, product-container, CLI, and JSON sign-off architecture. Small literal helper ports are allowed only with deliberate attribution and license review.

Frozen Oracle Contract

Item Frozen value
Upstream QZSS-Strategy-Office/madocalib
Commit 0089f7dc97e8e2ba283a40be2edf4b73a140df6c
Reported version MADOCALIB 2.1
Opt-in configure -DMADOCALIB_PARITY_LINK=ON -DMADOCALIB_ROOT_DIR=<checkout>
Default configure MADOCALIB_PARITY_LINK=OFF
Primary observation sample_data/data/rinex/MIZU00JPN_R_20250910000_01D_30S_MO.rnx
Primary navigation sample_data/data/rinex/BRDM00DLR_S_20250910000_01D_MN.rnx
L6E channels sample_data/data/l6_is-qzss-mdc-004/2025/091/2025091A.{204,206}.l6
L6D channels sample_data/data/l6/2025/091/2025091A.{200,201}.l6
Antenna model sample_data/data/igs20.atx
Primary window 2025-04-01 00:00:00--00:59:30, 30 s interval
Reference ECEF -3857167.6484 3108694.9138 4004041.6876 m

The madoca-parity GitHub Actions job is authoritative for oracle-linked tests. It checks out the exact commit above, builds run_tests, runs the helper/decoder parity suite, generates the MADOCALIB and native PPP-AR trajectories, and uploads their summaries and epoch matches.

The public materialization self-diff runner uses the same repository and commit. Changing either pin is a dedicated PR: record the upstream version, regenerate all oracle artifacts, and explain every behavior change before updating this table.

License Boundary

The upstream readme.txt identifies MADOCALIB as BSD 2-Clause with additional clauses and copyright notices for the Cabinet Office, Japan; Lighthouse Technology & Consulting; JAXA; Toshiba Electronic Technologies; and T. Takasu. Source and binary redistributions must retain or reproduce the upstream notice, conditions, and disclaimer. Companion binaries retain their original licenses. MADOCALIB_GUI_LICENSE.txt is a separate GUI license and the GUI is outside this migration.

Therefore:

  • external/madocalib is an external checkout, never a production source directory or installed runtime dependency;
  • oracle wrappers remain guarded by GNSSPP_HAS_MADOCALIB_ORACLE or GNSSPP_HAS_MADOCALIB_BRIDGE;
  • the default build must compile and test with both macros false;
  • direct upstream linkage is limited to the opt-in parity build and its artifacts; and
  • any literal native port must preserve the required attribution in the source and distribution documentation.

Capability Ledger

Status meanings: native is implemented without MADOCALIB and satisfies the declared #148 acceptance envelope; native opt-in additionally requires an explicit native feature input; oracle-only exists only in the opt-in linked lane; deferred is outside the frozen #148 solver/fixture contract and has a dedicated follow-up.

MADOCALIB surface Native implementation/evidence Status Exit evidence
System, signal, update-interval, URA, and bias-code helpers madoca_core, madoca_parity, MadocaParity tests native Keep deterministic helper parity at exact integer or 1e-6 scaled tolerance
L6E byte sync and subframe assembly MadocaL6eDecoder native Byte-stepped oracle test remains green
L6E mask/orbit/clock/code-bias/phase-bias/URA madoca_l6, SSRProducts materialization native No missing keys; supported numeric fields match the pinned oracle
Multi-file L6E decode and correction ordering decodeMadocaL6eFilesToProducts, correction contract native Materialization key, time, IOD, identity, and value diffs are zero for the pinned fixture
L6D coverage/correction decode MadocaL6dDecoder native Byte-stepped region/area parity remains green
L6D receiver-area selection and delay/std MadocaIonoStore native Selected receiver correction values match the oracle
L6D causal product snapshots MadocaIonoSnapshot, MadocaIonoProducts native File snapshot/application sequence parity remains green
L6D use inside PPP --madoca-l6d applies opt-in STEC rows; --madoca-l6d-shadow remains measurement-neutral native opt-in M3 one-hour row, gate, status-safety, and trajectory gates are pinned
PPP correction signs and ordering Native correction contract plus materialization/residual diff tools native Default PPP parity matrix remains within the accepted MIZU/ALIC envelope
PPP commit-on-success and diagnostics Native postfit validation/shadow telemetry native MIZU 1 h/6 h/24 h and ALIC regression gates remain green
GLONASS phase in MADOCA PPP Native L1/L2 rows plus corrected postfit-shadow audit native Keep the pinned per-satellite demeaned RMS at millimetre scale and span below 4 cm
MADOCALIB exec_ppp Native PPP runs end to end across the MIZU/ALIC release matrix native Keep all 1 h/6 h/24 h trajectory and runtime baselines green
MADOCALIB exec_pppar Windows and authoritative Ubuntu Release both match the oracle's 108 Fix / 10 Float over all 118 epochs native Keep exact status agreement, no wrong/missed Fix, RMS at most 0.20 m, and maximum at most 1.0 m
MADOCALIB exec_pppar-ion Native opt-in L6D application runs across the MIZU/ALIC release matrix with explicit status baselines native opt-in Keep all 1 h/6 h/24 h trajectory, status, row, and runtime baselines green
Triple/quad-frequency PPP-AR expansion Not part of the frozen dual-frequency #148 profiles or fixtures deferred to #387 Add dedicated fixtures, ambiguity/state parity, and release baselines before claiming support
Linked postpos() bridge Non-installed gnss_madocalib_oracle oracle-only Available only in MADOCALIB_PARITY_LINK=ON builds; absent from normal gnss_ppp help and parsing

Current Numerical Baselines

The frozen one-hour MIZU PPP-AR window has 118 matched output epochs. On both the Windows Release evidence lane and the authoritative Ubuntu Release lane, the MADOCALIB pppar oracle and native path produce 108 Fix / 10 Float with exact 118/118 status agreement and no native-only or oracle-only Fix.

On Windows, the native/oracle 3D delta RMS is 0.054269 m, the maximum is 0.419804 m, and the trailing-1800-second RMS is 0.035616 m. On Ubuntu, the 3D delta RMS is 0.189146 m and the maximum is 0.451489 m. These results meet the M2 status and trajectory exit gates.

M3--M6 are also complete for the frozen contract. The versioned M5 baseline covers all 18 combinations of MIZU/ALIC, ppp/pppar/pppar-ion, and 1 h/6 h/24 h. Required 1 h/6 h and manually dispatched 24 h matrices passed with their declared trajectory, status, row, artifact, and runtime gates. The milestone subsections below are chronological evidence: statements that a milestone "remains open" describe that intermediate slice, not the final state.

The float-PPP history and older MIZU/ALIC/full-day measurements remain in issue

148 and madoca_port_plan.md as regression context. The versioned release

matrix and milestone gates above are the current completion evidence.

Migration Milestones

Each milestone is a sequence of focused issue-#148 branches and PRs. Default behavior stays bit-identical until that milestone's default-flip gate is met.

M0 -- Freeze the ledger

  • Keep this capability table synchronized with code and CI.
  • Freeze the oracle, license boundary, fixtures, baseline metrics, and artifact names.
  • Define the M1--M6 acceptance gates before changing solver behavior.

Exit: docs build and link checks pass; issue #148 records the merged ledger.

M1 -- Reach the native PPP-AR resolver safely

  • Replace the misleading 3D convergence_threshold_horizontal measurement with an actual local-horizontal deviation and report horizontal and vertical telemetry separately.
  • Add a typed kinematic convergence policy; do not weaken the static default.
  • Sweep the kinematic window and threshold on the pinned 118-epoch case.
  • Prove that native per-frequency AR reaches candidate formation/LAMBDA while preserving the current default output when the policy is disabled.

Exit: after warm-up, at least one epoch reaches the per-frequency ambiguity candidate stage; telemetry distinguishes warm-up, horizontal, vertical, and candidate-stage rejection; default PPP/CLAS/MADOCA tests do not regress.

M1 implementation evidence (2026-07-13)

The native convergence window now reports the legacy ECEF 3D deviation plus local horizontal and vertical deviations. legacy-3d remains the default, so existing admission behavior is unchanged; local-enu is an explicit diagnostic policy with separate horizontal and vertical thresholds.

On the pinned MIZU 120-input-epoch smoke (118 published solutions), the legacy window reached maxima of 1.17008 m ECEF 3D, 0.660907 m horizontal, and 1.15825 m vertical and did not admit AR. The strict 0.10 m horizontal / 0.20 m vertical policy also did not admit AR. A bounded threshold sweep reached the native per-frequency resolver at 0.75 m horizontal / 1.25 m vertical (1200 s) and at 1.50 m / 2.00 m (1080 s); these relaxed values are evidence probes, not new defaults.

The 0.75 m / 1.25 m probe produced 78 resolver attempts. All 78 stopped at wide_lane_only_insufficient_n1; zero reached a complete N1 integer fix. The current solver nevertheless published those 78 epochs as PPP_FIXED. M1 therefore proves resolver reachability and identifies the exact next boundary: M2 must separate wide-lane-only updates from complete N1 Fix admission before any native Fix-rate or trajectory-parity claim is accepted.

M2 -- Port exec_pppar ambiguity behavior

  • Compare ambiguity eligibility, frequency pairs, datum/reference selection, float ambiguity values/covariance, integer candidates, ratio, and admission epoch by epoch.
  • Port the missing MADOCALIB behavior in small slices.
  • Apply the accepted integer constraint to a compatible native fixed state and compare the published solution, not Fix status alone.

Exit on the pinned window: 118 epochs remain aligned; native produces no wrong Fix against the oracle trajectory; Fix/Float status agreement is at least 95%; native Fix count is at least the oracle's 108; matched fixed-epoch 3D delta RMS is at most 0.20 m and maximum is at most 1.0 m. If upstream numerical limits make one threshold infeasible, change it only in a dedicated evidence PR.

M2a -- Wide-lane-only admission contract

The pinned MADOCALIB source returns 1 for a wide-lane-only result and 2 for a complete narrow-lane fix (src/ppp_ar.c). src/ppp.c uses the temporary wide-lane-constrained state for that epoch's position, maps SOLQ_FIX_WL back to SOLQ_PPP, and only holds the ambiguity state for SOLQ_FIX. Native follows the same contract: a wide-lane-only result publishes its temporary position as PPP_FLOAT, reports zero fixed ambiguities and zero ratio, then restores the pre-AR float filter state.

On the pinned 120-input-epoch MIZU Windows Release probe this changes 78 incorrectly labelled Fix rows to Float without changing any of the 118 published positions (before versus after 3D delta RMS and maximum are both 0 m). The stage counts remain 78 wide-lane-only attempts and zero complete N1 fixes. The Linux parity build currently publishes 84 Float rows with 44 wide-lane-only attempts, exposing a remaining platform/state-trajectory delta for M2b. A dedicated parity CI gate rejects any published Fix on every valid row while no complete N1 fix exists, independently of that row-count delta.

M2b -- One covariance update per MADOCA epoch

The pinned MADOCALIB pppos() loop copies rtk->x and rtk->P into its temporary state before every residual-screening pass and commits the first accepted result. It therefore applies an epoch's measurement covariance update once. Native previously fed the already-updated covariance into as many as eight relinearization iterations, repeatedly consuming the same rows and becoming overconfident before ambiguity resolution.

The opt-in GNSS_PPP_PFDUMP trace now reports the position covariance norm at the pre-EWL, post-EWL, and post-WL boundaries with GPS week/TOW. At 00:03:00 the native post-WL norm changes from 0.962 m to 2.281 m, versus 2.49 m in the MADOCALIB trace. At 00:03:30 it changes from 0.742 m to 1.812 m, versus 2.28 m. Remaining float-state differences are tracked separately; this slice only removes the proven repeated-update mismatch.

On the pinned 120-input-epoch MIZU probe, all 118 solution epochs remain aligned. Native produces 31 complete fixes, all inside MADOCALIB fixed epochs, and no wrong fix. The full-window native/oracle 3D delta RMS improves from 10.923 m to 4.391 m; the final 30-minute RMS is 1.046 m. The M2 exit criteria are not yet met.

M2c -- Measurement variance parity

The coherent MADOCA CLI already applies MADOCALIB's 300:1 code-to-phase error ratio. The per-frequency filter then multiplied the resulting code variance by the legacy diagnostic default of 9.0, making code sigma exactly three times the oracle value. That scale now defaults to 1.0 and remains available only as an explicit diagnostic override.

Two other variance terms were missing. MADOCALIB adds 0.01^2 m^2 estimated- troposphere model variance to every code and phase row and adds 0.1 times the SSR URA variance. Native now applies both terms on the coherent MADOCA path. The L6E decoder already retained subtype-7 URA indices, but product conversion dropped them; conversion now carries the DF389/MADOCALIB sigma into the SSR correction. A focused unit test pins the complete URA-index mapping.

At GPS week 2360, TOW 173010, native/oracle sigmas now agree at trace precision: G04 code/phase are 2.2719/0.0143 m, G08 are 1.8963/0.0136 m, and G16 are 1.3174/0.0129 m. On the pinned 120-input-epoch probe, all 118 solution epochs remain aligned. Native publishes 38 complete fixes, all within the oracle's fixed epochs, with no wrong fix. Full-window native/oracle 3D delta RMS improves from 4.391 m to 2.194 m, maximum delta falls from 14.78 m to 5.79 m, horizontal RMS is 1.396 m, Up RMS is 1.692 m, and final-30-minute RMS is 1.091 m. M2 remains open because status agreement and fixed-position thresholds are not yet met.

M2d -- First-epoch carrier-phase admission

MADOCALIB initializes each per-frequency ambiguity before ppp_res() and uses the corresponding carrier-phase row in that same epoch. Native previously required one completed ambiguity-lifecycle update, so its first usable MADOCA epoch contained 31 code rows and no phase rows. Coherent MADOCA per-frequency mode now uses a zero minimum lock count while the shared non-MADOCA default remains one.

On the pinned MIZU probe, the first native equation grows to 72 code and 68 phase rows and its first-epoch native/oracle 3D delta falls from 1.320 m to 0.770 m. Across all 118 aligned solution epochs, native produces 47 complete fixes, all inside oracle Fix epochs, with no wrong Fix. Full-window 3D delta RMS is 2.358 m, maximum is 5.907 m, and final-30-minute RMS is 1.125 m. The full-window RMS is higher than M2c's 2.194 m, so this row-contract correction does not close M2; remaining native-only rows and state priors must be aligned before judging its combined trajectory effect.

M2e -- Strict SSR broadcast-ephemeris admission

MADOCALIB's seleph() requires the broadcast ephemeris referenced by the SSR orbit IODE and rejects a satellite when that record is unavailable. Native's IODE-aware selector previously fell back to the nearest-age ephemeris, applying the SSR delta to a different broadcast orbit and admitting GPS, GLONASS, and BeiDou satellites before the oracle. The selector now returns no state when the requested record is missing. GPS, Galileo, QZSS, and GLONASS compare IODE directly; BeiDou follows MADOCALIB's Compact SSR rule, toes mod 2048 == (IODE * 8) mod 2048.

On the pinned MIZU trace, native now reproduces the oracle's constellation admission sequence exactly: G08 appears at 00:02:00, GLONASS at 00:02:30, C28 at 00:03:30, and the seven-satellite BeiDou set at 00:04:00. GPS and BeiDou row counts also match at every checked early epoch. The remaining row-count gap is isolated to six QZSS rows per epoch and four GLONASS rows after its admission, which are frequency-selection differences for a later slice.

Across all 118 aligned solution epochs, native produces 65 complete fixes, all inside oracle Fix epochs, with no wrong Fix. The full-window native/oracle 3D delta RMS is 0.317 m, maximum is 0.988 m, horizontal RMS is 0.213 m, Up RMS is 0.235 m, and final-30-minute RMS is 0.299 m. M2 remains open: 43 oracle Fix epochs are still native Float, status agreement remains below 95%, and the fixed-epoch RMS exit threshold is not yet proven.

M2f -- QZSS three-frequency row parity

MADOCALIB orders QZSS per-frequency observations as L1, L5, then L2. Native already preserved all three RINEX bands and implemented additional-frequency states, corrections, and measurement rows, but selected L2 as the secondary frequency by default. That made L2 ineligible for the additional slot and dropped L5, leaving six missing rows across J02/J03/J04 at every epoch.

Coherent MADOCA now selects L5 as the secondary frequency and retains L2 as the additional MADOCALIB ordinal by default. GNSS_PPP_MADOCA_QZSS_L5=0 remains an opt-out. A RINEX reader regression test pins simultaneous L1/L5/L2 retention rather than testing L5 replacement alone.

On the pinned early MIZU trace, QZSS grows from 12 to the oracle's 18 rows per epoch with exact L1/L5/L2 code-and-phase identities. Across all 118 aligned solution epochs, native produces 61 complete fixes, all inside oracle Fix epochs, with no wrong Fix. Full-window native/oracle 3D delta RMS is 0.337 m, maximum is 0.854 m, horizontal RMS is 0.232 m, Up RMS is 0.244 m, and the final-30-minute RMS is 0.309 m. The full RMS is 0.020 m above M2e while the maximum improves by 0.134 m; this slice accepts the exact oracle row contract. M2 remains open.

M2g -- GLONASS phase rows and STEC prior parity

MADOCALIB initializes each estimated STEC state with VAR_IONO=SQR(60.0) and admits both GLONASS L1 and L2 carrier-phase rows in the float filter. Native previously used a 100 m² generic STEC prior and suppressed only the GLONASS L1 phase row; L2 remained active despite the code-only comment and opt-out.

Coherent MADOCA now uses the oracle's 3600 m² STEC prior and enables both GLONASS phase rows by default. GNSS_PPP_INIT_IONO_VAR remains an explicit prior override, and GNSS_PPP_MADOCA_GLONASS_PHASE=0 consistently suppresses both GLONASS phase rows.

The earlier metre-scale GLONASS drift diagnosis was caused by postfit telemetry using receiver geometry materialized before the accepted filter update. With the corrected shadow from M2g's prerequisite PR, the four pinned GLONASS satellites show demeaned residual RMS of 2.4--5.2 mm, spans of 1.6--3.4 cm, and near-zero residual/elevation correlation. On the 120-epoch MIZU probe, all 118 solution epochs remain aligned, native remains at 90 Fix / 28 Float with no wrong Fix, and full-window 3D delta RMS improves from 0.196 m to 0.193 m. M2 remains open because the early-window status agreement gate is not met.

M2h -- GLONASS receiver code-IFB variance

MADOCALIB adds VAR_GLO_IFB=SQR(0.6) to every GLONASS pseudorange row while leaving carrier-phase rows unchanged. Native previously used the common code variance for GLONASS, underestimating its code sigma after the constellation entered the filter.

Coherent MADOCA per-frequency mode now adds the same 0.36 m^2 code-only variance. At GPS week 2360, TOW 173160, the native/oracle GLONASS code sigmas agree at trace precision: R09 is 3.1256 m, R19 is 6.3378 m, and R21 is 2.5205 m. On the pinned 120-epoch MIZU probe, all 118 solution epochs remain aligned, native remains at 90 Fix / 28 Float with no wrong Fix, and full-window 3D delta RMS is 0.193236 m with a 0.330669 m maximum. M2 remains open because the early-window status agreement gate is not met.

M2i -- Multi-frequency geometry-free slip scope

MADOCALIB evaluates the primary carrier against every configured secondary frequency and resets only the primary plus the frequency whose geometry-free jump crosses pos2-slipthres=0.15 m. Native previously checked only its first secondary and then reset every ambiguity for the satellite.

The coherent MADOCA path now uses the oracle threshold without the generic 0.5 m floor, evaluates every retained frequency, and performs frequency-scoped resets. The pinned G28 event at 00:02:00 has a 0.144 m L1-L2 change and a 0.175 m L1-L5 change, so L1/L5 reset immediately while L2 remains continuous. Across the 118 aligned output epochs, native improves from 91 Fix / 27 Float to 95 Fix / 23 Float, with no native-only Fix. Status agreement is 88.98%. M2 remains open because 13 oracle Fix epochs are still native Float.

M2j -- Solid-earth-tide convention

MADOCALIB uses the legacy Love-number solid-earth-tide displacement for this profile. Native's default IERS Step-1+2 model created a real receiver-geometry difference even though it was not the remaining AR admission cause. Coherent MADOCA now selects the legacy convention while other PPP profiles retain their configured tide model.

On the pinned window, the default output is exactly equal to the explicit legacy-tide A/B output for all 118 rows. Fix/Float counts remain 95/23, while the full native/oracle 3D RMS improves from 0.209425 m to 0.112100 m and the trailing-1800-second RMS improves from 0.221474 m to 0.070070 m. M2 remains open because the status and native-Fix-count gates are not met.

M2k -- SSR clock relativity and first-N1 confirmation

MADOCALIB satpos_ssr() recomputes broadcast clock relativity as -2 * dot(position, velocity) / c^2, using RTKLIB's 1 ms forward-difference velocity. Native previously retained the eccentric-anomaly form returned by its broadcast ephemeris propagation. The difference is satellite-specific at centimetre scale once harmonic orbit terms are included and was sufficient to shift the tightly weighted carrier residuals and PAR candidate ordering. Coherent MADOCA now reproduces the state-vector form with the exact SSR IODE; other PPP profiles keep their existing clock convention.

On Windows Release, the clock correction alone recovers every oracle Fix but admits one near-threshold native Fix one epoch early. To keep the established no-wrong-Fix guard without tuning the ratio threshold, the first coherent-MADOCA N1 success is provisional and must be followed by a consecutive successful AR attempt. The already-applied EWL/WL state is retained and published as Float during that confirmation epoch. After the first N1 commit, normal single-attempt reacquisition applies.

On the Windows Release probe, all 118 solution epochs align: native and oracle both produce 108 Fix / 10 Float, with zero native-only and zero oracle-only Fix. The native/oracle 3D delta RMS is 0.040094 m, the maximum is 0.246063 m, and the trailing-1800-second RMS is 0.032859 m.

The authoritative Ubuntu Release probe improves from its previous 76 Fix / 42 Float baseline to 77 Fix / 41 Float, still with zero native-only Fix. It misses 31 oracle Fix epochs, so M2 remains open. At the first cross-platform split (TOW 175260), Windows retains 16 WL pairs and reaches an N1 Fix, while Ubuntu retains 15 after E31-E06 crosses the 0.20-cycle WL admission boundary. The oracle starts with 18 N1 pairs, excludes only G31 and C09, then fixes 16 at ratio 2.333 against its 1.20 dimension-adjusted threshold. Ubuntu exhausts ten PAR exclusions without a fix. Covariance sigmas remain closely aligned; the remaining cause is the accumulated float ambiguity mean and WL candidate path, not the SSR clock formula.

M2l -- Galileo MW-supported WL admission

The first Ubuntu-only miss at TOW 175260 was caused by the weakly constrained float ambiguity gauge, not a different observation or covariance admission sigma. Windows and Ubuntu seed E06 with identical corrected code, phase, and STEC values, but nanometre-scale phase rounding at the first epoch grows along the ambiguity/STEC gauge. E31-E06 therefore reaches ordinary WL admission on Windows and misses the 0.20-cycle fractional gate on Ubuntu. Its independent Melbourne-Wübbena average is identical on both platforms.

Coherent MADOCA now lets a mature Galileo MW average support WL admission after 60 samples when its fractional distance is below 0.20 cycles. The filter-state WL value still supplies the integer and its covariance still must pass the existing 1.0-cycle standard-deviation gate. BeiDou is deliberately excluded: its MW and filter-state WL integers use different bias datums in this profile. A high-agreement MADOCA ratio may also pass within one percent of its adjusted threshold, containing numerical boundary movement to candidate sets whose two LAMBDA solutions already agree above 90 percent.

Windows remains exactly 108 Fix / 10 Float. Authoritative Ubuntu improves from 77 Fix / 41 Float to 83 Fix / 35 Float, recovers TOW 175260, and retains zero native-only Fix. Full-window 3D delta RMS is 0.172757 m and the maximum is 0.439642 m. Twenty-five oracle-Fix/native-Float epochs remain, beginning at TOW 175320, so M2 remains open.

M2m -- Frequency-scoped STEC discontinuity reset

At the first remaining miss, G31 suffered a frequency-specific carrier slip. MADOCALIB reset that carrier ambiguity and then cleared the carrier-derived ionosphere-delta history before the next STEC prediction. Native reset the ambiguity but retained the old delta baseline, so it counted the same carrier discontinuity again in the ionosphere prediction and displaced the ambiguity mean used by partial AR.

The native frequency-scoped reset now also clears the affected carrier's ionosphere-delta history. At TOW 175320, G31's pre-update native STEC then agrees with the oracle within 0.039 m and its post-update STEC within 0.006 m. Ubuntu remains at 83 Fix / 35 Float with no wrong Fix; full-window 3D delta RMS is 0.172594 m and the maximum is 0.428885 m. Windows remains exactly 108 Fix / 10 Float. The state-transition defect is removed, but M2 remains open because the cross-platform ambiguity-gauge sensitivity still requires an admission rule that is deterministic for equivalent valid partial-AR subsets.

M2n -- One-removal N1 subset lookahead

MADOCALIB's partial AR greedily removes the lowest-elevation satellite on which the first two LAMBDA candidates disagree. After the STEC reset was aligned, weak ambiguity/STEC gauge differences still changed that disagreement set and therefore the greedy removal order across platforms. At TOW 175320, the ordinary 14-pair Ubuntu set rejects at ratio 1.3746; removing the implicated G26 pair produces a 13-pair child at ratio 1.5179 against its unchanged 1.20 threshold and passes the existing candidate-agreement gate.

Before taking the greedy removal, coherent MADOCA per-frequency AR now examines implicated one-satellite children in canonical order and accepts the first one that already passes the same LAMBDA ratio, dimension, and candidate-agreement contracts. It adds no ambiguity candidate and relaxes no threshold. When no child passes, the previous MADOCALIB-compatible greedy path is unchanged.

Both Windows and authoritative Ubuntu Release now match the oracle's 108 Fix / 10 Float at all 118 epochs, with no wrong or missed Fix. Windows 3D delta RMS is 0.054269 m with a 0.419804 m maximum; Ubuntu RMS is 0.189146 m with a 0.451489 m maximum. The M2 exit criteria are therefore satisfied.

M3 -- Apply L6D ionosphere products

  • Promote the proven snapshot lookup from shadow telemetry to an explicit opt-in PPP STEC measurement/state input.
  • Match MADOCALIB area selection, age, uncertainty, signal coefficient, state initialization, reset, and process-noise behavior.
  • Compare pppar-ion row keys and values before final positions.

Exit: supported snapshot/application keys match exactly; delay/std values match within 1e-6; the configured common residual row set has no unmatched rows; MIZU pppar-ion produces no wrong Fix and improves or preserves M2 trajectory metrics. The feature remains opt-in until M5.

M3 prerequisite -- key snapshots by the completed L6D message

The file decoder previously keyed every snapshot with decodeTime(), whose contract is the primary PRN-200 channel time used by the global staleness gate. When a PRN-201 ionosphere message completed independently, its satellite correction times advanced but the snapshot key remained at the PRN-200 reference time. A downstream causal lookup could therefore select a snapshot whose corrections appeared about 3,487 seconds in the future and reject every one against MADOCALIB's 300-second age gate.

messageTime() now records the channel time of the most recently completed message, while decodeTime() retains its primary-channel contract. File snapshots use messageTime(). The real MIZU PRN-201 fixture verifies that no satellite correction is newer than its snapshot and that completed corrections share the snapshot time. In a layered, not-yet-published M3 STEC application experiment, this changes the first valid epochs from zero accepted rows to 9, 9, and 10 rows, each with a 24-second correction age. This establishes the causal snapshot fix as a prerequisite; row/value and solution parity remain M3 work.

M3 opt-in STEC application

--madoca-l6d now promotes the proven snapshots to prefit STEC state rows only for coherent native MADOCA per-frequency PPP. The default path and --madoca-l6d-shadow remain measurement-neutral. Admission matches const_iono_corr(): GPS, GLONASS, Galileo, and QZSS only; absolute age at most 300 seconds; standard deviation at most 1 m; independent constellation mean bias removal; H=1; and variance std^2.

The convergence gate uses the previous published solution covariance, matching MADOCALIB prev_qr, rather than the current predicted filter covariance. On the pinned MIZU hour this applies exactly the oracle's nine rows at the first valid epoch and skips the following 117 epochs after the horizontal/vertical 2 m/3 m covariance gate closes. The real decoder/oracle tests retain delay/std equality within 1e-6, and the first-epoch row satellites, residuals, standard deviations, and constellation biases match the oracle trace.

The active L6D constraint removes the unconstrained STEC-gauge premise of the M2 one-child N1 lookahead, so this opt-in path retains MADOCALIB's literal greedy partial-AR removal. The one-hour gate records native 89 Fix / 29 Float against oracle 97 Fix / 21 Float: eight Fix are conservatively missed and there are zero wrong Fix. Both reference trajectories improve over M2 (oracle 3D RMS 4.49578 to 4.44821 m; native 4.51256 to 4.44202 m). Native/oracle 3D delta RMS is 0.244372 m with a 0.459645 m maximum; the remaining status and component convergence belongs to M4.

M4 -- Close remaining row and boundary differences

  • Resolve QZSS atmosphere admission and every unexplained native-only or oracle-only row.
  • Trace differences in order: materialization, satellite set, row set, residual components, then solution.
  • Close the remaining 24-hour boundary residue without time-window-specific position guards.

Exit: all supported materialization and residual identities are accounted for; configured common component deltas pass their explicit tolerances; MIZU 24-hour maximum native/oracle 3D delta is at most 1.0 m with no new boundary spike.

M4 24-hour input and boundary gate

The CLI previously limited native --madoca-l6d and shadow input to three files, while the linked bridge copied only the first three --madocalib-mdciono paths into MADOCALIB's three stream slots. That made an explicit A--X hourly sequence impossible even though RTKLIB time-path expansion supports it. Native now accepts the complete sequence. The bridge validates every explicit file, then condenses two or more files from the same PRN stream into %Y/%n/%Y%n%HU.<prn>.l6; independent L6D streams still fail when they exceed the three-slot limit.

On the full-day MIZU pppar-ion run, the bridge publishes 2,878 solutions and native publishes 2,855, all of which match a bridge epoch. Native/oracle 3D delta RMS is 0.097181 m and the maximum is 0.459645 m; no matched epoch exceeds 1 m. Across the 23 hourly boundaries, using the eight epochs within plus/minus 120 seconds of each boundary, the largest 3D delta is 0.280746 m. This satisfies the M4 24-hour trajectory and boundary clause without a time-window-specific position guard.

Status remains deliberately conservative and is not hidden by that trajectory result: the bridge reports 2,857 Fix and 21 non-Fix solutions, while native reports 1,916 Fix and 939 Float solutions. All native Fix epochs are bridge Fix epochs, so there are zero wrong Fix and 918 matched missed-Fix epochs. The status baseline and any safe convergence improvement remain M5 evidence.

M5 -- Make parity a release gate

  • Run MIZU and ALIC for 1 h and 6 h on every solver-change PR.
  • Run their 24 h windows on the extended/optional lane.
  • Cover PPP, PPP-AR, and PPP-AR-ion with solution, status, row, and runtime artifacts.
  • Record exact commands and schema versions in the CI summary.

Exit: all six solver/dataset profiles have pinned baselines; required lanes fail on missing artifacts, status regression, trajectory regression, or runtime over 2x their accepted native baseline; default builds still run without MADOCALIB.

M5 implementation evidence (2026-07-30)

scripts/ci/run_madoca_release_matrix.py now owns the versioned madoca_release_matrix.v1, madoca_release_commands.v1, and madoca_release_baseline.v1 contracts. Every case preserves the exact bridge and native argv, both summaries and logs, both solution files, the solution diff, matched epochs, L6D constraint-row counters, and wall runtimes. The checked-in docs/madoca_release_baseline.json contains all 18 combinations of MIZU/ALIC, ppp/pppar/pppar-ion, and 1 h/6 h/24 h.

The native ppp command deliberately uses the CLI's static motion model. That is the historical Issue #148 native contract for these stationary receivers; an initial matrix draft incorrectly forced --kinematic on every profile and produced 24-hour deltas of 5.93 m (MIZU) and 4.50 m (ALIC). Restoring the profile-specific command gives MIZU 1 h/6 h/24 h RMS of 1.538738/1.162767/1.271684 m, consistent with the earlier tracker evidence. The AR profiles remain kinematic, matching the M2--M4 commands.

Selected measured results before the declared trajectory margin are:

Station/profile Window matched oracle-only wrong Fix missed Fix RMS 3D max 3D
MIZU PPP 24 h 2,597 281 0 0 1.271684 m 4.080235 m
MIZU PPP-AR 1 h 118 0 0 0 0.186378 m 0.451489 m
MIZU PPP-AR 24 h 2,855 23 0 921 0.088482 m 0.451489 m
MIZU PPP-AR-ion 24 h 2,855 23 0 918 0.097181 m 0.459645 m
ALIC PPP 24 h 2,855 23 0 0 0.766809 m 3.323228 m
ALIC PPP-AR 1 h 118 0 11 1 0.115636 m 0.515678 m
ALIC PPP-AR 24 h 2,855 23 11 554 0.091582 m 0.515678 m
ALIC PPP-AR-ion 24 h 2,855 23 0 579 0.088856 m 0.445660 m

Here wrong Fix means oracle non-Fix/native Fix, not a truth-labeled position blunder. The 11 ALIC PPP-AR epochs were investigated before accepting the baseline. Disabling the M2 canonical one-removal lookahead reduces them to seven but does not remove the later cluster; the oracle's float/covariance trajectory assigns low candidate agreement and rejects it, while native assigns 94--95% agreement near ratio 1.23--1.27. A 1.30 ratio floor would also remove at least 23 correct low-ratio MIZU fixes, and fixed-position shift does not separate the stations. No station- or time-specific solver guard was accepted. The mismatch remains explicit and any increase fails the status baseline.

Trajectory ceilings are the larger of 5% or 1 cm above the measured value. Native wall runtime is capped at twice its accepted measurement. Bridge runtime is still recorded but is not a production-runtime gate. The ordinary madoca-parity lane runs every 1 h and 6 h case; a manual workflow_dispatch additionally runs all 24 h cases. Both lanes upload the complete matrix and append schema, metric, and exact-command artifact links to the GitHub job summary. Default CMake builds retain MADOCALIB_PARITY_LINK=OFF.

M6 -- Complete the migration

  • Promote native behavior only after M2--M5 pass with an explicit opt-out for one release cycle.
  • Remove any runtime-facing bridge selection from normal user workflows while retaining the opt-in oracle build and tests.
  • Remove superseded preview knobs and guards after proving their replacements.
  • Update user documentation and close issue #148 with the final matrix.

M6 oracle CLI isolation (2026-07-30)

The linked postpos() selector is no longer part of the normal gnss_ppp command surface. Production and installed builds neither list nor accept any --madocalib-* options. An opt-in MADOCALIB_PARITY_LINK=ON configure creates the separate, non-installed gnss_madocalib_oracle executable; only that executable accepts --madocalib-bridge and the accompanying oracle arguments. The labeled parity lane uses the oracle executable for both sides of the comparison so exact commands remain reproducible without making the external runtime a user-facing solver choice.

M6 preview cleanup (2026-07-30)

Two superseded preview selectors were removed after the M5 matrix froze their accepted replacements. GNSS_PPP_MADOCA_SSR_REPLAY and its independent-stream decoder/API represented the rejected #136 replay hypothesis; production and materialization paths now have one decodeMadocaL6eFilesToProducts contract. The explicit GNSS_PPP_MADOCA_GALILEO_GATE selector was redundant after the Galileo admission behavior became part of default-on GNSS_PPP_MADOCA_EARLY_WINDOW; the latter remains the one-release-cycle opt-out.

The other promoted compatibility opt-outs remain for that release cycle. GNSS_PPP_MADOCA_POSTFIT_COMMIT=0 still selects the pre-promotion update behavior, and the 100 m spike guard remains its safety backstop. Bias, frequency, constellation, and early-window opt-outs likewise remain available for controlled rollback. Diagnostic dumps and measurement-neutral shadow inputs are retained because they observe the accepted path rather than select an alternative solver implementation.

M6 closeout evidence (2026-07-30)

PR #384 made the 18-case matrix a versioned release gate and passed both the required 1 h/6 h lane and the manually dispatched 24 h lane. PR #385 moved all linked postpos() parsing into the non-installed oracle executable. PR #386 removed the rejected replay implementation and redundant Galileo preview selector; its Docs, hygiene, Linux GCC/Clang, macOS Clang, static analysis, MADOCA parity, extended-test, and optional-signoff jobs all passed.

The accepted ppp and pppar-ion envelopes intentionally preserve measured non-zero solution/status deltas recorded by M5; #148 requires bounded, regression-gated native behavior rather than byte-identical whole-solver output. Complete MADOCALIB triple/quad-frequency solver parity was never exercised by the frozen profiles and is tracked separately in #387. It is a capability expansion, not an unaccounted item in the supported #148 matrix.

Exit (satisfied): production code, installed targets, and default CI have no MADOCALIB dependency; the linked checkout is used only by the labeled oracle lane; native PPP, PPP-AR, and PPP-AR-ion satisfy the M5 matrix; no required migration item remains open.

PR and Evidence Rules

  • Branch from current develop and use one behavior surface per PR, for example feature/148-madoca-ar-horizontal-convergence.
  • Link issue #148 and identify the milestone in every PR.
  • Keep diagnostics measurement-neutral unless the PR explicitly declares a solver change.
  • Run default tests without MADOCALIB and oracle tests with the pinned checkout.
  • Publish materialization, satellite-set, residual-row, residual-component, status, solution, and runtime artifacts in that order as applicable.
  • Do not accept a higher Fix count without wrong-Fix and constrained-position evidence.
  • Record rejected hypotheses in issue #148 and remove their runtime wiring.

Post-migration follow-up

Issue #148 has no remaining implementation slice. Keep the M5 matrix as the release regression contract, remove the compatibility opt-outs only after their documented one-release-cycle window, and use #387 for any future triple/quad-frequency solver expansion. Such expansion must add fixtures and baselines rather than silently widening the completed #148 contract.