Add DAX virtual audio, Hamlib CAT control, and persistent settings#11
Closed
pepefrog1234 wants to merge 4 commits into
Closed
Add DAX virtual audio, Hamlib CAT control, and persistent settings#11pepefrog1234 wants to merge 4 commits into
pepefrog1234 wants to merge 4 commits into
Conversation
When turning off MOX, immediately set m_transmitting=false and emit moxChanged(false) before sending "xmit 0" to the radio. This prevents TX audio from continuing to stream during the 7-15 second interlock state transition (UNKEY_REQUESTED → READY) on fw v1.4.0.0. Also gate TX in RadioModel::setTransmit() for the same reason. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement a rigctld protocol emulator that allows WSJT-X, fldigi, and other Hamlib-compatible software to control the radio. Includes: - RigctlProtocol: pure protocol parser/responder (no I/O), supports get/set freq, mode, VFO, PTT, split, dump_state, batch commands - RigctlServer: QTcpServer wrapper, multi-client, per-client state - RigctlPty: openpty() virtual serial port with /tmp/AetherSDR-CAT symlink Mode mapping verified against FLEX-8600 fw v1.4.0.0. Thread-safe writes via QMetaObject::invokeMethod with Qt::QueuedConnection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement DAX RX/TX audio path for digital mode software integration: - DaxStreamManager: creates 4 DAX RX streams via SmartSDR protocol (fw v1.4.0.0), routes decoded audio by stream ID - VirtualAudioBridge: POSIX shared memory IPC with lock-free ring buffers, 4 RX + 1 TX channels at native 24 kHz stereo - PanadapterStream: DAX stream ID routing, decodes PCC 0x0123 (int16) and PCC 0x03E3 (float32) audio, sendToRadio() for TX packets - AudioEngine: DAX TX audio path with VITA-49 framing, MOX gating, silence flush on unkey, accumulator for packet-sized chunks - HAL plugin (macOS): libASPL-based Core Audio driver creating 4 RX + 1 TX virtual audio devices at 24 kHz stereo, reads/writes shared memory ring buffers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CatApplet: settings panel for rigctld TCP, virtual serial port, and DAX audio enable/disable with port configuration - AppletPanel: persistent applet toggle states via QSettings, add CAT button; refactor addApplet() to restore saved visibility on startup - MainWindow: wire up RigctlServer, RigctlPty, DaxStreamManager, VirtualAudioBridge; auto-start CAT/DAX based on saved settings; save/restore frequency, mode, connection panel collapsed state - RadioModel: restore last frequency/mode from QSettings on reconnect - CMakeLists.txt: add all new source files and HAL plugin subdirectory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ten9876
added a commit
that referenced
this pull request
Mar 16, 2026
- Migrate QSettings → AppSettings for connection panel collapsed state - Add ConnectionPanel::isCollapsed() public getter - Remove deprecated band memory restore (issue #9) - All settings now use AppSettings XML format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Mar 16, 2026
ten9876
added a commit
that referenced
this pull request
Mar 16, 2026
- Migrate QSettings → AppSettings for connection panel collapsed state - Add ConnectionPanel::isCollapsed() public getter - Remove deprecated band memory restore (issue #9) - All settings now use AppSettings XML format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ten9876
added a commit
that referenced
this pull request
Mar 16, 2026
* Fix stuck TX by immediately gating MOX on unkey When turning off MOX, immediately set m_transmitting=false and emit moxChanged(false) before sending "xmit 0" to the radio. This prevents TX audio from continuing to stream during the 7-15 second interlock state transition (UNKEY_REQUESTED → READY) on fw v1.4.0.0. Also gate TX in RadioModel::setTransmit() for the same reason. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add Hamlib rigctld-compatible CAT control (TCP + PTY) Implement a rigctld protocol emulator that allows WSJT-X, fldigi, and other Hamlib-compatible software to control the radio. Includes: - RigctlProtocol: pure protocol parser/responder (no I/O), supports get/set freq, mode, VFO, PTT, split, dump_state, batch commands - RigctlServer: QTcpServer wrapper, multi-client, per-client state - RigctlPty: openpty() virtual serial port with /tmp/AetherSDR-CAT symlink Mode mapping verified against FLEX-8600 fw v1.4.0.0. Thread-safe writes via QMetaObject::invokeMethod with Qt::QueuedConnection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add CatApplet GUI, persistent settings, and MainWindow wiring - CatApplet: settings panel for rigctld TCP, virtual serial port, and DAX audio enable/disable with port configuration - AppletPanel: persistent applet toggle states via QSettings, add CAT button; refactor addApplet() to restore saved visibility on startup - MainWindow: wire up RigctlServer, RigctlPty, DaxStreamManager, VirtualAudioBridge; auto-start CAT/DAX based on saved settings; save/restore frequency, mode, connection panel collapsed state - RadioModel: restore last frequency/mode from QSettings on reconnect - CMakeLists.txt: add all new source files and HAL plugin subdirectory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fixup: resolve rebase conflicts for PR #11 - Migrate QSettings → AppSettings for connection panel collapsed state - Add ConnectionPanel::isCollapsed() public getter - Remove deprecated band memory restore (issue #9) - All settings now use AppSettings XML format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Style fixes for CatApplet: title bars and toggle buttons - Replace sectionHeader() with appletTitleBar() for gradient title bars - Replace QCheckBox enables with QPushButton toggles (green active style) - Matches STYLEGUIDE.md conventions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove DAX shared memory implementation, keep rigctld and TX fix DAX virtual audio needs PipeWire virtual devices to work with existing ham radio software (WSJT-X, fldigi, FreeDV). The shared memory IPC approach has no compatibility with standard audio apps. Removed: - DaxStreamManager, VirtualAudioBridge (shared memory IPC) - macOS HAL plugin (hal-plugin/) - DAX gain/TX gain sliders and enable button from CatApplet - All DAX wiring from MainWindow Kept: - Hamlib rigctld TCP server (port 4532) + PTY virtual serial port - CatApplet GUI with rigctld/PTY controls - TX stuck fix (MOX gating in TransmitModel) - DAX title bar with "coming soon" placeholder DAX tracked in issue #15. Co-Authored-By: pepeforg1234 <pty.11111@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Guard SpectrumWidget::paintEvent against zero-size painting SEGV occurred in QPainter::drawLine during paintEvent when rigctld was stopped while WSJT-X was actively polling. The crash was in operator new (allocation failure), likely triggered by painting with invalid geometry during a state transition. Added early return guard when width/height are too small to render. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Autostart CAT: rename menu items, save state, start on connect - Renamed "Autostart CAT/DAX with SmartSDR" to "with AetherSDR" - Menu items are now checkable toggles, persisted via AppSettings - When AutoStartCAT is enabled and radio connects, rigctld TCP server and PTY virtual serial port start automatically - Fixes first-launch WSJT-X connection failure (server wasn't running) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CAT server auto-starting: migrate QSettings to AppSettings CatApplet was reading cat/tcpEnable and cat/ptyEnable from old QSettings on construction, causing the toggle buttons to fire and auto-start the server even when Autostart CAT wasn't enabled. - Removed per-button state persistence (tcpEnable/ptyEnable) — CAT auto-start is now solely controlled by the "Autostart CAT with AetherSDR" menu item - Migrated port setting from QSettings cat/tcpPort to AppSettings CatTcpPort - Removed all QSettings usage from CatApplet Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CAT autostart and remove false "not implemented" message - Exclude wired actions (network, memory, spots, autostart CAT/DAX) from the catch-all "not implemented" handler - Add debug logging to AutoStartCAT to verify it fires on connect - Use saved CatTcpPort for autostart server port Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Split CAT autostart into rigctld and PTY, sync applet buttons - New "Autostart rigctld with AetherSDR" menu item (TCP server) - Existing "Autostart CAT with AetherSDR" now controls PTY only - Both persist independently in AppSettings - On radio connect, auto-started services sync their CatApplet Enable buttons to green via setTcpEnabled()/setPtyEnabled() - Removed stale auto-start logic from CatApplet::setRigctlPty() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix rigctld slow response: flush TCP socket after each write WSJT-X "Test CAT" took 46 seconds because socket->write() buffered the dump_state response without flushing. WSJT-X waited for data that was sitting in Qt's write buffer. Added socket->flush() after each response write. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix rigctld delayed commands: use TCP_NODELAY instead of flush socket->flush() may block if the client isn't reading. Replace with TCP_NODELAY (LowDelayOption) on client accept to disable Nagle's algorithm. Responses are sent immediately without buffering delays. This fixes WSJT-X commands (band change, PTT) being queued for 42+ seconds before executing all at once. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add rigctld command-level debug logging Log every command received and response sent for diagnosing WSJT-X connection delays. dump_state response format may need adjustment for faster NET rigctl handshake. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: pepeforg1234 <pty.11111@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
|
Thank you @pepefrog1234 for this excellent contribution! We've merged the following from your PR: Accepted and merged (PR #62):
Deferred:
Adaptations we made:
Known issue: dump_state handshake slow with WSJT-X (#63) 73 de KK7GWY |
ten9876
added a commit
that referenced
this pull request
Mar 18, 2026
- Rule #11: Use AppSettings, NEVER QSettings (prevents future regressions) - AI-to-AI Debugging section: describes how contributor AIs can open issues for the maintainer's AI to analyze and respond to Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
K5PTB
added a commit
to K5PTB/AetherSDR
that referenced
this pull request
Jun 10, 2026
…/shim - Delete default constructor (= delete) to prevent null-vector deref (aethersdr#1) - Remove noexcept from processBlock overrides, buildPrefilter, buildRrcLowpass — allocating ops inside noexcept terminate on OOM (aethersdr#2) - Unconditionally call lane.demod->reset() on gate-close so DPLL/AGC are not left stale across silence gaps (aethersdr#3) - Add lastHdlcFrameStartSampleIndex with 10-sample dedup window to prevent 9x inflation of totalHdlcFrameStarts in A+ mode (aethersdr#4) - Record demod diagnostics from lanes.size()/2 (middle slicer) instead of lane 0 (weakest slicer in A+ mode) (aethersdr#6) - Fix int*int overflow in duplicateWindowSamples: cast first operand to quint64 before multiply (aethersdr#8) - Extract vhfModeLayout() free function; replace two switch copies in configure() and ax25DemodLaneCount() (aethersdr#10) - AfskDemodWrapper<Demod,Result> template replaces two identical structs; using aliases preserve LibmodemAfskDemod / DirewolfAfskDemod names (aethersdr#11) - Delete dead try_demodulate(double, uint8_t&) overload; all callers use the demod_result overload (aethersdr#12) - Add pushLP() helper for LP ring-buffer writes; replaces 4 open-coded stores with manual bump in try_demodulate (aethersdr#13) Not fixed (intentional): aethersdr#5 — phaseOffsetSamples=0 on all A+ lanes: DPLL pull-in makes phase stagger redundant (kPllSearchingInertia=0.50 locks in 4-5 symbols from any starting phase, matching Direwolf behaviour) aethersdr#7 — Normal polarity hard-coded in setModemProfile: pre-existing in main since PR aethersdr#3279; no polarity UI or AppSettings key exists aethersdr#9 — DC-sum normalisation in buildRrcLowpass: correct for envelope detector on IQ baseband; energy norm only valid for matched filter Principle VIII. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
K5PTB
added a commit
to K5PTB/AetherSDR
that referenced
this pull request
Jun 11, 2026
…/shim - Delete default constructor (= delete) to prevent null-vector deref (aethersdr#1) - Remove noexcept from processBlock overrides, buildPrefilter, buildRrcLowpass — allocating ops inside noexcept terminate on OOM (aethersdr#2) - Unconditionally call lane.demod->reset() on gate-close so DPLL/AGC are not left stale across silence gaps (aethersdr#3) - Add lastHdlcFrameStartSampleIndex with 10-sample dedup window to prevent 9x inflation of totalHdlcFrameStarts in A+ mode (aethersdr#4) - Record demod diagnostics from lanes.size()/2 (middle slicer) instead of lane 0 (weakest slicer in A+ mode) (aethersdr#6) - Fix int*int overflow in duplicateWindowSamples: cast first operand to quint64 before multiply (aethersdr#8) - Extract vhfModeLayout() free function; replace two switch copies in configure() and ax25DemodLaneCount() (aethersdr#10) - AfskDemodWrapper<Demod,Result> template replaces two identical structs; using aliases preserve LibmodemAfskDemod / DirewolfAfskDemod names (aethersdr#11) - Delete dead try_demodulate(double, uint8_t&) overload; all callers use the demod_result overload (aethersdr#12) - Add pushLP() helper for LP ring-buffer writes; replaces 4 open-coded stores with manual bump in try_demodulate (aethersdr#13) Not fixed (intentional): aethersdr#5 — phaseOffsetSamples=0 on all A+ lanes: DPLL pull-in makes phase stagger redundant (kPllSearchingInertia=0.50 locks in 4-5 symbols from any starting phase, matching Direwolf behaviour) aethersdr#7 — Normal polarity hard-coded in setModemProfile: pre-existing in main since PR aethersdr#3279; no polarity UI or AppSettings key exists aethersdr#9 — DC-sum normalisation in buildRrcLowpass: correct for envelope detector on IQ baseband; energy norm only valid for matched filter Principle VIII. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ten9876
pushed a commit
that referenced
this pull request
Jun 26, 2026
) (#3799) ## Summary Fixes the weekly **ASan/UBSan** job failure in `cwx_local_keyer_drift_test` (#3740): a heap-use-after-free at `Fixture` teardown. This is a **test-only** lifetime bug — no production code change. ## Root cause `Fixture` members destruct in reverse declaration order. With `keyer` declared **before** `states`, `states` is freed **first**: ```cpp struct Fixture { TestKeyer keyer; // declared first -> destroyed LAST std::vector<int> states; // declared last -> destroyed FIRST (freed) Fixture() { keyer.setOnKeyDownChange([this](bool d){ states.push_back(d?1:0); }); } }; ``` When a test leaves the keyer **key-down** at teardown, the keyer's destructor still fires the callback into the already-freed vector: `~CwxLocalKeyer()` (`CwxLocalKeyer.cpp:48`) → `keyUpIfDown()` (`:235`) → `emitKeyDown(false)` (`:229`) → installed callback → `states.push_back(0)` **into freed memory**. `emitKeyDown` early-returns when the requested state already matches `m_lastEmittedKeyDown`, so only a test that ends **key-down** trips it. `testLateTicksShortenNextWait` starts `"S"`, ticks twice, and never drains or stops — leaving the key down — which is exactly the case the ASan trace points at. ## Fix Reorder the two members so `states` is declared first (destroyed last) and outlives `keyer`. The destructor callback now writes into a live vector. Robust against any future test that leaves the keyer keyed-down. ## Why production is unaffected - `MainWindow` resets the keyer **before** the audio sink it calls into. - The sidetone callback is null-guarded. So no runtime UAF is reachable — this only broke the sanitizer job (`priority: low`). ## Proof — agent automation bridge N/A; this is a sanitizer/unit-test bug The automation bridge drives the running app and can't observe a unit-test teardown UAF, so the proof here is a direct **before → after** ASan run. The keyer's worker thread makes the live test's key-up/key-down state at teardown timing-dependent (hence a *weekly* flake); to make the proof deterministic I replicated the exact synchronous `TestKeyer(spawnWorker=false)` path and the `testLateTicksShortenNextWait` sequence (which leaves `states` at `size=3, cap=4` — matching the CI trace's "12 bytes inside of 16-byte region"): **Before (buggy `states`-freed-first order)** — reproduces the CI stack frame-for-frame: ``` ==ERROR: AddressSanitizer: heap-use-after-free ... WRITE of size 4 #9 AetherSDR::CwxLocalKeyer::emitKeyDown(bool) CwxLocalKeyer.cpp:229 #10 AetherSDR::CwxLocalKeyer::keyUpIfDown() CwxLocalKeyer.cpp:235 #11 AetherSDR::CwxLocalKeyer::~CwxLocalKeyer() CwxLocalKeyer.cpp:48 SUMMARY: AddressSanitizer: heap-use-after-free in std::vector<int>::emplace_back exit=134 ``` **After (fixed `states`-outlives-`keyer` order)** — clean: ``` states=3 key DOWN; teardown fires keyUpIfDown into LIVE states OK: clean teardown exit=0 ``` **Real test, fixed, built with `-fsanitize=address`:** `54 OK, 0 FAIL`, no ASan report, `All tests passed.` ## Related - #3740 (this issue) — `good first issue`, `sanitizer`, `asan-ubsan`, `CW`. Fixes #3740 💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/tmp/AetherSDR-CAT) for WSJT-X, fldigi, N1MM integration. Supports get/set freq, mode, VFO, PTT, split, dump_state, batch commandsTest plan
echo "f" | nc localhost 4532returns frequency in Hz-Wall -Wextra -Wpedantic🤖 Generated with Claude Code