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
+37 -1
View File
@@ -67,7 +67,7 @@ type
rfVolume, rfMute, rfDrive, rfAtten,
rfBand, rfXvtr, rfBandRestore, // rfBandRestore: явная смена диапазона (сброс Wf-avg в UI)
rfCenterFreq, rfSpan, rfSampleRate,
rfCTun, rfDuplex,
rfCTun, rfDuplex, rfRxMuteOnTx, // rfRxMuteOnTx: QO-100 self-monitor toggle
rfRunning, rfConnected, rfTransmitting, rfTuning,
rfNR, rfNB, rfSNB, rfANF,
rfWfAGC, rfWfNF, rfDither, rfRandom,
@@ -217,6 +217,9 @@ type
FTuning: Boolean;
FSplitTxB: Boolean;
FDisplayDuplex: Boolean;
// QO-100 full-duplex self-monitor: True — RX-аудио глушится на TX (по умолч.),
// False — слышим свой downlink со спутника при передаче. Кнопка RX MUTE.
FRxMuteOnTx: Boolean;
FHWPTTActive: Boolean;
FHWPTTStartedTX: Boolean;
FWebClientActive: Boolean; // выставляет web-слой (connect/disconnect клиента)
@@ -496,6 +499,8 @@ type
// Отображение / приём
procedure SetCTun(On_: Boolean);
procedure SetDuplex(On_: Boolean);
procedure SetRxMuteOnTx(On_: Boolean); // QO-100 self-monitor (кнопка RX MUTE)
procedure ApplyRxMuteToEngine; // FRxMuteOnTx → FDSPEngine.MonitorRXDuringTX
procedure SetWfAGC(On_: Boolean);
procedure SetWfNF(On_: Boolean);
@@ -552,6 +557,8 @@ type
function BandPlan: TBandPlanArray;
// True если активный бэкенд — Pluto (VHF/UHF band-план вместо HF).
function IsPluto: Boolean;
// True — активен QO-100 (Pluto + full-duplex XVTR-слот); гейт UI beacon/RX MUTE.
function InQO100: Boolean;
// Человекочитаемое имя подключённой платы для статус-строки (UI/Web).
// HPSDR → тип платы; Pluto/LibreSDR → hw_model + серийник (если есть).
function BoardDisplayName: string;
@@ -593,6 +600,7 @@ begin
FMode := 1; FFilter := 5; FFilterBW := 2700;
FAGCMode := 1; FAGCTop := 90;
FRxGainMode := 2; FRxGainDb := 40; // Pluto: slow_attack по умолчанию
FRxMuteOnTx := True; // QO-100 self-monitor выкл по умолчанию (RX заглушён на TX)
FVolume := 70; FDrivePercent := 50; FDriveLevel := 0; FAtten := 0;
FCenterFreq := FVfoA; FSpanHz := 192000; FSampleRate := 192000;
FCurrentBand := 5; FCurrentXvtr := -1;
@@ -1552,6 +1560,7 @@ begin
Result.WidebandFill := FWidebandFill;
Result.SpectrumFill := FSpectrumFill;
Result.DisplayDuplex := FDisplayDuplex;
Result.RxMuteOnTx := FRxMuteOnTx;
Result.AudioOutDevice := FAudioOutDevName;
Result.AudioInDevice := FAudioInDevName;
Result.SendAudioToRadio := FSendAudioToRadio;
@@ -2371,6 +2380,21 @@ begin
Changed(rfDuplex);
end;
procedure TRadioController.ApplyRxMuteToEngine;
begin
// Монитор имеет смысл только в full-duplex (KeepRXDuringTX); сам гейт в движке
// это учитывает. Здесь просто транслируем инверсию флага RX MUTE.
if FWDSPReady and Assigned(FDSPEngine) then
FDSPEngine.MonitorRXDuringTX := not FRxMuteOnTx;
end;
procedure TRadioController.SetRxMuteOnTx(On_: Boolean);
begin
FRxMuteOnTx := On_;
ApplyRxMuteToEngine;
Changed(rfRxMuteOnTx);
end;
procedure TRadioController.SetWfAGC(On_: Boolean);
begin FWfAGCEnabled := On_; Changed(rfWfAGC); end;
@@ -2783,6 +2807,8 @@ begin
FNetwork.SetSpeakerAudio(FSendAudioToRadio);
// RX hw-gain в бэкенд (Pluto). HPSDR — no-op.
ApplyRxGainToBackend;
// QO-100 self-monitor: RX-аудио на TX (full-duplex). По умолчанию заглушено.
ApplyRxMuteToEngine;
SendDUCSpecificFromSettings;
// Применяем TX-цепь (фильтр, mic gain, EQ, leveler, ALC, comp, ...).
ApplyTXSettingsToDSP;
@@ -2949,6 +2975,7 @@ begin
FWidebandFill := FLoadedGlobal.WidebandFill;
FSpectrumFill := FLoadedGlobal.SpectrumFill;
FDisplayDuplex := FLoadedGlobal.DisplayDuplex;
FRxMuteOnTx := FLoadedGlobal.RxMuteOnTx;
// Настройки дисплея/сетки + DSP display + аудио-устройства
if FLoadedGlobal.FFTSize > 0 then
begin
@@ -3177,6 +3204,15 @@ begin
Result := (FNetwork <> nil) and (FNetwork.Caps.Kind = bkPluto);
end;
function TRadioController.InQO100: Boolean;
// True — активен full-duplex транспондер (QO-100): Pluto + выбран XVTR-слот с
// флагом FullDuplexTx. Гейт для UI-элементов, осмысленных только в этом режиме
// (beacon-lock, RX MUTE self-monitor).
begin
Result := IsPluto and (FCurrentXvtr >= 0)
and FXvtrSettings.Entries[FCurrentXvtr].FullDuplexTx;
end;
function TRadioController.BoardDisplayName: string;
var
Dev: THPSDRDevice;