Phase 3 (batch 18): move MOX (TX core) into the controller

SetMOX now carries the TX engine/network logic: DoNotTx/RXOnly safety
gates, drive recalc, the PTT relay sequence (relay-before-PTT on TX-on,
PTT-before-relay on TX-off), mic-source selection (web/HW-PTT/default),
KeepRXDuringTX, SetTXRun, and the non-DUP TX->RX FlushRX + post-TX mute.
New controller fields FWebClientActive (mirrored from the web server in
the thin ApplyMOX wrapper) and FWebMicActive (moved from MainForm), plus
DefaultMicSource. UI render (BtnMOX style, FSpecView TX overlay/mode,
grid, FDUCPendingCount reset) moved to OnControllerState(rfTransmitting).

Also fix a pre-existing status-bar bug: web TX was labelled 'TX radio
mic' (the old if/else folded any non-soundcard source into radio); now
a case shows 'TX web mic' for txmsWeb.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-05 16:35:54 +03:00
co-authored by Claude Opus 4.8
parent c249b46b17
commit 603e0850cf
2 changed files with 117 additions and 101 deletions
+87 -1
View File
@@ -159,6 +159,8 @@ type
FDisplayDuplex: Boolean;
FHWPTTActive: Boolean;
FHWPTTStartedTX: Boolean;
FWebClientActive: Boolean; // выставляет web-слой (connect/disconnect клиента)
FWebMicActive: Boolean; // True если текущий TX идёт через txmsWeb
FSendAudioToRadio: Boolean;
FAudioOutDevName: string;
FAudioInDevName: string;
@@ -223,6 +225,10 @@ type
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
function CalcDriveByte: Byte;
// Источник микрофона по умолчанию для MOX-кнопки/Web-TX: звуковая карта
// если настроена, иначе HW mic-стрим трансивера. HW PTT → txmsRadio в SetMOX.
function DefaultMicSource: TTXMicSource;
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
// MakeBandSettings — снимок текущего состояния в запись диапазона.
// SaveCurrentBand — сохраняет текущее состояние в кэш+конфиг (или в XVTR-слот).
@@ -1251,8 +1257,88 @@ begin
Changed(rfANF);
end;
function TRadioController.DefaultMicSource: TTXMicSource;
begin
if FAudioInDevName <> '' then Result := txmsSoundCard
else Result := txmsRadio;
end;
procedure TRadioController.SetMOX(On_: Boolean);
begin FTransmitting := On_; Changed(rfTransmitting); { TODO wiring: ApplyMOX } end;
var
WasTransmitting: Boolean;
begin
// Safety: блокируем TX на бэндах с DoNotTx (Alex) и на RX-only трансвертере.
if On_ and FDevConnected and (FCurrentXvtr < 0)
and FAlexSettings.DoNotTx[EnsureRange(FCurrentBand, 0, 10)] then
Exit;
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then
Exit;
WasTransmitting := FTransmitting;
FTransmitting := On_;
if On_ then FDriveLevel := CalcDriveByte;
// Сетевая PTT-последовательность TX-on: сначала реле (Alex bit27=1, drive) без
// PTT, затем PTT=1 — переключаем реле раньше передачи.
if FTransmitting and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
begin
FNetwork.ClearDUCIQQueue;
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
FDriveLevel, True, True, True);
FNetwork.SendFullHP; // 1й пакет: Alex bit27=1 (T/R relay), drive — без PTT
FNetwork.SendPTT(True); // 2й пакет: PTT=1 после переключения реле
end;
if FWDSPReady and Assigned(FDSPEngine) then
begin
// Mic source автоматически по источнику TX:
// Web TX → txmsWeb; HW PTT → txmsRadio; MOX/CAT → DefaultMicSource.
if FTransmitting then
begin
if FWebClientActive then
begin
FWebMicActive := True;
FDSPEngine.SetTXMicSource(txmsWeb);
end
else if FHWPTTActive then
FDSPEngine.SetTXMicSource(txmsRadio)
else
FDSPEngine.SetTXMicSource(DefaultMicSource);
end
else if FWebMicActive then
begin
FWebMicActive := False;
FDSPEngine.SetTXMicSource(DefaultMicSource);
end;
// В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX, чтобы
// TX leakage не накапливался в RXA pipeline / FFT-истории анализатора.
FDSPEngine.KeepRXDuringTX := FDisplayDuplex;
FDSPEngine.SetTXRun(FTransmitting);
if WasTransmitting and (not FTransmitting) and (not FDisplayDuplex) then
begin
FDSPEngine.FlushRX;
if Assigned(FAudioOut) then
FAudioOut.Clear;
{$IFDEF WINDOWS}
// Windows: на TX→RX дропаем И RX-IQ, И аудио-выход — radio FPGA TX-buffer +
// PA slew-down дотравливают «хвост» (на Windows тянется 200500мс).
FDSPEngine.BeginPostTXMute(250);
{$ENDIF}
end;
// SetTXRun переключает FActiveDisplayID на TX_DISP_ID; при DUP возвращаем
// источник на RX, чтобы видеть приём во время передачи.
if FTransmitting and FDisplayDuplex then
FDSPEngine.SetDisplaySourceTX(False);
end;
// Сетевая PTT-последовательность TX-off: сначала снимаем PTT, затем реле RX.
if (not FTransmitting) and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
begin
FNetwork.SendPTT(False); // 1й пакет: PTT=0 — сначала снимаем PTT
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
FDriveLevel, False, True, True);
FNetwork.ClearDUCIQQueue;
FNetwork.SendFullHP; // 2й пакет: Alex bit27=0 (T/R relay RX)
end;
Changed(rfTransmitting);
end;
procedure TRadioController.SetTune(On_: Boolean);
begin FTuning := On_; Changed(rfTuning); { TODO wiring: ApplyTUN } end;