Skip to content

servo_sim: closed-loop DO/GNSS simulator (closedLoopServoSim)#81

Merged
bobvan merged 2 commits into
mainfrom
charlie/closedLoopServoSim
May 29, 2026
Merged

servo_sim: closed-loop DO/GNSS simulator (closedLoopServoSim)#81
bobvan merged 2 commits into
mainfrom
charlie/closedLoopServoSim

Conversation

@bobvan
Copy link
Copy Markdown
Owner

@bobvan bobvan commented May 29, 2026

Summary

Closed-loop DO/GNSS servo simulator — the closedLoopServoSim bead (do-first item in main's 2026-05-29 handoff to charlie). A deterministic test bed so servo/gate/fusion regressions stop being discovered overnight on lab hardware.

  • Closed-loop, not replay: a ground-truth plant emits measurements that respond to the servo's commanded adjfine each epoch (z_ticc = -φ_do - qerr(φ_rx)), so it exercises acquisition / gate over-rejection / coast dynamics that validate_ocxo_gate_phase4.py (replay) cannot.
  • Drives the REAL code: DOFreqEst (EKF + 6 arms + chi² gate) and OcxoTrustedGate. The only new code is the plant + loop wiring. Verified the actuator sign against peppar_fix_engine.py:8148 (adjfine_ppb = -servo.update(...)).
  • New files: scripts/peppar_fix/servo_sim.py (lib), scripts/servo_sim.py (CLI), scripts/peppar_fix/test_servo_sim.py (11 tests), docs/closed-loop-servo-sim.md.

Faithfulness (honest — see docs table)

Behavior Status
DO free-run floor (clkPoC3 45ps / PiFace 54ps) ✅ exact (calibrated)
TICC innovation median vs day0527 captures (~3ns) ✅ matches
OCXO gate starves TICC-only loop (clkPoC3-gated never-locks) ✅ mechanism
Gate + non-TICC x[2] carrier (EXTINT) still locks ✅ mechanism
Coast → long-τ hump ◑ right shape, ~2× high
EKF overconfidence divergence on long coast ✅ reproduced
Exact lock TDEV(1s) (104/74/565ps) ✗ ~2× off — needs coast scheduler + fat-tail innovations (next increment)

Findings the sim already surfaces

  1. The OCXO gate only gates Arm 4 (TICC) — a gated host locks iff x[2] has a non-TICC carrier (EXTINT). Supports disciplineModeFsm mode-keyed gating.
  2. Long coasts diverge because √P[3,3] shrinks while x[3] (the hidden DO-freq state) stays biased — the longTauGnssCoupling overconfidence problem, reproduced deterministically. This is the test bed for its coast-cap fix.

Test plan

  • 11 new unit tests pin the invariants (floors, lock, gate-starvation, sign-stability, determinism, two-clock).
  • Full suite: 1391 passed, 1 skipped, 22 subtests passed (pytest scripts/peppar_fix/ tests/).
  • Next increment: wire the real DisciplineScheduler (adaptive coast) for longTauGnssCoupling validation; fat-tail innovation model to recover the 565→74ps gate win.

🤖 Generated with Claude Code

bobvan and others added 2 commits May 29, 2026 12:18
Deterministic closed-loop test bed for servo/gate/fusion design, so
regressions stop being discovered overnight on lab hardware.  Drives the
REAL DOFreqEst (EKF + 6 arms + chi² gate) and OcxoTrustedGate in a closed
loop: a ground-truth plant emits measurements that respond to the servo's
commanded adjfine each epoch (z_ticc = -phi_do - qerr(phi_rx)).

Faithfulness (honest, see docs): free-run floors calibrated EXACT
(clkPoC3 45ps / PiFace 54ps), TICC-innovation median matches the day0527
captures (~3ns, driven by the modeled ~2.3ns PPS-edge jitter the PPP arm
can't see), and the gate-starvation + EKF-overconfidence-on-coast failure
modes reproduce.  Exact lock-quality numbers (104/74/565ps) need the
adaptive coast scheduler + fat-tail innovations — the next increment, and
the test bed bravo needs for longTauGnssCoupling.

Findings the sim already surfaces:
 - the OCXO gate only gates Arm 4 (TICC); a gated host locks iff x[2] has
   a non-TICC carrier (EXTINT) -> supports disciplineModeFsm mode-keyed gating
 - long coasts diverge because sqrt(P[3,3]) shrinks while x[3] (the hidden
   DO-freq state) stays biased -> the longTauGnssCoupling overconfidence
   problem, reproduced deterministically

CLI: --preset / --faithfulness / --sweep field=v1,v2 / --two-clock /
--set / --out / --plot.  11 new unit tests pin the invariants.
Full suite: 1391 passed, 1 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per main's PR #81 review (APPROVE): (1) document that plant and filter
import the SAME _qerr/tick model, so the sim validates loop dynamics but
cannot reveal a bug in the measurement model itself; (2) correct the
run_two_clock docstring/CLI — it shares the WHOLE RNG (DO noise too) and
desyncs across differing arm configs, so its |Δ| is a plumbing smoke
test, not a cross-host-bound measurement, until next-increment #3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bobvan bobvan force-pushed the charlie/closedLoopServoSim branch from 952e039 to 6dfc3d2 Compare May 29, 2026 17:20
@bobvan bobvan merged commit 6dfc3d2 into main May 29, 2026
@bobvan bobvan deleted the charlie/closedLoopServoSim branch May 29, 2026 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant