Skip to content

[cwx] Make CWX availability follow the TX slice#4269

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/4173-cwx-tx-slice
Jul 19, 2026
Merged

[cwx] Make CWX availability follow the TX slice#4269
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/4173-cwx-tx-slice

Conversation

@jensenpat

Copy link
Copy Markdown
Collaborator

Summary

  • derive CWX availability from the radio's TX slice instead of the selected slice
  • keep the mutually exclusive CWX/DVK F1-F12 shortcut sets tied to the selected slice
  • refresh keyer availability when TX-slice assignment or either relevant slice mode changes

Root cause

MainWindow::updateKeyerAvailability() used one selected-slice mode value for two different policies. Selected-slice mode is correct for shortcut routing, but CWX itself targets the radio's TX slice (matching FlexLib CWX::getTXFrequency()). In a split setup with a selected DIGU slice and a CW TX slice, that coupling incorrectly disabled CWX.

Agent automation bridge proof

The freshly built app was driven into the reported two-slice state without keying:

  • selected slice A: active=true, mode=DIGU, txSlice=false
  • slice B: active=false, mode=CW, txSlice=true
  • visible CWX indicator from dumpTree: enabled=true, visible=true
  • transmit model throughout proof and cleanup: transmitting=false, mox=false, tuning=false

The bridge sequence used slice add, slice mode, slice tx, slice select, get slices, get transmit, dumpTree, and grab MainWindow. After capture, slice A was restored to USB/TX, temporary slice B was removed, and the radio returned to its original one-slice state.

Proof

Reporter confirmation before the fix: CWX is unavailable with the multi-slice setup.

Before

Fixed build: selected slice A remains DIGU while slice B is the CW TX slice and CWX is available.

After

Testing

  • cmake --build build -j8 — passed (765/765 build steps)
  • ctest --test-dir build --output-on-failure -j8 — 113/113 runnable tests passed; one quarantined test skipped as designed
  • python3 tools/check_engine_boundary.py --strict — zero blocking findings
  • python3 tools/check_a11y.py — no findings in changed lines
  • live agent automation bridge proof — passed; radio state restored and lock released

Bridge note

The bridge can inspect the CWX label structurally, but invoke/targeted clickAt cannot resolve it because the interactive QLabel has neither an object name nor an accessibility identifier. This did not block the exact enabled-state proof, but assigning a stable target or converting the interactive label to a keyboard-accessible control would improve future activation testing.

Fixes #4173

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat

Keep F1-F12 shortcut routing tied to the selected slice while deriving CWX availability from the radio TX slice. Refresh the availability gate when TX assignment or either relevant slice mode changes.

Fixes aethersdr#4173
@jensenpat jensenpat self-assigned this Jul 16, 2026
@jensenpat jensenpat changed the title Make CWX availability follow the TX slice [cwx] Make CWX availability follow the TX slice Jul 16, 2026
@jensenpat
jensenpat marked this pull request as ready for review July 16, 2026 16:11
@jensenpat
jensenpat requested a review from a team as a code owner July 16, 2026 16:11

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, correct, well-scoped fix. Splitting the two policies — shortcut routing keeps following the selected slice while CWX availability now derives from the TX slice — matches how FlexLib CWX::getTXFrequency() behaves, and the split DIGU-selected / CW-TX case is exactly the reported trap. The re-evaluation triggers line up with the three cases the summary claims: TX reassignment via txSliceChanged, and mode changes on either the active or the TX slice via the widened guard in the modeChanged lambda. Behavior in the common single-slice case is unchanged (selected == TX). Nothing blocking.

Non-blocking notes

  • updateKeyerAvailability open-codes a "find the TX slice's mode" loop that already exists verbatim in the setTxModeGetter lambda (MainWindow.cpp:1094) and as isTxSlice() scans in several other spots — a small shared helper would avoid the third copy. Purely a cleanup; fine to leave for a follow-up.

Thanks for the thorough write-up and the before/after proof.


🤖 aethersdr-agent · cost: $6.5320 · model: claude-opus-4-8

Comment thread src/gui/MainWindow.cpp Outdated
@jensenpat jensenpat added help wanted Extra attention needed priority: medium Medium priority labels Jul 17, 2026
NF0T
NF0T previously requested changes Jul 17, 2026

@NF0T NF0T left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct root-cause diagnosis — splitting "shortcut routing follows the selected slice" from "CWX availability follows the TX slice" is the right conceptual fix, and the split-DIGU-selected/CW-TX repro in the automation bridge proof is exactly the reported trap. CI is green and the commit is signed.

Two issues before this can merge, both surfacing in the exact split-slice scenario this PR targets:

1. The CWX panel can auto-close and never reopen during a TX-slice handoff. SliceModel::setTxSlice() only sends a wire command — each slice's TX flag updates independently via its own async status echo, so a TX handoff between two slices (e.g. the adjacent "#1351 Active follows TX slice" external-client case) fires two unsynchronized txSliceChanged signals. If the old slice's tx=false lands before the new slice's tx=true arrives, the new txMode scan momentarily finds no TX slice at all, txIsCw goes false, and updateKeyerAvailability calls m_cwxPanel->hide() — even though CW TX capability was never actually lost. When the corrective call fires moments later with txIsCw true again, the panel is already hidden and there's no re-show path anywhere in the function. The panel silently disappears mid-session until the operator manually reopens it.

2. CwxPanel's F1-F12 macro shortcuts still gate on the selected slice, contradicting the fix. CwxPanel's shortcut handler reads m_activeModeProvider() (still wired to activeSlice()->mode(), untouched by this PR) and silently no-ops if that's not CW — while the macro button click handler right next to it sends unconditionally. So after this fix, with selected=USB/TX=CW: the indicator now correctly shows CWX available and the panel opens, but pressing F1 does nothing, because the keyboard-shortcut gate is still selected-slice-based. That's the exact bug class this PR fixes, one hop away, in the same panel.

Non-blocking, worth doing in the same pass since you're already touching this code:

  • RadioModel::txSlice() (RadioModel.cpp:1355) already implements the "scan for the TX slice" loop this PR adds a copy of — it's currently unused anywhere in the codebase. Making it public and calling it here (and from setTxModeGetter/updateDaxTxMode, which duplicate the same scan) would collapse several hand-rolled copies into one.
  • SliceModel* active = activeSlice(); updateKeyerAvailability(...) is pasted at both new call sites — a small no-arg helper would remove the duplication.
  • The new protocol-behavior comment ("CWX sends on the radio's TX slice... FlexLib CWX::getTXFrequency()") doesn't cite a FlexLib file/line, pcap, or firmware version — worth pinning down given Principle I's evidence bar, since right now it's an asserted claim rather than a demonstrated one.
  • DVK has the identical structural gap (slice-agnostic playback, availability still on the selected slice) — same bug shape, worth a follow-up issue rather than blocking this one.

Happy to re-review quickly once the panel re-show and the shortcut gate are addressed — the core direction here is right.

@NF0T NF0T self-assigned this Jul 17, 2026
… slice

The initial fix made the CWX *indicator* follow the TX slice but left the
F1-F12 shortcut set and the whole DVK path on the *selected* RX slice,
which produced two defects:

1. Split VFO (selected=USB, TX=CW): the CWX indicator lit and the panel
   opened, but F1-F12 no-oped — the CwxPanel handler and setShortcutsEnabled
   still gated on the selected slice. And because CWX shortcuts derived from
   the TX slice's mode while DVK shortcuts derived from the selected slice's
   mode, both F1-F12 sets could be enabled at once (TX=CW + selected=USB),
   re-opening the activatedAmbiguously hole aethersdr#2464/aethersdr#2582 closed.

2. A momentary "no TX slice" — during a TX handoff between slices, or a
   band-recall that drops+recreates the TX slice — made txIsCw false and
   hid an open CWX panel with no re-show path, so the panel silently
   vanished until the operator reopened it.

Fix: drive both keyers' availability *and* their F1-F12 shortcuts from the
single TX slice. One source keeps the two shortcut sets mutually exclusive
(a slice can't be CW and voice at once), and only auto-hide a panel when a
TX slice actually exists and is in the wrong mode — a transient absence no
longer yanks the panel.

Also:
- Reuse RadioModel::txSlice() (promoted from private) instead of the
  hand-rolled slice scan the fix added — dedupes a 3rd copy of the loop.
- Point CwxPanel's F1-F12 mode guard at the TX slice (setTxModeProvider).
- Cite the Principle I evidence for TX-slice targeting:
  reference/FlexLib_API_v4.1.5.39794/FlexLib/CWX.cs:186-199 getTXFrequency().
- Collapse the redundant activeMode/txMode split; updateKeyerAvailability()
  now takes no argument and reads the TX slice itself.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with a follow-up commit (66a72d07) that resolves both of @NF0T's blocking findings plus the dedup. Built clean (1476/1476) and full suite green (114/114, 1 quarantined skip).

What the follow-up changes

The initial commit moved the CWX indicator to the TX slice but left the F1-F12 shortcut set and the entire DVK path on the selected RX slice. That's not just incomplete — it reopens a closed bug:

  • F1-F12 no-op in the split case (NF0T #2). CwxPanel's handler and setShortcutsEnabled both gated on the selected slice, so with selected=USB / TX=CW the indicator lit and the panel opened but the macros did nothing.
  • Ambiguous-shortcut regression. Worse, once CWX shortcuts derive from the TX slice's mode while DVK shortcuts derive from the selected slice's mode, both F1-F12 ApplicationShortcut sets can be enabled simultaneously (TX=CW and selected=USB) — exactly the activatedAmbiguously hole #2464/#2582 closed. Mutual exclusion only holds if both keyers derive from the same slice.
  • Panel vanish on transient (NF0T #1). A momentary "no TX slice" (TX handoff between slices, or a band-recall that drops+recreates the TX slice) made txIsCw false and hid an open panel with no re-show path.

Fix: drive both keyers' availability and their F1-F12 shortcuts from the single TX slice, and only auto-hide a panel when a TX slice actually exists and is in the wrong mode (a transient absence no longer yanks the panel).

Also addressed

  • Reuses RadioModel::txSlice() (promoted from privatepublic) instead of the hand-rolled scan — removes the 3rd copy of that loop. Note for the record: txSlice() was not unused; it's called ~8× inside RadioModel.cpp. It just wasn't reachable from MainWindow because it was private.
  • Points CwxPanel's F1-F12 mode guard at the TX slice (setActiveModeProvidersetTxModeProvider).
  • Principle I: cites reference/FlexLib_API_v4.1.5.39794/FlexLib/CWX.cs:186-199 getTXFrequency() — the FlexLib scan that returns the IsTransmitSlice's freq.
  • Collapses the activeMode/txMode split; updateKeyerAvailability() is now argument-free and reads the TX slice itself, simplifying all 6 call sites.

@NF0T — this expands your #2 into "shortcuts follow the TX slice" (not the selected slice) because keeping them split would re-introduce the ambiguous-key bug. If you'd rather keep DVK/selected-slice semantics, that's a design call worth a thread. Filed the DVK "same structural gap" note you raised as a follow-up.

@ten9876
ten9876 enabled auto-merge (squash) July 19, 2026 00:34
@ten9876
ten9876 dismissed NF0T’s stale review July 19, 2026 00:38

Requested change have been made

@ten9876
ten9876 merged commit 58b0632 into aethersdr:main Jul 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

help wanted Extra attention needed priority: medium Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CWX disabled with second Slice opened

3 participants