Repository navigation
feat(eskf): optional vertical sigma and per-fix GNSS covariance (T2-0a) - #129
Merged
Merged
Conversation
…nerate golden Two opt-in settings, both off by default: gnss_vertical_sigma_m (a separate vertical sigma) and gnss_per_fix_covariance (weight each fix with its own GnssFix.cov). Both are serialised in as_dict so the config hash covers them. The golden snapshot changes only in the 15 config hashes and the two new null/false keys. No number moves. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Say in the config comment and docs/MODEL.md that the per-fix covariance is a diagonal and cannot carry a correlation between axes. Add tests that a very small per-fix variance keeps the covariance symmetric and semidefinite, that the gate sees the same R as the update, and that the declared vertical sigma alone decides whether a height jump passes the gate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s (T2-0a) On the per-fix path only, a variance that is negative, NaN or infinite raises ValueError naming the epoch and the axis, before anything is gated or fused, so a data defect is not counted as a faulty receiver. Every other variance is raised to at least gnss_per_fix_min_sigma_m squared (new field, default 0.01, validated finite and positive). Defaults and the benchmark numbers do not change; the golden file gains the new key and the config hashes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #108
Three commits, from a fresh clone, branch from
origin/mainat 83370c9:01ecf35, the stranded commit (796db17), cherry-picked with its author.1897f2a, review: diagonal-only wording, tests for a tiny variance, the gate seeing the same R, the vertical sigma.gnss_per_fix_min_sigma_m).The second commit on the stranded branch (the UrbanNav Tunnel-1 run,
a23512c) is not taken. That is T2-0b.Cherry-pick conflict
One conflict, in
tests/test_estimators.py. Both sides appended tests at the end of the file: main added the reset and transition-matrix tests, the stranded commit added three vertical-sigma and per-fix tests. I kept both, main's block first.eskf.pyanddocs/MODEL.mdmerged cleanly.Golden file
Step 3, main's golden file, test run without
--update(the script has no check mode, so this ispytest tests/test_golden_benchmark.py): 7 failed, 6 passed. The 7 failures are the 7 per-case tests, and each reports exactly 3 differences: theconfig_hash,gnss_per_fix_covarianceandgnss_vertical_sigma_m("unexpected (golden lacks it)"). No metric differs. The per-case test covers 7 of the 15 cases, so the other 8 are covered by the diff and by the run comparison below.Step 4, after the review fix added the third key,
--update, thengit diff origin/mainof the golden file, collapsed by line:That is exactly 15 hash replacements and three new keys per case. Nothing else.
Nothing moved
navkit run --outonorigin/main(a separate worktree) and on this branch, withruntime_s,wall_sandrealtime_factorstripped, compared path by path:No other path differs.
check_doc_tables.pypasses without editing any table.Answers
a. Defaults. With both settings at their defaults,
_gnss_updatereceivessigma_vertical=Noneandvariance=None, so neitherifruns and R is the sameeye(3) * sigma**2. In the run loop,ownisNonewhengnss_per_fix_covarianceis False, so a fix'scovis never read. When the setting is on and a fix has nocov, the sigmas are used. The golden file and the run comparison above confirm it on all 15 cases. Tests: a badcovis ignored when the setting is off (the run equals the fixed-sigma run exactly), and the setting on with nocovequals the setting off.b. Diagonal only.
GnssFix.covis(N, 3)variances, so a correlation between axes (east-north) cannot be expressed. Said in theEskfConfigcomment and indocs/MODEL.md. No cross terms were added.c. Zero, negative, NaN, inf. Before this PR the code did neither reject nor floor. Zero was accepted as an exact fix (the state jumped onto the measurement and the covariance on that axis went to zero). Negative, NaN and inf were rejected only by accident, as a gate rejection with an infinite distance, so FDIR would have counted a data defect as a faulty receiver. Behaviour chosen by the maintainer and now in place:
variancegiven, which happens only whengnss_per_fix_covariance=Trueand the fix carries acov), a negative, NaN or infinite variance raisesValueErrorinside_gnss_update, before anything is gated or fused. The message names the epoch and the axis index and says the value is not usable. It is not a gate rejection, nothing is logged for FDIR, and no counter was added.gnss_per_fix_min_sigma_msquared, per axis. The newEskfConfigfieldgnss_per_fix_min_sigma_mdefaults to 0.01 m (1 sigma). It is validated finite and positive. Zero, a negative, NaN or inf floor raisesValueErrorwhen the config is built. The value 0.01 m is the maintainer's pick and it is a config field, so it is easy to change. It has no effect unlessgnss_per_fix_covarianceis on, so defaults and the benchmark numbers do not change.d. Tiny variance. A variance of 1e-12 per axis is below the floor, so the update equals the floored one exactly, is accepted, has a finite Mahalanobis distance, and leaves
Pfinite, symmetric and positive semidefinite.e. Vertical sigma. It sets
R[2,2]only. The innovation covariance the gate sees is built from the sameRcovas the update. A test checksS[2,2] = P[5,5] + sigma_v^2and that the horizontal and off-diagonal terms are unchanged. A second test shows the declared vertical sigma alone decides whether a 12 m height jump is gated.f.
test_eskf_config_as_dict_covers_every_fieldpasses. All three fields are inas_dict, so the config hash covers them.g. The text added to
docs/MODEL.mdis in words and carries no measured number. The only numeral is the config default in the table, as for the neighbouring rows.Tests added
Written first, shown failing on the earlier branch tip (22 failed: the new field missing, and "DID NOT RAISE" for the run-level bad-covariance test), now passing:
ValueErrorwith the epoch,axis Nand "not usable"; the state and covariance are unchanged after the raise (deep copy compared), and no innovation or bias record was appended;covwith the setting off equals the no-covrun; with the setting on it raises; with the setting on and nocovit equals the setting off;For T2-0b (not changed here)
src/navkit/urbannav_data.pyis on the stranded second commit and is not in this package. When it feeds the filter withgnss_per_fix_covariance=True, a fix with a missing or NaN covariance now raisesValueError(a missingcovarray falls back to the sigmas, but a NaN entry raises). The reader must drop such fixes before they reach the filter. T2-0b has to check this.Checks
ruff check,ruff format --check,mypy src,check_doc_tables.py(against the fresh benchmark, no table edited),metrics.py --checkandcheck_links.pypass. The test count is regenerated. Full suite on the final tree:1490 passed, 24 skipped, 2 xfailed in 576.38s (0:09:36). The coverage ratchet is left to CI.Left
Nothing unresolved. The floor of 0.01 m is the maintainer's choice and is a config field.
🤖 Generated with Claude Code