mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37: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;
|
FWebBindAddr: string;
|
||||||
FWebUser: string;
|
FWebUser: string;
|
||||||
FWebPass: string;
|
FWebPass: string;
|
||||||
FWebMicActive: Boolean; // True если текущий TX идёт через txmsWeb
|
|
||||||
// --- CAT ---
|
// --- CAT ---
|
||||||
FCATEngine: TCATEngine;
|
FCATEngine: TCATEngine;
|
||||||
FCATSerial: TCATSerialManager;
|
FCATSerial: TCATSerialManager;
|
||||||
@@ -1326,6 +1325,28 @@ begin
|
|||||||
if PbRuler <> nil then PbRuler.Invalidate;
|
if PbRuler <> nil then PbRuler.Invalidate;
|
||||||
end;
|
end;
|
||||||
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:
|
rfBand:
|
||||||
// В XVTR-режиме HF-кнопки гасим (активна XVTR-кнопка, см. rfXvtr).
|
// В XVTR-режиме HF-кнопки гасим (активна XVTR-кнопка, см. rfXvtr).
|
||||||
for i := 0 to BAND_COUNT - 1 do
|
for i := 0 to BAND_COUNT - 1 do
|
||||||
@@ -3010,10 +3031,12 @@ begin
|
|||||||
begin
|
begin
|
||||||
if FController.FTransmitting then
|
if FController.FTransmitting then
|
||||||
begin
|
begin
|
||||||
if FController.FDSPEngine.TXMicSource = txmsSoundCard then
|
case FController.FDSPEngine.TXMicSource of
|
||||||
SetStatusText(5, 'TX sound card')
|
txmsSoundCard: SetStatusText(5, 'TX sound card');
|
||||||
|
txmsWeb: SetStatusText(5, 'TX web mic');
|
||||||
else
|
else
|
||||||
SetStatusText(5, 'TX radio mic');
|
SetStatusText(5, 'TX radio mic');
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
SetStatusText(5, 'TX idle');
|
SetStatusText(5, 'TX idle');
|
||||||
@@ -5339,104 +5362,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.ApplyMOX(Active: Boolean);
|
procedure TMainForm.ApplyMOX(Active: Boolean);
|
||||||
var
|
|
||||||
WasTransmitting: Boolean;
|
|
||||||
begin
|
begin
|
||||||
// Safety: block TX on bands marked DoNotTx in Alex config
|
// Web-слой пока не владеет своим флагом — зеркалим текущее состояние клиента
|
||||||
if Active and FController.FDevConnected and (FController.FCurrentXvtr < 0)
|
// в контроллер перед командой (определяет mic-source при TX-on).
|
||||||
and FController.FAlexSettings.DoNotTx[EnsureRange(FController.FCurrentBand, 0, 10)] then
|
FController.FWebClientActive := Assigned(FWebServer) and FWebServer.WebClientActive;
|
||||||
Exit;
|
FController.SetMOX(Active); // ядро TX (safety/PTT/DSP) + рендер через rfTransmitting
|
||||||
// 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;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.BtnMOXClick(Sender: TObject);
|
procedure TMainForm.BtnMOXClick(Sender: TObject);
|
||||||
|
|||||||
+87
-1
@@ -159,6 +159,8 @@ type
|
|||||||
FDisplayDuplex: Boolean;
|
FDisplayDuplex: Boolean;
|
||||||
FHWPTTActive: Boolean;
|
FHWPTTActive: Boolean;
|
||||||
FHWPTTStartedTX: Boolean;
|
FHWPTTStartedTX: Boolean;
|
||||||
|
FWebClientActive: Boolean; // выставляет web-слой (connect/disconnect клиента)
|
||||||
|
FWebMicActive: Boolean; // True если текущий TX идёт через txmsWeb
|
||||||
FSendAudioToRadio: Boolean;
|
FSendAudioToRadio: Boolean;
|
||||||
FAudioOutDevName: string;
|
FAudioOutDevName: string;
|
||||||
FAudioInDevName: string;
|
FAudioInDevName: string;
|
||||||
@@ -223,6 +225,10 @@ type
|
|||||||
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
||||||
function CalcDriveByte: Byte;
|
function CalcDriveByte: Byte;
|
||||||
|
|
||||||
|
// Источник микрофона по умолчанию для MOX-кнопки/Web-TX: звуковая карта
|
||||||
|
// если настроена, иначе HW mic-стрим трансивера. HW PTT → txmsRadio в SetMOX.
|
||||||
|
function DefaultMicSource: TTXMicSource;
|
||||||
|
|
||||||
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
|
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
|
||||||
// MakeBandSettings — снимок текущего состояния в запись диапазона.
|
// MakeBandSettings — снимок текущего состояния в запись диапазона.
|
||||||
// SaveCurrentBand — сохраняет текущее состояние в кэш+конфиг (или в XVTR-слот).
|
// SaveCurrentBand — сохраняет текущее состояние в кэш+конфиг (или в XVTR-слот).
|
||||||
@@ -1251,8 +1257,88 @@ begin
|
|||||||
Changed(rfANF);
|
Changed(rfANF);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TRadioController.DefaultMicSource: TTXMicSource;
|
||||||
|
begin
|
||||||
|
if FAudioInDevName <> '' then Result := txmsSoundCard
|
||||||
|
else Result := txmsRadio;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TRadioController.SetMOX(On_: Boolean);
|
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);
|
procedure TRadioController.SetTune(On_: Boolean);
|
||||||
begin FTuning := On_; Changed(rfTuning); { TODO wiring: ApplyTUN } end;
|
begin FTuning := On_; Changed(rfTuning); { TODO wiring: ApplyTUN } end;
|
||||||
|
|||||||
Reference in New Issue
Block a user