fix(nets): restore 'band not available' feedback when net-tuning an unconfigured transverter band (#3930)#3992
Conversation
…nconfigured transverter band (aethersdr#3930) — Principle XI. PR aethersdr#3921 correctly routed tuneToNet() through applyTuneRequest(AbsoluteJump) to fix the VFO-display desync, but applyTuneRequest() only runs preselectBandStackForTune() — home of the XvtrPolicy support check and its "band isn't available" status-bar feedback — for CommandedTargetCenter. A net scheduled on a band the radio can't reach (no native coverage, no matching XVTR entry) therefore silently issued the slice tune + recenter with no feedback. Scoped fix per the issue's Option 1: pre-check the target band in tuneToNet() before the tune, mirroring the preselectBandStackForTune() guard exactly — same HF↔HF and same-band early-outs, same XvtrPolicy::resolveBandStackKey() decision, same no-XVTR wording, same lcProtocol warning shape — and refuse before touching the radio. New xvtr_policy_test cases pin the decision matrix the pre-check relies on: 440 with no XVTR is unsupported (the repro), 440 with a covering XVTR resolves, 2m resolves natively on has2Meters models (no over-blocking), and plain HF stays native. Co-authored-by: Don @ cloaked.agency <don@cloaked.agency>
There was a problem hiding this comment.
Thanks for this, @Ozy311 — well-diagnosed and cleanly scoped. The root cause is right: applyTuneRequest() only runs preselectBandStackForTune() for CommandedTargetCenter, so the AbsoluteJump net path skipped the XVTR support check entirely.
I verified the pre-check against the real preselectBandStackForTune() at MainWindow.cpp:5771 and it's a faithful mirror:
- The guard
freqMhz > 54.0 || slice->frequency() > 54.0is the exact De Morgan equivalent of the original'smhz <= 54.0 && slice->frequency() <= 54.0early-out. - Same
targetBand != currentBandgate, sameresolveBandStackKey()call, same no-XVTR reason override (freqMhz > 54.0 && xvtrs.isEmpty()). xvtrPolicyBandsFrom/xvtrListSummaryare declared in the publicMainWindowHelpers.h(already included here), andXvtrPolicy/BandSettingsare pulled in — so it compiles cleanly, and CI is green on all three platforms + CodeQL.
Two small observations, neither blocking:
-
Guard duplication risk. This is now the third copy of the same XVTR-support decision (this net path,
preselectBandStackForTune, and the wiring path aroundMainWindow_Wiring.cpp:2835). They can drift — if the canonical guard ever changes its threshold or reason wording, this copy won't follow. Your test pinningresolveBandStackKey()mitigates the decision core, but the surrounding guard (thresholds, message shape) is still copy-pasted. The Option 2 consolidation you flagged as follow-up is the real fix; worth a tracking issue so this doesn't quietly diverge. -
Minor behavioral divergence on empty pan. The original returns
NotNeededwhenslice->panId().isEmpty()(skips the check, allows the tune). The net pre-check has no such early-out, so a slice with an empty panId on a VHF+ band would be refused here where the canonical path would proceed. This is an unusual/transient slice state and low-risk, but it's the one spot where the mirror isn't 1:1.
The test pins the decision matrix rather than tuneToNet() itself, which is a reasonable limitation given the GUI coupling — and the honest note about resolveBandStackKey() matching XVTRs by name vs. frequency range is a good separate-issue callout. Nice work. 73
🤖 aethersdr-agent · cost: $4.4216 · model: claude-opus-4-8
jensenpat
left a comment
There was a problem hiding this comment.
Thanks for the net reminder XVTR fix, Ozy (and for testing w/ automation bridge!)
Summary
PR #3921 correctly routed
tuneToNet()throughapplyTuneRequest(..., TuneIntent::AbsoluteJump, "net-tune")to fix the VFO-display desync — butapplyTuneRequest()only runspreselectBandStackForTune()(home of theXvtrPolicysupport check and its "band isn't available" status-bar feedback) forCommandedTargetCenter. A net scheduled on a band the radio can't reach (no native coverage, no matching XVTR) therefore silently issued theslice tune+ recenter with no feedback. This implements the issue's Option 1: a scoped pre-check intuneToNet()that mirrors thepreselectBandStackForTune()guard exactly and refuses — with the status-bar message — before touching the radio.Why
MainWindow::preselectBandStackForTune(): same HF↔HF and same-band early-outs, sameXvtrPolicy::resolveBandStackKey()decision (capabilities-aware, so 2 m on a FLEX-6700 stays native and is never over-blocked), same no-XVTR wording, samelcProtocolwarning shape.MainWindow_Menus.cpp, sameAbsoluteJump, same gap) and typed-VFO consolidation into the canonical tune policy (the issue's Option 2) are a natural follow-up.Scope
src/gui/MainWindow_Nets.cpp(pre-check +core/LogManager.hinclude forlcProtocol)tests/xvtr_policy_test.cpp(newtestNetTunePrecheckBandSupport()pinning the decision matrix)Constitution principle honored
Principle XI — Fixes Are Demonstrated. Bug and fix verified A/B on a live FLEX-6700 (via the automation bridge, RX-only), plus new unit tests pinning the decision core.
Test plan
xvtr_policy_testgreen (37/37 incl. 4 new cases: 440-no-XVTR unsupported, 440-with-XVTR resolves, 2m native onhas2Metersmodels, HF control)ninja -C buildclean on Linux (Nobara 43, Qt 6.10.3, gcc 15.2.1)lcProtocolwarningObserved while verifying, pre-existing and unchanged by this PR:
resolveBandStackKey()matches XVTR entries by name against theBandSettingsband name — the lab 6700 reports an XVTR entry named70CM, which does not match band440, so the tune refuses on the canonical preselect path too (typed VFO, spot recall). Whether name-matching should become frequency-range matching is a separate policy question worth its own issue.Checklist
Closes #3930
73, Ozy K6OZY
AI compute partnership: cloaked.agency — (model: claude-fable-5)