Skip to content

Multi-epoch ambiguity-resolution shadow audit

Scope

This experiment asks whether repeated integer candidates on an uninterrupted double-difference ambiguity arc provide a safe, truth-free way to recover FLOAT epochs. It is deliberately independent of the shipped estimator: the shadow cannot add graph factors, pin ambiguities, change the selected partial AR subset, or alter the reported position and FIX/FLOAT status.

The implementation is not a TDCP-specific change. It uses the fixed-lag ambiguity marginal already produced by tightly coupled carrier-phase/IMU FGO. An ambiguity enters the counterfactual subset only after its integer candidate is identical for three consecutive epochs on the same internal arc index. At least four such ambiguities are required. The current joint covariance is then reduced to that subset and a fresh two-candidate LAMBDA search reports:

  • persistent ambiguity count and minimum history support;
  • ratio and bootstrapped success-rate lower bound;
  • whether the fresh search agrees with the historical integers;
  • counterfactual ECEF position and its float/IMU-prediction separation.

Enable it in the parity harness with --multiepoch-ar-shadow. The default is off.

Research and OSS audit

  • The ION GNSS+ 2024 multi-epoch FGO-RTK study compares single-epoch AR, ambiguity merging, and multi-epoch AR with single-differenced ambiguity constraints. It motivates keeping arc identity and window history explicit, rather than treating repeated solution labels as independent votes: Li et al., 2024.
  • Integer-aperture theory requires controlling the probability of an incorrect integer output, not merely maximizing availability. A repeated candidate is therefore telemetry, not sufficient evidence for promotion: Teunissen, 2003.
  • RTKLIB delays ambiguity holding until a configurable number of consecutive validated fixes and resets that evidence when FIX is lost. This supports tracking persistence separately from the per-epoch ratio test: RTKLIB rtkpos.c.
  • GICI-LIB uses partial ambiguity resolution and validates the constrained graph by rejecting an integer update when the range cost worsens. This supports requiring an independent post-fix witness before activation: GICI-LIB ambiguity resolution.
  • GraphGNSSLib demonstrates windowed DD pseudorange/carrier/Doppler FGO with LAMBDA, but does not supply a production-grade integer-aperture activation rule that can replace dataset validation here: GraphGNSSLib.

The audited stash was not applied wholesale. Its public LAMBDA diagnostics, stale-key filtering, bootstrapped success rate, and solution-separation ideas are already present on develop in a more complete form. Adaptive covariance scaling and held-only anchor mutation were excluded from this isolated multi-epoch experiment.

Validation protocol

The shipped Tokyo preset is replayed on PPC Tokyo runs 1, 2, and 3. The baseline and shadow must have identical status, ECEF position, ratio, fixed ambiguity count, and AR outcome at every epoch. Candidate correctness is used only offline: a counterfactual candidate is classed correct when its 3D error is below 0.5 m. Activation may be considered only if a truth-free rule adds no wrong fixes on any run; otherwise the feature remains monitor-only.

Results

The full shadow replays produced the following counterfactual candidates. “FLOAT correct/wrong” counts only epochs where the shipped solution remained FLOAT, so these are the potential additions to fix rate.

Run Epochs All candidates correct/wrong FLOAT correct/wrong FLOAT ratio > 3 correct/wrong
Tokyo run1 11,905 4,608 / 2,859 293 / 1,024 87 / 170
Tokyo run2 9,147 7,139 / 255 114 / 164 57 / 45
Tokyo run3 15,294 10,785 / 384 413 / 233 17 / 29

The baseline and shadow CSVs have the same row count on every run. Status, ECEF position, reported ratio, fixed ambiguity count, and AR outcome are identical in every one of the 36,346 rows. A separate 500-epoch current-build A/B replay also had zero differing rows. This confirms that the implementation is diagnostic-only.

Bootstrapped success rate does not rescue the rule: at a threshold of 0.999 it accepted 1,314/1,317, 273/278, and 645/646 FLOAT candidates on runs 1, 2, and 3 respectively, including the wrong integer basins. Exact integers can remain stable for many epochs when the underlying urban measurement model is biased.

A threshold grid was selected using runs 1 and 2 only, then checked on run 3. The best zero-wrong training family required ratio above 20, float separation at most 0.05 m, IMU-prediction separation at most 0.1 m, and a fresh SPP witness within 5 m (the default three-epoch/four-ambiguity requirements still apply). It recovered only three correct epochs in the training runs and three correct epochs on held-out run 3. Six epochs over 36,346 is about 0.0165 percentage points, far below a meaningful fix-rate improvement, and the threshold was selected after a grid search. It is not promoted to a shipping gate.

The diagnostic also has measurable cost when enabled. Solver wall time was 463.520→566.505 s, 584.568→543.092 s, and 844.915→930.754 s for runs 1–3; summed wall time increased by about 7.8% (individual runs include normal timing noise). Since the default is off, shipped runtime is unchanged.

Decision

Keep --multiepoch-ar-shadow monitor-only. Do not increase fix rate from temporal integer agreement, ratio, or BSR alone. The next activation experiment should add an independent observation-domain witness, such as the constrained graph range-cost check used by GICI-LIB or a held-out carrier residual that was not part of the LAMBDA marginal. Any future gate must again be selected without run3 truth and then validated on a held-out course.

Held-out carrier witness follow-up

The follow-up implements the second experiment without changing estimator output. For each multi-epoch candidate, it passes only the ambiguities in the persistent subset to the existing surplus-satellite validator. Carrier rows outside that subset are therefore re-differenced against an alternate reference and evaluated at the candidate position without contributing an integer or covariance entry to the reduced LAMBDA search. The CSV fields are:

  • multiepoch_ar_surplus_eval: an independent pool was large enough to decide;
  • multiepoch_ar_surplus_pass: every required held-out check passed;
  • multiepoch_ar_surplus_level: constellation fallback, 0 for the strongest GQEBR pool through 5 for GQ;
  • multiepoch_ar_surplus_used: number of held-out satellites in the deciding pool.

This follows the fixed-hypothesis validation direction used by GICI-LIB, which rejects a constrained update when range cost increases, and RTKLIB, which recomputes and validates fixed-solution post-fit residuals. The important difference is explicit sample splitting: the new verdict uses carrier rows whose ambiguity was not fixed by the multi-epoch search.

The shipping preset was replayed over all of Tokyo runs 1 and 2. Correct and wrong below classify counterfactual FLOAT candidates by 3D error at or below, or above, 0.5 m. The shadow continued to leave status, ECEF position, ratio, fixed ambiguity count, and AR outcome unchanged.

FLOAT candidate filter Run1 correct / wrong Run2 correct / wrong
All multi-epoch candidates 293 / 1,024 114 / 164
Held-out verdict available 264 / 856 102 / 144
Held-out verdict passed 118 / 230 34 / 33
Strongest GQEBR pool passed 100 / 184 25 / 27
Run1-selected gate 45 / 0 3 / 15

The run1-selected gate required the strongest GQEBR pool, ratio above 50, and candidate/IMU-prediction separation at most 0.1 m. It looked safe in development but failed immediately on run2, where 15 of 18 accepted candidates were wrong and the maximum candidate error was 1.755 m. An exploratory run1+2 grid adding fresh-SPP separation found no non-empty zero-wrong rule. Run3 remains an unconsumed holdout for this follow-up because the candidate already failed its validation course.

Decision: retain the held-out verdict as diagnostic evidence, but do not add a multi-epoch rescue switch or change the shipping FIX rate. Independent carrier rows can share the same urban multipath basin and are not, by themselves, a safe integer-aperture witness.

Constrained graph-cost follow-up

The next follow-up reuses the estimator's existing GICI-style integer-constrained reoptimization as a read-only witness for multi-epoch FLOAT candidates. A shared helper copies the active fixed-lag factors and current values, adds tight ambiguity priors for the persistent integer hypothesis, performs the configured LM refinement (one iteration in this audit), and then evaluates both solutions on the original graph with the integer priors removed. The optimized values are discarded. The CSV adds:

  • multiepoch_ar_graph_cost_eval and _pass;
  • multiepoch_ar_graph_cost_factors;
  • multiepoch_ar_graph_cost_before, _after, and _delta.

The strict pass condition is the existing absolute rule, after <= before + 1e-6. For cross-epoch thresholding, the audit also divides delta by active factor count so changes in fixed-lag graph size do not directly become the gate. Candidate correctness remains truth-only offline evidence and is never an estimator input.

A current-build Tokyo run1 development grid used only deployable telemetry. Graph cost alone was not safe: among FLOAT candidates its strict pass retained 71 correct and 34 wrong candidates. The best non-empty zero-wrong development rule required:

  • held-out carrier validation pass;
  • candidate/IMU-prediction separation at most 0.05 m; and
  • graph-cost delta per active factor at most zero.

The rule was frozen after run1. It passed run2 without adjustment, which unlocked the previously unused run3 holdout. Run3 then produced one accepted wrong candidate at TOW 179614.8 with 1.97 m 3D error, so activation failed.

FLOAT graph-cost population Run1 correct / wrong Run2 correct / wrong Run3 correct / wrong
All evaluated candidates 205 / 644 79 / 226 27 / 174
Strict graph-cost pass 71 / 34 42 / 23 3 / 41
Frozen combined gate 36 / 0 1 / 0 0 / 1

The shadow remained non-interfering in a current-build 500-epoch A/B replay: all 500 rows matched exactly in status, ECEF position, ratio, fixed ambiguity count, and AR outcome. The shadow evaluated 497 graph hypotheses (493 strict passes and four failures). Runtime increased from 114.634 s to 128.623 s in that short replay. Full diagnostic replays are substantially more expensive: run2 took 1338.44 s and run3 took 3563.02 s because each candidate launches a batch refinement. The feature remains default-off.

Decision: retain constrained graph cost as diagnostic telemetry, but do not promote multi-epoch FLOAT candidates or change the shipping fix rate. The held-out run3 failure demonstrates that temporal agreement, alternate carrier rows, IMU proximity, and a non-worsening prior-free graph cost can still share the same biased urban measurement basin. Any next activation attempt needs a genuinely different observation model; TDCP is still blocked until receiver clock-jump/slip handling removes the observed approximately 299,793 m raw jumps.