# VBS Exploration Findings — Session 1 (2026-07-27) Exploration of DIY virtual-base-station synthesis from GCGC/NGS multi-station data, targeting offshore (extrapolation) PPK for buoy deployments. Full design + gate definitions: see the approved plan; scripts: `vbs_synth.py`, `vbs_iono.py`. Data: NGS CORS 30 s dailies, DOY 204 (2026-07-23) — MSIN, MARY, SBCH, COVG, MSEV (+ ALDI/ALFO/AL90/AL92/ALMJ downloaded for geometry D, unused so far); ESA rapid SP3 (GPS+GLONASS — Galileo pending a multi-GNSS SP3 source, e.g. ESA finals ~2 weeks post-date); NGS brdc RINEX 2 nav (demo5 rnx2rtkp did NOT parse BKG's RINEX 3.05 mixed nav — use NGS brdc `.26n/.26g`). ## Gate G1 — geometric synthesis engine: **GREEN** | Check | Result | |---|---| | G1a identity (zero displacement) | **PASS** — 44,884 surviving records bit-identical (max diff 0.000, flags preserved) | | G1b round-trip MSIN→MARY site→back | **PASS** — max diff 0.001 (= 1 LSB of RINEX quantization) | | G1c clock perturbation | **PASS at physical magnitude** (see below) | | G1d A→B truth (MARY as rover, 43.7 km) | **PASS** — VBS-at-MARY base reproduces the direct MSIN-base solution (mean 3D diff 13 cm = float noise); truth errors statistically identical (horiz RMS 0.634 vs 0.639 m; mean N/E offset actually slightly smaller for VBS) | G1d detail (4 h window 16–20 UT, GPS+GLO, 30 s, broadcast nav, kinematic): direct fix rate 37.4% with **69 wrong fixes** (Q=1 & >10 cm truth error); VBS 43.7% with 31 wrong fixes. Wrong fixes are present in BOTH — confirming the plan's central honesty rule: never score on fix rate; score truth error under Q=1. 44 km daytime solar-max AR is marginal, as expected. ### The clock findings (validates the timing concern raised in review) - A **1 ms** injected clock offset broke the solution (0.63 m shifts, AR lost) — but diagnosis showed the *test* was unphysical, not the engine: shifting observables without shifting the sampling instant creates an inconsistent receiver. RTKLIB derives the base clock from pseudoranges and time-shifts satellite positions accordingly — so timestamp↔pseudorange **self-consistency in synthesized files is mandatory** (our engine preserves the master's real, self-consistent clock, hence G1d passing). - At the physically representative **1 µs** (steered CORS clocks): both-float epochs agree to **max 4.7 mm / mean 0.8 mm** → the engine is clock-robust. All larger deviations (≤12 cm) came from 18/481 epochs where a *borderline AR validation decision flipped* — chaotic threshold sensitivity inherent to marginal AR, present under any tiny perturbation, not a synthesis defect. ## Gate G2a — iono LIM cross-validation: **GREEN (GPS-only)** Carrier-leveled geometry-free slant iono (arc-leveled to code), 5-min bins, 20° mask, per-satellite planar fit (LIM) across 4 stations, leave-one-out: | Held-out | Centroid dist | median\|r\| | 95%\|r\| | |---|---|---|---| | MSIN | 18 km | 0.050 m | 0.158 m | | MARY | 52 km | 0.050 m | 0.124 m | | SBCH | 65 km | 0.057 m | 0.185 m | | COVG | 38 km | 0.100 m | 0.267 m | | MSEV | **135 km (true extrapolation)** | **0.191 m** | 0.551 m | - **Overall median 0.070 m ≤ 0.10 m gate → PASS.** - **Residual-growth slope: 1.2 cm per 10 km** — matches the literature band (1–2 cm/10 km) used in the design review. Offshore implication: ~10–15 cm slant-iono prediction error at 60 km beyond the network edge — supportive of dm-level (not cm-level) VBS improvement targets for extrapolated points. ### Two instructive failures on the way (both diagnosed + fixed) 1. **v1 estimator failed the gate by 10x** (median 0.7–1.4 m; COVG 4.6–6.9 m). Cause: per-bin free per-station bias parameters are near-degenerate with a satellite-common gradient in a 4-station fit — extrapolated planes explode. Fix: estimate station biases (DCBs) as **daily constants** via median polish (hardware DCBs are stable), then fit per-bin planes with biases fixed → residuals collapsed 20x. Lesson recorded for Phase 2b: DCB handling is *the* conditioning issue in small-network LIM. 2. **P1/C1 column trap**: MSEV logs GPS code in C1 with a blank P1 column — column-level fallback silently produced a GLONASS-only station. Value-level P1→C1 / P2→C2 fallback required. (GLONASS remains excluded from the iono fit for now: per-slot inter-channel code biases need per-(station,slot) bias terms — queued for Phase 2b if GPS-only corrections prove insufficient.) ## Pivot VRS benchmark (2026-07-27, orders V304-V307) — **the G0 question answered** GCGC's portal DOES offer VRS orders, and **accepted both offshore points (25 km and 60 km beyond the network edge) without complaint** — 1 s files, zero missing epochs, positions declared exactly as requested (NAD83(2011); note +1.4 m vs ITRF ellipsoidal height in this region). Files in `experiments/data/orders/` (V304 = capture site 50 min; V305 = MARY site 4 h; V306/V307 = offshore probes 4 h). **MARY truth benchmark** (rover = MARY 30 s, 16-20 UT, GPS+GLO, broadcast nav; truth = MARY's published coordinates, frame-matched per leg): | Base | Fix rate | Wrong fixes | Truth horiz RMS | Fixed-only RMS / CEP95 | |---|---|---|---|---| | Pivot VRS at MARY (0 km) | 41.4% | 45 | 0.548 m | **0.078 / 0.129 m** | | DIY VBS at MARY (0 km, geometric-only) | 43.7% | 31 | 0.634 m | **0.075 / 0.149 m** | | Direct MSIN (44 km) | 37.4% | 69 | 0.639 m | 0.136 / 0.298 m | Findings: 1. **Commercial Pivot VRS ~= DIY geometric-only VBS at this site/day** — the network's atmospheric corrections added no measurable advantage over pure geometric displacement (single quiet-ish day, GPS+GLO, 30 s caveats apply). Both zero-baseline methods beat the 44 km direct base *when fixed* (7.5-7.8 cm vs 13.6 cm) and roughly halve wrong fixes. 2. **Wrong-fix rates are high for ALL methods** (~20-35% of Q=1 epochs) — processing-config improvements (fix-and-hold, mask, L5) are currently a bigger lever than base choice; reinforces truth-error-under-fix scoring. 3. **Capture-site leg (V304 via the production pipeline)**: ppk_gcgc ran automatically (base 0.0 km) but stayed float with ratio ~1.1 and *worse* scatter than the MSEV solution (CEP50 1.10 vs 0.44 m), plus a ~3.5 m unexplained height offset beyond the ~1.4 m datum difference — float-bias behavior consistent with the u-blox rover's GAL/BDS signals being unusable against a G+R-only 2.11 base plus VRS correction noise; needs a dedicated look (multi-frequency conf, `-f 3`, RINEX 3.04 VRS re-order at the site). 4. Offshore V306/V307 files are in hand for the Phase 2b/3 obs-domain comparison (Pivot's extrapolated corrections vs our LIM extrapolation). ## Obs-domain analysis: how much correction does Pivot actually embed? (vbs_compare.py) DIY geometric VBS synthesized at each Pivot file's exact declared position (same NAD83 frame/master), observations differenced satellite-by-satellite, DD'd against the highest satellite, per-satellite ambiguity constants removed. What remains = network correction content relative to pure geometry (+ master noise; 95% tails include uncleaned re-levelings): | VRS point | Displacement | L1 DD variation median / 95% | P1 code DD median | |---|---|---|---| | V304 capture site (edge) | 34 km | **0.026 / 0.163 m** | 0.278 m | | V305 MARY (in-network) | 29 km | **0.064 / 2.127 m** | 0.369 m | | V306 25 km offshore | 75 km | **0.063 / 0.914 m** | 0.446 m | | V307 60 km offshore | 92 km | **0.065 / 1.020 m** | 0.517 m | **Three independent measurements now agree**: (1) LOOCV says LIM-interpolable iono differences are 5-10 cm median in-network, growing 1.2 cm/10 km; (2) the MARY truth benchmark says that correction content is too small to change positioning outcomes on this day (Pivot ~= DIY); (3) the obs-domain analysis says Pivot embeds a median of only **~3-7 cm of correction beyond pure geometry, roughly flat out to 92 km displacement** (code DD grows mildly with distance, dominated by master code noise). Consistent conclusion for buoys: at these distances/conditions, a geometric virtual base + our own LIM layer is competitive with the commercial product; the decisive factors are processing config (AR strategy, multi-frequency) and disturbed-day behavior (untested). Capture-site V304 anomaly **RESOLVED (2026-07-27, promotion session)**: the production ppk_vrs stage (our engine, same MSEV master, same zero baseline, same rover) is clean - matches direct-MSEV to 3 cm and slightly beats its scatter (CEP50 0.42 vs 0.44 m). The ~5 m bias + 3x noise is therefore **in Pivot's V304 file itself** (network-edge synthesis at Hattiesburg where MSHT is decommissioned), not in the rover pairing. Method note: the obs-domain DD comparison could not see this because per-satellite ambiguity detrending also absorbs constant biases - it measures time-varying content only. The RINEX 3.04 re-order of the capture-site VRS is now optional curiosity, not a blocker. Fix-and-hold truth test at MARY: +10% fix rate on the Pivot leg only, wrong-fix counts UNCHANGED on all legs - wrong fixes are a validation-threshold problem (30 s epochs, ratio 3.0), not an AR-strategy problem; the next real levers are multi-frequency processing and stricter/partial validation. ## Promotion (2026-07-27, user decision) The DIY VRS is promoted into `process_gps.py` as the **standard GCGC-provided solution `ppk_vrs`**: auto-synthesized at the capture position from the NGS stage's master whenever that master is a GCGC station (agency header / MS station check), ESA orbits (FIN>RAP, ~1 day lag, "pending" until published), frame-consistent ITRF, zero nominal baseline. Reported alongside all other solutions with its own color/validation; `--no-vrs` disables. Track output now selects among PPK-family solutions by empirical scatter, protecting against poor network-edge VRS data (exactly the V304 case). Caveats carried with the promotion: single-day validation, GPS+GLO only until multi-GNSS orbits are available for the day (Galileo joins via ESA finals), disturbed-iono behavior untested (Phase 3). ## Status vs plan - G0: partially done — station set + data availability verified (NGS path); **USER ACTION open: check the GCGC portal (rtn.usm.edu) "new order" screen for a "Virtual Reference Station" order type**, and whether it permits offshore points; GCGC Data Shop 1 s downloads not yet needed but will be for Phase 3's disturbed-day / 1 Hz legs. - G1: **GREEN** (all four checks). - G2a: **GREEN** (GPS-only, one quiet day). Pending for robustness: ≥2 more days incl. a disturbed day (Phase 3 requirement, not a G2a blocker). - Next (Phase 2b): apply LIM corrections in `vbs_synth` (`--iono` hook exists in design): sign-correct code/phase application, (f_L1/f)² scaling, phase-continuity audit (<λ/4 steps), then geometry B/C/D truth runs with the wrong-fix-rate scoring, day/night splits, and the 0–60 km offshore sweep. ## Reproduction ```bash # G1 (identity/round-trip/clock/truth) — see session commands; engine: venv/bin/python experiments/vbs_synth.py experiments/data/204/msin2040.26o \ --xyz --master-xyz \ --sp3 experiments/data/204/ESA0OPSRAP_20262040000_01D_05M_ORB.SP3 \ --nav experiments/data/204/BRDC00WRD_R_20262040000_01D_MN.rnx \ -o experiments/data/204/vbs_at_mary.obs # G2a: venv/bin/python experiments/vbs_iono.py --stations msin:... mary:... sbch:... \ covg:... msev:... --dir experiments/data/204 --sp3 ... --nav ... --gps-only ``` (ITRF2020 station coordinates epoch-propagated via `process_gps.cors_station_xyz`.)