Changelog¶
All notable changes to this project. Entries are grouped by kind, and the kind is part of the point: a reader asking "did the estimator change?" should not have to read a formatting commit to find out.
Format follows Keep a Changelog, and this project uses semantic versioning.
[Unreleased]¶
Added¶
navkit euroc:fetch,runandselftest.fetchreads only the IMU and ground-truth files of an EuRoC sequence out of the Research Collection archives, by HTTP byte range, retrying on 429 with the server'sRetry-After, and writes aMANIFEST.jsonwith the SHA-256 of every file.runfeeds the recorded IMU to the filter, simulates GNSS from the ground truth, optionally cuts outages, and scores the run with the same ATE, NEES and coverage code as the benchmark.selftestruns the whole path on a synthetic sequence in the EuRoC layout. Results are labelledreal_imu_simulated_gnss. No result from real data is quoted in the README or the docs. See ADR-0013, accepted.ErrorStateKalmanFilter.run(..., initial=InitialState(...)): start from a given pose, velocity and biases instead of the origin at rest. The default is unchanged.navkit euroc run --preset adis16448: scales the filter's assumed IMU noise by 3 and declares a 0.05 initial bias 1-sigma. Chosen on MH_01_easy and checked on the other ten EuRoC sequences (Machine Hall and Vicon rooms); a few runs on MH_04, MH_05 and V1_01 still lose GNSS after an outage. Explicit--noise-scaleand--bias-sigmaoverride it. It is a tuning for this sensor, not a filter property.navkit euroc compare: runs the default, theadis16448preset and optionally thelegacyform over every fetched EuRoC sequence, one 20 s GNSS outage at a time, and counts the runs that lose GNSS (more than a fifth of the fixes rejected). It writes one CSV row per run and a JSON record with the input hashes from the fetch manifest, and prints a summary table. The FDIR "faulted" flag is reported next to the count and not used for it, since it misses some losses. This is how the statements in ADR-0014 are regenerated.navkit euroc run --preset adis16448-walkand--bias-walk-scale: the second preset leaves the white noise as the sensor file gives it and scales only the two bias random walks by 10, with the same bias prior.navkit euroc comparegains--first-seedand awalk10configuration, and fills a second page block with--block euroc-validation. See the follow-up in ADR-0014.navkit sweep attributionanddocs/attribution.md(P5-06): the falsification experiments the research baseline listed and never ran, plus the bias-fault question from limitation L8. On the synthetic fixture, removing every inertial error source leaves the peak outage error within two percent, the error grows with the same exponent with or without them, and what dominates is the state error the filter has when GNSS is lost (making the start exact cuts it by about nine tenths). A source has to be about thirty to a hundred times the unit's before it matters. A small or moderate accelerometer bias fault is not noticed; a large one makes the filter reject the healthy GNSS on its return and declare it faulty, the lockout behind the lost runs on the recordings.run_casegains animu_hook, and the benchmark runner accepts the initial-uncertainty keys.navkit euroc timinganddocs/product_management/04_sensor_sync_and_calibration_spec.md(P5-07): the timing model, the calibration assumptions, and what is measured on the recordings versus assumed in simulation. The tool reports each sequence's timestamp regularity, the share of the ground truth lost to dropouts, and the offset between the ground truth and the IMU, from the angular rate (a vector comparison that removes gyroscope bias, with a magnitude comparison as a cross-check). On the 17 sequences the offsets are within a few milliseconds, far below the simulated GNSS noise; the TUM VI motion capture has dropouts of up to a few seconds, which the TUM VI page now states. The table is generated fromdocs/data/timing.csvand a test checks it and six statements.navkit tumvi fetchand--dataset tumvionnavkit euroc runandnavkit euroc compare: TUM VI room sequences (CC BY 4.0, ADR-0016, accepted). The archives are whole TARs, so the fetcher downloads them (resuming a partial file), checks the publisher's MD5, keeps the IMU, motion-capture and noise files, and deletes the archive. The ground truth has no velocity or bias, so the start velocity is a local line fit and the start biases are estimated from the quietest stretch of the first seconds; a result says so. The dataset's own noise file is read twice: its inflated active figures and the raw figures from its comments.EskfConfig.vision_model = "clone"(opt-in, default"anchor") and four benchmark cases (outage_visual_clone,outage_visual_degraded_camera_rereferenced,outage_visual_degraded_camera_clone,vision_only_clone): the previous visual pose carried as a stochastic clone, the B1 spike (ADR-0017, accepted). On the synthetic fixture the clone is calibrated under GNSS denial where the single anchor is not, across the seed, trajectory and outage sweeps. The default,vision_enabledand the single-anchor rows are unchanged. Its Jacobians are checked against numerical derivatives.vision.noise_corr_s,outlier_fraction,outlier_scale,scale_sigma,scale_tau_s(scenario settings, all off by default, drawn from a stream of their own so no existing number moves) and four benchmark cases (outage_visual_clone_outliers,_scale_drift,_correlated,_correlated_inflated): stress tests of the clone. It survives gross outliers and scale drift. It does not survive visual errors correlated over a couple of seconds unless the assumed visual noise is inflated (4x restores calibration here; found by trial). A 150 s run without GNSS keeps a growing claimed uncertainty and accepts GNSS on return (tests/test_clone_long_run.py).vision.rereference(scenario setting, default off): after frames are dropped, each delivered measurement is taken against the last delivered frame, as a front end tracking against its last keyframe would report. The generator otherwise measures against a dropped frame, which a filter comparing with its last received frame cannot be consistent with.docs/euroc.mdanddocs/data/euroc_compare.csv: the comparison of the default and the preset over all eleven EuRoC sequences, with the failing runs listed and the caveats of ADR-0013 beside the table. The table is generated bynavkit euroc compare --pageand a test checks it, and the statements in the prose, against the committed CSV files, includingdocs/data/euroc_validation.csvfor the runs made after the settings were chosen.--from-csvrebuilds the page without rerunning. The dataset itself is not in the repository.docs/data/metrics.jsonandscripts/metrics.py: the test, ADR and line-coverage figures the documents quote now come from one generated file through<!-- metric:... -->markers. CI fails when a marker or the JSON is stale, when a count is typed by hand, when an ADR is missing from the index, or when measured coverage differs from the recorded figure by more than half a point on the recorded interpreter.-
navkit sweep separabilityanddocs/separability.md: whether the first GNSS gate can tell an honest return after a 20 s outage from a spoofed one, on real recorded IMU data (EuRoC, TUM VI) with simulated GNSS. The spoof population is derived from the honest innovations, so no spoof is injected into a running filter. With the default noise the two overlap. With a calibrated bias walk they separate, but only for spoofs larger than the claimed uncertainty. The filter now records each GNSS innovation before gating, andrun_sequencecan return them. ADR-0018 is accepted: use the calibrated bias walk on recorded IMUs, leave the gate alone, and state the limit. -
navkit sweep rampanddocs/slow_ramp.md: a GNSS spoof that grows from 0.05 to 2 m/s, on real recorded IMU data with simulated GNSS, healthy before the onset and after a 20 s outage. The filter follows the spoof in every spoofed run. With the calibrated bias walk the gate does not react to ramps up to 1 m/s on EuRoC.RunOptions.spoofapplies the ramp. ADR-0019 is accepted: the limit is documented in the limitations, the risk log and the roadmap. -
docs/product_management/05_work_breakdown.md: the remaining work as packages with a deliverable, a dependency and an exit test, and three options for closing blocker B4 with a recommendation. Sizes are judgements, and the page says so. -
navkit sweep vision-rampanddocs/vision_ramp.md: the slow-ramp runs with a simulated visual source fused by the stochastic clone. With independent visual errors a 2 m/s ramp is caught and not followed. A 1 m/s ramp is seen and still followed. With correlated errors nothing is caught.RunOptions.visionadds the source. ADR-0020 is accepted: record the floor, measure a real front end next, then test a different second source. -
navkit sweep faultclassanddocs/fault_classification.md: whether the GNSS innovations can name the cause of a fault, on real recorded IMU data with simulated GNSS. Multipath is called correctly in 99% of runs and large degradation in 98%. Spoofing is called in 74% of runs after five features were added to the first five (38% before), and weak faults are missed. A confirmation run on unseen fault sizes and seeds called multipath in all 68 runs and in none of the other 476, kept spoofing at 77% and degradation at 91%, and found a control false-alarm rate of 35% against 18% in cross-validation.RunOptions.faultinjects the faults. The Track A exit test is not met. ADR-0021 is accepted: ship a multipath detector at most, and report every other detected fault as cause unknown. -
navkit.fdir.multipathanddocs/multipath.md: a multipath signature (NIS and lag-1 autocorrelation of a window of GNSS innovations) in a sensitive and a specific rule, with shipping criteria written before the fresh runs. The specific rule met them: it called all multipath of 3 m and above and none of the other runs, and none of 136 controls. The sensitive rule did not (14% of the other runs). It is a function, not wired into the filter. ADR-0022 is accepted.navkit sweep multipathruns the study. -
navkit euroc fetch --images,navkit.vo_frontend,navkit euroc voanddocs/real_front_end.md: a NumPy two-view feature-tracking front end, measured against the ground truth on 30 s of real EuRoC frames. Its errors are small (0.08° and 8 mm per one-frame pair, with the true scale) and correlated for 0.2 to 0.25 s. A Gaussian surrogate with that size and correlation time keeps the stochastic clone inside its gate. Replaying the measured errors does not (NEES 27, 55% coverage), and assuming four times the measured noise brings it back (NEES 4.3, 91%). One sequence and one window. ADR-0023 is accepted: the clone stays opt-in, with the inflation. -
A wider real-front-end measurement (
docs/real_front_end.md): six windows of five Machine Hall sequences, the design committed before the run, the measured errors of each window replayed into the stochastic clone. At the measured noise the clone meets the gate in two of six windows, at four times in all six, and the single anchor in none. The first window was also run again with the run aligned to its frames (the earlier run began 1.08 s after them, so the last second had no vision), which moved its figures slightly.navkit euroc vo --wideruns it. ADR-0024 is accepted: the clone stays opt-in, four times is the supported assumption, and it is a named setting (vision_noise_preset: measured-front-end, orRunOptions.vision_noise_preset). No shipped case or default uses it.
Changed¶
-
The implementation plan carries the same point-in-time banner as the other baseline records. P5-09 and P5-12 are closed in the roadmap: the records stay as dated snapshots, and no pull request or Dependabot item is open.
-
Blocker B4 is resolved by retiring the claim. The two tests that compared a published Basalt estimate of TUM VI room1 with a subsampled ground truth always skipped, because neither file could be fetched. They are removed. The claim of a reproduction of the published 0.069 m is dropped.
tests/test_recorded_orientation.pytakes over the purpose: the ground-truth attitude rate must match the recorded gyroscope, and a mis-ordered quaternion must not. It always runs on a synthetic sequence and runs on every fetched sequence. -
The initial IMU bias sigmas are read (ADR-0015, accepted).
gyro_bias_sigmaandaccel_bias_sigmawere accepted and read by nothing. The generator now draws an initial bias from each, on its own random stream, and the benchmark filter is told the same sigmas through the newinitial_gyro_bias_sigmaandinitial_accel_bias_sigma, which fall back toinitial_bias_sigmawhen unset. The synthetic numbers move, most for the vision cases, and no verdict changes. The golden snapshot, the tables, the sweeps and the figures were regenerated. One fault-matrix count moved: the 22 m GNSS spoof after an outage earned an inflation grant in one seed of five before and in none after. ErrorStateKalmanFilternow defaults toprocess_noise_form="textbook"(ADR-0014): IMU white noise enters the process covariance assigma_g^2 dton attitude andsigma_a^2 dton velocity with the matching position terms. The earlier form had no velocity term and useddt^3 / 3on attitude, and grew the covariance far below the white-noise theory."legacy"reproduces the old behaviour. Every benchmark, sweep, figure and table was regenerated: the synthetic numbers move a little (the headlineoutage_visualcase still has mean NEES in the hundreds against an expected 3) and no verdict changes. Numbers in older ADRs, the roadmap's completed items and the dated baselines were measured with the legacy form and were not recomputed. The golden snapshot was regenerated.CONSTRAINTS.md,ROADMAP.mdandCONTRIBUTING.mdno longer carry hand-typed passing and skipped counts, which went stale (860 collected there against 888 measured, 629 in the ratchet table and inROADMAP.md, "8 of 8 docs" for twelve ADRs). They quote the collected count, the coverage figure and the ADR count from the JSON and say that all collected tests pass except the declared xfails and skips.- The revised-roadmap audit snapshot no longer names teams that do not exist (Estimator, Docs, QA, Core, Config, Perf and Research teams, a Release manager) as owners; the owner is the maintainer. The open-item tables in the SRS and the SWaP-C matrix label the column Area, since they name workstreams. The engineering baseline no longer quotes the first characters of a provider key. A test fails if a table names a team or manager as owner or a document quotes the start of a provider key.
uv.lockis removed. ADR-0011 called it stale, and CI never used it.
Found, not fixed¶
- The synthetic IMU uses the opposite gravity sign to a physical z-up accelerometer. The EuRoC path passes gravity explicitly; a test shows the default would be wrong by orders of magnitude on a physical IMU.
- The FDIR layer's "channel faulted" flag misses long runs of rejected GNSS fixes: some EuRoC runs reject hundreds of fixes and are never declared faulty (ADR-0014).
Fixed¶
- The two anchor drift strengths were named per second and are per square-root second (P5-05). They are now
anchor_pos_drift_sigma_m_sqrt_sandanchor_rot_drift_sigma_deg_sqrt_s. The old names (anchor_pos_drift_sigma_m_s,anchor_rot_drift_sigma_deg_s) are still accepted by theEskfConfigconstructor and as benchmark estimator keys, with aDeprecationWarning; giving an old and a new name with different values is an error. The value and the filter are unchanged. The serialised config uses the new names, so everyconfig_hashchanges and the golden snapshot was regenerated: the diff is the hashes and the two key names, and no number moves. - The mechanism library said that a constant accelerometer bias gives linear error growth. It gives quadratic growth;
linear growth is a velocity error. The
gnss_deniedentries now state the textbook exponents, name the experiment that tests them, and record that the accelerometer-bias explanation is falsified on the fixture. Limitation L8 is rewritten to match. - Two CodeQL findings on the recorded-dataset code:
navkit euroc compareread a variable it had not set on the--from-csvpath (unreachable, since--jsonis refused there, but fragile), andeuroc_eval,tumvi_dataandeuroc_compareimported one another. The shared sequence type, its error and the z-up gravity vector moved tonavkit.recorded, and thenavkit eurocrouter tonavkit.euroc_cli. A test now fails on any import cycle, lazy imports included. navkit.types.finite_differencereturned twice the slope at interior samples for a first derivative (a ramp of slope 2 gave 4). Interior points now use the central difference over their two neighbours, exact for a line on a non-uniform grid.Trajectory.velocities()inherited the defect; nothing in the benchmark called it.
[0.2.0] - 2026-10-08¶
Added¶
- A release workflow (
.github/workflows/release.yml): on a version tag it checks that the tag, the version, the notes and the changelog agree, builds and attests the sdist and wheel, and creates a draft release with a CycloneDX SBOM and checksums; a manual run is a dry run that publishes nothing. The Dockerfile base image is pinned by digest. (#51) navkitcommand line:navkit run,navkit sweep seeds|scenes|outages,navkit figures(--animate). The scripts underscripts/remain as wrappers. The default scenario file ships inside the wheel. (#16, #19, #22, #25)- Outage sweep: the GNSS outage start and duration swept for the control and the visual case, with bootstrap intervals, a CSV and a figure. (#22)
- Noise-mismatch sweep (
navkit sweep mismatch): the filter is told sensor noise that is a factor of the true value, with the estimator keysgnss_sigma_scaleandvision_sigma_scale(default 1, no change to any result). New page Noise mismatch. (#42) - Fault matrix (
navkit sweep faults): eight fault modes measured against a clean control over five seeds, with detection and false-alarm counts from the FDIR event log. New page Fault matrix; the FMEA-lite rows are updated from the results.run_casegainsgnss_hook,fdir_eventsandforce_inject, all off by default. (#43) - SRS traceability check (
scripts/check_srs_trace.py,docs/product_management/srs_trace.csv): every requirement and acceptance criterion has a test, a checked configuration value or a declared gap, and the roll-up counts follow from the verdicts. It found two wrong roll-up counts and stale prose, now fixed. (#49) - Project tooling: a
Makefile, pre-commit hooks, aDockerfileand a devcontainer, a secret scanner that also reads history, a security workflow (dependency review,pip-audit, a CycloneDX SBOM), a relative-link checker, CODEOWNERS and issue and pull request templates. (#29) - MkDocs Material site with an API reference, a strict build in CI and a Pages deployment that checks the documented tables
first; the README is rewritten from 516 to 129 lines and gains a "Scope and responsible use" section; ADR-0010 (determinism)
and ADR-0011 (CI and supply chain).
docs/index.htmlis removed. (#36) - A social preview image and its generator. (#37)
- A RICE-ordered roadmap with a stability check (#40) and a risk register (#41).
- Golden snapshot test of the seeded benchmark (#12); Hypothesis property tests for rotations, the filter's covariance and attitude, NEES and the chi-square functions (#21).
- macOS and Windows test legs, and ADR-0009 on cross-platform numerics. (#24)
- Hero figure and animation of error against the claimed bound. (#25)
benchmarks/timing harness anddocs/PERFORMANCE.mdwith measured numbers. (#17)
Changed¶
- The synthetic IMU is derived analytically instead of by a finite difference whose rounding error moved with the math library. Headline ATE moves by at most 1.1e-5 m and mean NEES by at most 3.6e-3; coverage, counts and verdicts do not change. (#26)
interpolate_trajectoryis vectorised: the sum of the seven benchmark case medians fell from about 15.1 s to 11.1 s (1.36x), with 97 of 73,846 numbers differing by at most 3.6e-16. (#17)ErrorStateKalmanFilter._updateandfdir_manager.pyare split into named pieces with the benchmark output byte-identical to before. (#14, #15)- Third-party GitHub Actions are pinned to commit SHAs. (#9)
- The README headline is qualified: the overconfidence of visual aiding holds for every outage window tried, but the accuracy gain holds only for outages that start early. (#22)
Removed¶
docs/architecture/demo.html(11 MB) anddemo.workflow.json: the page embedded a raw pixel buffer labelled as a PNG that a browser cannot render, and typed its headline numbers. (#27)
Fixed¶
- Every benchmark scenario now goes through the injection layer. The runner skipped it for a scenario
with no outage or camera drop, which ran
gnss_only,dead_reckoning,vision_anchor_in_measurement_noiseandvision_onlywith a noiseless IMU and ignored a scenario that set only a timestamp offset. Those four cases move slightly (ATE by 0.003 m to 0.371 m) and no verdict changes; the three outage cases and the headline result are bit-identical. ADR-0012, golden snapshot regenerated. (#56) - Five scenario descriptions in
configs/benchmark.yamlcontradicted the measured results and were copied into every result record:vision_onlywas "calibrated, and much better than dead reckoning" (it is overconfident and less accurate),outage_visualwas "tens of metres wrong" (2.541 m),gnss_onlyandoutage_controlwere "calibrated" and "nominal" (their verdict is mixed), and the degraded-camera case "checks the anchor model does not collapse" (it does not change the result). The text now says what the numbers say. Only thedescriptionfields andconfig_sha256change in the golden snapshot. - The demo reported mean NEES 1.4e10 and a claimed sigma of 7 mm while the README said 419.4 and 0.161 m, because it ran its own copy of the filter configuration. It now runs the benchmark case and a test compares its headline with the golden snapshot. (#27)
Earlier entries, from the 2026-09-29 audit:
read_euroc_imuraisedAttributeErroron every call._NUMwas declared as a plain string and invoked with.fullmatch(), so no row was ever read. The documented "run this against EuRoC" path had therefore never executed.- The nanosecond-detection heuristic in
read_euroc_imuinspected only the first sample, so a stream beginning neart=0was read as seconds and a 20 s recording became a 20-billion-second one. The decision now uses the maximum timestamp. align_trajectorywas annotated as returningnp.ndarrayfor its reference, but the only caller needsref.metadata["index"]. It returns aTrajectory; the annotation was wrong.- Four
pytest.raises(match=...)patterns intests/test_config.pycontained unescaped., sodataset.kindalso matcheddatasetXkind. - The
outage_visual_degraded_cameracase specifiedcamera_drop.fraction, butCameraDropConfignames that fielddrop_fraction. The key was not recognised and the dataclass default was used instead, so the case ran with 20% of camera frames dropped in 2 s bursts instead of the 30% in 1 s bursts the case intends. The benchmark config now setsdrop_fraction, and the case's published numbers are those of the scenario it claims to measure.
Security¶
opencode.jsonis now ignored. It sits at the repository root and holds a provider API key; the previous rule covered only.opencode/, so the file was stageable and a plaingit add .would have committed a live credential. The key was never committed, so no history rewrite is needed. Rotation is still owed by its owner.- CI runs with
contents: readby default, with only the CodeQL job grantedsecurity-events: writefor its upload. - Added CodeQL analysis for Python on push, pull request, and weekly.
- Added Dependabot for GitHub Actions and pip, grouped so a transitive bump does not open a pull request per package.
- Added
SECURITY.mdwith a reporting path and a scope that distinguishes a silent numerical fault from a documented estimator failure.
Engineering¶
- The lint gate is green for the first time.
ruff checkwith no configuration selected whatever ruff's defaults were that month, and the count went from 14 to 131 across one upstream release with no change to this repository -- and since thebuildjob depends onlint, packaging was unverified in CI as a result. The rule set is now declared inpyproject.tomlwith reasons for each exclusion, andruff/mypyare pinned so CI and a local checkout resolve the same versions. ruff formatapplied across 32 files, in a commit of its own so the behavioural changes above stay reviewable.FilterStatereplacesdict[str, np.ndarray]on the ESKF state. That annotation was wrong in both directions -- it forbade the two integers, the boolean, and theNonethe state genuinely holds. With it in place, the three mypy error codes that were disabled inpyproject.tomlare no longer suppressed, and mypy reports no issues.zip(..., strict=True)at four sites pairing an estimate against its reference. A length mismatch there silently truncates and yields plausible wrong numbers, so the failure is now loud. The benchmark output is bit-identical before and after.- Duplicate
STATUS_REJECTED_PERSISTENTimport removed fromnavkit.fdir;__all__had 22 entries for 21 unique names. ClaimTypeis aStrEnum. Verified first that the only consumer renders.value, which is identical under both, and that comparison and JSON serialisation are unchanged.uv.lockis committed, so a fresh clone resolves the same versions.python_versionfor mypy is 3.13, matching the interpreter the CI lint job uses. At the declared minimum of 3.11, mypy rejected numpy's own stubs and could not check the code it was written to check.- CI now verifies that
seed_sweep.pyandscene_sweep.pyare reproducible across runs, at three seeds. - Added
CONTRIBUTING.md, including the five local gates and the conventions the project holds to (no# noqa, no mypy suppression, no hand-typed numbers).
Testing¶
- Added
tests/test_imu_io.py(10 tests). The loader it covers had no test at all, which is why two independent defects survived in it. Both were confirmed to fail the new tests when reverted. - Added
tests/test_scene_sweep.py(10 tests) andtests/test_scene_sweep_harness.py(14 tests). test_sweep_payload_is_labelled_syntheticassertedX or True, so it was permanently true and verified nothing -- while nominally guarding the constraint that stops a synthetic sweep being quoted as real-sensor performance. Both needles were also wrong, so a corrected version would still have failed.- Claim lookups in
tests/test_findings.pyand event lookups intests/test_thresholds_and_injection.pyindexed with[0]or callednext(). Both pass when a duplicate is emitted, which is the duplication those assertions exist to catch. Both now assert exactly one match. - Suite: 563 passed, 2 skipped, 2 xfailed before this release; 597 passed, 2 skipped, 2 xfailed after. Coverage 86.58% to 87.59%.
Research¶
- Added a multi-scene sweep. The existing seed sweep varies sensor noise with the trajectory pinned to one scene, so a result that was really an artefact of that path was indistinguishable from a property of the estimator. This was the largest threat to the headline claim and was untested.
synthetic.seeded_scenevaries the motion parameters -- radius, turning rate, sway, roll -- by a log-uniform multiplier, preserving the exactt=0initial state that makes the fixture a known-answer test, and leaving duration and rate untouched so swept results stay comparable to the published ones.scene_sweep.pyreports bootstrap confidence intervals rather than a standard deviation over 8 draws, and reportsverdict_stableper case with its counts, so a split verdict cannot be averaged away.run_benchmark.pygained--scene-seed, an axis independent of--seed.
Result over 8 scenes, noise held fixed:
| Case | NEES mean (95% CI) | Coverage @2σ | Verdict |
|---|---|---|---|
gnss_only |
3.7 [3.7, 3.8] | 100.0% | mixed, stable |
outage_control |
4.1 [4.0, 4.1] | 100.0% | mixed, stable |
outage_visual |
419.7 [414.4, 424.9] | 20.0% | overconfident, 8/8 |
vision_anchor_in_measurement_noise |
383.7 [380.8, 387.4] | 16.1% | overconfident, 8/8 |
vision_only |
327.9 [316.6, 341.2] | 0.7% | overconfident, 8/8 |
outage_visual_degraded_camera |
223.5 [217.9, 230.5] | 19.7% | overconfident, 8/8 |
Path length varies 2-3x across the scenes and every verdict is unanimous, with both controls staying calibrated. The overconfidence is therefore a property of the filter rather than of one trajectory. This does not address external validity: it remains a synthetic known-answer fixture.
Documentation¶
- Added a prioritized implementation plan (
docs/IMPLEMENTATION_PLAN.md) with P0-P4 classification and the reasoning for deferring each deferred item. - Added the audit baseline:
PROJECT_STATE.md,REPOSITORY_MAP.md,ENGINEERING_BASELINE.md,ML_BASELINE.md,RESEARCH_BASELINE.md,PUBLICATION_READINESS.md. - Corrected 16 GitHub URLs from a non-existent
contested-nav/contested-navorganisation toTelschow/contested-nav, so the badges resolve.
Breaking¶
- None. No public API changed. One published number changed: see the
outage_visual_degraded_cameracorrection under Fixed.
[0.1.0] and earlier¶
Commits abb62bb, cc998d1, c571e91, 8f6fcd7 and their ancestors. (These
were c81c6f9, 5d83216, 4fa780b and 466ab48 until the history rewrite
described in docs/PROJECT_STATE.md §5 replaced the author identity.) No
changelog was kept; see git log and docs/audit/.