mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:27:33 +00:00
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:
co-authored by
Claude Opus 4.8
parent
c249b46b17
commit
603e0850cf
+30
-100
@@ -218,7 +218,6 @@ type
|
||||
FWebBindAddr: string;
|
||||
FWebUser: string;
|
||||
FWebPass: string;
|
||||
FWebMicActive: Boolean; // True если текущий TX идёт через txmsWeb
|
||||
// --- CAT ---
|
||||
FCATEngine: TCATEngine;
|
||||
FCATSerial: TCATSerialManager;
|
||||
@@ -1326,6 +1325,28 @@ begin
|
||||
if PbRuler <> nil then PbRuler.Invalidate;
|
||||
end;
|
||||
end;
|
||||
rfTransmitting:
|
||||
begin
|
||||
if FController.FTransmitting then
|
||||
begin
|
||||
BtnMOX.ClrNorm := TColor($00000044);
|
||||
BtnMOX.ClrText := TColor($000000FF);
|
||||
BtnMOX.ClrTextAct := TColor($000000FF);
|
||||
BtnMOX.Active := True;
|
||||
end else
|
||||
StyleButton(BtnMOX, False);
|
||||
// TX-overlay в SpectrumView: TX-частота/VFO-индекс (по селектору
|
||||
// FSplitTxB, независимо от активного RX-VFO) и режим.
|
||||
FSpecView.TXFreq := ActiveTXFreqHz;
|
||||
if FController.FSplitTxB then FSpecView.TXVfoIndex := 1
|
||||
else FSpecView.TXVfoIndex := 0;
|
||||
// DUP: данные от RX-анализатора (TXMode=False), TX-полоса рисуется
|
||||
// поверх через TXOverlay. Без DUP при TX оба флага True.
|
||||
FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex;
|
||||
FSpecView.TXOverlay := FController.FTransmitting;
|
||||
ApplySpecViewGridFromState; // сетка спектра под активный тракт
|
||||
FDUCPendingCount := 0;
|
||||
end;
|
||||
rfBand:
|
||||
// В XVTR-режиме HF-кнопки гасим (активна XVTR-кнопка, см. rfXvtr).
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
@@ -3010,10 +3031,12 @@ begin
|
||||
begin
|
||||
if FController.FTransmitting then
|
||||
begin
|
||||
if FController.FDSPEngine.TXMicSource = txmsSoundCard then
|
||||
SetStatusText(5, 'TX sound card')
|
||||
case FController.FDSPEngine.TXMicSource of
|
||||
txmsSoundCard: SetStatusText(5, 'TX sound card');
|
||||
txmsWeb: SetStatusText(5, 'TX web mic');
|
||||
else
|
||||
SetStatusText(5, 'TX radio mic');
|
||||
end;
|
||||
end
|
||||
else
|
||||
SetStatusText(5, 'TX idle');
|
||||
@@ -5339,104 +5362,11 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainForm.ApplyMOX(Active: Boolean);
|
||||
var
|
||||
WasTransmitting: Boolean;
|
||||
begin
|
||||
// Safety: block TX on bands marked DoNotTx in Alex config
|
||||
if Active and FController.FDevConnected and (FController.FCurrentXvtr < 0)
|
||||
and FController.FAlexSettings.DoNotTx[EnsureRange(FController.FCurrentBand, 0, 10)] then
|
||||
Exit;
|
||||
// Safety: block TX on RX-only transverter
|
||||
if Active and (FController.FCurrentXvtr >= 0) and (FController.FCurrentXvtr < CFG_XVTR_COUNT)
|
||||
and FController.FXvtrSettings.Entries[FController.FCurrentXvtr].RXOnly then
|
||||
Exit;
|
||||
WasTransmitting := FController.FTransmitting;
|
||||
FController.FTransmitting := Active;
|
||||
if Active then FController.FDriveLevel := CalcDriveByte;
|
||||
FDUCPendingCount := 0;
|
||||
if FController.FTransmitting then
|
||||
begin
|
||||
BtnMOX.ClrNorm := TColor($00000044);
|
||||
BtnMOX.ClrText := TColor($000000FF);
|
||||
BtnMOX.ClrTextAct := TColor($000000FF);
|
||||
BtnMOX.Active := True;
|
||||
end else
|
||||
StyleButton(BtnMOX, False);
|
||||
// TX-overlay в SpectrumView: фиксируем актуальную TX-частоту/VFO-индекс и режим
|
||||
FSpecView.TXFreq := ActiveTXFreqHz;
|
||||
// TX-VFO задаётся селектором FSplitTxB, независимо от активного RX-VFO.
|
||||
if FController.FSplitTxB then FSpecView.TXVfoIndex := 1
|
||||
else FSpecView.TXVfoIndex := 0;
|
||||
// DUP: данные остаются от RX-анализатора (TXMode=False), но TX-полоса
|
||||
// фильтра рисуется поверх через TXOverlay. Без DUP при TX оба флага True.
|
||||
FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex;
|
||||
FSpecView.TXOverlay := FController.FTransmitting;
|
||||
// Перерисовываем сетку спектра под активный тракт (Thetis-style).
|
||||
ApplySpecViewGridFromState;
|
||||
if FController.FTransmitting and FController.FNetwork.Connected and FController.FNetwork.Running then
|
||||
begin
|
||||
FController.FNetwork.ClearDUCIQQueue;
|
||||
FController.FNetwork.UpdateState(XvtrTranslate(FController.FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FController.FDriveLevel,
|
||||
True, True, True);
|
||||
FController.FNetwork.SendFullHP; // 1й пакет: Alex bit27=1 (T/R relay), drive — без PTT
|
||||
FController.FNetwork.SendPTT(True); // 2й пакет: PTT=1 после переключения реле
|
||||
end;
|
||||
if FController.FWDSPReady then
|
||||
begin
|
||||
// Mic source — определяем автоматически по источнику TX:
|
||||
// Web TX → txmsWeb
|
||||
// HW PTT (FController.FHWPTTActive при входе в ApplyMOX уже True) → txmsRadio
|
||||
// MOX-кнопка/CAT → sound card если настроена, иначе txmsRadio
|
||||
if FController.FTransmitting then
|
||||
begin
|
||||
if Assigned(FWebServer) and FWebServer.WebClientActive then
|
||||
begin
|
||||
FWebMicActive := True;
|
||||
FController.FDSPEngine.SetTXMicSource(txmsWeb);
|
||||
end
|
||||
else if FController.FHWPTTActive then
|
||||
FController.FDSPEngine.SetTXMicSource(txmsRadio)
|
||||
else
|
||||
FController.FDSPEngine.SetTXMicSource(DefaultMicSource);
|
||||
end
|
||||
else if FWebMicActive then
|
||||
begin
|
||||
FWebMicActive := False;
|
||||
FController.FDSPEngine.SetTXMicSource(DefaultMicSource);
|
||||
end;
|
||||
// В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX,
|
||||
// чтобы TX leakage не накапливался в RXA pipeline и FFT-истории
|
||||
// RX-анализатора. В DUP RX-тракт работает как обычно.
|
||||
FController.FDSPEngine.KeepRXDuringTX := FController.FDisplayDuplex;
|
||||
FController.FDSPEngine.SetTXRun(FController.FTransmitting);
|
||||
if WasTransmitting and (not FController.FTransmitting) and (not FController.FDisplayDuplex) then
|
||||
begin
|
||||
FController.FDSPEngine.FlushRX;
|
||||
if Assigned(FController.FAudioOut) then
|
||||
FController.FAudioOut.Clear;
|
||||
{$IFDEF WINDOWS}
|
||||
// Windows: на TX→RX дропаем И RX-IQ пакеты, И аудио-выход на это окно.
|
||||
// Источник «хвоста» — radio FPGA TX-buffer + PA slew-down: после MOX-off
|
||||
// радио дотравливает буфер передатчика, RX1 ловит это как leak, и WDSP
|
||||
// отдаёт это и в водопад, и в звук. На Linux наблюдается окно <30мс,
|
||||
// на Windows может тянуться 200–500мс. Подобрать значение можно здесь
|
||||
// без перекомпиляции engine. На Linux код-путь не активируется.
|
||||
FController.FDSPEngine.BeginPostTXMute(250);
|
||||
{$ENDIF}
|
||||
end;
|
||||
// SetTXRun переключает FActiveDisplayID на TX_DISP_ID; при DUP возвращаем
|
||||
// источник на RX, чтобы видеть приём во время передачи.
|
||||
if FController.FTransmitting and FController.FDisplayDuplex then
|
||||
FController.FDSPEngine.SetDisplaySourceTX(False);
|
||||
end;
|
||||
if (not FController.FTransmitting) and FController.FNetwork.Connected and FController.FNetwork.Running then
|
||||
begin
|
||||
FController.FNetwork.SendPTT(False); // 1й пакет: PTT=0 — сначала снимаем PTT
|
||||
FController.FNetwork.UpdateState(XvtrTranslate(FController.FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FController.FDriveLevel,
|
||||
False, True, True);
|
||||
FController.FNetwork.ClearDUCIQQueue;
|
||||
FController.FNetwork.SendFullHP; // 2й пакет: Alex bit27=0 (T/R relay RX), drive=0
|
||||
end;
|
||||
// Web-слой пока не владеет своим флагом — зеркалим текущее состояние клиента
|
||||
// в контроллер перед командой (определяет mic-source при TX-on).
|
||||
FController.FWebClientActive := Assigned(FWebServer) and FWebServer.WebClientActive;
|
||||
FController.SetMOX(Active); // ядро TX (safety/PTT/DSP) + рендер через rfTransmitting
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnMOXClick(Sender: TObject);
|
||||
|
||||
+87
-1
@@ -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 тянется 200–500мс).
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user