Skip to content

feat(tci): add tx_gain command + ALC field in tx_sensors#2950

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
nigelfenton:feat/tci-txgain-alc
May 23, 2026
Merged

feat(tci): add tx_gain command + ALC field in tx_sensors#2950
ten9876 merged 1 commit into
aethersdr:mainfrom
nigelfenton:feat/tci-txgain-alc

Conversation

@nigelfenton

@nigelfenton nigelfenton commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Two small TCI-layer additions that give external tooling the hooks to characterise TX drive — sweeping drive against forward power and ALC. (Motivating use: finding the WSJT-X digital-mode drive sweet spot — maximum clean output with ALC held near zero.)

Changes

tx_gain TCI command — AetherSDR extension (not TCI v2.0 spec), range 0-100. Gets/sets TciServer's outbound TX gain (m_txGain), applied to WSJT-X/JTDX audio before the radio. GET reads the persisted TciTxGain; SET stashes a pending value that TciServer applies via setTxGain() after handleCommand() — the same indirection cmdVolume uses, since TciProtocol has no TciServer handle. Makes the TX-drive level scriptable.

alc field in tx_sensors — appends a trailing alc_dbfs field (SW-ALC peak, from MeterModel::swAlcChanged) to the periodic tx_sensors broadcast. Trailing position is backward compatible — index-based parsers ignore the extra field.

Verification

  • Builds clean (647/647) on Linux / Qt 6.4.2.
  • Binary launches and runs.
  • Runtime-verified on live RF (FlexRadio): a calibration harness exercised both hooks across a full TX-drive sweep — tx_gain SET/GET and the tx_sensors alc field. See the verification comment below for evidence.

Test plan

  • Compiles + links clean
  • Binary launches
  • tx_gain; GET / tx_gain:N; SET round-trip over a TCI client
  • alc field seen in tx_sensors during TX

Two TCI-layer additions that support automated TX-drive calibration
tooling (sweeping drive against forward power and ALC).

tx_gain (AetherSDR extension, 0-100): gets/sets TciServer's outbound TX
gain (m_txGain), applied to WSJT-X/JTDX audio before the radio. GET reads
the persisted TciTxGain value; SET stashes m_pendingTxGain and TciServer
applies it via setTxGain() after handleCommand() returns — the same
indirection cmdVolume uses, since TciProtocol cannot reach TciServer.

tx_sensors: append a trailing alc_dbfs field (SW-ALC peak, sourced from
MeterModel::swAlcChanged). Trailing position keeps it backward compatible
— index-based parsers ignore the extra field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nigelfenton

Copy link
Copy Markdown
Contributor Author

Runtime verification — both hooks exercised on live RF (FlexRadio).

A calibration harness drove these hooks directly: it steps tx_gain via the new command and reads the tx_sensors alc field at each step.

  • tx_gain — stepped 10→100 across a full sweep; each SET took effect (forward power and ALC tracked it). tx_gain; GET returns the persisted value.
  • tx_sensors alc — read live during TX throughout; produced a clean, monotonic, run-to-run-repeatable SW-ALC curve, e.g.:
tx_gain   fwd(W)   alc(dBFS)
   30       25       -11.4
   50       58        -7.0
   70       77        -4.1
  100       79        -1.0

Test plan:

  • Compiles + links clean
  • Binary launches
  • tx_gain; GET / tx_gain:N; SET round-trip over a TCI client
  • alc field seen in tx_sensors during TX

(Test build also carried the #2814 fix from #2946, so repeated trx: keying + client disconnect across the sweep wouldn't trip that unrelated use-after-free.)

@nigelfenton nigelfenton marked this pull request as ready for review May 22, 2026 06:38

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, focused addition — thanks @nigelfenton. Pattern review is clean: cmdTxGain mirrors cmdVolume precisely (pending-int stash, AppSettings-backed GET, notification broadcast via m_pendingNotification), and m_pendingTxGain resets at the top of handleCommand like its sibling. The tx_sensors field is appended trailing as the PR description says, which is index-safe for existing parsers. Scope is tight — all four files are TCI core. Uses AppSettings, QStringLiteral, qBound, std::clamp consistent with the surrounding code.

One minor thing worth a look (not blocking):

m_cachedAlc{0.0f} initial value (TciServer.h:181)

ALC is dBFS, where 0.0 is clipping. If a TCI client polls tx_sensors in the very small window between isTransmitting() going true and the first swAlcChanged emission, the broadcast will report alc_dbfs=0.0 — i.e. apparent clip indication, not "unknown". That window should be tiny in practice (MeterModel updates fast during TX) and your harness presumably wouldn't have caught it, but for symmetry with m_cachedMicLevel{-50.0f} consider an obviously-not-real sentinel like -50.0f or lower. Trivial to change.

Other notes:

  • setTxGain() persists but doesn't itself broadcast tx_gain:N; to TCI clients — that's fine here because the SET path goes through m_pendingNotification, which the existing loop at TciServer.cpp:577–580 already broadcasts to other clients. Just flagging it in case a future non-TCI caller (UI etc.) calls setTxGain() directly and we want connected TCI clients to learn about it; that'd be a separate change.
  • The GET reads TciTxGain from AppSettings rather than the in-memory m_txGain — same pattern cmdVolume uses (TciProtocol has no TciServer handle), and setTxGain() saves synchronously, so they stay in lockstep. ✅

Verified the new connect(&meterModel(), &MeterModel::swAlcChanged, …) sits inside the existing if (m_model) block, so no null-deref risk. Looks good to me modulo the init value.

@ten9876 ten9876 merged commit 3095a33 into aethersdr:main May 23, 2026
5 checks passed
@ten9876

ten9876 commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Claude here — merged. Thanks @nigelfenton, second careful TCI PR
in a row.

What stood out on review:

The mirror of `cmdVolume`'s indirection pattern — protocol stashes
`m_pendingTxGain`, server applies via existing `setTxGain()` after
`handleCommand` returns — kept the new code structurally identical
to existing patterns rather than inventing a new mechanism. Future
TCI extensions that need TciServer side effects from TciProtocol
have an obvious template to follow.

The trailing-field policy for `tx_sensors` extension is the right
call (and you documented the rationale in source). Index-based
parsers in WSJT-X / JTDX won't break on the extra `alc_dbfs`
field as long as it stays at the tail. Worth codifying as a TCI
telemetry convention if more extensions like this appear.

And the use case is concrete and operationally useful — a
calibration harness that finds the WSJT-X drive sweet spot
(max clean output, ALC near zero) without a human watching meters.
Real harness-verified on FlexRadio, not just compile-clean.

Also appreciate the cleanness with respect to #2946 — no new
`invokeMethod` callsites in TciProtocol, so no new UAF surface
introduced. The auto-merge with #2946 was conflict-free.

One small follow-up worth knowing about: `m_cachedAlc` defaults to
`0.0f`, which conventionally reads as "ALC at full scale." It's
gated on isTransmitting() before broadcast so the default never
reaches clients today, but if anyone removes that gate the misleading
value would surface. Trivial fix when convenient (sentinel like
`-200.0f`); not worth a follow-up PR on its own.

Ships in v26.5.3.

73,
Jeremy KK7GWY & Claude (AI dev partner)

nigelfenton added a commit to nigelfenton/tci-monitor that referenced this pull request May 23, 2026
- New ### TX Cal subsection under Toolkit tabs, sibling to Inspect /
  Console / Compare / Replay.  Covers what the panel does, the
  shipped safety model (arm/dry-run/watchdog/UNKEY/auto-DIGU), the
  self-identifying CSV header (build identity + AE identity +
  no-carrier WARNING), and the upstream AetherSDR PR requirement
  (aethersdr/AetherSDR#2950 for tx_gain + ALC).
- Embedded a docs/tx-cal-curve.png screenshot captured against
  Mac AE / Flex 6300 (tx_gain=34, 30.7 W, ALC -10.3 dBFS) showing
  the smoothed twin-axis chart with the glow markers and the
  alongside "how to apply" guidance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ten9876 added a commit that referenced this pull request May 24, 2026
… + @G6PWY-Chris (#3067)

Two attribution errors in the v26.5.3 release CHANGELOG:

1. @M7HNF-Ian (Ian, separate GitHub account) → @nigelfenton (Nigel
   Fenton, G0JKN) — Nigel authored all 5 of the commits I bucketed
   under that line: TCI Network dialog tab (#2879), tx_gain + ALC
   (#2950), use-after-free fix (#2946), vfo emit on band-change
   (#2828), mic-capture suppress during TCI feed (#2982).

2. @chrisb1964 → @G6PWY-Chris (Chris G6PWY) — single commit author
   in the cycle was the latter; chrisb1964 is unrelated.

Reported by Nigel directly.  All other handles verified against
gh api repos/.../commits/<sha>.

GitHub release notes already updated; this lands the same fix in
the in-repo CHANGELOG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nigelfenton nigelfenton deleted the feat/tci-txgain-alc branch May 25, 2026 18:36
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
)

## Summary

Two small TCI-layer additions that give external tooling the hooks to
characterise TX drive — sweeping drive against forward power and ALC.
(Motivating use: finding the WSJT-X digital-mode drive sweet spot —
maximum clean output with ALC held near zero.)

## Changes

**`tx_gain` TCI command** — AetherSDR extension (not TCI v2.0 spec),
range `0-100`. Gets/sets `TciServer`'s outbound TX gain (`m_txGain`),
applied to WSJT-X/JTDX audio before the radio. GET reads the persisted
`TciTxGain`; SET stashes a pending value that `TciServer` applies via
`setTxGain()` after `handleCommand()` — the same indirection `cmdVolume`
uses, since `TciProtocol` has no `TciServer` handle. Makes the TX-drive
level scriptable.

**`alc` field in `tx_sensors`** — appends a trailing `alc_dbfs` field
(SW-ALC peak, from `MeterModel::swAlcChanged`) to the periodic
`tx_sensors` broadcast. Trailing position is backward compatible —
index-based parsers ignore the extra field.

## Verification

- Builds clean (`647/647`) on Linux / Qt 6.4.2.
- Binary launches and runs.
- **Runtime-verified on live RF** (FlexRadio): a calibration harness
exercised both hooks across a full TX-drive sweep — `tx_gain` SET/GET
and the `tx_sensors` `alc` field. See the verification comment below for
evidence.

## Test plan
- [x] Compiles + links clean
- [x] Binary launches
- [x] `tx_gain;` GET / `tx_gain:N;` SET round-trip over a TCI client
- [x] `alc` field seen in `tx_sensors` during TX

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
… + @G6PWY-Chris (aethersdr#3067)

Two attribution errors in the v26.5.3 release CHANGELOG:

1. @M7HNF-Ian (Ian, separate GitHub account) → @nigelfenton (Nigel
   Fenton, G0JKN) — Nigel authored all 5 of the commits I bucketed
   under that line: TCI Network dialog tab (aethersdr#2879), tx_gain + ALC
   (aethersdr#2950), use-after-free fix (aethersdr#2946), vfo emit on band-change
   (aethersdr#2828), mic-capture suppress during TCI feed (aethersdr#2982).

2. @chrisb1964 → @G6PWY-Chris (Chris G6PWY) — single commit author
   in the cycle was the latter; chrisb1964 is unrelated.

Reported by Nigel directly.  All other handles verified against
gh api repos/.../commits/<sha>.

GitHub release notes already updated; this lands the same fix in
the in-repo CHANGELOG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants