Skip to content

fix(cat): rigctld dialect — gray out the meaningless VFO B selector#3698

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
K5PTB:fix/cat-rigctld-vfob-config
Jun 21, 2026
Merged

fix(cat): rigctld dialect — gray out the meaningless VFO B selector#3698
ten9876 merged 2 commits into
aethersdr:mainfrom
K5PTB:fix/cat-rigctld-vfob-config

Conversation

@K5PTB

@K5PTB K5PTB commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #3694.

The CAT-config UI exposed a VFO B selector for every port regardless of dialect, but the rigctld (Hamlib) dialect is single-VFO per portCatPort wires a rigctld client with only the RX slice (RigctlProtocol has no VFO B input), and split is create-on-demand (VFOB/SUB resolves to a dedicated TX slice only when a client issues set_split_vfo 1). So a configured VFO B has no effect on a rigctld port, yet the selector was shown and persisted.

  • Gray out VFO B for rigctld — the selector is forced to "—" and disabled for the Rigctld dialect, as a display override only.
  • Lossless dialect round-trip — switching a port to Rigctld no longer clobbers the operator's saved VFO B; it's preserved and restored when switching back to a dual-VFO dialect (Flex / TS-2000).
  • Single-VFO is now a dialect capabilitydialectSupportsVfoB(CatDialect) in CatPort.h, instead of scattered hard-coded checks, so every config surface shares one rule.
  • No VFO lost when the radio shrinks — the VFO combos are bounded by the radio's slice-receiver count; a reconnect to a smaller radio could leave a chosen slice unrepresentable. The selectors now restore by data (not raw index + clamp, which silently repointed a VFO), and a saved-but-unavailable slice is preserved rather than clobbered, reappearing when the slices return. (Applies to VFO A and B.)
  • Migration seeds VfoB=-1 (not 1) for the default rigctld port.
  • Delete src/gui/CatPopoutWindow.{cpp,h} — a dead duplicate of the CAT-config row (same VFO B selector + applyRowToSettings) that was never instantiated and not in the build.

Constitution principle honored

N/A — narrow GUI/config bug fix. No new persistence schema (uses the existing CatPort_*_Vfo* keys) and no protocol surface change.

Test plan

Checklist

  • Commits are signed
  • No new flat-key AppSettings calls — only existing CatPort_*_Vfo* / Dialect keys
  • Code is clean-room
  • Documentation updated if user-visible behavior changed (n/a — selector behavior is self-evident)

Built and smoke tested on: MacBook Air M2 / RPi 5 Debian trixie / Win 11 i3

🤖 Generated with Claude Code

K5PTB and others added 2 commits June 20, 2026 14:16
The rigctld (Hamlib) dialect is single-VFO per port: CatPort wires a rigctld
client with only the RX slice (RigctlProtocol has no VFO B input), and split is
create-on-demand (VFOB/SUB resolves to a dedicated TX slice only once a client
issues set_split_vfo 1). So a configured VFO B has no effect on a rigctld port,
yet the CAT-config UI exposed the selector unconditionally and persisted it.

- CatControlApplet: make the VFO B selector dialect-aware — for the Rigctld
  dialect force it to "-" and disable it. As a DISPLAY override only: switching a
  port's dialect to Rigctld no longer clobbers the operator's saved VFO B
  (applyRowToSettings skips persisting VfoB while the dialect is Rigctld), and the
  selector is restored from the saved value when the dialect is switched back to a
  dual-VFO dialect (Flex / TS-2000). A dialect round-trip is lossless.

- Settings migration seeds VfoB=-1 (not 1) for the default rigctld port.

- Delete src/gui/CatPopoutWindow.{cpp,h}: a dead duplicate of the CAT-config row
  (same VFO B selector + applyRowToSettings) that was never instantiated and not
  even in the build — it would only drift further out of sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Robustness fixes from review of the rigctld VFO-B change:

- VFO B no longer lost on a rigctld↔dual round-trip when the saved slice isn't
  currently representable. The VFO combos are bounded by the radio's slice count,
  so a reconnect to a smaller radio can leave a previously-chosen slice off the
  list; the combo falls back to its first entry. New persistVfo() keeps the stored
  value in that case instead of clobbering it with the fallback — it reappears when
  the slices return. Applies to both VFO A and VFO B.

- setMaxSlices() now restores the VFO selectors by DATA value, not by raw index +
  qMin clamp. The index-based restore could silently repoint a VFO to a different
  slice when the "—" entry shifted indices or the count shrank; restoring by data
  falls back to "—"/slice A only when the saved slice is genuinely gone.

- Single-vs-dual-VFO is now a dialect capability — dialectSupportsVfoB(CatDialect)
  in CatPort.h — instead of scattered hard-coded `== CatDialect::Rigctld` checks,
  so every config surface shares one definition.

- updateRowLocked() is display-state only again: the force-to-"—" for single-VFO
  dialects is idempotent, and the VFO-B restore moved to restoreVfoBForDialect(),
  invoked from the dialect-change handler (it's a transition, not something to
  re-run on every lock refresh).

- Use CatPort::kVfoNone instead of the magic -1, and derive the dialect from the
  combo's enum data rather than its row index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@K5PTB K5PTB requested review from a team as code owners June 20, 2026 20:06
@jensenpat

jensenpat commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

Drive-by comment (not a formal review) — the title vs. the diff.

The title, "gray out the meaningless VFO B selector," reads as a one-line cosmetic setEnabled(false), but the diff is doing meaningfully more than that:

  • A per-dialect capability gate (dialectSupportsVfoB() in CatPort.h) — a real abstraction, not just a disable.
  • A persistence rewrite (persistVfo() + restore-by-data in setMaxSlices) that changes save/restore semantics for all dialects, including the dual-VFO TS-2000/Flex ports — not just rigctld.
  • A round-trip restore handler (restoreVfoBForDialect()).
  • Deletion of ~460 lines of dead CatPopoutWindow.

Two small things:

  1. "meaningless" is only true for rigctld. VFO B is very much meaningful for TS-2000/Flex (dual-VFO, cf. CAT/TCP TS-2000: VFO-1/VFO-2 frequency and mode not passed through to control software #3633 / CAT TCP - Flex and TS-2000 - Bugs #3625 / Implement native SmartCAT TCP server — full FlexRadio and TS-2000 dialect support #2962). As worded the title implies the selector itself is pointless, which is the opposite of the PR's intent. Something like fix(cat): VFO B is a dialect capability — disable for rigctld, preserve across switches (#3694) is closer.

  2. The riskiest part isn't the graying-out — it's the persistence change. persistVfo/setMaxSlices now alter save behavior for working dual-VFO ports. The "preserve an unrepresentable saved slice" logic is a nice fix for the shrunk-radio case (CAT ports beyond the radio's slice-receiver capacity silently fail to start #3693), but it also means that on a smaller-radio reconnect, a deliberate user pick of the fallback entry (slice A for VFO A, "—" for VFO B) can't be distinguished from an auto-fallback and won't persist. Edge case, acceptable trade-off, but it's the bit that deserves a review eye — and the current title actively steers reviewers away from it.

No objection to the behavior; runtime wiring (CatPort.cpp / RigctlProtocol.cpp) is untouched and VFO B stays functional where it's actually consumed. Just flagging that the title undersells the scope.


Maintainer note, and this is the real ask: anything that touches shared CAT/persistence behavior with the potential to affect SmartCAT/SmartDAX needs a linked PR + issue review before it lands. SmartCAT/SmartDAX is our largest platform and the primary CAT control path — it is not the place for a drive-by change landed on title-skim alone. This PR is scoped as a rigctld UI fix but it rewrites save/restore semantics for the dual-VFO dialects too (see point 2), and that crossover is exactly what I need surfaced and reviewed explicitly, not buried under a cosmetic title.

Timing matters here: dropping a change that reaches into the shared CAT persistence layer one day out from the end of a release cycle is how we end up cleaning up regressions in the field. For changes like this please (a) cross-link the issue and any related PRs, (b) call out in the title/summary that it affects the dual-VFO/SmartCAT path, and (c) give it room in the cycle to be reviewed and tested — not a last-day merge. If it's genuinely rigctld-only, scope it to rigctld-only and leave the dual-VFO persistence path alone.

@K5PTB

K5PTB commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

@jensenpat This only affects rigctld protocol dialect. It does not affect TS-2000 or Flex: those are preserved --- other than the change you note if connecting to a smaller radio. That was noted by /code-review before I submitted, and I didn't think it should be left configured to address a slice that didn't exist. Technically, it's scope creep, but I expected it to be called out by the automated review and didn't want to leave it unresolved.

I did submit an issue for this, and the deletion of the dead code was suggested by AetherClaude's review. I can restore that if you like. Or, we can adjust the title.

In response to some of the Issues you cited, I've been doing a full sweep of the split VFO behavior, comparing ASDR to SSDR for Win and Mac, reconciling the slight differences. My goal has been to make them match the others. I'll be submitting an issue with those findings in the next day or so.

Edit: I didn't expect this PR to make it into this release cycle. I only thought the previous one was important. But, I wanted to get this in for review over the next week.

@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.

Thanks for a careful, well-documented fix, @K5PTB — the comments explaining why each branch behaves the way it does made this easy to review. The core design (dialectSupportsVfoB() as a single capability rule, display-only override, lossless dialect round-trip) is sound, and CI is green across build/macOS/Windows/CodeQL.

I verified the parts that aren't obvious from the diff alone:

  • CatPopoutWindow.{cpp,h} deletion is safe. No references anywhere outside the two deleted files, and it isn't in CMakeLists.txt (the source list is explicit, not a glob), so it was genuinely dead. Good cleanup.
  • Load-time graying works. buildTableRows() calls updateRowLocked() per row (CatControlApplet.cpp:485), so a port already saved as rigctld comes up with VFO B forced to "—" and disabled — not just after a dialect toggle.
  • setMaxSlices() restore-by-data preserves the disabled state for rigctld rows, since it restores from the combo's current data ("—") rather than re-reading settings.
  • MainWindow scope is clean. The three-dot PR diff only touches the migration default (CatPort_0_VfoB-1); the locale-date hunk that shows up in a two-dot diff is just a stale-branch artifact (branched at 23ef4556, before #3511 merged) and won't revert that feature on merge.

Two small observations, neither blocking:

  1. persistVfo() ambiguity for VFO A. When a saved-but-unavailable slice exists (smaller radio) and the combo has fallen back to the fallback entry, a user deliberately selecting that fallback slice (e.g. genuinely picking slice A) is indistinguishable from the fallback and gets ignored in favor of the preserved value. This is the documented, intended trade-off and the right default — just noting it's a real (if rare) corner where a deliberate pick won't stick until the slices return.

  2. Pre-existing migrated users keep CatPort_0_VfoB="1". The migration is guarded by !contains("CatPort_0_Port"), so anyone who already migrated under the old seed retains the stored 1 for the rigctld port. It's harmless (rigctld ignores it, and applyRowToSettings no longer persists it), and it would resurface as slice B only if they later switch that port to a dual-VFO dialect — arguably reasonable. Worth a mention only so it's a known behavior, not a surprise.

Nice work, and the issue write-up in #3694 made the intent very clear. 👍


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

@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.

Thorough review complete — approving.

Verified independently (not just from the diff):

  • Satisfies #3694 fully — all three acceptance criteria: rigctld VFO B forced to "—" and disabled (load-time via updateRowLocked at build, not just on toggle); saved VFO B preserved across a dialect switch and restored on switch-back; migration no longer seeds a VFO B for the default rigctld port.
  • CatPort::kVfoNone reuse replaces magic -1 literals (constant pre-existed) — clean.
  • CatPopoutWindow deletion is safe — every reference lived inside the two deleted files, and it was not in CMakeLists.txt (explicit source list), so it was never compiled.
  • MainWindow scope is clean — three-dot diff touches only the migration default; no #3511 locale-date revert on merge.
  • Restore-by-data (VFO A and B, in load + setMaxSlices) fixes a latent index/qMin repointing bug; runtime wiring (CatPort.cpp/RigctlProtocol.cpp) is untouched, so VFO B stays functional where consumed.

On @jensenpat's hold (well-raised): the persistence change does reach the shared path, but its behavioral delta is confined to the smaller-radio-reconnect corner — when a saved slice is unrepresentable, persistVfo preserves it instead of clobbering; for any representable selection on a same-size radio it is byte-for-byte identical to the prior setValue, so the dual-VFO/SmartCAT common path is unchanged. The one documented limitation (a deliberate re-pick of the fallback entry in that corner won't stick until the slices return) is an accepted trade-off.

Decision to land now rather than hold: our release velocity makes a long-lived parallel branch drift too far from main to test meaningfully — holding it would add integration risk rather than reduce it. Scope captured accurately in the squash subject per @jensenpat's point.

Thanks @K5PTB — the per-branch "why" comments made this easy to verify, and the #3694 write-up was excellent.

@ten9876 ten9876 merged commit a3659a3 into aethersdr:main Jun 21, 2026
6 checks passed
@K5PTB K5PTB deleted the fix/cat-rigctld-vfob-config branch June 22, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CAT config: rigctld dialect exposes a VFO B selector that has no effect

3 participants