20260907 - Default the Doppler span to +/-300 Hz and let it reach deployed nodes - #36
Merged
Merged
Conversation
…loyed nodes owl was widened to +/-1000 Hz on 2026-08-11 to catch a 402 m/s target that sat at -674 Hz when the node ran at fc 503 MHz. At 213 MHz (lambda 1.407 m) that same target appears at 286 Hz, so the window that justified +/-1000 now costs 265 ms/CPI to cover speeds nothing flies at. +/-300 Hz reaches ~+/-422 m/s for +42 ms/CPI span-attributable, measured over 74 CPIs on owl on 2026-09-07. Doppler span costs no resolution: bin spacing is pinned at 1/CPI and the span sets bin count, paid for out of nCorr = fs/dopplerSpan. What it does cost, at +/-300 against +/-200, is ~0.4 dB more far-range integration loss at delay bin 400 (the linear-correlation taper deepens as nCorr falls from 4975 to 3322) and 1.5x the raw CFAR false alarms, before centroiding and the minDoppler gate. Changing default.yml alone would have reached nothing. The merger seeds user.yml with a whole copy of default.yml on first boot, so every node that has ever booted holds the shipped span in its own overlay, which wins over defaults. owl's overlay carries all six top-level sections for exactly this reason. migrate_doppler_span drops the pair from the overlay when it is exactly the +/-200 that used to ship, so the default shows through. It follows migrate_gain_reduction in kind, and differs in three ways: - It tests both bounds together. -200/1000 or -200/400 is somebody's decision and is kept whole. owl's -1000/1000 survives untouched. - It deletes rather than rewrites, so the value lives in default.yml alone and the next span change needs no merger edit. - It runs before the user merge rather than after the forced one, so forced.yml keeps the last word. A node that deliberately chose +/-200 cannot be told apart from a first-boot copy, so it moves too and has to be set again. That is the cost of the overlay never recording who wrote a value. user.yml on disk is left alone, which makes this function load-bearing rather than one-shot: remove it and those nodes fall back to the +/-200 their overlay still holds. test_doppler_span_migration_does_not_rewrite_user_yml pins that so it is not deleted as spent. Co-Authored-By: Claude Opus 5 (1M context) <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.
Ticket: https://app.clickup.com/t/123zgec0m7t
Why
owl was widened to +/-1000 Hz on 2026-08-11 to catch a 402 m/s target that sat at -674 Hz when the node ran at fc 503 MHz. At 213 MHz that same target appears at 286 Hz, so the window that justified +/-1000 now costs 265 ms/CPI to cover speeds nothing flies at. +/-300 Hz reaches ~+/-422 m/s for +42 ms/CPI span-attributable, measured over 74 CPIs on owl.
Doppler span costs no resolution: bin spacing is pinned at 1/CPI and the span sets bin count, paid out of
nCorr = fs/dopplerSpan. At +/-300 against +/-200 the real costs are ~0.4 dB more far-range integration loss at delay bin 400 (nCorr falls 4975 -> 3322, deepening the linear-correlation taper) and 1.5x the raw CFAR false alarms, before centroiding and the minDoppler gate.The part that is not just a config edit
Changing
default.ymlalone would have reached nothing. The merger seedsuser.ymlwith a whole copy ofdefault.ymlon first boot, so every node that has ever booted holds the shipped span in its own overlay, which wins over defaults.migrate_doppler_spandrops the pair from the overlay when it is exactly the +/-200 that used to ship, so the default shows through. It followsmigrate_gain_reductionin kind and differs in three ways:-200/1000or-200/400is somebody's decision and is kept whole. owl's-1000/1000survives untouched.default.ymlalone and the next span change needs no merger edit.forced.ymlkeeps the last word.A node that deliberately chose +/-200 cannot be told apart from a first-boot copy, so it moves too and has to be set again.
user.ymlon disk is left alone, which makes this function load-bearing rather than one-shot: remove it and those nodes fall back to the +/-200 their overlay still holds.test_doppler_span_migration_does_not_rewrite_user_ymlpins that. It also means rollback is clean: redeploy the previous merger image and a migrated node returns to +/-200 by itself.Verification
30/30 merger tests pass, 7 new covering the guard table above.
Deployed to owl and verified live on 2026-09-07, running against the node's real files inside the released
retina-config-merger:v0.4.5.0image with this script mounted in:Dropping first-boot Doppler span (-200/200 Hz), output +/-300, overlay on disk unchanged.tar1090.envandretina-tracker.yamlregenerate identical.Found while deploying, and it shapes the rollout: restarting blah2 was followed ~70 s later by the whole stack restarting, which re-ran the released merger and overwrote a hand-generated
config.ymlseconds before blah2 read it. This has to ship as an image; a hand-run merger is not a deployment.🤖 Generated with Claude Code