Skip to content

[dax] Add support for DAX2 and TCI multi-stream routing#2140

Merged
ten9876 merged 1 commit intoten9876:mainfrom
jensenpat:aether/dax2-support
Apr 29, 2026
Merged

[dax] Add support for DAX2 and TCI multi-stream routing#2140
ten9876 merged 1 commit intoten9876:mainfrom
jensenpat:aether/dax2-support

Conversation

@jensenpat
Copy link
Copy Markdown
Collaborator

@jensenpat jensenpat commented Apr 29, 2026

image

Summary

This PR updates the DAX/TCI audio path for the newer FlexRadio 4.2.18 firmware behavior while preserving compatibility with earlier firmware status formats.

  • Filter DAX RX/IQ stream status by client_handle when the radio reports stream ownership, while accepting ownership-less status lines from older firmware.
  • Track DAX RX, DAX IQ, DAX TX, and DAX mic stream status in RadioModel so the logs identify stream IDs, ownership, DAX channel, slice, active/TX state, and removal events.
  • Handle radio stream removal messages by unregistering DAX/IQ streams and clearing TCI DAX stream placeholders.
  • Advertise TCI receivers as contiguous owned-slice indexes (0..N-1) rather than raw Flex slice IDs, with a raw-slice fallback for older clients.
  • Route TCI RX audio by the slice that owns the incoming DAX channel, and honor per-client audio_start:<receiver> selection so multi-slice WSJT-X instances receive the intended audio stream.
  • Add focused diagnostics for first DAX VITA packets, TCI receiver maps, DAX RX audio delivery, DAX TX route selection, and DAX channel reassertion.

Why

Firmware 4.2.18 reports DAX stream ownership more explicitly, and multiple clients can now expose DAX-related stream status for the same DAX channel. The previous path could register another client's DAX stream or expose raw Flex slice IDs through TCI, which breaks clients like WSJT-X when this AetherSDR instance owns slice 1 but advertises only one TCI receiver.

The fix makes stream ownership explicit where the firmware provides it, keeps older firmware working when client_handle is absent, and keeps TCI receiver numbering aligned with what clients actually see in trx_count.

Compatibility

Older firmware remains supported:

  • DAX stream status without client_handle is still accepted.
  • TCI commands that reference raw Flex slice IDs still fall back to slice-id lookup after contiguous receiver lookup.
  • Receiver-less audio_start keeps the existing all-receiver audio behavior.
  • DAX channel routing falls back to channel - 1 if a slice-to-DAX mapping is unavailable.

Multi-Slice Behavior

TCI now treats the current owned slice list as the receiver namespace:

  • First owned slice is TCI receiver 0.
  • Second owned slice is TCI receiver 1.
  • Signal broadcasts compute the receiver index live, so slice removal or reordering does not leave stale captured indexes.
  • DAX RX audio is delivered only to clients that requested the mapped receiver via audio_start:<receiver>, while legacy clients still receive all enabled audio.

Validation

  • cmake --build build -j10
  • ctest --test-dir build --output-on-failure -j10 reports no registered tests in this build tree.
  • Manual WSJT-X validation by @jensenpat:
    • Confirmed DAX TX/RX QSOs.
    • Confirmed TCI TX/RX QSOs.
    • Confirmed multi-slice TCI operation using WSJT-X TCI1 and TCI2 drivers.

Notes

The added logging is intentionally targeted around DAX/TCI stream ownership and first-packet/audio-routing diagnostics. It should make future firmware behavior changes easier to identify without changing the core protocol behavior for older radios.

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat

@jensenpat jensenpat changed the title Add DAX-aware TCI stream routing [dax] Add support for DAX2 and TCI stream routing Apr 29, 2026
@jensenpat jensenpat marked this pull request as ready for review April 29, 2026 03:51
@jensenpat jensenpat requested a review from ten9876 as a code owner April 29, 2026 03:51
@jensenpat jensenpat changed the title [dax] Add support for DAX2 and TCI stream routing [dax] Add support for DAX2 and TCI multi-slice stream routing Apr 29, 2026
@jensenpat jensenpat changed the title [dax] Add support for DAX2 and TCI multi-slice stream routing [dax] Add support for DAX2 and TCI stream routing Apr 29, 2026
@jensenpat jensenpat changed the title [dax] Add support for DAX2 and TCI stream routing [dax] Add support for DAX2 and TCI multi-stream routing Apr 29, 2026
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jensenpat for tackling this — the DAX ownership filtering and contiguous TCI receiver mapping are exactly the right approach for firmware 4.2.18 compatibility, and the backwards-compat fallbacks are well thought through. The manual WSJT-X validation across both DAX and TCI paths is appreciated.

A few things worth addressing before merge:

1. Duplicated helper functions across three files

parseStatusHandle() and streamStatusBelongsToUs() are copy-pasted identically into both TciServer.cpp and MainWindow.cpp. A third near-duplicate parseStreamToken() lives in RadioModel.cpp. Similarly, tciTrxForSlice() is duplicated between TciProtocol.cpp (taking a QList<SliceModel*>) and TciServer.cpp (taking a RadioModel*).

These should live in one place — either a small shared header or on RadioModel itself. Duplicated parsing logic is a maintenance risk if the handle format evolves with future firmware.

2. Duplicate log lines at registration

In TciServer.cpp around the DAX RX stream registration, the new code adds a qCDebug(lcCat) log immediately followed by the existing qCInfo(lcCat) that logs the same event:

qCDebug(lcCat) << "TCI: registered DAX RX stream"
               << "0x" + QString::number(streamId, 16)
               << "for channel" << ch;
qCInfo(lcCat) << "TCI: registered DAX RX stream" << Qt::hex << streamId
              << "for channel" << ch << "(#1331)";

One of these should be removed — the debug line is sufficient for diagnostics and the info line already existed.

3. const_cast in tciTrxForSlice

Both copies of tciTrxForSlice use const_cast<SliceModel*>(slice) to call QList::indexOf. Since slices() returns QList<SliceModel*> (non-const pointers), and the parameter is const SliceModel*, the cast is technically needed — but it would be cleaner to accept SliceModel* directly (both callers already have non-const pointers) or use std::find to avoid the cast.

4. m_rxAudioPackets incremented but timer never started

TciServer.h adds m_rxAudioLogTimer, m_rxAudioPackets, and m_rxAudioFramesSent, and onDaxAudioReady() increments m_rxAudioPackets. However, I don't see where m_rxAudioLogTimer is started or where these counters are ever read/logged. If the periodic summary log was intended, it looks like it didn't make it into the diff. If it's scaffolding for future work, consider removing the unused members until they're wired up — dead state is confusing.

5. MainWindow.cpp changes are borderline out-of-scope

The MainWindow.cpp diff adds parseStatusHandle/streamStatusBelongsToUs helpers plus rewires sliceAddedwireSlice to use contiguous TCI indexes. The TCI rewiring makes sense here, but the status-handle parsing helpers duplicate TciServer.cpp. If MainWindow doesn't use them yet (they're only defined, not called from any visible diff context), they could be deferred until actually needed.

6. Minor: m_daxStreamDebug map grows without bound

RadioModel.h adds QMap<quint32, DaxStreamDebugState> m_daxStreamDebug for tracing. The streamStatusRemoved() helper exists but I don't see cleanup code that erases entries from this map when streams are removed. Over a long session with many stream create/destroy cycles, this could grow. Consider erasing entries on removal.


Overall the core logic — ownership filtering, contiguous receiver mapping, per-client audio_start:<receiver> routing — is solid and well-motivated. The compatibility fallbacks are careful. The main ask is to consolidate the duplicated helpers and clean up the unused timer/counter scaffolding. Nice work on a tricky firmware compat area.

@ten9876 ten9876 merged commit e49875b into ten9876:main Apr 29, 2026
5 checks passed
aethersdr-agent Bot added a commit that referenced this pull request Apr 29, 2026
…CI receiver-index policy (#2142)

- Lift parseStatusHandle() and streamStatusBelongsToUs() into shared
  StreamStatus.h; remove duplicate copies from MainWindow.cpp and
  TciServer.cpp.  Alias parseStreamToken() in RadioModel.cpp to call
  the shared parseStatusHandle().
- Move tciTrxForSlice() to TciProtocol as a public static method,
  unifying the two slightly different signatures from TciServer.cpp
  and TciProtocol.cpp.
- Annotate m_daxTxClientHandle as diagnostics-only (set but never
  consulted in routing logic).
- Add docs/tci-receivers.md documenting the contiguous 0..N-1
  receiver-index policy introduced in #2140.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
ten9876 added a commit that referenced this pull request Apr 29, 2026
Community-driven release.  WAVE Phase 2 visualization (#2124),
DAX-aware TCI multi-stream routing for FlexRadio firmware 4.2.18
(#2140), TCXO frequency-offset calibration (#2119), VFO marker
tri-state UX (#2141), v4.2.18 discovery beacon parsing (#2138).
Bug fixes from the community: r8b heap corruption (#2114, NF0T),
serial PTT triple-fix (#2125, chibondking), slice-audio mute on
band change (#2128, jensenpat), CWX Live toggle (#2122, jensenpat),
connect-radio dialog polish (#2121, jensenpat).

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
ten9876 pushed a commit that referenced this pull request Apr 30, 2026
…CI receiver-index policy (#2142) (#2145)

- Lift parseStatusHandle() and streamStatusBelongsToUs() into shared
  StreamStatus.h; remove duplicate copies from MainWindow.cpp and
  TciServer.cpp.  Alias parseStreamToken() in RadioModel.cpp to call
  the shared parseStatusHandle().
- Move tciTrxForSlice() to TciProtocol as a public static method,
  unifying the two slightly different signatures from TciServer.cpp
  and TciProtocol.cpp.
- Annotate m_daxTxClientHandle as diagnostics-only (set but never
  consulted in routing logic).
- Add docs/tci-receivers.md documenting the contiguous 0..N-1
  receiver-index policy introduced in #2140.

Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <[email protected]>
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