Skip to content

Fix TGXL presence when detected via direct TCP only (Fixes #2174)#2250

Merged
ten9876 merged 2 commits intoten9876:mainfrom
chrisb1964:fix/tgxl-direct-presence-2174
May 1, 2026
Merged

Fix TGXL presence when detected via direct TCP only (Fixes #2174)#2250
ten9876 merged 2 commits intoten9876:mainfrom
chrisb1964:fix/tgxl-direct-presence-2174

Conversation

@chrisb1964
Copy link
Copy Markdown
Contributor

Summary

Fixes #2174 — TUN applet not visible when the radio (Flex-8600 firmware 4.2.18) does not report the TGXL via the amplifier API.

Root cause

presenceChanged(true) was only emitted from setHandle(), which requires the radio to push amplifier <handle> model=TunerGeniusXL. The existing directConnectionChanged signal fired correctly on direct TCP connect (port 9010) but was never wired to presenceChanged — leaving the TUN applet permanently hidden even though the device was fully operational.

Confirmed by protocol log: on this firmware the radio never sends an amplifier status for the TGXL. The PGXL is reported normally (model=PowerGeniusXL).

Changes

TunerModel.h/.cpp — add m_directPresence fallback flag:

  • isPresent() returns !m_handle.isEmpty() || m_directPresence
  • connected lambda: sets m_directPresence = true, emits presenceChanged(true) if handle is absent
  • disconnected lambda: clears m_directPresence, emits presenceChanged(false) only if handle is also empty
  • setHandle() uses isPresent() for before/after comparison so clearing the handle does not hide the applet while a direct connection is live

RadioModel.cpp — two diagnostic/correctness improvements:

  • Log every amplifier status handle + model at lcProtocol debug level (previously silent on no-match, making firmware-variant bugs undiagnosable)
  • Handle amplifier removal via kvs.contains("removed"), clearing TunerModel handle or m_ampHandle/m_hasAmplifier as appropriate (previously only cleared on radio disconnect)

Testing

Flex-8600 firmware 4.2.18.41174, TGXL firmware 1.1.20, AetherSDR v0.9.4.

TUN applet visible with direct TCP connection

TUN applet now appears, relay positions display correctly, TUNE initiates autotune cycle. Operate/standby via radio API requires a valid amplifier handle (unavailable when the radio omits the amplifier status message) — this is a pre-existing limitation tracked separately.

Notes for reviewer

  • RadioModel.cpp is touched only for logging and the removal handler — no behavioural change to the existing TGXL/PGXL routing logic
  • The m_directPresence flag is intentionally separate from m_handle so the two presence sources don't interfere with each other

When the radio does not report the TGXL via the amplifier API (observed
on Flex-8600 firmware 4.2.18), the TUN applet never appeared even though
the direct TCP connection on port 9010 was fully operational.

Root cause: presenceChanged(true) was only emitted from setHandle(), which
requires the radio to send "amplifier <handle> model=TunerGeniusXL".
The existing directConnectionChanged signal was emitted on direct TCP
connect but never wired to presenceChanged.

Fix (3 parts):

1. TunerModel: add m_directPresence flag, set it when direct TCP connects,
   emit presenceChanged(true/false) from the connected/disconnected lambdas.
   isPresent() now returns true if either the radio handle or direct
   connection is active. setHandle() uses isPresent() so clearing the
   handle does not hide the applet while a direct connection is live.

2. RadioModel: log every amplifier status handle + model at lcProtocol
   debug level. Previously unrecognised models were silently dropped,
   making firmware-variant bugs impossible to diagnose from logs.

3. RadioModel: handle amplifier removal via kvs.contains("removed"),
   clearing TunerModel handle or m_ampHandle/m_hasAmplifier as appropriate.
   Previously amplifier state was only cleared on radio disconnect.

Tested: Flex-8600 firmware 4.2.18.41174, TGXL firmware 1.1.20.
TUN applet now appears and relay/TUNE controls work. Operate/standby
via radio API requires a valid handle (not available when radio omits
the amplifier status) — tracked separately.
The radio sends object-removal status in two forms — verified against
FlexLib Radio.cs:14060/14073, which uses substring `s.Contains("removed")`
to handle both:

  1) bare:  "amplifier <handle> removed"        (no '=' anywhere)
  2) kvs:   "amplifier <handle> ... removed=1"  (key in kvs map)

Our CommandParser splits status on the last space before the first '=',
so form 1 lands entirely in `object` (kvs is empty), while form 2
splits normally and `removed` ends up as a kvs key.

The original PR only handled form 2. Form 1 is the form actually used
in observed protocol traffic (e.g. `S<h>|stream 0x04000008 removed`),
so add an `ampRemovedRe` regex check before the regular `ampRe` match.

This mirrors the pattern already used by `streamStatusRemoved` at
RadioModel.cpp:155-161.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ten9876
Copy link
Copy Markdown
Owner

ten9876 commented May 1, 2026

Claude here — thanks Chris, this is a great first contribution! 🎉

Solid root-cause analysis on the TGXL detection bug, and the m_directPresence fallback flag is exactly the right shape — keeps the two presence sources (amplifier-API handle vs. direct TCP connection) cleanly independent.

Follow-up we pushed (574f796)

While reviewing, I checked the new amplifier-removal handler against FlexLib's reference implementation (Radio.cs:14060/14073) and our own streamStatusRemoved helper at RadioModel.cpp:155-161. The radio sends removal status in two forms:

  1. bare: amplifier <handle> removed — no = anywhere in the body
  2. kvs: amplifier <handle> ... removed=1removed lands as a key

Our CommandParser splits status on the last space before the first =, so form 1 ends up entirely in object (with kvs empty), while form 2 splits normally. Your check kvs.contains("removed") correctly handles form 2, but form 1 would silently fall through.

Empirical confirmation that form 1 exists — from a recent local log:

S2B141612|stream 0x04000008 removed

We pushed a small follow-up that adds a second regex ampRemovedRe = R"(^amplifier\s+(\S+)\s+removed$)" checked before the regular ampRe. Same removal logic, just both code paths covered.

The TunerModel changes and the diagnostic logging are unchanged — those were exactly right.

Merging now. Thanks again for the careful diagnosis and the clean fix!

73, Jeremy KK7GWY & Claude (AI dev partner)

@ten9876 ten9876 merged commit 8c856e3 into ten9876:main May 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tuner Genius XL not detected

2 participants