Skip to content

Fix DAX/SSB TX edge sync and isolate DAX TX path#353

Merged
ten9876 merged 3 commits into
aethersdr:mainfrom
pepefrog1234:fix/dax-ssb-sync-pr-20260329
Mar 29, 2026
Merged

Fix DAX/SSB TX edge sync and isolate DAX TX path#353
ten9876 merged 3 commits into
aethersdr:mainfrom
pepefrog1234:fix/dax-ssb-sync-pr-20260329

Conversation

@pepefrog1234

Copy link
Copy Markdown
Contributor

Summary

This PR fixes TX edge-timing regressions that affected both digital DAX TX and traditional SSB TX behavior.

It delivers two concrete outcomes:

  1. DAX TX start/stop behavior remains edge-synchronous and resistant to shared-memory backlog drift.
  2. SSB TX no longer waits on interlock confirmation to start audio, eliminating audible key-down delay and key-up tail.

In addition, this PR includes a detailed engineering report:

  • TX_SYNC_FIX_REPORT.md

Problem

During iterative DAX latency tuning, we observed two classes of issues:

  • DAX path: inconsistent timing at PTT edges under backlog pressure.
  • SSB path: delayed voice start and occasional TX tail when PTT was released.

Root causes were primarily gating/routing related:

  • SSB TX-audio start was effectively tied to interlock progression instead of the local MOX/PTT edge.
  • DAX TX callback traffic was not strictly isolated by TX mode in all paths.
  • Shared-memory TX polling could accumulate stale data under load.

What Changed

1) DAX edge and backlog stabilization

  • Added local TX-intent tracking and gate control (m_txRequested, txAudioGateChanged) to keep pipeline decisions aligned with local key/unkey edges.
  • Hardened interlock handling so local unkey and non-owned TX states force safe TX-off behavior.
  • Tightened shared-memory TX polling/draining strategy and backlog clamping for low-latency behavior.
  • Cleared TX accumulators on TX-off to avoid stale carry-over between bursts.

2) SSB immediate edge-sync restoration

  • Restored immediate TX-audio gating from moxChanged so SSB starts/stops on the local edge (no intentional wait-for-interlock on TX-on).
  • Kept interlock-driven gate handling as a TX-off safety fallback.

3) DAX/SSB source isolation

  • Enforced DAX callback guard so DAX TX audio is only accepted when DAX TX mode is active.
  • Prevents DAX-origin callback traffic from contaminating SSB routing.

Files Included

Code changes:

  • src/core/AudioEngine.cpp
  • src/core/AudioEngine.h
  • src/core/VirtualAudioBridge.cpp
  • src/gui/MainWindow.cpp
  • src/gui/MainWindow.h
  • src/models/RadioModel.cpp
  • src/models/RadioModel.h
  • src/models/TransmitModel.cpp

Documentation:

  • TX_SYNC_FIX_REPORT.md

Validation

  • Rebased onto latest main.
  • Built successfully with:
    • cmake --build build-new -j8

Scope Control

This PR intentionally contains only the TX sync fix code and the accompanying technical report. Unrelated local workspace changes were not included.

@ten9876

ten9876 commented Mar 29, 2026

Copy link
Copy Markdown
Collaborator

Merge Plan

Thanks for this PR — the DAX edge-sync and SSB delay fixes are well-documented and the engineering report is thorough. CI passes clean. Here's our plan for merging.

Merge approach

We'll rebase onto current main ourselves in a testing branch. Several files have diverged since this PR was opened (MIDI controller support, FlexControl, FreeDV spots, USB cables all landed today in the v0.7.9 cycle), so we expect conflicts in MainWindow.cpp and AudioEngine.cpp.

Conflict resolution plan

File-by-file analysis:

1. AudioEngine.h — moderate conflicts

  • PR adds: setDaxTxMode(), setDaxTxUseRadioRoute(), setTransmitting(), clearTxAccumulators(), m_daxPreTxBuffer, m_daxTxUseRadioRoute, PCC_DAX_REDUCED, TX_SAMPLES_PER_PACKET, TX_PCM_BYTES_PER_PACKET
  • Our changes: setPcMicGain(), pcMicLevelChanged signal, m_pcMicGain, mic metering members
  • Conflict resolution: Take both — PR adds DAX/TX members, ours add mic metering members. No overlap.
  • Watch out: PR replaces #include <cmath> with #include <algorithm>. We need BOTH — <cmath> is used by std::log10 in mic metering (added in v0.7.8). Must keep both includes.

2. AudioEngine.cpp — significant conflicts

  • PR changes:
    • Expands feedDaxTxAudio() with two routing modes (radio-native vs low-latency)
    • Adds setDaxTxMode(), setTransmitting(), setDaxTxUseRadioRoute() methods
    • Clears TX accumulators on unkey
  • Our changes:
    • onTxAudioReady() — removed separate VOX path, added PC mic gain, added mic metering, changed Opus frame size to 240, changed VITA-49 header fields
    • Added processBnr() diagnostic logging (already reverted)
  • Conflict resolution: The onTxAudioReady() function will be the main conflict zone. PR's changes are in feedDaxTxAudio() and new methods (after onTxAudioReady), while ours are inside onTxAudioReady(). Should be resolvable by keeping both sets of changes in their respective functions.
  • Critical check: PR's setTransmitting() clears accumulators including m_txFloatAccumulator. Our Opus path uses m_opusTxAccumulator — verify PR doesn't clear that, or add it to the clear list.

3. MainWindow.cpp — significant conflicts

  • PR changes:
    • New moxChanged handler for immediate SSB TX audio gating
    • New txAudioGateChanged handler (TX-off safety fallback)
    • DAX bridge TX callback mode guard
  • Our changes (v0.7.8-v0.7.9):
    • MIDI controller wiring (50+ params, registerMidiParams())
    • FlexControl wiring (tuneSteps, buttonPressed)
    • FreeDV client creation and spot forwarding
    • USB Cables dialog wiring
    • MIDI Mapping menu entry
    • PC mic gain wiring (setPcMicGain, pcMicLevelChanged)
  • Conflict resolution: PR touches the TX state handling section (~line 450-500 area). Our changes are mostly in different sections (MIDI at end of file, FlexControl at ~line 1050, FreeDV at ~line 230, menu items at ~line 1530). The moxChanged handler overlap is the key conflict — we need to merge PR's immediate-gating logic with our existing moxChanged handler.

4. MainWindow.h — minor

  • PR adds: one member (likely DAX-related)
  • Our changes: MidiControlManager, FlexControlManager, FreeDvClient members
  • Conflict resolution: Take both.

5. RadioModel.cpp — moderate conflicts

  • PR adds: m_txRequested tracking, txAudioGateChanged signal, interlock fallback logic
  • Our changes: UsbCableModel status routing, usb_cable handler, commandReady wiring
  • Conflict resolution: PR touches the interlock handler section. Our changes are in the status routing section (different area). Should merge cleanly after rebase. Verify the interlock handler doesn't conflict with our FlexControl/MIDI MOX toggle path.

6. RadioModel.h — minor

  • PR adds: txAudioGateChanged signal, m_txRequested, m_txAudioGate members
  • Our changes: UsbCableModel member and accessor
  • Conflict resolution: Take both.

7. TransmitModel.cpp — minor

  • PR changes: Adjusts setTransmitting() behavior
  • Our changes: None in this file since v0.7.7
  • Conflict resolution: Take PR's changes directly.

8. VirtualAudioBridge.cpp — no conflicts

  • PR changes: Reduced TX poll interval, backlog clamping
  • Our changes: None in this file
  • Conflict resolution: Take PR's changes directly.

9. TX_SYNC_FIX_REPORT.md — housekeeping

  • Move to docs/TX_SYNC_FIX_REPORT.md instead of repo root
  • Or remove from the merge and reference the PR description instead

Testing plan

After rebase, test these scenarios:

  1. SSB voice TX (hardware mic) — PTT press should produce immediate audio, no delay. Release should cut cleanly.
  2. SSB voice TX (PC mic) — same as above, with Opus encoding on remote_audio_tx
  3. DAX TX (WSJT-X/VARA) — digital mode TX via virtual audio should start/stop cleanly at PTT edges
  4. VOX — voice-triggered TX should still work (mic audio streams during RX for met_in_rx=1)
  5. MIDI MOX toggle — our new MIDI tx.mox parameter should work with the new TX gating
  6. FlexControl MOX button — same as above
  7. DVK recording — verify recording works with both hardware and PC mic (related to Looks like DVK got broken in 0.7.8 #354)
  8. BNR — verify RX audio path unaffected

Timeline

We'll create a test/pr353-dax-ssb-sync branch, rebase, resolve conflicts, and test. Will report back here with results.

73, Jeremy KK7GWY & Claude (AI dev partner)

ten9876 added a commit that referenced this pull request Mar 29, 2026
Co-Authored-By: pepefrog1234 <pepefrog1234@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 merged commit 5cba034 into aethersdr:main Mar 29, 2026
3 checks passed
@ten9876

ten9876 commented Mar 29, 2026

Copy link
Copy Markdown
Collaborator

Merged in 08ae00e. Tested and verified:

  • SSB voice TX: immediate edge timing, no delay on key-down, clean release
  • WSJT-X FT8 DAX TX: working with radio-native route (dax=1, default)
  • VOX: working
  • DVK playback: working
  • BNR: unaffected

Adjustments made during merge:

  • Default DaxTxLowLatency changed from True to False — standard DAX (WSJT-X, VARA, fldigi) works out of the box. FreeDV/RADE users opt in via Settings → "Low-Latency DAX (FreeDV)"
  • Menu label shortened to "Low-Latency DAX (FreeDV)"
  • TX_SYNC_FIX_REPORT.md moved to docs/

Thanks for the thorough fix and documentation, @pepefrog1234!

73, Jeremy KK7GWY & Claude (AI dev partner)

ten9876 added a commit that referenced this pull request Mar 29, 2026
v0.7.10 highlights:

- **PR #353 merged**: DAX/SSB TX edge sync fix — immediate SSB TX
  audio gating, DAX backlog stabilization, low-latency DAX route
  option for FreeDV
- **MIDI CW keying**: straight key, iambic dit/dah paddle, and PTT
  via Gate param type (#295)
- **Spot hover tooltips**: callsign, frequency, source, spotter,
  comment, spotted time on hover
- **Spot trigger on click**: sends 'spot trigger <index>' to radio
  for external logging software (#341)
- **Spot lines stop at label**: no visual clutter above spot labels
  in FFT (#337)
- **Single-click-to-tune**: configurable View menu toggle (#342)
- **SpotHub cross-band tuning**: spot click now uses tuneAndRecenter
  so band changes work (#352)
- **3v/4v pan layouts**: 3 and 4 vertical pan stacking options (#312)
- **Line out volume fix**: master slider controls radio line out when
  PC Audio is disabled (#244)
- **Audio output preserved on profile change**: PcAudioEnabled check
  prevents profile load from switching to PC audio (#336)
- **CW decode after profile change**: deferred re-check restarts
  decoder when mode status arrives late (#305)
- **XVTR band stack fix**: added 2m, 1.25m, 440, 33cm, 23cm to
  frequency lookup table (#346)
- **Full model reset on disconnect**: prevents stale capabilities
  carrying over between different radio models (#359)
- **Bundled RtMidi**: MIDI support on all platforms without external
  package dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 mentioned this pull request Mar 29, 2026
2 tasks
ten9876 added a commit that referenced this pull request May 22, 2026
… PR 8) (#2953)

Eight docs leave the top-level docs/ tree, partitioned by purpose:

docs/archive/ — historical planning/session notes, kept for grep
context:
  - issue-2136-feasibility-report.md     (PR #2759, shipped)
  - issue-2136-implementation-plan.md    (PR #2759, shipped)
  - TX_SYNC_FIX_REPORT.md                 (PR #353, RadioModel.cpp still
    references the report by name)
  - aether-dsp-session-notes.md          (#796 maintainer notebook)

docs/data/ — reference fixtures (not bundled, not runtime):
  - SSDR.settings + SmartSDR.exe.config   (SmartSDR parity reference)
  - rxapplet_mode_settings.csv            (per-mode RX control research)
  - vfo_mode_filters.csv                  (VFO filter-preset research;
    VfoWidget.cpp comment updated to new path)

Kept in docs/ (still active):
  - audio_test_plan.md  (17 KB, edited 2026-04-29, ongoing test plan)
  - profile-import-export-manual-checklist.md (PR #2641 QA checklist)

Both new dirs include a one-paragraph README explaining the convention
so a future maintainer doesn't have to guess what belongs where.

Cross-references audited:
  - AppSettings.h:9 mentions "SmartSDR's SSDR.settings" as a concept,
    not a path — no change.
  - RadioModel.cpp:3991 references TX_SYNC_FIX_REPORT by name — no
    change, file is findable via grep/find.
  - VfoWidget.cpp:3324 carried a literal "docs/vfo_mode_filters.csv"
    path — updated to docs/data/.

Verified with a clean local Release build (cmake --build … --target
AetherSDR exits 0).

After this PR, docs/ contains 18 markdown files (architecture/user
docs), the assets/ subdir from PR 5, and the new archive/ + data/
subdirs — matches the acceptance criteria in the cleanup plan.

Principle I.

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…sdr#2933, PR 8) (aethersdr#2953)

Eight docs leave the top-level docs/ tree, partitioned by purpose:

docs/archive/ — historical planning/session notes, kept for grep
context:
  - issue-2136-feasibility-report.md     (PR aethersdr#2759, shipped)
  - issue-2136-implementation-plan.md    (PR aethersdr#2759, shipped)
  - TX_SYNC_FIX_REPORT.md                 (PR aethersdr#353, RadioModel.cpp still
    references the report by name)
  - aether-dsp-session-notes.md          (aethersdr#796 maintainer notebook)

docs/data/ — reference fixtures (not bundled, not runtime):
  - SSDR.settings + SmartSDR.exe.config   (SmartSDR parity reference)
  - rxapplet_mode_settings.csv            (per-mode RX control research)
  - vfo_mode_filters.csv                  (VFO filter-preset research;
    VfoWidget.cpp comment updated to new path)

Kept in docs/ (still active):
  - audio_test_plan.md  (17 KB, edited 2026-04-29, ongoing test plan)
  - profile-import-export-manual-checklist.md (PR aethersdr#2641 QA checklist)

Both new dirs include a one-paragraph README explaining the convention
so a future maintainer doesn't have to guess what belongs where.

Cross-references audited:
  - AppSettings.h:9 mentions "SmartSDR's SSDR.settings" as a concept,
    not a path — no change.
  - RadioModel.cpp:3991 references TX_SYNC_FIX_REPORT by name — no
    change, file is findable via grep/find.
  - VfoWidget.cpp:3324 carried a literal "docs/vfo_mode_filters.csv"
    path — updated to docs/data/.

Verified with a clean local Release build (cmake --build … --target
AetherSDR exits 0).

After this PR, docs/ contains 18 markdown files (architecture/user
docs), the assets/ subdir from PR 5, and the new archive/ + data/
subdirs — matches the acceptance criteria in the cleanup plan.

Principle I.

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants