The TCP Protocol and Quisk
By Jim Ahlstrom, N2ADR, the author of Quisk SDR
June 7, 2026. Initial draft. Expect frequent updates.
June 11, 2026. Added input from Heiko, DL1BZ, Maintainer of the deskHPSDR project.
Please use the group https://groups.io/g/n2adr-sdr for discussions.
This paper discusses software that I do not use myself. Please report errors.
What is TCI
I am interested in adding TCI to implement Rx and Tx audio streams that connect Quisk to external digital
mode programs, CW skimmers, logging programs and contest programs. These programs typically use only a few control commands,
often just PTT and frequency. TCI provides many more controls such as output power control,
filter width, CW macro speed and AGC mode. It is a more general transceiver control protocol.
The TCI protocol was developed by Expert Electronics.
Their SDR hardware is controlled by their ExpertSDR3 and legacy ExpertSDR2 software.
TCI is a protocol built into this software that enables external programs to control the radio and have
access to the receive and transmit audio streams. This can be used by external digital mode and logging programs.
The TCI protocol uses a network connection and it eliminates the need for COM ports and virtual audio cables.
This is an attractive feature. The digital mode program WSJT-X supports TCI, and to connect it to a radio you select
the radio "TCI" and the audio "TCI audio" and you are connected. Using PC audio to transfer digital samples
between computer programs has always been a bother.
-
TCI is a client/server protocol. The radio that can tune in signals and provide audio samples is the server.
Clients are external programs that need to receive Rx audio and perhaps send Tx audio to be transmitted.
-
TCI protocol version 1.4 is described here.
-
TCI protocol version 2.0 is described here.
Why I Don't Like TCI
I am the author of Quisk SDR software. When my users first requested TCI, I initially declined.
I don't like the TCI protocol for these reasons:
-
TCI uses the Websocket protocol over TCP. Websocket was developed to add a persistent TCP connection to a normal
web page. To open a websocket you start by opening a normal html web page. That is more complicated than is needed.
-
The TCI protocol is poorly specified. Version 1.4 does not define the number of channels, and so we can expect that
different program authors will invent different conventions. For either version 1.4 or 2.0 it is not clear to me whether
Stream.length represents the number of frames (L/R stereo pairs) or the number of floats (one stereo pair equals two floats).
-
The use of TxChrono should not be needed. The client sent PTT to the server, and the client should just start
sending Tx audio by itself.
-
The TCI protocol contains a large number of commands that are peculiar to their SunSDR hardware.
There should be a defined subset of commands that would support a data connection to external programs.
-
There is no mechanism to indicate to a client that a command is unavailable except that the command will not be echoed.
There is no mechanism to negotiate acceptable audio sample rate and type parameters between a client and server.
-
The change from older versions like 1.4 to the latest version 2.0 created problems of compatibility,
and TCI provided no solution or upgrade path.
Experiences with WSJT-X
Although I don't like the TCP protocol, my users want to connect to WSJT-X, and it supports TCI. So I felt obliged to add TCI to Quisk.
I have WSJT-X version 3.0 on Linux and version 3.1 on Windows to test. I could not find a technical description of the TCI protocol
as used by WSJT-X (is there one?) so I looked in the source code.
I saw a comment that indicated an early version 1.2 of TCI and I saw the Stream header lacked the number of channels.
So I had to resort to experimentation to figure out how to connect. I discovered the following:
-
WSJT-X only uses these TCI commands:
- The VFO command is the Tx/Rx frequency.
- The SPLIT_ENABLE is always set to False.
- The TRX command changes between Rx and Tx (PTT).
- The AUDIO_START and AUDIO_STOP commands control Rx audio to the client.
-
WSJT-X does not recognize the number of channels in the header array at index 8,
and this can contain garbage when it sends data.
-
The Rx audio stream sent from Quisk to WSJT-X must have two channels with the same data in each channel.
The Stream length is the number of floating point numbers, not the number of left/right pairs.
-
The Tx audio stream sent from WSJT-X to the server is two channels, and the Stream length is the number of floats.
But the binary message received has a data segment twice as large as requested with garbage in the second half.
Experiences with deskHPSDR
I do not have the software defined radio deskHPSDR but I have been in contact with its developer Heiko, DL1BZ.
He has implemented TCI version 2.0 and has successfully connected to the clients JTDX, MSHV RumLogNG and
UT4LW's SDC (Software Defined Connectors) and its build-in CW Skimmer.
He has no interest in supporting protocols earlier than the current version 2.0 of TCI.
He provided the following insights:
-
I only support TCI version 2.0.
Supporting multiple historical TCI versions would significantly increase complexity and testing effort.
For that reason I chose to focus on the current protocol version.
-
The Stream.length field must be interpreted as the number of floating point values, not the number of stereo frames.
-
I agree that older implementations cannot be trusted to provide a valid channels value.
-
While testing SDC CW Skimmer I encountered a 650 Hz offset in CWU/CWL operation.
The root cause turned out to be an interaction between CW sidetone frequency and the exported IQ stream.
The issue was solved by applying the correction only to the TCI IQ output stream, leaving the receiver DSP chain unchanged.
-
In my opinion, explicit negotiation of AUDIO_STREAM parameters
is highly desirable and should be considered part of modern TCI best practices.
What to Do
Heiko wants to support only TCI 2.0.
I don't disagree, but this creates problems for me. My TCI version 1.4 currently connects to WSJT-X,
and I believe that TCI version 2 will not. So changing to version 2 will anger my users.
The problem goes away when and if WSJT-X upgrades to version 2, but that may never happen.
And there may be other TCI version 1.4 software out there that I don't know about, and I will break it too.
I am not a philosopher, I am an engineer with the mindset of a businessman. I want to make my users happy and
contribute to the advancement of amateur radio. We should be using modern technology, not COM ports and audio hacks.
An ideal solution would be to assemble all authors of client and server software and hash out a protocol that
is simple to implement and satisfies everyone. But this is not an ideal world. I proposed this to FreeDV and
they were too busy with their own software.
But I think that if TCI authors follow the "TCI Best Practices", then all TCI 2.0 users will connect successfully
and I can figure out a way to support older clients.
TCI Best Practices
Please use these Best Practices to increase the chances of a successful connection.
This is not a finished document. Please suggest changes.
-
All new TCI client and server software will be written to the version 2.0 standard available
here.
Older software should be upgraded. But older software may be accommodated if possible.
-
A client using audio streams must specify all parameters at an early time even
if the value is the default.
The first two are not in TCI version 1.4, and so indicate to the server that version 2.0 is active.
These are:
- AUDIO_STREAM_SAMPLE_TYPE
- AUDIO_STREAM_CHANNELS
- AUDIO_SAMPLERATE
-
There is a small set of commands and their meanings that are guaranteed to be available.
This is not a restriction on the server, and the server can implement additional commands as desired.
But clients can depend on the following commands to be available:
- The VFO command is the Tx/Rx frequency.
- The SPLIT_ENABLE enables split Rx and Tx frequencies (??).
- The TRX command changes between Rx and Tx and is equivalent to push-to-talk.
- The AUDIO_START and AUDIO_STOP commands control Rx audio from the server to the client.
- More commands need to be added, but only if they are in use by real clients.
-
The Websocket message length is available. An audio frame has a 64-byte header and a data area.
So for an audio message with two channels (stereo) and a Stream.length of 1024, the message length with FLOAT32 samples
is 64 bytes + 1024 * sizeof(float). And it represents 512 frames or I/Q pairs.
But note that WSJT-X can send a bad message length.
-
The MODULATION must be known strings. These are:
- am, amplitude modulation
- lsb, lower sideband
- usb, upper sideband
- cw, CW received as upper sideband.
- nfm, narrow FM
- digl, digital lower sideband (different from lsb?)
- digu, digital upper sideband (different from usb?)
- Other modulations sam, dsb, wfm, spec, and drm may be available.
-
TCI specifies that case does not matter. But clients and servers should use lower case except for
strings such as DEVICE.
-
TCI 1.4 only has a FLOAT32 data type. Version 2.0 has more. Integer byte order is little-endian.
These data types are always available:
-
TCI 1.4 only has a 48000 sample rate. Version 2.0 has more. These sample rates are always available:
Further Work
I need the support of TCI client and server software authors to make this work. I welcome feedback, both positive and negative.
Please let me know what you think on the news group named above.
Jim Ahlstrom, N2ADR