TCI Protocol Reference

Implementation guide for SDR software developers — TCI Remote & TCI Remote Compactor compatibility

Introduction

The TCI (Transceiver Control Interface) protocol is an open WebSocket-based protocol originally developed by Expert Electronics for their ExpertSDR2 software. It has since been adopted and extended by Thetis / OpenHPSDR and a growing number of other SDR platforms. TCI provides a standardised, language-agnostic way for client applications to control a software-defined radio over a local or remote network connection.

This document describes the TCI commands required for full compatibility with TCI Remote — a web-based remote SDR control application — and the TCI Remote Compactor, a companion proxy that adds audio compression and multi-user access on top of the standard TCI protocol.

If your SDR software correctly implements the commands in this document, users will be able to connect to it with TCI Remote either directly over a local network or through the Compactor proxy for remote access, without any modification to your server code.

Who this document is for Developers writing or porting SDR server software who want it to be controllable by TCI Remote. Knowledge of WebSocket programming and basic DSP concepts is assumed. This document covers only the standard TCI protocol — no proprietary extensions are described here.
Compactor compatibility note The TCI Remote Compactor acts as a transparent proxy between TCI Remote and your TCI server. From your server's perspective the Compactor is simply another TCI client. You do not need to implement anything special to support Compactor-connected users — standard TCI compliance is sufficient.
How to read this document Everything up to and including Keepalive is standard TCI: implement it and TCI Remote will work with your server. CAT Commands Used lists the concrete CAT strings that are sent, and Command Usage Matrix lists every single command that actually goes over the wire, so you can see at a glance what is exercised in practice and what is only tolerated. Compactor Extensions describes messages that exist only between TCI Remote and the Compactor — you never need to implement those.

Verified against TCI Remote v3.5 and TCI Remote Compactor v1.3.4.1 by reading both source files, not from memory.

Transport Layer

TCI uses a plain WebSocket connection (RFC 6455). The server listens on a configurable TCP port; the conventional default is 50001. No WebSocket subprotocol negotiation header is used. Connections are always initiated by the client.

Minimum IQ Sample Rate TCI Remote requires the IQ stream sample rate to be at least 96,000 Hz. If a lower rate is reported in the iq_samplerate announcement, TCI Remote will display an error and disconnect. A rate of 192,000 Hz is strongly recommended for full spectrum display quality.

Server Handshake

Immediately after the WebSocket connection is established, the server must push an initialisation block to the client. The client does not send any commands until it receives the ready message that terminates this block.

// Required server → client init messages, in order:

protocol:2;                             // TCI protocol version
device:ExpertSDR2;                      // radio / software name
receive_only:false;                     // true if TX is not supported
trx_count:1;                            // number of independent TRX channels
channels_count:2;                       // audio channels per TRX
vfo_limits:100000,450000000;            // min and max VFO frequency in Hz
if_limits:-96000,96000;                // min and max IF offset in Hz
modulations_list:AM,SAM,DSB,LSB,USB,CWL,CWU,NFM,DIGL,DIGU,SPEC,DRM;
iq_samplerate:192000;                   // MUST be ≥ 96000
audio_samplerate:48000;                 // must be 48000
tx_profiles_ex:Default,Contest,DX;     // comma-separated TX profile names
ready                                    // no trailing semicolon on this one

tx_profiles_ex populates the TX profile dropdown. The remaining init fields are accepted and ignored — see the note below.

After sending ready, the server should also push the current value of all stateful parameters (frequency, mode, filters, PTT state, etc.) so that the client UI immediately reflects the actual radio state without the client needing to poll. TCI Remote does not rely on this alone: it also sends an explicit query batch.

What TCI Remote does and does not parse Send the full init block — it is part of the protocol and other clients need it. But be aware that TCI Remote v3.5 currently acts only on device, iq_samplerate, audio_samplerate, tx_profiles_ex and ready. protocol, receive_only, trx_count, channels_count, vfo_limits, if_limits and modulations_list are received and discarded.

The practical consequence is the mode list. TCI Remote has a fixed set of mode buttons and does not build them from modulations_list: LSB, USB, DSB, CWL, CWU, NFM, AM, SAM, DIGL, DIGU. Your server should accept those spellings in modulation: and echo them back in the same spelling. A mode your radio has but this list does not is not reachable from the UI; a different spelling for the same mode will leave the mode button showing the wrong value.

Bare messages — no colon, no semicolon Receive

Three server → client messages carry no parameters and no trailing semicolon. A parser that splits on : must not throw them away.

MessageDescription
readyEnds the init block. The client sends nothing before this arrives.
start The radio hardware is powered on and running. TCI Remote clears its standby banner.
stop The radio hardware is in standby while the TCI server itself is still up — Thetis sends this when the radio is switched off. TCI Remote shows a banner offering to power the radio on, and if the user enabled auto-wake it sends run_cat_ex:PS1; after 500 ms. If your server cannot distinguish "software running, radio off", simply never send stop.

Message Format

All TCI text messages follow a consistent format:

command_name:param1,param2,paramN;   // general form
ready                                 // no colon, no semicolon
keepalive;                            // command with no parameters

The Query Form

Most commands have a query form: the same command name with the TRX index only and no value. The server answers with the normal, fully populated message. TCI Remote relies on this — it does not assume the init push covered everything.

// set                          // query                 // server answers
rx_nb_enable:0,true;            rx_nb_enable:0;         rx_nb_enable:0,true;
agc_auto_ex:0,false;            agc_auto_ex:0;          agc_auto_ex:0,false;
tx_profile_ex:Contest;          tx_profile_ex;          tx_profile_ex:Contest;

Immediately after ready, TCI Remote sends this batch. If your server ignores the query form, every one of these controls will show a stale value until the user touches it.

C→S  sql_enable:0;          // squelch on/off
C→S  sql_level:0;           // squelch threshold
C→S  run_cat_ex:ZZSO;       // squelch source — sql_enable cannot express it
C→S  rx_anf_enable:0;
C→S  lock:0;
C→S  split_enable:0;
C→S  mute:0;
C→S  mon_enable:0,false;    // this one is a set, not a query: monitor off
C→S  rx_volume:0,0;         // trx 0, channel 0
C→S  drive:0;
C→S  tune_drive:0;
C→S  modulation:0;          // query only — the app never pushes the mode at connect,
C→S  modulation:1;          //   so an externally set mode is not overridden
C→S  rx_filter_band:0;      // or a set, if the user has a saved passband
C→S  rx_filter_band:1;
C→S  rx_nb_enable:0;
C→S  rx_nb2_enable:0;
C→S  rx_bin_enable:0;
C→S  agc_auto_ex:0;
C→S  tx_profiles_ex;        // no index at all
C→S  tx_profile_ex;
Ambiguity you should be aware of For commands whose set form is <trx>,<value> the query form is unambiguous. For split_enable:, whose set form carries only a state, the query split_enable:0; is indistinguishable from "set split off" unless you look at the argument type. Thetis treats the numeric form as a query. If you implement it the other way round, split will be forced off at every connect.

Stream Setup

After receiving ready, the client sends these commands to configure and start the IQ and audio streams.

Client → Server Send

CommandDescription
iq_start:0;Start IQ sample streaming for TRX 0.
iq_stop:0;Stop IQ sample streaming for TRX 0.
iq_samplerate:<rate>;Request an IQ sample rate. TCI Remote only sends this if the server announced a rate below 96,000 Hz — since v3.5 the rate configured on the radio is otherwise left exactly as it is, and the panadapter zoom follows it.
audio_start:0;Start RX audio streaming for TRX 0.
audio_stop:0;Stop RX audio streaming for TRX 0.
audio_stream_sample_type:float32;Request 32-bit IEEE 754 float samples. This is the only format TCI Remote supports.
audio_stream_channels:2;Request stereo (2-channel) audio.
audio_stream_samples:<n>;Number of samples per audio frame (e.g. 2400 for 50 ms at 48 kHz).
audio_samplerate:48000;Confirm 48 kHz audio sample rate.
tx_stream_audio_buffering:<ms>;TX audio buffer depth in milliseconds. Sent before TX audio begins.
rx_sensors_enable:0,true;Enable periodic S-meter and RX sensor reporting for TRX 0.
tx_sensors_enable:true;Enable periodic TX power and SWR reporting.

Server → Client Receive

CommandDescription
iq_samplerate:<rate>;Active IQ sample rate in Hz. Also sent during init. Must be ≥ 96000.
audio_samplerate:<rate>;Active audio sample rate (48000).
audio_start:0;Confirms audio streaming has started for TRX 0.

Frequency & VFO

Bidirectional Both

CommandParametersDescription
vfo: <trx>,<vfo>,<freq> VFO frequency in Hz. vfo = 0 for VFO-A, 1 for VFO-B. Client sends to change frequency; server echoes the confirmed value and pushes unsolicited updates when frequency changes from any source.
dds: <trx>,<freq> DDS (panadapter center) frequency in Hz. Defines the center of the spectrum display window. Client may set it; server echoes and pushes unsolicited updates.
if: <trx>,<hz> IF offset in Hz. Used to calculate the displayed frequency relative to the DDS center.
vfo_lock: <trx>,<vfo>,<state> Lock or unlock a specific VFO (true = locked, false = unlocked).
lock: <trx>,<state> General VFO lock for all VFOs on a TRX.
split_enable: <trx>,<state> Enable split operation — RX on VFO-A, TX on VFO-B.

Client → Server only Send

CommandParametersDescription
vfo_swap_ex:<trx>Swap VFO-A and VFO-B frequencies.

Modulation

Bidirectional Both

CommandParametersDescription
modulation: <trx>,<mode> Get or set the modulation mode. Mode strings (case-insensitive):
LSB  USB  DSB  CWL  CWU  NFM  AM  SAM  DIGL  DIGU  SPEC  DRM
The full list supported by your server is announced in modulations_list during init.

Filters & Passband

Bidirectional Both

CommandParametersDescription
rx_filter_band: <trx>,<lo>,<hi> RX passband edges in Hz, relative to the VFO frequency. Negative = below carrier, positive = above. Example for a 2800 Hz USB passband: rx_filter_band:0,0,2800;. Example for LSB: rx_filter_band:0,-2800,0;.
The server echoes the applied values back.
tx_filter_band_ex: <trx>,<lo>,<hi> TX passband filter edges in Hz. Same sign convention as rx_filter_band.

RIT / XIT

RIT (Receiver Incremental Tuning) shifts the receive frequency without moving the transmit frequency. XIT does the reverse.

Bidirectional Both

CommandParametersDescription
rit_enable:<trx>,<state>Enable or disable RIT.
rit_offset:<trx>,<hz>RIT offset in Hz (signed integer, applied to receive frequency).
xit_enable:<trx>,<state>Enable or disable XIT.
xit_offset:<trx>,<hz>XIT offset in Hz (signed integer, applied to transmit frequency).

DSP Features

Bidirectional Both

CommandParametersDescription
rx_nb_enable:<trx>,<state>Enable or disable Noise Blanker 1 (NB).
rx_nb2_enable:<trx>,<state>Enable or disable Noise Blanker 2 (NB2).
rx_nb_enable_ex: <trx>,<state>,<which> Extended noise-blanker control. which: 1 = NB1, 2 = NB2, 0 when state is false.
TCI Remote sends the plain rx_nb_enable: and this extended form together, so a server that implements only the plain one still works.
rx_bin_enable:<trx>,<state>Enable or disable the BIN noise reduction function.
rx_anf_enable:<trx>,<state>Enable or disable the Automatic Notch Filter (ANF).
rx_nr_enable:<trx>,<state>Enable or disable Noise Reduction. Basic form.
rx_nr_enable_ex: <trx>,<state>,<level> Extended NR control with algorithm selection.
level: 0 = off, 1 = NR1, 2 = NR2, 3 = NR3, 4 = NR4.

AGC & Squelch

Bidirectional Both

CommandParametersDescription
agc_auto_ex:<trx>,<state>Enable automatic AGC (true) or switch to manual gain (false).
agc_gain:<trx>,<gain>Manual AGC gain value. Active when agc_auto_ex is false.
sql_enable:<trx>,<state>Enable or disable squelch.
sql_level:<trx>,<level>Squelch threshold level.

PTT & Transmit Control

Safety — TX Watchdog If the client connection is lost while TX is active, the server must not remain in TX indefinitely. Implement a server-side PTT watchdog timer that returns the radio to RX automatically after a configurable inactivity period (recommended: 60–120 seconds). Do not rely solely on the client sending a PTT-off command.

Bidirectional Both

CommandParametersDescription
trx: <trx>,<state> PTT control. true = enter TX, false = return to RX.
The server must echo the actual resulting hardware state back to all connected clients so every UI reflects the correct TX/RX state, regardless of which client triggered the change.
tune: <trx>,<state> Tune mode — transmits a continuous carrier for antenna tuning. Same echo requirement as trx.
drive: <trx>,<level> TX drive level (0–100 or implementation-defined range).
mute: <trx> Mute RX audio output.

Client → Server only Send

CommandParametersDescription
tune_drive:<trx>,<level>Drive level used in tune mode, separate from drive:. TCI Remote queries it at connect (tune_drive:0;).
tx_antenna:<trx>,<idx>Select TX antenna by index.
rx_antenna:<trx>,<idx>Select RX antenna by index.
rx_enable:<trx>,<state>Enable or disable a secondary RX channel (e.g. dual-watch RX2).

Audio Control

Bidirectional Both

CommandParametersDescription
rx_volume: <trx>,<ch>,<level> RX audio volume. ch = 0 for main receive channel.
mon_enable: <trx>,<state> Enable or disable TX monitor (sidetone / self-monitor).
mon_volume: <trx>,<level> TX monitor volume level.

Server → Client Receive

CommandParametersDescription
line_in: <trx>,<state> Notification that the audio input source changed (line-in versus microphone). TCI Remote logs it; it does not send this command.

TX Profiles

TX profiles store transmit settings (equaliser, compressor, microphone gain, etc.) under named presets. TCI Remote displays the profile list in a dropdown and allows remote switching.

Server → Client on init Receive

CommandParametersDescription
tx_profiles_ex: <name1>,<name2>,... Comma-separated list of all available TX profile names. Must be sent during server initialisation.

Bidirectional Both

CommandParametersDescription
tx_profile_ex: <name> Get or set the active TX profile. Client sends the desired profile name; server echoes the currently active profile back to all clients.

Sensors & Meters

Sensor data is pushed by the server at a regular interval once enabled. Typical update rates: S-meter at 10–20 Hz, TX telemetry at 4–10 Hz while transmitting.

Server → Client Receive

CommandParametersDescription
rx_smeter: <trx>,<dbm> Received signal strength in dBm (negative integer or float, e.g. -85).
TCI Remote maps dBm to S-units using the ITU scale:
S1 = −121 dBm  ·  S9 = −73 dBm  ·  S9+60 dB = −13 dBm.
Sent periodically after the client sends rx_sensors_enable:0,true;.
tx_sensors: <trx>,<mic_db>,<fwd_pwr>,<rev_pwr>,<swr> TX telemetry while transmitting.
mic_db = microphone level in dB.
fwd_pwr = forward power in watts.
rev_pwr = reverse (reflected) power in watts.
swr = SWR ratio (e.g. 1.5).
Sent after tx_sensors_enable:true;.
tx_swr: <trx>,<swr> SWR value (may be sent in addition to or instead of the SWR field in tx_sensors).
tx_forward_power: <pwr> Forward TX power in watts (alternative format, used by some implementations).
rx_channel_sensors: <data> Extended RX sensor data block. Format is implementation-defined.
s_meter:
smeter:
<trx>,<dbm> Accepted as aliases of rx_smeter:. TCI Remote reads the last comma-separated field, so a longer parameter list is handled. Send rx_smeter: in new implementations.
tx_power: <pwr> Alias of tx_forward_power:. Last field wins.
swr: <swr> Alias of tx_swr:. Values below 1 are discarded as implausible.

CAT Pass-through

TCI provides a pass-through mechanism for CAT (Computer Aided Transceiver) strings, allowing clients to use radio-specific features not covered by the native TCI command set.

Client → Server Send

CommandParametersDescription
run_cat_ex: <cat_string> Execute a raw CAT command string. The format depends on the radio's CAT dialect (Kenwood-style, ICOM CI-V, Elecraft, etc.). Your server may route these to the connected hardware or ignore them if CAT pass-through is not supported.

CAT Commands Used

Everything below travels inside run_cat_ex:. These are the only CAT strings TCI Remote sends on its own initiative. They are all Kenwood-style / Thetis ZZ dialect, because that is what Thetis exposes.

None of these are required CAT is an escape hatch for things TCI has no command for. A server with no CAT pass-through is fully usable — the affected controls simply do nothing. Nothing else breaks.

Sent by TCI Remote Send

CAT stringWhy it is sentDetail
PS1; Power the radio on Sent when the user taps "Turn it on" in the standby banner, and automatically 500 ms after a stop message if the user enabled auto-wake.
PS0; Power the radio off Host power control from the connection screen. Radio Network visitors are blocked from sending this.
ZZTX0; Force RX before a power command Sent immediately before PS0/PS1 so the radio is never powered while transmitting.
ZZSO; Query the squelch source sql_enable: says whether squelch is on, not which squelch. Thetis answers with run_cat_ex:ZZSO;,ZZSO1; — note that it echoes the query and the answer in one message, so parse the last field.
ZZSO0; ZZSO1; ZZSO2; Set the squelch source 0 = off, 1 = SQL (noise), 2 = VSQL (voice). Followed by a ZZSO; read-back 500 ms later.
ZZOA<n>; RX antenna selection Thetis does not implement rx_antenna: / tx_antenna:, so the app sends both the TCI commands and these CAT equivalents. A server that implements the TCI commands needs neither.
ZZOC<n>; TX antenna selection
user-defined ATU / accessory buttons Three configurable buttons in the app send any CAT string the user types, up to 20 characters. The factory defaults are ZZOV1 and ZZOW0 with the third slot empty, and all three are disabled until the user enables them.

Sent by the Compactor

None. The Compactor never originates a CAT command. It only decides whether a client's run_cat_ex: is allowed through — see Compactor Extensions.

Keepalive

Client → Server Send

CommandDescription
keepalive; Sent periodically by the client (typically every 30 seconds) to prevent the WebSocket connection from being closed by intermediate network equipment. The server must not close the connection on receipt. No reply is required.

IQ Binary Frames

IQ sample data is transmitted as WebSocket binary frames. The frame format used by Thetis / OpenHPSDR (and followed by TCI Remote) is a 64-byte header followed by interleaved float32 I/Q sample pairs.

flags
uint32 LE · 4 B
sample_rate
uint32 LE · 4 B
hdr[2]
uint32 LE · 4 B
hdr[3]
uint32 LE · 4 B
sample_count
uint32 LE · 4 B
frame_type
uint32 LE · 4 B
reserved
40 B padding
IQ payload
float32 · I₀ Q₀ I₁ Q₁ …
FieldOffsetTypeDescription
flags0uint32 LEImplementation-defined frame flags.
sample_rate4uint32 LEIQ sample rate in Hz (e.g. 192000). TCI Remote uses this to distinguish IQ frames from audio frames: a value > 48000 indicates IQ data.
header fields8–19uint32 LE ×3Implementation-specific header fields. May carry TRX index, format version, etc.
sample_count20uint32 LENumber of complex sample pairs in the payload.
frame_type24uint32 LE0 = IQ frame  ·  1 = RX audio  ·  2 = TX audio  ·  3 = chrono/timing
reserved2840 bytesReserved / padding. Must be present to align the payload to byte offset 64.
IQ payload64float32 LE pairsInterleaved I and Q samples: I₀, Q₀, I₁, Q₁, … Each value is a 32-bit float, nominally in the range −1.0 to +1.0.
Frame type identification TCI Remote classifies a binary frame as IQ data when frame_type == 0 AND sample_rate > 48000. Frames with frame_type ∈ {1, 2} and sample_rate ≤ 48000 are treated as audio frames.

Audio Binary Frames

RX audio (server → client) and TX audio (client → server) use the same 64-byte header structure as IQ frames, with a different frame_type value and 48,000 Hz sample rate.

frame_typeDirectionDescription
1Server → ClientRX audio — stereo PCM float32 at 48,000 Hz.
2Client → ServerTX audio — stereo PCM float32 at 48,000 Hz.

The audio payload (starting at byte offset 64) contains interleaved stereo float32 samples:

// Stereo interleaved layout:
L₀, R₀, L₁, R₁, L₂, R₂, ...   // float32 LE each

// Total payload size:
sample_count × 2 × 4   // × 2 channels × 4 bytes per float32
Alternative short header format Some older TCI implementations use an 8-byte header: a 2-byte frame_type at offset 0, a 2-byte reserved field at offset 2, and a 4-byte sample count at offset 4, with the audio payload starting at offset 8. If your implementation uses the full 64-byte Thetis-standard header, no special handling is required on either side.

Compactor Extensions

Not part of TCI — do not implement This section exists so that nobody mistakes these messages for protocol they must support. They are exchanged between TCI Remote and the TCI Remote Compactor only. Your TCI server never sees them: the Compactor terminates them and speaks plain, standard TCI towards you.

Text messages

MessageDirectionDescription
compactor:1,<tx>,<email>,<version>,<trial>; Compactor → app Identifies the proxy and its capabilities. Sent once at connect and again whenever TX compression is toggled. This is how the app knows it is not talking to a bare TCI server.
compactor_cooldown:<secs>;Compactor → appSeconds remaining before an unlicensed session may reconnect.
compactor_timeout:…Compactor → appLegacy. The app still handles it; Compactor v1.3.4.1 no longer sends it.
qping:<n>; → qpong:<n>;app ↔ CompactorRound-trip time measurement for the connection-quality strip. The Compactor echoes the message back with the verb changed and never forwards it to the TCI server.
rn_* (16 messages) Compactor → app Radio Network session control: rn_user_joined, rn_user_left, rn_promoted, rn_demoted, rn_queue_update, rn_kicked, rn_disconnected, rn_idle_warning, rn_idle_timeout, rn_window_closing, rn_cooldown_reset, rn_local_operator_off, rn_ic_idle, rn_ic_busy, rn_ic_rejected, rn_demoted. Multi-user turn-taking, nothing to do with radio control.

Binary frames

Between app and Compactor the standard 64-byte TCI binary frames are replaced by compressed frames with a four-byte ASCII magic. Between Compactor and your TCI server, ordinary uncompressed TCI frames are used.

MagicReplacesPayload
TCRSIQ frames (frame_type 0)Default. The Compactor runs the FFT itself and sends a spectrum frame instead of IQ: uint16 FFT size, uint32 sample rate, float32 noise floor, then one uint8 per bin, zstd-compressed. This costs CPU on the Compactor machine and saves a great deal of bandwidth.
TCRCIQ frames (frame_type 0)Fallback for raw IQ: the original frame, losslessly zstd-compressed.
TCRAAudio frames (types 1 and 2)Opus. Used in both directions when TX compression is enabled.
TCRM—TX monitor audio.

Command gating

When a client connects as a Radio Network visitor rather than as the owner, the Compactor filters what it forwards. This is the one place where a command may be silently dropped before it reaches your server.

What the Compactor sends on its own

One thing only: mox:0;tune:0; on its monitoring connection, to force the radio out of TX when a session ends or a watchdog fires. Everything else it puts on the wire towards your server is a client command it decided to forward.


Complete Initialisation Sequence

The annotated exchange below shows the full message flow at connection time.

═══ WebSocket connection established ════════════════════════════

── Server → Client: init block ──────────────────────────────────
S→C  protocol:2;
S→C  device:MySDR;
S→C  receive_only:false;
S→C  trx_count:1;
S→C  channels_count:2;
S→C  vfo_limits:100000,450000000;
S→C  if_limits:-96000,96000;
S→C  modulations_list:AM,SAM,DSB,LSB,USB,CWL,CWU,NFM,DIGL,DIGU;
S→C  iq_samplerate:192000;
S→C  audio_samplerate:48000;
S→C  tx_profiles_ex:Default,Contest,DX;
S→C  ready

── Client → Server: stream setup ────────────────────────────────
C→S  audio_stream_sample_type:float32;
C→S  audio_stream_channels:2;
C→S  audio_stream_samples:2400;
C→S  audio_samplerate:48000;
C→S  audio_start:0;
C→S  iq_start:0;
C→S  rx_sensors_enable:0,true;
C→S  tx_sensors_enable:true;

── Server → Client: stream confirmation + current state push ────
S→C  audio_start:0;
S→C  vfo:0,0,14225000;
S→C  dds:0,14225000;
S→C  modulation:0,USB;
S→C  rx_filter_band:0,0,2800;
S→C  tx_profile_ex:Default;
S→C  trx:0,false;
S→C  split_enable:false;
S→C  agc_auto_ex:0,true;
S→C  agc_gain:0,60;
S→C  rx_nb_enable:0,false;
S→C  rx_anf_enable:0,false;
S→C  rx_nr_enable_ex:0,false,0;
S→C  sql_enable:0,false;
S→C  rit_enable:0,false;
S→C  xit_enable:0,false;
S→C  mute:false;

═══ Steady state — IQ and audio binary frames flowing ════════════

S→C  rx_smeter:0,-85;         // ~S5, repeated at 10–20 Hz

── User changes frequency ───────────────────────────────────────
C→S  vfo:0,0,14195000;
S→C  vfo:0,0,14195000;        // echo confirmed value to all clients
S→C  dds:0,14195000;          // center frequency may shift too

── User presses PTT ─────────────────────────────────────────────
C→S  trx:0,true;
S→C  trx:0,true;              // echoed to ALL connected clients
      … TX audio binary frames (frame_type=2) client → server …
      … tx_sensors frames server → client while TX active …
C→S  trx:0,false;
S→C  trx:0,false;             // echoed to all clients

State Echo Rules

A TCI server is a stateful broker. These rules ensure that all connected clients stay in sync regardless of which client triggered a change.

Command Usage Matrix

Every TCI command that TCI Remote v3.5 or Compactor v1.3.4.1 actually puts on the wire or acts upon. Extracted from the source of both, not from this document — if a command is not in this table, it is not used.

CommandC→SS→CCompactorNote
Handshake and streams
protocolrelayaccepted, discarded
device✓relayshown in the UI
receive_onlyrelayaccepted, discarded
trx_countrelayaccepted, discarded
channels_countrelayaccepted, discarded
vfo_limitsrelayaccepted, discarded
if_limitsrelayaccepted, discarded
modulations_listrelayaccepted, discarded — mode list is fixed
ready✓relaybare, no semicolon
start✓relaybare — radio powered on
stop✓relaybare — radio in standby
iq_start / iq_stop✓relay
iq_samplerate✓✓relayonly sent if the radio reports < 96 kHz; drives the panadapter zoom
audio_start / audio_stop✓✓relay
audio_samplerate✓✓relay48000
audio_stream_sample_type✓relayfloat32
audio_stream_channels✓relay2
audio_stream_samples✓relay
tx_stream_audio_buffering✓relay
keepalive✓relay
Frequency, mode and VFO
vfo✓✓gated
dds✓✓gated
modulation✓✓gatedqueried at connect, never pushed
vfo_swap_ex✓gated
vfo_lock / lock✓✓gated
split_enable✓✓gatedblocked for all Radio Network visitors
rit_enable / rit_offset✓gatedecho is not consumed
xit_enable / xit_offset✓gatedecho is not consumed
Filters and DSP
rx_filter_band✓✓gated
tx_filter_band_ex✓✓gated
rx_nb_enable✓✓gated
rx_nb2_enable✓gated
rx_nb_enable_ex✓✓gatedsent alongside rx_nb_enable
rx_bin_enable✓✓gated
rx_anf_enable✓✓gated
rx_nr_enable✓✓gated
rx_nr_enable_ex✓✓gated
agc_auto_ex✓✓gated
agc_gain✓✓gated
sql_enable✓✓gated
sql_level✓✓gated
PTT, TX and audio
trx✓✓gatedtrue blocked for visitors, false always allowed
tune✓✓gatedsame rule as trx
tune_drive✓relayqueried at connect
drive✓✓gated
tx_antenna✓gatedplus CAT ZZOC
rx_antenna✓gatedplus CAT ZZOA
rx_enable✓gatedsecond receiver
rx_volume✓✓gatedlisteners get a client-side volume instead
mute✓✓gated
mon_enable✓✓gated
mon_volumegatedblocked but never sent by the app
line_in✓relaylogged only
Profiles, sensors and CAT
tx_profiles_ex✓✓relayqueried with no index
tx_profile_ex✓✓gated
rx_sensors_enable✓✓relay
tx_sensors_enable✓✓relay
rx_smeter✓✓relayalso sent as a nudge
s_meter / smeter✓relayaliases
tx_sensors✓relaymic level, power, SWR
tx_swr / swr✓relay
tx_forward_power / tx_power✓relay
rx_channel_sensors✓relay
run_cat_ex✓✓gatedsee CAT Commands Used
Tolerated but unused TCI Remote also recognises these names well enough not to clutter its log with them, but takes no action: if, tx_frequency, tx_frequency_ex, tx_frequency_thetis, rx_ctun_ex, rx_mute, volume, calibration_ex, rx_channel_enable, tx_enable, rx_step_att_ex, rx_step_att_enabled_ex, rx_preamp_att_ex. Sending them is harmless; not sending them is equally harmless.

Compatibility Checklist

A server that satisfies all items below will be fully compatible with TCI Remote, both for direct connections and when used through the TCI Remote Compactor proxy.

Transport & Handshake

Frequency & Mode

Filters & DSP

PTT & Audio Streams

TX Profiles & Sensors

Query Form and Power State