Skip to content

TCI Client for TLF #508

@LU5DX

Description

@LU5DX

Request preparation

  • I checked for existing issues covering the same feature

Feature Request: TCI (Transceiver Control Interface) client for integration with modern SDR radios and software

What

Add a built-in TCI client to TLF that connects to any TCI-capable SDR or radio software over the standard TCI protocol (WebSocket on a configurable TCP port, default 40001).

From the user's perspective: you add a TCI=<host>:<port> line to logcfg.dat, and TLF gets full CAT control, PTT, S-meter readings, CW keying, and spot injection — all over a single WebSocket connection, with zero additional configuration of virtual serial ports, rigctld processes, or PulseAudio routing.


Why

Problem 1 — Too many moving parts for a simple integration

Today, connecting TLF to a modern SDR (e.g. a FlexRadio running AetherSDR, or an Expert Electronics SunSDR) requires setting up multiple independent channels:

  1. CAT control — via hamlib / 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. CW keying — via cwdaemon or winkeyer daemon as a separate process
  5. Spots — DX cluster telnet connection managed separately

Each of these is a separate failure point. If one breaks (PulseAudio module doesn't load, PTY disappears, rigctld port conflict, cwdaemon crashes), the whole chain falls apart — right in the middle of a contest. Linux users are especially affected because DAX audio routing 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 well beyond the Expert Electronics / SunSDR world. Software and radios that already support TCI natively — and that TLF users are likely running:

  • AetherSDR — the open-source FlexRadio client for Linux, recently added a built-in TCI server
  • ExpertSDR3 (Expert Electronics / SunSDR) — the reference TCI implementation
  • SDC (by Yuri) — TCI server bridging FlexRadio's SmartSDR API to TCI clients
  • JTDX / MSHV — TCI for rig control and audio (common companion apps alongside TLF)
  • WSJT-X (via TCI-Hamlib Adapter by DL3NEY) — bridges TCI ↔ Hamlib
  • Log4OM v2 — full TCI client built in, proving the pattern works for loggers
  • Not1MM and TRlinux — fellow Linux contest loggers with open TCI client feature requests
  • HamDeck — Stream Deck automation via TCI
  • Various ESP32/Arduino hardware controllers — TCI for physical knobs and buttons

By adding a TCI client, TLF becomes compatible with all of these without requiring each SDR vendor to add TLF-specific support.

Problem 3 — The hamlib/rigctld path is functional but limited

hamlib works well for basic frequency/mode control, but it was designed for traditional rigs. It doesn't carry audio, doesn't handle spot injection natively, and adds latency through an extra process hop (rigctld). TCI gives TLF a direct, richer channel to the radio — a single connection that covers CAT, PTT, CW keying, S-meter, and spots.

Problem 4 — TLF already runs the CW/keyer and cluster as separate daemons

TLF's existing architecture requires cwdaemon or a Winkeyer server running alongside it for CW, and a separate telnet connection for DX cluster spots. For radios that support TCI, all of these can be unified into a single WebSocket connection — simplifying setup significantly, especially for contest operators who set up and tear down quickly.


How Other Software Does It

AetherSDR (FlexRadio client for Linux)

AetherSDR recently shipped a built-in TCI server specifically to enable zero-configuration integration with loggers and digital mode software. It exposes frequency, mode, PTT, S-meter, audio, IQ, CW keying, and spot injection — all over one WebSocket. TLF adding a TCI client would complete that loop for FlexRadio + Linux contest operators immediately.

Log4OM v2

Log4OM already implements a full TCI client — CAT control, audio, IQ, and spot injection to the panorama display — demonstrating that a logger can integrate TCI cleanly and to great effect.

Not1MM and TRlinux

Both Linux-native contest loggers have open feature requests for TCI client support for the same reasons outlined here. Momentum is building across the Linux contesting ecosystem toward TCI as the standard radio interface.

TCI-Hamlib Adapter (DL3NEY)

For software that doesn't speak TCI natively, Florian DL3NEY wrote tciadapter — a Go program translating TCI ↔ Hamlib rigctld. That workaround exists precisely because demand for TCI bridging is high. Building native support directly into TLF eliminates the need for this extra process.


Suggested Behavior

Configuration (logcfg.dat)

TCI radio interface (replaces HAMLIB= for TCI-capable radios)
TCI=127.0.0.1:40001
TCI transceiver index (default 0, for multi-TRX servers)
TCI_TRX=0
Use TCI for CW keying (replaces cwdaemon for supported radios)
TCI_CW=TRUE
Use TCI for spot injection to bandmap (optional)
TCI_SPOTS=TRUE

Startup behavior

  1. TLF reads TCI= from logcfg.dat and opens a WebSocket connection to ws://<host>:<port>
  2. The TCI server sends its initialization burst (PROTOCOL:, DEVICE:, VFO:, MODULATION:, RX_ENABLE:, READY;)
  3. TLF parses the init sequence to populate its internal radio state (frequency, mode, TX capability)
  4. TLF logs the connection to the console: TCI connected: FlexRadio FLEX-6600 on 127.0.0.1:40001
  5. If the connection drops, TLF attempts to reconnect automatically with a configurable interval

Runtime behavior

TCI event / command TLF action
VFO:trx,channel,freq; received → Update displayed frequency
MODULATION:trx,mode; received → Update mode
TRX:trx,true/false; received → Update TX indicator
RX_SMETER:trx,channel,value; received → Update S-meter display
SPOT:callsign,mode,freq,...; received → Inject into bandmap (if TCI_SPOTS=TRUE)
User changes frequency (band change, spot click) → Send VFO:0,0,<freq>;
User changes mode → Send MODULATION:0,<mode>;
PTT activated (F-key macro, footswitch) → Send TX_ENABLE:0,true; / TX_ENABLE:0,false;
CW message sent (if TCI_CW=TRUE) → Send CW_MSG:<text>; instead of cwdaemon
CW speed change → Send CW_MACROS_SPEED:<wpm>;

Connection resilience

  • Auto-reconnect with configurable retry interval if the server goes away
  • Graceful handling of READY; — do not consider the connection active until READY; is received
  • Fall back to hamlib/cwdaemon if TCI connection fails and TCI_FALLBACK=TRUE is set
  • Ignore unknown TCI commands silently (forward compatibility with newer protocol versions)

Protocol Hints

TCI client side (needs implementation in TLF)

  • WebSocket clientlibwebsockets (C library, already packaged in all major distros: apt install libwebsockets-dev) is the natural fit for TLF's C codebase. Alternatively, libcurl has WebSocket support since 7.86.0
  • TCI protocol parser — ASCII commands terminated by ;, trivial to parse: strtok() on : for command name, strtok() on , for arguments — a few dozen lines of C
  • Connection sequence — connect → receive init burst → wait for READY; → begin normal operation
  • Reference spec — Full protocol at github.com/ExpertSDR3/TCI, protocol version 1.5+ recommended
  • Reference Python clientgithub.com/ars-ka0s/eesdr-tci — useful for testing and understanding message flow

TLF side (already available)

TLF already maintains all the state needed to drive and respond to TCI:

  • Frequency / mode — already managed via hamlib; same data feeds TCI with a different transport
  • PTT / TX — already triggered by F-key macros and footswitch input
  • CW keying — already abstracted behind cwdaemon; TCI keying becomes an alternative backend
  • Bandmap / spots — already receives DX cluster spots via telnet; TCI SPOT: injection is an additional source
  • S-meter — already polled via hamlib; TCI pushes it unsolicited, reducing polling overhead

Suggested implementation phases

  1. Phase 1 — Core CAT: WebSocket client + parse init burst + VFO/mode read-write + PTT → enough for basic contesting with a TCI-capable radio, no hamlib needed
  2. Phase 2 — CW keying: Send CW_MSG: and CW_MACROS_SPEED: → replaces cwdaemon for radios with built-in keyers (SunSDR, FlexRadio)
  3. Phase 3 — Spot injection: Receive SPOT: from TCI server and push to bandmap → supplements or replaces the DX cluster telnet connection
  4. Phase 4 — S-meter push: Receive RX_SMETER: unsolicited → eliminates hamlib polling for meter updates

Additional Context

  • The TCI protocol is open source and MIT-licensed — no legal or licensing concerns
  • AetherSDR recently shipped a built-in TCI server specifically to enable this kind of zero-configuration logger integration — TLF adding TCI client support would complete that loop for FlexRadio + Linux contest operators immediately
  • libwebsockets is a mature, widely packaged C library that fits naturally into TLF's existing build system — the implementation footprint is small
  • TLF already abstracts its radio interface (hamlib) and keyer interface (cwdaemon) behind configuration directives; TCI follows the exact same pattern with a new backend
  • This would make TLF one of the very few contest loggers on any platform with native TCI support — a meaningful advantage for operators running modern SDR hardware on Linux
  • Fellow Linux contest loggers Not1MM and TRlinux have open feature requests for the same capability, signalling that this is a shared priority across the Linux contesting community

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions