Add ShackSwitch antenna switch integration (Peripherals tab + applet)#2214
Add ShackSwitch antenna switch integration (Peripherals tab + applet)#2214ten9876 merged 1 commit intoten9876:mainfrom
Conversation
There was a problem hiding this comment.
Nice contribution, @nigelfenton — well-structured PR with clean separation from the existing AG code path. The approach of reusing AntennaGeniusModel for protocol compatibility is solid. A few things to address before merge:
Dangling reference capture in RadioSetupDialog.cpp
In buildPeripheralsTab(), the web UI button handler captures settings by reference:
auto& settings = AppSettings::instance();
// ...
connect(webBtn, &QPushButton::clicked, this, [this, &settings]() {
QString ip = settings.value("SS_ManualIp", "").toString();
// ...
});settings is a local reference on the stack of buildPeripheralsTab(). Capturing it by reference in a lambda that outlives the function (button click handler) is undefined behavior — the lambda holds a dangling reference after the function returns.
Fix: drop the &settings capture and call AppSettings::instance() directly inside the lambda, or capture this only (which you're already doing):
connect(webBtn, &QPushButton::clicked, this, [this]() {
auto& settings = AppSettings::instance();
// ...
});Duplicated ShackSwitch detection logic
The check serial.startsWith("G0JKN") || name.contains("ShackSwitch", Qt::CaseInsensitive) appears in at least 5 locations across 3 files:
AntennaGeniusApplet.cppMainWindow.cpp(lambda + two connect handlers)RadioSetupDialog.cpp(isRealAg, isSsConnected, webBtn handler — 3 times)
The PR description mentions an "isShackSwitch flag on AntennaGeniusModel" but I don't see such a method on the model. Consider adding a static helper or member function on AntennaGeniusModel:
static bool isShackSwitch(const AgDeviceInfo& info) {
return info.serial.startsWith("G0JKN") ||
info.name.contains("ShackSwitch", Qt::CaseInsensitive);
}This avoids the risk of the detection logic drifting between call sites if e.g. a new hardware revision uses a different serial prefix.
Misleading comment on openShackSwitchWebUi
The comment says "Tries Chromium-based browsers with --app flag; falls back to default browser" but the implementation is just QDesktopServices::openUrl(). Either update the comment to match the implementation, or remove the wrapper entirely and call QDesktopServices::openUrl() directly at the call site — the function adds no value as-is.
7-second QTimer::singleShot delay for AG manual connect
In MainWindow.cpp onConnectionStateChanged:
QTimer::singleShot(7000, this, [this, agIp, agPort]() {
if (!m_antennaGenius.isConnected())
m_antennaGenius.connectToAddress(QHostAddress(agIp), agPort);
});A hardcoded 7s delay is fragile — if the ShackSwitch TCP handshake takes longer than expected, or if onConnectionStateChanged fires again during that window (reconnect scenario), this could race. The isConnected() guard helps, but consider:
- Is 7s always long enough for ShackSwitch to complete its connect + enrichment?
- If the user disconnects and reconnects the radio within 7s, the timer from the first connection still fires with stale
agIp/agPortvalues.
A safer pattern would be to cancel the pending timer on disconnect (store the timer ID and call killTimer or use a QTimer member).
Minor: webPort field on AgDeviceInfo
The new webPort field defaults to 0 and is parsed from the UDP beacon's webport key. The beacon validation (info.serial.isEmpty() || info.ip.isNull()) doesn't check webPort, which is correct since it's optional. Just noting that the port validation in the click handler (> 1024 threshold, falling back to SS_WebPort setting, then to 5000) is well thought out.
Everything else looks good — the AppletPanel integration follows the existing AG pattern, the CMakeLists change is minimal, and the model enrichment logic for late UDP beacons is a nice touch. The AgDeviceInfo struct additions (webPort, lastRadioBand getter) are clean and backward-compatible. Thanks for the thorough testing notes and rollback section in the PR description.
Adds a ShackSwitchApplet and Peripherals tab that auto-detects and controls a ShackSwitch v2.0 antenna switch (Arduino Uno Q) via the existing Antenna Genius AG protocol. Features: - Auto-detection from AG UDP beacon (webport field used for Settings URL) - Peripherals tab in RadioSetupDialog lists connected ShackSwitch devices - ShackSwitchApplet shows up to 8 antenna ports as labelled buttons; active port highlighted; clicking switches the antenna - SO2R / dual-radio mode: Input A and Input B shown side by side with independent port selection and interlock awareness - Single-radio mode (R4 hardware): Input B UI hidden automatically; determined from AG prologue version field at connect time - Dummy-load / deselect: pressing the active port button deselects it - Conflict indicator: flags when both inputs share the same port - Settings button opens the ShackSwitch web UI in the default browser - Port count inferred from AG TCP prologue (4-port R4 vs 8-port Uno Q) - isShackSwitch flag on AntennaGeniusModel drives all conditional UI Hardware tested: - ShackSwitch R4 1x4 (Arduino Uno R4 WiFi, single radio, 4 ports) - ShackSwitch v2.0 (Arduino Uno Q, SO2R, 8 ports, MCP23017 matrix) - AetherSDR on Windows 11, FlexRadio 6700 as SDR source
Summary
Adds native support for the ShackSwitch open-source Arduino antenna switch in AetherSDR via a new Peripherals tab and ShackSwitchApplet.
ShackSwitch uses the existing Antenna Genius AG protocol, so no new protocol support is required — auto-detection works via the AG UDP beacon.
What's added
Hardware tested
Rollback
The feature is entirely self-contained —
ShackSwitchApplet.cpp/.hcan be deleted and a singlegit revertremoves all changes to existing files cleanly.