Difficulties of the project

PROJECT JOURNEY — PART 2

Difficulties of the project

The technical challenges, dead ends, and hard-won solutions encountered during the development of DXLab – Thetis Bridge.

AUTHOR Nuno Lopes CT2IRY
LOCATION Lisbon, Portugal
DATE April 2026
STATUS Public release v7.1.0

Phase 3 — The limitation discovered

By v7.1.0, Bridge worked reliably for the UDP spot path. However, one limitation remained: the UDP broadcast omits background colours. Bridge’s local DXView cache compensated for this, but the architecture would be cleaner — and more future-proof — if the spots arrived from Commander already fully coloured.

Investigation revealed that Commander already does this for one specific case: when configured to control a Flex Signature radio, Commander pushes spots to the radio over a TCP pipeline that carries both foreground and background colours. The protocol is undocumented but observable.

This discovery led to the focused two-week reverse-engineering effort documented below.

The challenge of reverse-engineering

Commander’s Flex protocol is essentially SmartSDR’s command set — well-known to FlexRadio owners but undocumented in any form Bridge could consume directly. There is no specification published; the protocol is observable only by capturing traffic between Commander and a real Flex transceiver.

Compounding the challenge: Commander only pushes spots to the radio after a specific handshake sequence completes successfully. Any protocol violation in the handshake causes Commander to silently abandon the subscription — with no error message and no spot delivery. There was no feedback of any kind when something was wrong.

Iterative implementation

A local-only fork of the C# Bridge was used to implement the Flex emulator. The work proceeded in roughly this order:

  • Initial burst — TCP listener accepting Commander as client; emitting V/H/M lines, S|radio identification, filter_sharpness lines, interlock pair, panadapter, slice
  • Command ACK loop — receiving Commander’s commands (sub pan all, sub slice all, sub spot all, info, meter list, sub tx all, keepalive disable) and ACKing each correctly
  • R5 info response — multiple iterations to get the format exactly right: comma-separated, quoted strings, all required fields including model, chassis_serial, name, callsign, software_ver
  • Sub-client-all handler — when Commander sends sub client all, Bridge must ACK and immediately emit a client connected status broadcast with a freshly generated UUID
  • Client bind handler — Commander echoes the UUID back; Bridge ACKs
  • Spot parser — receive spot add directives with callsign, color, background_color, priority, lifetime_seconds, rx_freq, mode, comment fields
  • Spot ID assignment — assign monotonically increasing internal ID per spot, ACK with the ID
  • TCI forward — translate the spot to TCI’s SPOT command and send via WebSocket to Thetis on port 50001
  • Spot remove and clear handlers — forward equivalent removals to TCI

Bugs found and fixed

Several non-obvious bugs were discovered and resolved through iterative testing:

The VB6 first-character skip artifact — Commander, written in VB6, has a parser idiom that skipped the first character of unquoted values in some contexts. Initial code compensated by duplicating first characters, which broke .NET-side parsing. Fixed by adopting the real Flex’s quoted-string format — the opening quote naturally absorbs the VB6 skip.

Client connected emitted in the wrong place — an early implementation emitted the client-connected status broadcast in the initial burst. The real Flex emits it only after sub client all. Commander parsed the premature emission as a protocol violation and silently aborted the subscription.

Both interlock variants required — the Flex protocol has two distinct interlock messages with different field sets: S0|interlock (global TX state) and S|interlock (per-radio hardware config). Initially only one was emitted; both are needed to match what the real radio sends.

Comment field whitespace handling — the naive split-on-whitespace parsing truncated comments at the first space. Fixed with a regex that captures comment= to end-of-line.

UI thread saturation under sustained load — at 500+ spots per minute during contests, the WPF Dispatcher queue from the in-app log sink could back up, freezing the UI. The file-based log continued to work normally throughout — the issue was visual only. Queued for post-demo polish: rate-limit the UI sink, batch dispatches at 100ms intervals.

On reverse-engineering with limited resources

The Flex protocol was reconstructed from a single 1.8 MB packet capture, without access to FlexRadio’s source code or formal specification. This was possible because:

  • The protocol is text-based, not binary — making capture analysis tractable with grep and pattern recognition
  • Commander’s verbose debug logging exposes its own parsing decisions, including which fields it reads, which conditions trigger flag changes, and which command sequences follow which events
  • The DXLab author’s commitment to long-lived, stable wire formats means that protocol behaviour observed in 2026 reliably reflects how the protocol behaved years earlier
  • Iterative testing — implement a hypothesis, deploy, observe failure mode, refine — converged on a working implementation in fewer iterations than expected

The critical version string

One of the most important discoveries during the reverse-engineering effort was the role of a single field in the R5 info response:

software_ver=4.1.5.39794

This version-string field is parsed by Commander to decide whether to enable spot delivery to the radio. Without the exact correct version string matching the N3GZ capture, Commander would complete the handshake but silently never push spots. This took multiple iterations to identify as the gating condition.

Scroll to Top