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
+4
View File
@@ -261,6 +261,7 @@ type
WidebandFill: Boolean; // True = fill wideband spectrum with gradient
SpectrumFill: Boolean; // True = fill main spectrum under the curve with gradient
DisplayDuplex: Boolean; // DUP: при TX показывать RX-водопад, TX-фильтр overlay'ем
RxMuteOnTx: Boolean; // QO-100 self-monitor: True=RX-аудио глушится на TX (умолч.)
// --- Display FPS ---
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
// --- Theme ---
@@ -448,6 +449,7 @@ begin
G.WidebandFill := False;
G.SpectrumFill := True;
G.DisplayDuplex := False;
G.RxMuteOnTx := True;
G.DisplayFPS := 60;
G.FreqMhzDigits := 3;
// CAT defaults
@@ -671,6 +673,7 @@ begin
G.WidebandFill := JB(GObj,'wideband_fill',False);
G.SpectrumFill := JB(GObj,'spectrum_fill',True);
G.DisplayDuplex := JB(GObj,'display_duplex',False);
G.RxMuteOnTx := JB(GObj,'rx_mute_on_tx',True);
G.DisplayFPS := JI(GObj,'display_fps',60);
G.LightTheme := JB(GObj,'light_theme',False);
G.FreqMhzDigits := EnsureRange(JI(GObj,'freq_mhz_digits',3), 3, 5);
@@ -774,6 +777,7 @@ begin
JW(O,'wideband_fill',G.WidebandFill);
JW(O,'spectrum_fill',G.SpectrumFill);
JW(O,'display_duplex',G.DisplayDuplex);
JW(O,'rx_mute_on_tx',G.RxMuteOnTx);
JW(O,'display_fps',G.DisplayFPS);
JW(O,'light_theme',G.LightTheme);
JW(O,'freq_mhz_digits',G.FreqMhzDigits);