Skip to content

TCI Interface #528

Description

@opalito

Request preparation

  • I used an AI assistant to help structure this request
  • I checked for existing issues covering the same feature

What would you like?

Feature Request: TCI (Transceiver Control Interface) server for integration with logging and digital mode software

What

Add a built-in TCI server to AetherSDR that exposes radio state (frequency, mode, S-meter, TX status, audio streams, IQ data, spots) over the standard TCI protocol (WebSocket on a configurable TCP port, default 40001).

From the user's perspective: you go to Settings → TCI (or a new tab in Radio Setup), enable the TCI server, and any TCI-capable logging or digital mode software on the same machine (or LAN) can connect to AetherSDR — getting CAT control, RX/TX audio, IQ streaming, CW macros, and spot injection — all over a single WebSocket connection, with zero additional configuration of virtual serial ports, DAX audio pipes, or PulseAudio routing.

Why

Problem 1 — Too many moving parts for a simple integration

Today, connecting AetherSDR to a contest logger (e.g. N1MM Logger+) or digital mode software (e.g. JTDX, MSHV, fldigi) requires setting up multiple independent channels:

  1. CAT control — via rigctld TCP or a PTY virtual serial port
  2. RX audio — via DAX PulseAudio pipe modules (or JACK routing)
  3. TX audio — via a second DAX pipe in the reverse direction
  4. Spots — no unified mechanism; each app has its own cluster setup

Each of these is a separate failure point. If one breaks (PulseAudio module doesn't load, PTY disappears, rigctld port conflict), the whole chain falls apart. Linux users are especially affected because DAX on Linux is inherently more fragile than on Windows (pipe modules, PipeWire compatibility, buffer sizing).

Problem 2 — Growing TCI ecosystem

TCI has become a de facto standard beyond the Expert Electronics / SunSDR world. Software that already supports TCI natively:

  • Log4OM v2 — full TCI support (CAT, audio, IQ, spots on panorama)
  • Swisslog — TCI as a transceiver interface option
  • MSHV — TCI for rig control and audio
  • JTDX — TCI support
  • SDC (by Yuri) — TCI client for FlexRadio (the existing bridge solution)
  • HamDeck — Stream Deck automation via TCI
  • TCI-Hamlib Adapter (DL3NEY) — bridges TCI ↔ Hamlib for WSJT-X/fldigi/CQRLog
  • Various ESP32/Arduino projects — hardware controllers via TCI

By offering a TCI server, AetherSDR becomes a first-class citizen in this ecosystem without requiring each application to add FlexRadio-specific API support.

Problem 3 — SDC is the current workaround, but it's redundant

Operators who want TCI with a FlexRadio currently run SDC (by Yuri/ten9876) as a bridge between the FlexRadio API and TCI clients. With AetherSDR already connected to the radio and maintaining full state, having a separate SDC instance is redundant — it's a second client consuming radio resources. A built-in TCI server eliminates this duplication.

How Other Clients Do It

ExpertSDR3 (Expert Electronics)

ExpertSDR3 is the reference TCI implementation. The SDR software acts as the TCI server — any number of clients connect via WebSocket to ws://<ip>:40001. The settings panel has a simple enable/disable toggle and port configuration. On connection, the server sends an initialization sequence (PROTOCOL:, DEVICE:, VFO:, MODULATION:, RX_ENABLE:, etc.) so the client gets the complete radio state immediately. Audio and IQ are streamed as binary WebSocket frames.

SDC (by Yuri — SmartSDR for Linux)

SDC connects to the FlexRadio via the SmartSDR TCP/UDP protocol and then exposes a TCI server on a local port. This is exactly the architecture proposed here, but built into AetherSDR rather than running as a separate application.

TCI-Hamlib Adapter (DL3NEY)

For software that doesn't speak TCI natively (like WSJT-X), Florian DL3NEY wrote tciadapter — a Go program that translates between TCI and Hamlib rigctld protocol. This demonstrates the demand for bridging between ecosystems and also shows that TCI → Hamlib is a solved problem at the adapter level.

Suggested Behavior

Configuration (Settings → TCI or Radio Setup → TCI tab)

  • Enable TCI server — checkbox (default: off)
  • Listen port — spinbox, default 40001
  • Listen address — dropdown: 127.0.0.1 (localhost only) / 0.0.0.0 (all interfaces)
  • Audio sample rate — dropdown: 48000 / 24000 Hz (for RX_AUDIO_STREAM / TX_AUDIO_STREAM)
  • IQ sample rate — dropdown: 48000 / 96000 / 192000 Hz (for IQ_STREAM, if enabled)
  • IQ enable — checkbox (default: off, because IQ streaming uses significant bandwidth)
  • Connected clients — read-only list showing currently connected TCI clients (IP, software name if provided)

Startup behavior

  1. User enables TCI server in settings
  2. AetherSDR opens a WebSocket server (using QWebSocketServer from Qt WebSockets module) on the configured port
  3. Status bar shows a TCI indicator (e.g. "TCI: 40001" or a small icon) when active, with client count badge

Connection sequence (what happens when a client connects)

  1. Client opens WebSocket to ws://<ip>:<port>
  2. AetherSDR sends the initialization burst:
    PROTOCOL:ExpertSDR3,1.5;DEVICE:FlexRadio FLEX-8600;RECEIVE_ONLY:false;TRXS_COUNT:1;CHANNELS_COUNT:2;VFO:0,0,14225000;IF:0,0,0;MODULATION:0,USB;RX_ENABLE:0,true;TX_ENABLE:0,true;DRIVE:50;TUNE_DRIVE:10;IQ_SAMPLERATE:48000;AUDIO_SAMPLERATE:48000;CW_MACROS_SPEED:25;...READY;
    
  3. Client can then send commands (VFO:0,0,14226000; to change frequency, TX_ENABLE:0,true; for PTT, etc.)
  4. AetherSDR translates TCI commands to FlexRadio API calls and vice versa

Runtime behavior

TCI command/event AetherSDR action
VFO:trx,channel,freq; → slice tune <freq_mhz>
MODULATION:trx,mode; → slice set mode=
TX_ENABLE:trx,true; → xmit 1
DRIVE:value; → transmit set rfpower=
RX_AUDIO_STREAM (binary) ← Feed from AudioEngine (same data as DAX RX)
TX_AUDIO_STREAM (binary) → Feed into AudioEngine TX path
IQ_STREAM (binary) ← Feed from DAX IQ channel (when #124 is implemented)
SPOT:callsign,mode,freq,...; → Forward to SpotHub for display on spectrum
CW_MACROS_SPEED:wpm; → cw wpm
CW_MSG:text; → cw send
Slice status change → Broadcast VFO:, MODULATION:, etc. to all connected clients
Meter update (S-meter) → Broadcast RX_SMETER:trx,channel,value;
TX state change → Broadcast TRX:trx,true/false;

Audio streaming

  • RX audio: AetherSDR taps the same audio pipeline that feeds DAX RX channels (PCC 0x0123 int16 data or 0x03E3 float32 data), converts to the TCI binary frame format (little-endian float32, per the TCI spec), and sends as WebSocket binary messages
  • TX audio: Client sends binary frames → AetherSDR feeds them into the TX audio path (same routing as DAX TX)

Multi-slice mapping

The FlexRadio can have multiple slices. TCI uses trx (transceiver index) and channel (sub-receiver). Suggested mapping:

  • Slice A → TRX 0, channel 0
  • Slice B → TRX 0, channel 1
  • (If Multi-Flex with 2 independent TX) → TRX 1

Applet integration

A small TCI toggle button could be added to the AppletPanel row (alongside ANLG, RX, TX, etc.) that opens a minimal TCI status applet showing: server on/off, port, connected clients count, and data rates.

Protocol Hints

FlexRadio side (already implemented in AetherSDR)

AetherSDR already maintains all the state needed to serve TCI:

  • Frequency/mode/filterSliceModel (RF_frequency, mode, filter_lo, filter_hi)
  • TX stateTransmitModel (xmit, rfpower, tunepower)
  • S-meterMeterModel (LEVEL meter from SLC source)
  • Audio RXAudioEngine / PipeWireAudioBridge DAX pipeline
  • Audio TXAudioEngine TX path
  • Spots — SpotHub system
  • CWcw send, cw wpm commands via RadioConnection

TCI side (needs implementation)

  • WebSocket server — Qt provides QWebSocketServer in the Qt::WebSockets module (add find_package(Qt6 COMPONENTS WebSockets) to CMakeLists.txt)
  • TCI protocol parser — ASCII commands terminated by ;, very simple to parse (split on : for command name, split on , for arguments)
  • Binary audio frames — TCI audio uses a small header (type, sample rate, codec, CRC, length) followed by float32 PCM samples in little-endian byte order
  • Reference implementation — Full protocol spec at github.com/ExpertSDR3/TCI, protocol version 1.5+ recommended
  • Reference Python clientgithub.com/ars-ka0s/eesdr-tci — useful for testing

Implementation phases (suggested)

  1. Phase 1 — Core CAT: WebSocket server + VFO/mode/filter/TX commands + status broadcast → enough for loggers
  2. Phase 2 — Audio: RX audio streaming + TX audio input → enough for JTDX/MSHV/fldigi
  3. Phase 3 — Extended: IQ streaming, spot injection, CW macros, S-meter broadcast → full feature parity
  4. Phase 4 — Settings UI: Radio Setup tab with configuration options

Additional context

  • The TCI protocol is open source and documented (MIT license)
  • This feature would make AetherSDR the first FlexRadio client with built-in TCI — even SmartSDR for Windows doesn't have it
  • On Linux, where DAX audio routing is inherently more complex than on Windows, TCI's all-in-one-WebSocket approach is especially valuable
  • Since AetherSDR already has RigctlServer as a precedent for serving a third-party protocol from internal state, TCI follows the same architectural pattern — just with a different wire format and richer feature set
# Feature Request: TCI (Transceiver Control Interface) server for integration with logging and digital mode software

What

Add a built-in TCI server to AetherSDR that exposes radio state (frequency, mode, S-meter, TX status, audio streams, IQ data, spots) over the standard [TCI protocol](https://github.com/ExpertSDR3/TCI) (WebSocket on a configurable TCP port, default 40001).

From the user's perspective: you go to Settings → TCI (or a new tab in Radio Setup), enable the TCI server, and any TCI-capable logging or digital mode software on the same machine (or LAN) can connect to AetherSDR — getting CAT control, RX/TX audio, IQ streaming, CW macros, and spot injection — all over a single WebSocket connection, with zero additional configuration of virtual serial ports, DAX audio pipes, or PulseAudio routing.

Why

Problem 1 — Too many moving parts for a simple integration

Today, connecting AetherSDR to a contest logger (e.g. N1MM Logger+) or digital mode software (e.g. JTDX, MSHV, fldigi) requires setting up multiple independent channels:

  1. CAT control — via rigctld TCP or a PTY virtual serial port
  2. RX audio — via DAX PulseAudio pipe modules (or JACK routing)
  3. TX audio — via a second DAX pipe in the reverse direction
  4. Spots — no unified mechanism; each app has its own cluster setup

Each of these is a separate failure point. If one breaks (PulseAudio module doesn't load, PTY disappears, rigctld port conflict), the whole chain falls apart. Linux users are especially affected because DAX on Linux is inherently more fragile than on Windows (pipe modules, PipeWire compatibility, buffer sizing).

Problem 2 — Growing TCI ecosystem

TCI has become a de facto standard beyond the Expert Electronics / SunSDR world. Software that already supports TCI natively:

  • Log4OM v2 — full TCI support (CAT, audio, IQ, spots on panorama)
  • Swisslog — TCI as a transceiver interface option
  • MSHV — TCI for rig control and audio
  • JTDX — TCI support
  • SDC (by Yuri) — TCI client for FlexRadio (the existing bridge solution)
  • HamDeck — Stream Deck automation via TCI
  • TCI-Hamlib Adapter (DL3NEY) — bridges TCI ↔ Hamlib for WSJT-X/fldigi/CQRLog
  • Various ESP32/Arduino projects — hardware controllers via TCI

By offering a TCI server, AetherSDR becomes a first-class citizen in this ecosystem without requiring each application to add FlexRadio-specific API support.

Problem 3 — SDC is the current workaround, but it's redundant

Operators who want TCI with a FlexRadio currently run SDC (by Yuri/ten9876) as a bridge between the FlexRadio API and TCI clients. With AetherSDR already connected to the radio and maintaining full state, having a separate SDC instance is redundant — it's a second client consuming radio resources. A built-in TCI server eliminates this duplication.

How Other Clients Do It

ExpertSDR3 (Expert Electronics)

ExpertSDR3 is the reference TCI implementation. The SDR software acts as the TCI server — any number of clients connect via WebSocket to ws://<ip>:40001. The settings panel has a simple enable/disable toggle and port configuration. On connection, the server sends an initialization sequence (PROTOCOL:, DEVICE:, VFO:, MODULATION:, RX_ENABLE:, etc.) so the client gets the complete radio state immediately. Audio and IQ are streamed as binary WebSocket frames.

SDC (by Yuri — SmartSDR for Linux)

SDC connects to the FlexRadio via the SmartSDR TCP/UDP protocol and then exposes a TCI server on a local port. This is exactly the architecture proposed here, but built into AetherSDR rather than running as a separate application.

TCI-Hamlib Adapter (DL3NEY)

For software that doesn't speak TCI natively (like WSJT-X), Florian DL3NEY wrote tciadapter — a Go program that translates between TCI and Hamlib rigctld protocol. This demonstrates the demand for bridging between ecosystems and also shows that TCI → Hamlib is a solved problem at the adapter level.

Suggested Behavior

Configuration (Settings → TCI or Radio Setup → TCI tab)

  • Enable TCI server — checkbox (default: off)
  • Listen port — spinbox, default 40001
  • Listen address — dropdown: 127.0.0.1 (localhost only) / 0.0.0.0 (all interfaces)
  • Audio sample rate — dropdown: 48000 / 24000 Hz (for RX_AUDIO_STREAM / TX_AUDIO_STREAM)
  • IQ sample rate — dropdown: 48000 / 96000 / 192000 Hz (for IQ_STREAM, if enabled)
  • IQ enable — checkbox (default: off, because IQ streaming uses significant bandwidth)
  • Connected clients — read-only list showing currently connected TCI clients (IP, software name if provided)

Startup behavior

  1. User enables TCI server in settings
  2. AetherSDR opens a WebSocket server (using QWebSocketServer from Qt WebSockets module) on the configured port
  3. Status bar shows a TCI indicator (e.g. "TCI: 40001" or a small icon) when active, with client count badge

Connection sequence (what happens when a client connects)

  1. Client opens WebSocket to ws://<ip>:<port>
  2. AetherSDR sends the initialization burst:
    PROTOCOL:ExpertSDR3,1.5;
    DEVICE:FlexRadio FLEX-8600;
    RECEIVE_ONLY:false;
    TRXS_COUNT:1;
    CHANNELS_COUNT:2;
    VFO:0,0,14225000;
    IF:0,0,0;
    MODULATION:0,USB;
    RX_ENABLE:0,true;
    TX_ENABLE:0,true;
    DRIVE:50;
    TUNE_DRIVE:10;
    IQ_SAMPLERATE:48000;
    AUDIO_SAMPLERATE:48000;
    CW_MACROS_SPEED:25;
    ...
    READY;
    
  3. Client can then send commands (VFO:0,0,14226000; to change frequency, TX_ENABLE:0,true; for PTT, etc.)
  4. AetherSDR translates TCI commands to FlexRadio API calls and vice versa

Runtime behavior

TCI command/event AetherSDR action
VFO:trx,channel,freq; slice tune <id> <freq_mhz>
MODULATION:trx,mode; slice set <id> mode=<mode>
TX_ENABLE:trx,true; xmit 1
DRIVE:value; transmit set rfpower=<value>
RX_AUDIO_STREAM (binary) ← Feed from AudioEngine (same data as DAX RX)
TX_AUDIO_STREAM (binary) → Feed into AudioEngine TX path
IQ_STREAM (binary) ← Feed from DAX IQ channel (when #124 is implemented)
SPOT:callsign,mode,freq,...; → Forward to SpotHub for display on spectrum
CW_MACROS_SPEED:wpm; cw wpm <wpm>
CW_MSG:text; cw send <text>
Slice status change → Broadcast VFO:, MODULATION:, etc. to all connected clients
Meter update (S-meter) → Broadcast RX_SMETER:trx,channel,value;
TX state change → Broadcast TRX:trx,true/false;

Audio streaming

  • RX audio: AetherSDR taps the same audio pipeline that feeds DAX RX channels (PCC 0x0123 int16 data or 0x03E3 float32 data), converts to the TCI binary frame format (little-endian float32, per the TCI spec), and sends as WebSocket binary messages
  • TX audio: Client sends binary frames → AetherSDR feeds them into the TX audio path (same routing as DAX TX)

Multi-slice mapping

The FlexRadio can have multiple slices. TCI uses trx (transceiver index) and channel (sub-receiver). Suggested mapping:

  • Slice A → TRX 0, channel 0
  • Slice B → TRX 0, channel 1
  • (If Multi-Flex with 2 independent TX) → TRX 1

Applet integration

A small TCI toggle button could be added to the AppletPanel row (alongside ANLG, RX, TX, etc.) that opens a minimal TCI status applet showing: server on/off, port, connected clients count, and data rates.

Protocol Hints

FlexRadio side (already implemented in AetherSDR)

AetherSDR already maintains all the state needed to serve TCI:

  • Frequency/mode/filterSliceModel (RF_frequency, mode, filter_lo, filter_hi)
  • TX stateTransmitModel (xmit, rfpower, tunepower)
  • S-meterMeterModel (LEVEL meter from SLC source)
  • Audio RXAudioEngine / PipeWireAudioBridge DAX pipeline
  • Audio TXAudioEngine TX path
  • Spots — SpotHub system
  • CWcw send, cw wpm commands via RadioConnection

TCI side (needs implementation)

  • WebSocket server — Qt provides QWebSocketServer in the Qt::WebSockets module (add find_package(Qt6 COMPONENTS WebSockets) to CMakeLists.txt)
  • TCI protocol parser — ASCII commands terminated by ;, very simple to parse (split on : for command name, split on , for arguments)
  • Binary audio frames — TCI audio uses a small header (type, sample rate, codec, CRC, length) followed by float32 PCM samples in little-endian byte order
  • Reference implementation — Full protocol spec at [github.com/ExpertSDR3/TCI](https://github.com/ExpertSDR3/TCI), protocol version 1.5+ recommended
  • Reference Python client — [github.com/ars-ka0s/eesdr-tci](https://github.com/ars-ka0s/eesdr-tci) — useful for testing

Implementation phases (suggested)

  1. Phase 1 — Core CAT: WebSocket server + VFO/mode/filter/TX commands + status broadcast → enough for loggers
  2. Phase 2 — Audio: RX audio streaming + TX audio input → enough for JTDX/MSHV/fldigi
  3. Phase 3 — Extended: IQ streaming, spot injection, CW macros, S-meter broadcast → full feature parity
  4. Phase 4 — Settings UI: Radio Setup tab with configuration options

Additional context

  • The TCI protocol is [open source and documented](https://github.com/ExpertSDR3/TCI) (MIT license)
  • This feature would make AetherSDR the first FlexRadio client with built-in TCI — even SmartSDR for Windows doesn't have it
  • On Linux, where DAX audio routing is inherently more complex than on Windows, TCI's all-in-one-WebSocket approach is especially valuable
  • Since AetherSDR already has RigctlServer as a precedent for serving a third-party protocol from internal state, TCI follows the same architectural pattern — just with a different wire format and richer feature set

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing featureexternal devicesFlexControl, MIDI, serial port, USB peripheralsprotocolSmartSDR protocol

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions