fix(cat): stop set_split_vfo poll from ping-ponging TX between slices#2931
Conversation
Each rigctld/PTY CAT channel is bound to a fixed slice. cmdSetSplitVfo reclaimed TX onto that slice whenever a client sent `set_split_vfo 0` (split disabled). PR aethersdr#2568 added an `!isTxSlice()` guard to stop the command spam, but that only suppressed the redundant case — it did not stop TX from being seized. Hamlib loggers (VARA, VARAC, N1MM, fldigi) poll `set_split_vfo 0 VFOA` every few seconds as part of their state-sync loop. With multiple slices and a CAT channel bound to a non-TX slice, the sequence was: 1. user clicks slice A's TX badge → `slice set 0 tx=1`, A is TX 2. ~1.7 s later the logger polls split → channel bound to slice B 3. cmdSetSplitVfo sees B is no longer TX → `slice set 1 tx=1` 4. TX jumps back to B So the `!isTxSlice()` guard actually *guaranteed* the re-seize: the user moving TX away is exactly the moment the bound slice becomes non-TX, which re-satisfies the guard on the next poll. TX ping-pongs back within seconds and the user cannot keep it on the slice they chose. Confirmed in the connection log: a manual `slice set 0 tx=1` is followed ~1.7 s later by four `slice set 1 tx=1` commands and the radio echoes slice 1 tx=1. Fix: track the last split state per protocol instance (tri-state) and only reclaim TX on a genuine split→non-split *transition* (1→0 edge), never on a steady-state poll or the first report after connect. A logger that sits in non-split and polls it repeatedly no longer fights the user's TX choice, while a real "turn split off" still moves TX back to the client's slice. Tested on macOS with FLEX-6600 fw 4.2.18 + VARAC: with slice A as TX and a second slice B added, TX now stays on A after the user selects it instead of being pulled back to B every couple of seconds. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks @pepefrog1234 — clean, well-reasoned fix. The write-up makes the bug obvious in hindsight: the !isTxSlice() guard from #2568 was exactly what armed the re-seize on the next poll.
Logic review:
m_lastSplitEnableis perRigctlProtocolinstance, which matches the "each CAT channel is bound to a fixed slice" framing — only the client that toggled split will reclaim, no cross-client interference.- Tri-state walk:
- First poll (
enable=0):wasEnabled=false,firstReport=true→ no action, state recorded. ✓ - Steady-state
enable=0polls:wasEnabled=false→ no action. ✓ - Client raises split (
1), then drops it (0):wasEnabled=true,firstReport=false→ genuine reclaim. ✓ - First poll is
enable=1(logger started mid-split): falls through the!enablebranch, state records as 1, next 0 triggers reclaim. ✓
- First poll (
currentSlice()null-guard preserved.- State is only updated inside
cmdSetSplitVfo;cmdSetSplitFreqand friends don't touch it, which is fine — split state in this protocol is solely what the client has told us.
Scope is exactly two files matching the stated fix. No conventions / RAII / system-boundary concerns. LGTM as a community contribution — pending the reviewer note on N1MM-style true-split workflows on Windows/Linux that you already called out in the test plan.
…dr#2995 Reconciles three concurrent improvements to cmdSetSplitVfo: - pepefrog1234's edge-detection (m_lastSplitEnable tri-state) so the Hamlib-logger poll doesn't ping-pong TX away from the user's choice - K5PTB's deferred-promotion state (m_pendingSplitEnable / m_pendingTxSlice / m_pendingSplitFreqMHz / m_pendingSplitMode) from aethersdr#2975 - The m_pendingTxSliceChange same-pass race flag from aethersdr#2975 The combined logic: reclaim TX on the RX slice only when we observe a real 1→0 split transition OR the same-pass race (split-1 then split-0 in one onClientData() pass). Steady-state poll-zeros and the first-report-after- connect leave the user's TX badge alone. The race path stays distinct so the queued setTxSlice() race from aethersdr#2975 is still covered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Merged as 4f64d31. Thanks @pepefrog1234 — clean diagnosis: catching that #2568's Two things stood out in the implementation:
Test plan note: VARAC's checked, N1MM true-split workflows on Windows/Linux haven't been verified — please file a follow-up if you observe any regression there. 73, |
…aethersdr#2931) ## Summary With two or more slices, TX would not stay on the slice the user picked — it kept jumping back to another slice every couple of seconds, and the user couldn't keep TX where they wanted it. Root cause is `cmdSetSplitVfo` reclaiming TX on every `set_split_vfo 0` **poll** rather than on a real split-state change. ## Root Cause Each rigctld / PTY CAT channel is bound to a fixed slice (`m_sliceIndex`). `cmdSetSplitVfo` moved TX onto that slice whenever a client sent `set_split_vfo 0` (split disabled). aethersdr#2568 added an `!isTxSlice()` guard to stop the redundant **command spam**, but that only suppressed the already-TX case — it didn't stop TX from being **seized**. Hamlib loggers (VARA, VARAC, N1MM, fldigi) poll `set_split_vfo 0 VFOA` every few seconds. With a CAT channel bound to a non-TX slice the sequence was: ``` user clicks slice A TX badge → slice set 0 tx=1 (A is TX) ~1.7 s later logger polls split → channel bound to slice B cmdSetSplitVfo sees B != TX → slice set 1 tx=1 TX jumps back to B ``` The `!isTxSlice()` guard actually *guaranteed* the re-seize: the user moving TX away is exactly the moment the bound slice becomes non-TX, which re-satisfies the guard on the very next poll. Captured in the connection log (slice 0 = A, slice 1 = B, single client, no split): ``` 08:11:59.663 TX: C162|slice set 0 tx=1 ← user clicks A's TX badge 08:11:59.785 RX: slice 0 ... tx=1 ← A is TX (user succeeded) 08:12:01.379 TX: C167|slice set 1 tx=1 ← logger split poll, 1.7 s later 08:12:01.379 TX: C168|slice set 1 tx=1 08:12:01.379 TX: C169|slice set 1 tx=1 08:12:01.379 TX: C170|slice set 1 tx=1 08:12:01.498 RX: slice 1 ... tx=1 ← TX yanked back to B ``` ## Fix Track the last split state per `RigctlProtocol` instance (tri-state: unknown / disabled / enabled) and reclaim TX only on a genuine split→non-split **transition** (1→0 edge) — never on a steady-state poll or the first report after connect. ```cpp const bool wasEnabled = (m_lastSplitEnable == 1); const bool firstReport = (m_lastSplitEnable < 0); m_lastSplitEnable = enable ? 1 : 0; if (!enable && wasEnabled && !firstReport) { if (auto* s = currentSlice(); s && !s->isTxSlice()) s->setTxSlice(true); } ``` A logger that sits in non-split and polls repeatedly no longer fights the user's TX choice, while a real "turn split off" still moves TX back to the client's slice. ## Test plan - [x] macOS, FLEX-6600 fw 4.2.18, VARAC on rigctld - [x] Slice A = TX, add slice B → select TX back to A → TX **stays** on A (previously yanked to B within ~2 s) - [x] Genuine split toggle (split on → split off) still moves TX to the channel's slice - [ ] Reviewer: confirm contest-logger split workflows (N1MM true split on/off) still behave on Windows/Linux Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Jeremy Fielder <kk7gwy@aethersdr.com>
Summary
With two or more slices, TX would not stay on the slice the user picked — it kept jumping back to another slice every couple of seconds, and the user couldn't keep TX where they wanted it. Root cause is
cmdSetSplitVforeclaiming TX on everyset_split_vfo 0poll rather than on a real split-state change.Root Cause
Each rigctld / PTY CAT channel is bound to a fixed slice (
m_sliceIndex).cmdSetSplitVfomoved TX onto that slice whenever a client sentset_split_vfo 0(split disabled). #2568 added an!isTxSlice()guard to stop the redundant command spam, but that only suppressed the already-TX case — it didn't stop TX from being seized.Hamlib loggers (VARA, VARAC, N1MM, fldigi) poll
set_split_vfo 0 VFOAevery few seconds. With a CAT channel bound to a non-TX slice the sequence was:The
!isTxSlice()guard actually guaranteed the re-seize: the user moving TX away is exactly the moment the bound slice becomes non-TX, which re-satisfies the guard on the very next poll.Captured in the connection log (slice 0 = A, slice 1 = B, single client, no split):
Fix
Track the last split state per
RigctlProtocolinstance (tri-state: unknown / disabled / enabled) and reclaim TX only on a genuine split→non-split transition (1→0 edge) — never on a steady-state poll or the first report after connect.A logger that sits in non-split and polls repeatedly no longer fights the user's TX choice, while a real "turn split off" still moves TX back to the client's slice.
Test plan