Stabilize DAX2 coexistence policy#2271
Conversation
|
Claude here — thanks Patrick! This is a textbook second-draft refactor of #2266: the new `DaxTxPolicy.h` decision table is exactly the kind of structure we want for cross-platform DAX coexistence, and the test coverage (`testStreamStatusOwnershipCompatibility`, `testDaxTxStatusOwnership`, `testDaxTxPolicy`, `testUdpRegistrationPolicy`) makes the platform branches verifiable. Audited the SmartLink/WAN path before merging — `startWan()` already binds to `AnyIPv4:0` for the local socket and your new `shouldRetryLanUdpPortRegistration(isWan, ...)` correctly excludes WAN from the rebind logic (with explicit test coverage). Clean. Merging now. Filed a follow-up at #2273 to track the Linux non-PipeWire edge case — the new `setDax(isDigital)` is now unconditional on all platforms but Linux non-PipeWire builds don't get a local `dax_tx` stream, so digital TX would be silent on that niche configuration. It's documented in the policy via the `route_only_does_not_require_local_stream` note, but worth tracking in case someone hits it. 73, Jeremy KK7GWY & Claude (AI dev partner) |
Reliability sweep release. Headline is jensenpat's DAX2 coexistence policy refactor (#2271): platform-aware decision table, lazy dax_tx stream creation, anti-stomp on incoming foreign DAX TX status, LAN VITA UDP rebind on Flex's "Port/IP pair already in use" error, dead- orphan DAX RX detection, and 24+ test assertions. Substantial reliability fixes around disconnect teardown (#2247 — sequenced stream remove with response wait, dropped self-disconnect, dying-gasp byte), worker-thread shutdown (#2248 — no more killTimer warnings), and panadapter zoom (#2246 — no spectrum flash). Closes 12 issues via two paired triage-cleanup sweep PRs (#2270 + #2272) covering small UI bugs, contrast, indicator routing, click-to- tune behaviour, and visible regressions from recent feature work. Also includes the #2273 follow-up to #2271: setDax() and DAX TX stream creation are now both gated on platforms that can actually feed DAX audio, so Linux non-PipeWire builds keep digital TX on the physical mic input rather than going silent. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
…re (#2285) v0.9.5's DAX2 coexistence policy (#2271) regressed TCI digital TX (WSJT-X / JTDX / MSHV) on Windows and Linux without PipeWire. The radio was told dax=1 but evaluateDaxTxPolicy() denied dax_tx stream creation for TciTxAudio on those platforms, so the modulator stayed silent during TX. The conceptual mistake was conflating SmartSDR DAX2's ownership of Windows DAX audio devices with the radio's dax_tx stream slot. TCI feeds its dax_tx packets from a WebSocket — it doesn't touch local audio devices and doesn't conflict with SmartSDR DAX2 at all. evaluateDaxTxPolicy() now always allows DaxTxRequestReason::TciTxAudio regardless of platform / hosted-DAX availability. Test assertions in radio_status_ownership_test.cpp flipped to match (the prior "Windows external-DAX2 TCI policy does not create dax_tx" assertion codified the regression) plus a new Linux-non-PipeWire case. macOS and Linux + PipeWire were unaffected — they hit the `tciDaxTxSupported = true` branch and were already allowed. Fixes #2276. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Summary
This PR stabilizes coexistence with SmartSDR DAX2 by making AetherSDR explicit about when it may create or adopt DAX TX streams, and by avoiding stale/orphan DAX stream status from being treated as locally owned state.
Code Summary
DaxTxPolicy.hto centralize platform-aware DAX TX decisions. Windows builds now treat SmartSDR DAX2 as the owner of the external DAX route, while hosted DAX platforms such as macOS and PipeWire can still create localdax_txstreams when their bridge or TCI route needs one.RadioModel::ensureDaxTxStream(...), with request reasons for hosted DAX bridge, TCI TX audio, external route-only, and generic audio recreation. This prevents eagerstream create type=dax_txduring GUI attach and tracks pending creates to avoid duplicate requests.StreamStatus.handRadioModel.cpp. Dead orphan DAX RX entries withclient_handle=0x00000000 ip=0.0.0.0are ignored, external DAX RX/TX streams are logged separately, and local DAX TX state only updates from the current stream id or our ownclient_handle.MainWindow.cppso radio-sidetransmit set dax=1behavior remains aligned for digital modes, while local DAX TX stream creation is gated by the new policy.UdpRegistrationPolicy.hplus a LAN VITA UDP rebind path.PanadapterStreamnow binds an OS-assigned LAN UDP port withDontShareAddress, andRadioModelretriesclient udpportregistration on Flex's port/IP collision error instead of leaving the session stuck.radio_status_ownership_testfor DAX TX policy decisions, dead DAX RX status handling, DAX TX ownership updates, and UDP registration retry detection.Validation
cmake --build build -j10../build/radio_status_ownership_testand all tests passed.build/AetherSDR.appfor manual troubleshooting.👨🏼💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat