aethersdr / aethersdr/AetherSDR

HL2: TX Delay (and the other Transmit timing fields) cannot hold a value — dead Flex command plane control

Open
#5,370 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug external devices GUI maintainer-review safety
Dominant language
C++
Stars
221
Forks
117
Avg merge
2d 7h
Merged PRs (30d)
299

Description

Summary

On a Hermes-Lite 2, Radio Setup → Transmit → Timings → "TX Delay" cannot hold a value. Typing one and leaving the field reverts it to 0. The control is present, editable and looks functional, but nothing it does reaches the radio.

This is the HERMES.md §17 dead-control shape — "the GUI drove a Flex command plane that this radio does not have, so the widget moved, the setting persisted, and nothing reached the hardware" — except here it does not even persist. Same defect family as #5277 (split toggle sending dead Flex commands on HL2/Icom).

Flagging it as safety-adjacent rather than cosmetic: TX Delay is the control an operator would reach for to sequence an external amplifier, and a value sitting in the box implies a PTT-to-RF delay that is not being applied.

Reproduction

  1. Connect a Hermes-Lite 2
  2. Radio Setup → Transmit → Timings (in ms)
  3. Enter any value in TX Delay and press Tab/Enter
  4. The field returns to 0

The log shows the command being discarded:

WRN aether.protocol: RadioModel: no command plane for this backend, dropping interlock set tx_delay=30

Cause

The field commits through the Flex command plane (RadioSetupDialog.cpp):

connectTimingField(txDelayEdit, "tx_delay");
// ->
m_model->sendCommand(QString("interlock set %1=%2").arg(key).arg(val * scale));

RadioModel::sendCommand drops it for any backend without a command plane (RadioModel.cpp, the kNoCommandPlaneCode path), so the radio is never told and TransmitModel::m_txDelay never changes from its {0} default.

The field is seeded once at construction from that same model value:

auto* txDelayEdit = addTimingField(0, 1, "TX Delay:", tx.txDelay());

Nothing writes back to the model except a radio confirming the change, so on any rebuild of the page the field reseeds to 0. The value is structurally incapable of sticking on this radio.

The neighbouring fields in the same group (ACC TX, RCA TX1/2/3, Timeout) commit through the identical helper and will behave the same way.

Notes for whoever picks this up

There is no obvious HL2 equivalent to delegate to. The gateware does implement a key-down delay — cw_ptt_delay, 0–255 ms, at C&C address 0x0f (gateware/rtl/cw_openhpsdr.v) — but it lives in the internal-keyer module, and AetherSDR keys host-side by generating shaped CW IQ under MOX, so that register is not in the path. deskHPSDR sends it (default 30 ms) because it drives the gateware keyer.

That suggests two independent decisions, and I have deliberately not assumed either:

  1. Short term — hide or disable the timing group for backends with no command plane, so the control stops implying protection that is not there.
  2. Longer term — whether a host-side TX delay is wanted for non-Flex backends (assert MOX, clock silence for N ms, then release audio/carrier). Worth noting Hl2Backend::setTune currently does the opposite by design, bringing the carrier up before keying so no time is wasted at the start of an over — sensible barefoot, less so with an amplifier downstream.

Happy to implement whichever direction you prefer, but this seemed like a design call rather than something to arrive with a patch for.

Environment

  • AetherSDR v26.9.1, Linux (Arch), Qt 6.11.2, built from source
  • Hermes-Lite 2, gateware v7.5 (0x4b), board id 0x06, N2ADR filter board + HL2 IO Board

Follow-up: the guard already exists in this same file

RadioModel::usesFlexCommandPlane() is the established predicate for exactly this
(RadioModel.h), and its comment states the rule directly — "Callers that must
work on both planes branch on this rather than on the family name."

bool usesFlexCommandPlane() const { return m_family == QLatin1String("flex"); }

The callsign field ~900 lines above the timing group in RadioSetupDialog.cpp
already applies it, and shows the shape a field is expected to have:

if (m_model->usesFlexCommandPlane()) {
    m_model->sendCommand("radio callsign " + entered);
}
m_model->setStationCallsign(entered);   // persists regardless of plane
m_callsignEdit->setText(entered);       // and the UI keeps the value

connectTimingField does neither half: it sends unconditionally and has no
local persistence, so on a non-Flex radio the command is dropped and nothing
retains the value. That is why the field reverts rather than merely being
inert — it is the callsign pattern with both safeguards missing.

Worth noting the two halves are separable. Guarding the send and persisting the
value would stop the field lying about what it holds, but a persisted TX delay
that still does not delay anything would arguably be worse than one that visibly
reverts — an operator would reasonably read a retained 30 ms as amp protection.
So the UI fix probably should not land ahead of a decision on whether non-Flex
backends get a real delay, or the group should simply be hidden when
usesFlexCommandPlane() is false.


🤖 Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading RadioSetupDialog.cpp, RadioModel.h/.cpp, and the existing callsign handling that uses usesFlexCommandPlane(). Reproduce the TX Delay reset and inspect the non-command-plane path, then resolve whether non-Flex timing should be hidden, disabled, or implemented differently. Done requires an agreed behavior for all neighboring timing fields and verification that the UI no longer implies an unapplied safety delay.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend, desktop
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.