feat: QO-100 self-monitor (RX MUTE) + gate beacon/RX-mute buttons to QO-100

Add a per-operator toggle to hear your own downlink off the satellite
while transmitting (full-duplex self-monitor), and restrict QO-100-only
controls to the QO-100 mode.

- WDSPEngine: new MonitorRXDuringTX flag; audio gate becomes
  `not FTXActive or (FKeepRXDuringTX and FMonitorRXDuringTX)` so RX audio
  can pass to speakers during TX in full-duplex.
- Controller: FRxMuteOnTx (default True = mute RX on TX), SetRxMuteOnTx/
  ApplyRxMuteToEngine, rfRxMuteOnTx notify; new InQO100 predicate
  (Pluto + full-duplex XVTR slot) as the single gate for QO-100 UI.
- Settings: global RxMuteOnTx (rx_mute_on_tx, default True), persisted.
- MainForm: new RX MUTE button in TX panel next to DUP, visible only in
  QO-100; BEACON button visibility tightened from "any transverter" to
  InQO100 so it no longer shows in plain VHF/UHF modes.
- doc: plan updated (RF-AGC done, two temps, TX/RX locked split,
  self-monitor, phase 6 done; remaining TODOs incl. web wiring).

Web intentionally left unchanged for now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 19:50:30 +03:00
co-authored by Claude Opus 4.8
parent 6375092741
commit 3bd37b956e
5 changed files with 117 additions and 11 deletions
+14 -1
View File
@@ -223,6 +223,10 @@ type
// (мы выяснили на практике что стоп/старт RXA добавляет задержки на
// Windows без видимого выигрыша).
FKeepRXDuringTX: Boolean;
// Полный дуплекс QO-100: пропускать RX-аудио на колонки и во время TX (слышим
// свой downlink через транспондер). Имеет смысл только при FKeepRXDuringTX.
// По умолчанию False — обычное поведение (RX-аудио глушится на TX).
FMonitorRXDuringTX: Boolean;
// Дедлайн (GetTickCount64), до которого RX-аудио не пишется в FOnAudio.
// Используется на Windows для маскировки «хвоста» TX leakage (radio
// FPGA TX-buffer + PA slew-down → RX1 IQ → audio) после MOX-off.
@@ -461,6 +465,10 @@ type
// RX-анализатор продолжает получать IQ. Должен ставиться UI ДО SetTXRun.
property KeepRXDuringTX: Boolean read FKeepRXDuringTX
write FKeepRXDuringTX;
// Full-duplex self-monitor: при TX пропускать RX-аудио на колонки (QO-100 —
// слышим себя со спутника). Действует только вместе с KeepRXDuringTX.
property MonitorRXDuringTX: Boolean read FMonitorRXDuringTX
write FMonitorRXDuringTX;
// Активный display ID — RX_DISP_ID или TX_DISP_ID (зависит от FTXActive).
// SpectrumView/Waterfall должны читать пиксели именно из этого ID.
property ActiveDisplayID: Integer read FActiveDisplayID;
@@ -788,6 +796,7 @@ begin
FTXMicTail := 0;
FTXActive := False;
FKeepRXDuringTX := False;
FMonitorRXDuringTX := False;
FPostTXMuteUntil := 0;
FActiveDisplayID := RX_DISP_ID;
FTXMicSource := txmsRadio;
@@ -1302,7 +1311,11 @@ begin
// через unsigned-сравнение, GetTickCount64 не зовётся когда дедлайн = 0.
if (FPostTXMuteUntil <> 0) and (GetTickCount64 >= FPostTXMuteUntil) then
FPostTXMuteUntil := 0;
if Assigned(FOnAudio) and not FMuted and not FTXActive
// Full-duplex self-monitor (QO-100): при FMonitorRXDuringTX не глушим RX-аудио
// на TX — слышим свой downlink со спутника. Иначе (по умолчанию) глушим, как
// Thetis/piHPSDR при не-duplex MOX. FPostTXMuteUntil-хвост уважаем в любом случае.
if Assigned(FOnAudio) and not FMuted
and (not FTXActive or (FKeepRXDuringTX and FMonitorRXDuringTX))
and (FPostTXMuteUntil = 0) then
begin
for i := 0 to FAudioBufSize - 1 do