mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(puresignal): PureSignal-предыскажение для openHPSDR P2 + кнопки PS/2TON
Протокол (по pihpsdr new_protocol.c): на PTT при PS DDC0<-ADC0 (RX-feedback), DDC1<-ADC2 (TX DAC loopback), 192к/24бит, sync DDC1->DDC0 (byte 1363=0x02, interleaved в потоке DDC0), частоты DDC0/1 = TX в HP-пакете, Alex0 bit18 (мост обратной связи). Caps.HasPureSignal для плат с DDCBase=2 (Angelia/Orion/Orion2/Saturn); попутно NumADCs=2 для Angelia. WDSPEngine: PushPSFeedback (деинтерлив -> pscc(TXA, tx=DDC1, rx=DDC0) из сетевого потока), PS-параметры calcc в кэше с переприменением после пересоздания TXA; SetTXTwoTone (PostGen mode 1, 700/1900, 0.49+0.49, инверсия LSB/CWL/DIGL, мьют mic как TUN). RadioController: state machine команд (порт Thetis PSForm timer1) в PureSignalTick @10Гц + auto-attenuate (AttOnTX по уровню feedback, цель ~152, через DUC Specific); SetPSMox и перестройка feedback-DDC на T/R; маршрутизация DDC0 в pscc из OnDDCIQ; SetTwoTone с гейтами safety. UI: кнопки PS (ЛКМ = поповер настроек, ПКМ = быстрый toggle) и 2TON в блоке TX; поповер TPureSignalPopup (дочерний контрол, Wayland-safe): PS ON/OFF, CAL, RESET, 2TON, лампа/уровень feedback, cals, corr, GetPk/SetPk, MOX/CAL wait, PH#, Auto-atten, Relax ptol, Pin/Map/Stbl, Tint, тона двухтональника. Персист ps_* в tx-секции per-device. Не проверено на железе; Save/Restore коррекции не реализованы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+377
-2
@@ -74,6 +74,7 @@ type
|
||||
rfCenterFreq, rfSpan, rfSampleRate, rfZoom,
|
||||
rfCTun, rfDuplex, rfRxMuteOnTx, // rfRxMuteOnTx: QO-100 self-monitor toggle
|
||||
rfRunning, rfConnected, rfTransmitting, rfTuning,
|
||||
rfPureSignal, rfTwoTone, // PureSignal вкл/статус; 2TON вкл/выкл
|
||||
rfNR, rfNB, rfSNB, rfANF,
|
||||
rfWfAGC, rfWfNF, rfDither, rfRandom,
|
||||
rfFMDeviation, rfFMCTCSS, rfFMCTCSSTone, rfFMSQ, rfFMSQLevel,
|
||||
@@ -289,6 +290,22 @@ type
|
||||
FRunning: Boolean;
|
||||
FTransmitting: Boolean;
|
||||
FTuning: Boolean;
|
||||
// ---- PureSignal ----
|
||||
FPSEnabled: Boolean; // авто-калибровка PS включена (кнопка PS)
|
||||
FPSSingleCal: Boolean; // запрошена одиночная калибровка
|
||||
FPSResetReq: Boolean; // запрошен сброс коррекции (OFF)
|
||||
FPSCmdState: Integer; // eCMDState Thetis (см. PureSignalTick)
|
||||
FPSAAState: Integer; // auto-attenuate state (0=monitor,1=set,2=restore)
|
||||
FPSAASaveAuto: Integer; // сохранённый automode на время смены атт.
|
||||
FPSAASaveSingle: Integer;
|
||||
FPSAADeltaDB: Integer;
|
||||
FPSInfo: array[0..15] of Integer; // кэш GetPSInfo (UI-поток)
|
||||
FPSInfoPrev: array[0..15] of Integer;
|
||||
FPSMaxTX: Double; // кэш GetPSMaxTX
|
||||
// Feedback-тракт вооружён (сеть DDC0/1 + pscc в движке). Пишет UI-поток
|
||||
// (PureSignalTick/PSSetFeedbackPath), читает сетевой (OnDDCIQ).
|
||||
FPSFeedbackOn: Boolean;
|
||||
FTwoTone: Boolean; // 2TON активен
|
||||
FSplitTxB: Boolean;
|
||||
FTxSliceId: Integer; // 0 = главный VFO (A/B split); >0 = слайс-источник TX
|
||||
FVfoLock: Boolean; // блокировка перестройки VFO (Andromeda Lock)
|
||||
@@ -721,6 +738,31 @@ type
|
||||
// Передача
|
||||
procedure SetMOX(On_: Boolean);
|
||||
procedure SetTune(On_: Boolean); // TUN
|
||||
|
||||
// ---- PureSignal ----
|
||||
// Доступность PS у активного бэкенда/платы (Caps.HasPureSignal).
|
||||
function PSAvailable: Boolean;
|
||||
// Вкл/выкл авто-калибровки PureSignal (кнопка PS). Вооружает feedback-тракт
|
||||
// (DDC0/1 на TX) и WDSP calcc automode; выкл = reset коррекции.
|
||||
procedure SetPureSignal(On_: Boolean);
|
||||
procedure PSSingleCal; // одиночная калибровка
|
||||
procedure PSReset; // выключить и сбросить коррекцию
|
||||
// Тик PS (~10 Гц, из UI-таймера): state machine команд (порт Thetis
|
||||
// PSForm timer1/timer2), опрос GetPSInfo, auto-attenuate через AttOnTX.
|
||||
procedure PureSignalTick;
|
||||
procedure ApplyPSSettingsToDSP; // FTXSettings.PS* → WDSP calcc
|
||||
// Вооружение/снятие feedback-тракта (сеть + движок), идемпотентно.
|
||||
procedure PSSetFeedbackPath(En: Boolean);
|
||||
// Статус для UI (кэш последнего PureSignalTick)
|
||||
function PSFeedbackLevel: Integer; // info[4], оптимум ~140-160
|
||||
function PSCalCount: Integer; // info[5]
|
||||
function PSCorrecting: Boolean; // info[14]=1 — коррекция применяется
|
||||
function PSMaxTXVal: Double; // GetPSMaxTX (пик TX для SetPk)
|
||||
function PSSingleCalActive: Boolean; // идёт одиночная калибровка
|
||||
|
||||
// 2TON: двухтональник (PostGen two-tone) + MOX — тест IMD / проверка PS.
|
||||
procedure SetTwoTone(On_: Boolean);
|
||||
|
||||
procedure SetSplit(TxOnB: Boolean); // split: TX на VFO-B (иначе VFO-A)
|
||||
procedure SetVfoLock(On_: Boolean); // блокировка перестройки VFO
|
||||
|
||||
@@ -815,6 +857,16 @@ const
|
||||
// (TUNLevel), падение уровня IQ ~1.6дБ компенсируется калибровкой TUNLevel.
|
||||
TUN_TONE_MAG = 0.83;
|
||||
|
||||
// Состояния командной state machine PureSignal (порт Thetis eCMDState;
|
||||
// Restore-ветка (7) не реализована — Save/Restore коррекции нет).
|
||||
PS_ST_OFF = 0;
|
||||
PS_ST_TURNON_AUTO = 1;
|
||||
PS_ST_AUTOCAL = 2;
|
||||
PS_ST_TURNON_SINGLE = 3;
|
||||
PS_ST_SINGLECAL = 4;
|
||||
PS_ST_STAYON = 5;
|
||||
PS_ST_TURNOFF = 6;
|
||||
|
||||
constructor TDMRSliceDecoder.Create(AOwner: TRadioController;
|
||||
ASliceId: Integer);
|
||||
begin
|
||||
@@ -1065,13 +1117,20 @@ begin
|
||||
FDDCSeqValid[DDCIndex] := True;
|
||||
end;
|
||||
|
||||
// Подаём IQ в DSP: активный DDC → главный тракт, пан-DDC → его пан (3.2).
|
||||
// Подаём IQ в DSP: активный DDC → главный тракт, пан-DDC → его пан (3.2),
|
||||
// DDC0 при вооружённом PureSignal на TX → feedback в pscc (DDC0 несёт
|
||||
// interleaved пары DDC0+DDC1; на PS-платах главный DDC ≥ 2, конфликта нет).
|
||||
if FWDSPReady then
|
||||
begin
|
||||
SamplesPerFrame := (Integer(Data.SamplesPerFrame[0]) shl 8) or
|
||||
Integer(Data.SamplesPerFrame[1]);
|
||||
if SamplesPerFrame <= 0 then SamplesPerFrame := 238; // fallback
|
||||
if DDCIndex = FActiveDDC then
|
||||
if (DDCIndex = 0) and FPSFeedbackOn and (FActiveDDC <> 0) then
|
||||
begin
|
||||
if FTransmitting then
|
||||
FDSPEngine.PushPSFeedback(Data.IQData, 0, SamplesPerFrame);
|
||||
end
|
||||
else if DDCIndex = FActiveDDC then
|
||||
FDSPEngine.PushDDCPacket(Data.IQData, 0, SamplesPerFrame)
|
||||
else if (DDCIndex >= 0) and (DDCIndex < MAX_DDCS)
|
||||
and (FDDCPanMap[DDCIndex] > 0) then
|
||||
@@ -4063,6 +4122,8 @@ begin
|
||||
FDSPEngine.SetTXWaterfallDisplay(FTXSettings.TXWfDetector,
|
||||
FTXSettings.TXWfAvgMode,
|
||||
FTXSettings.TXWfAvgTimeMS);
|
||||
// PureSignal-параметры calcc (mox/loop/tx delay, ptol, pin/map/stbl, SetPk)
|
||||
ApplyPSSettingsToDSP;
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetMOX(On_: Boolean);
|
||||
@@ -4081,6 +4142,14 @@ begin
|
||||
Exit;
|
||||
WasTransmitting := FTransmitting;
|
||||
FTransmitting := On_;
|
||||
// 2TON: прямое снятие MOX (кнопкой/CAT/web) гасит и двухтональник.
|
||||
if (not On_) and FTwoTone then
|
||||
begin
|
||||
FTwoTone := False;
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
FDSPEngine.SetTXTwoTone(False, 0, 0);
|
||||
Changed(rfTwoTone);
|
||||
end;
|
||||
if On_ then
|
||||
begin
|
||||
FDriveLevel := CalcDriveByte;
|
||||
@@ -4096,6 +4165,9 @@ begin
|
||||
FDriveLevel, True, True, True);
|
||||
FNetwork.SendFullHP; // 1й пакет: Alex bit27=1 (T/R relay), drive — без PTT
|
||||
FNetwork.SendPTT(True); // 2й пакет: PTT=1 после переключения реле
|
||||
// PureSignal: перевести feedback-DDC0/1 в TX-конфигурацию (частоты = TX,
|
||||
// sync, Alex bit18). No-op если PS не вооружён (гейт внутри бэкенда).
|
||||
FNetwork.SetPureSignalTX(True);
|
||||
// Pre-roll: THROTTLE (2000, порог FPGA — DAC не стартует с пустого буфера)
|
||||
// + ДВА WDSP-блока (2×2048) нулевых пар подушкой в очередь. Раньше было 3
|
||||
// блока: обоснование «2 не хватало — щелчок через ~0.5с» теперь под вопросом,
|
||||
@@ -4140,6 +4212,8 @@ begin
|
||||
// Мультислайс-TX: передающий тракт под режим слайса-источника (SetMode мог
|
||||
// оставить главный режим). Ставим перед стартом TX-потока.
|
||||
if FTransmitting then FDSPEngine.SetTXMode(ActiveTXMode);
|
||||
// PureSignal: mox-флаг calcc (гейтит сбор данных калибровки в pscc).
|
||||
FDSPEngine.PSSetMox(FTransmitting);
|
||||
FDSPEngine.SetTXRun(FTransmitting);
|
||||
if WasTransmitting and (not FTransmitting) and (not FDisplayDuplex) then
|
||||
begin
|
||||
@@ -4161,6 +4235,8 @@ begin
|
||||
if (not FTransmitting) and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.SendPTT(False); // 1й пакет: PTT=0 — сначала снимаем PTT
|
||||
// PureSignal: вернуть DDC0/1 в RX-конфигурацию (убрать feedback-DDC).
|
||||
FNetwork.SetPureSignalTX(False);
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz),
|
||||
FDriveLevel, False, True, True);
|
||||
FNetwork.ClearDUCIQQueue;
|
||||
@@ -4181,6 +4257,7 @@ begin
|
||||
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then Exit;
|
||||
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
|
||||
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
|
||||
if On_ and FTwoTone then SetTwoTone(False); // TUN поверх 2TON — гасим тона
|
||||
if On_ then
|
||||
begin
|
||||
// Тон включаем ДО запуска TX-аудио, чтобы первые сэмплы уже шли с тоном.
|
||||
@@ -4210,6 +4287,285 @@ begin
|
||||
Changed(rfTuning);
|
||||
end;
|
||||
|
||||
// ===========================================================================
|
||||
// PureSignal — оркестрация (порт state machine Thetis PSForm timer1/timer2)
|
||||
// ===========================================================================
|
||||
|
||||
function TRadioController.PSAvailable: Boolean;
|
||||
begin
|
||||
Result := FDevConnected and BackendCaps.HasPureSignal;
|
||||
end;
|
||||
|
||||
procedure TRadioController.PSSetFeedbackPath(En: Boolean);
|
||||
// Единая точка вкл/выкл feedback-тракта: сеть (DDC0/1 на TX) + движок (pscc).
|
||||
// Намеренно БЕЗ раннего выхода при том же значении: движок сбрасывает armed
|
||||
// при Close (ChangeSampleRate), сеть гейтится внутри — повторный вызов с тем
|
||||
// же значением дёшев и восстанавливает рассинхрон.
|
||||
begin
|
||||
FPSFeedbackOn := En;
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
FDSPEngine.SetPureSignalArmed(En);
|
||||
if Assigned(FNetwork) then
|
||||
begin
|
||||
FNetwork.SetPureSignal(En);
|
||||
// Включение/выключение посреди передачи — сразу перестроить DDC.
|
||||
if FTransmitting then FNetwork.SetPureSignalTX(En);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetPureSignal(On_: Boolean);
|
||||
// Кнопка PS = режим авто-калибровки (Thetis AutoCalEnabled). Выключение —
|
||||
// через запрос сброса: state machine выполнит reset коррекции и погасит тракт.
|
||||
begin
|
||||
if On_ = FPSEnabled then Exit;
|
||||
if On_ and not PSAvailable then Exit;
|
||||
FPSEnabled := On_;
|
||||
if On_ then
|
||||
begin
|
||||
ApplyPSSettingsToDSP; // параметры calcc до старта калибровки
|
||||
FPSResetReq := False;
|
||||
end
|
||||
else
|
||||
FPSResetReq := True;
|
||||
Changed(rfPureSignal);
|
||||
end;
|
||||
|
||||
procedure TRadioController.PSSingleCal;
|
||||
// Одиночная калибровка (Thetis btnPSCalibrate): авто-режим выключается.
|
||||
begin
|
||||
if not PSAvailable then Exit;
|
||||
if FPSSingleCal then begin FPSSingleCal := False; Exit; end; // отмена
|
||||
ApplyPSSettingsToDSP;
|
||||
FPSEnabled := False;
|
||||
FPSSingleCal := True;
|
||||
Changed(rfPureSignal);
|
||||
end;
|
||||
|
||||
procedure TRadioController.PSReset;
|
||||
begin
|
||||
FPSEnabled := False;
|
||||
FPSSingleCal := False;
|
||||
FPSResetReq := True;
|
||||
Changed(rfPureSignal);
|
||||
end;
|
||||
|
||||
procedure TRadioController.ApplyPSSettingsToDSP;
|
||||
var
|
||||
Ints, Spi: Integer;
|
||||
begin
|
||||
if not (FWDSPReady and Assigned(FDSPEngine)) then Exit;
|
||||
case FTXSettings.PSIntsIdx of
|
||||
1: begin Ints := 8; Spi := 512; end;
|
||||
2: begin Ints := 4; Spi := 1024; end;
|
||||
else begin Ints := 16; Spi := 256; end;
|
||||
end;
|
||||
FDSPEngine.PSSetParams(FTXSettings.PSMoxDelay, FTXSettings.PSLoopDelay,
|
||||
FTXSettings.PSTXDelayNs, FTXSettings.PSRelaxPtol,
|
||||
FTXSettings.PSPin, FTXSettings.PSMap,
|
||||
FTXSettings.PSStabilize, Ints, Spi,
|
||||
FTXSettings.PSHWPeak);
|
||||
end;
|
||||
|
||||
function TRadioController.PSFeedbackLevel: Integer;
|
||||
begin
|
||||
Result := FPSInfo[4];
|
||||
end;
|
||||
|
||||
function TRadioController.PSCalCount: Integer;
|
||||
begin
|
||||
Result := FPSInfo[5];
|
||||
end;
|
||||
|
||||
function TRadioController.PSCorrecting: Boolean;
|
||||
begin
|
||||
Result := FPSInfo[14] = 1;
|
||||
end;
|
||||
|
||||
function TRadioController.PSMaxTXVal: Double;
|
||||
begin
|
||||
Result := FPSMaxTX;
|
||||
end;
|
||||
|
||||
function TRadioController.PSSingleCalActive: Boolean;
|
||||
begin
|
||||
Result := FPSSingleCal
|
||||
or (FPSCmdState in [PS_ST_TURNON_SINGLE, PS_ST_SINGLECAL]);
|
||||
end;
|
||||
|
||||
procedure TRadioController.PureSignalTick;
|
||||
// Вызывается из UI-таймера (~10 Гц). Командная state machine — порт Thetis
|
||||
// PSForm timer1code (без Restore-ветки: Save/Restore коррекции не реализованы)
|
||||
// + auto-attenuate (timer2code): подстройка AttOnTX по уровню feedback.
|
||||
var
|
||||
NewAtt: Integer;
|
||||
ddB: Double;
|
||||
CalAttemptsChanged: Boolean;
|
||||
begin
|
||||
if not (FWDSPReady and Assigned(FDSPEngine)) then Exit;
|
||||
if not PSAvailable then
|
||||
begin
|
||||
// Устройство сменилось/отключилось: гарантированно всё погасить.
|
||||
if FPSFeedbackOn then PSSetFeedbackPath(False);
|
||||
FPSCmdState := PS_ST_OFF;
|
||||
Exit;
|
||||
end;
|
||||
// Полный простой (PS ни разу не включался / выключен и сброшен) — не дёргаем
|
||||
// WDSP каждые 100 мс впустую.
|
||||
if (FPSCmdState = PS_ST_OFF) and not (FPSEnabled or FPSSingleCal
|
||||
or FPSResetReq or FPSFeedbackOn) then Exit;
|
||||
|
||||
// Опрос статуса WDSP calcc
|
||||
Move(FPSInfo[0], FPSInfoPrev[0], SizeOf(FPSInfo));
|
||||
FDSPEngine.PSGetInfo(FPSInfo);
|
||||
FPSMaxTX := FDSPEngine.PSGetMaxTX;
|
||||
|
||||
// --- Командная state machine ---
|
||||
case FPSCmdState of
|
||||
PS_ST_OFF:
|
||||
begin
|
||||
FDSPEngine.PSControl(1, 0, 0, 0);
|
||||
PSSetFeedbackPath(False);
|
||||
FPSResetReq := False;
|
||||
if FPSEnabled then
|
||||
FPSCmdState := PS_ST_TURNON_AUTO
|
||||
else if FPSSingleCal then
|
||||
FPSCmdState := PS_ST_TURNON_SINGLE;
|
||||
end;
|
||||
PS_ST_TURNON_AUTO:
|
||||
begin
|
||||
FDSPEngine.PSControl(1, 0, 1, 0);
|
||||
PSSetFeedbackPath(True);
|
||||
FPSCmdState := PS_ST_AUTOCAL;
|
||||
end;
|
||||
PS_ST_AUTOCAL:
|
||||
begin
|
||||
PSSetFeedbackPath(True); // ре-синк (движок мог переоткрыться)
|
||||
if FPSResetReq or (not FPSEnabled) then
|
||||
FPSCmdState := PS_ST_TURNOFF
|
||||
else if FPSSingleCal then
|
||||
FPSCmdState := PS_ST_TURNON_SINGLE;
|
||||
end;
|
||||
PS_ST_TURNON_SINGLE:
|
||||
begin
|
||||
FDSPEngine.PSControl(1, 1, 0, 0);
|
||||
PSSetFeedbackPath(True);
|
||||
FPSCmdState := PS_ST_SINGLECAL;
|
||||
end;
|
||||
PS_ST_SINGLECAL:
|
||||
begin
|
||||
PSSetFeedbackPath(True); // ре-синк (движок мог переоткрыться)
|
||||
FPSSingleCal := False;
|
||||
if FPSResetReq then
|
||||
FPSCmdState := PS_ST_TURNOFF
|
||||
else if FPSEnabled then
|
||||
FPSCmdState := PS_ST_TURNON_AUTO
|
||||
else if PSCorrecting then
|
||||
FPSCmdState := PS_ST_STAYON;
|
||||
end;
|
||||
PS_ST_STAYON:
|
||||
begin
|
||||
// Коррекция применяется, калибровка остановлена, feedback не нужен.
|
||||
PSSetFeedbackPath(False);
|
||||
if FPSResetReq then
|
||||
FPSCmdState := PS_ST_TURNOFF
|
||||
else if FPSEnabled then
|
||||
FPSCmdState := PS_ST_TURNON_AUTO
|
||||
else if FPSSingleCal then
|
||||
FPSCmdState := PS_ST_TURNON_SINGLE;
|
||||
end;
|
||||
PS_ST_TURNOFF:
|
||||
begin
|
||||
// Reset исполняется внутри pscc — держим feedback-тракт, пока calcc
|
||||
// не дойдёт до LRESET (как Thetis: PSEnabled=true в состоянии TurnOFF).
|
||||
FDSPEngine.PSControl(1, 0, 0, 0);
|
||||
PSSetFeedbackPath(True);
|
||||
FPSResetReq := False;
|
||||
if FPSEnabled then
|
||||
FPSCmdState := PS_ST_TURNON_AUTO
|
||||
else if FPSSingleCal then
|
||||
FPSCmdState := PS_ST_TURNON_SINGLE
|
||||
else if (not PSCorrecting) and (FPSInfo[15] = 0) then // 0 = LRESET
|
||||
FPSCmdState := PS_ST_OFF;
|
||||
end;
|
||||
end;
|
||||
|
||||
// --- Auto-attenuate (Thetis timer2code) ---
|
||||
// По завершении очередной попытки калибровки: если feedback слишком горячий
|
||||
// (>181) или слабый при ненулевом атт. (<=128), подстроить AttOnTX так,
|
||||
// чтобы уровень лёг к оптимуму 152.
|
||||
case FPSAAState of
|
||||
0: begin
|
||||
CalAttemptsChanged := FPSInfo[5] <> FPSInfoPrev[5];
|
||||
if FTXSettings.PSAutoAtten and CalAttemptsChanged
|
||||
and ((FPSInfo[4] > 181) or
|
||||
((FPSInfo[4] <= 128) and (FTXSettings.AttOnTX > 0))) then
|
||||
begin
|
||||
FPSAAState := 1;
|
||||
if (FPSInfo[4] > 0) and (FPSInfo[4] <= 256) then
|
||||
begin
|
||||
ddB := 20.0 * Log10(FPSInfo[4] / 152.293);
|
||||
if ddB < -100.0 then ddB := -100.0;
|
||||
if ddB > +100.0 then ddB := +100.0;
|
||||
end
|
||||
else
|
||||
ddB := 31.1;
|
||||
FPSAADeltaDB := Round(ddB);
|
||||
FPSAASaveAuto := Ord(FPSCmdState = PS_ST_AUTOCAL);
|
||||
FPSAASaveSingle := Ord(FPSCmdState = PS_ST_SINGLECAL);
|
||||
FDSPEngine.PSControl(1, 0, 0, 0); // всё стоп + reset на время смены
|
||||
end;
|
||||
end;
|
||||
1: begin
|
||||
FPSAAState := 2;
|
||||
NewAtt := EnsureRange(FTXSettings.AttOnTX + FPSAADeltaDB, 0, 31);
|
||||
if NewAtt <> FTXSettings.AttOnTX then
|
||||
begin
|
||||
FTXSettings.AttOnTX := NewAtt;
|
||||
SendDUCSpecificFromSettings; // применить атт. немедленно
|
||||
Changed(rfPureSignal); // UI: показать новый ATT
|
||||
end;
|
||||
end;
|
||||
2: begin
|
||||
FPSAAState := 0;
|
||||
FDSPEngine.PSControl(0, FPSAASaveSingle, FPSAASaveAuto, 0);
|
||||
end;
|
||||
end;
|
||||
|
||||
// UI-статус (feedback-лампа/счётчик) обновляем при изменении info
|
||||
if not CompareMem(@FPSInfo[0], @FPSInfoPrev[0], SizeOf(FPSInfo)) then
|
||||
Changed(rfPureSignal);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetTwoTone(On_: Boolean);
|
||||
// 2TON: двухтональник PostGen + MOX (как Thetis chkTestIMD). Уровень — обычный
|
||||
// Drive; тона PSTTFreq1/2 (700/1900 по умолчанию).
|
||||
begin
|
||||
if On_ = FTwoTone then Exit;
|
||||
if On_ and (ActiveTXMode = MODE_DMR) then Exit;
|
||||
if On_ and FTuning then Exit; // не поверх TUN-тона
|
||||
// Safety: те же блокировки, что SetMOX/SetTune.
|
||||
if On_ and FDevConnected and (FCurrentXvtr < 0)
|
||||
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then Exit;
|
||||
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
|
||||
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
|
||||
if On_ then
|
||||
begin
|
||||
// Генератор ДО запуска TX-тракта — первые блоки уже с тонами.
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
FDSPEngine.SetTXTwoTone(True, FTXSettings.PSTTFreq1, FTXSettings.PSTTFreq2);
|
||||
FTwoTone := True;
|
||||
if not FTransmitting then SetMOX(True);
|
||||
end
|
||||
else
|
||||
begin
|
||||
FTwoTone := False;
|
||||
if FTransmitting then SetMOX(False);
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
FDSPEngine.SetTXTwoTone(False, 0, 0);
|
||||
end;
|
||||
Changed(rfTwoTone);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetFMStep(Idx: Integer);
|
||||
begin
|
||||
if Idx < 0 then Idx := 0 else if Idx > 3 then Idx := 3; // FM_STEP_COUNT = 4
|
||||
@@ -4441,6 +4797,25 @@ begin
|
||||
if FWDSPReady then FDSPEngine.SetTXTone(False, 0, 0);
|
||||
Changed(rfTuning); // UI: TUN-кнопка off
|
||||
end;
|
||||
if FTwoTone then
|
||||
begin
|
||||
FTwoTone := False;
|
||||
if FWDSPReady then FDSPEngine.SetTXTwoTone(False, 0, 0);
|
||||
Changed(rfTwoTone); // UI: 2TON-кнопка off
|
||||
end;
|
||||
// PureSignal: погасить калибровку и feedback-тракт (без сброса коррекции —
|
||||
// сеть уже останавливается; state machine стартует заново при след. Run).
|
||||
if FPSEnabled or FPSSingleCal or FPSFeedbackOn then
|
||||
begin
|
||||
FPSEnabled := False;
|
||||
FPSSingleCal := False;
|
||||
FPSResetReq := False;
|
||||
FPSCmdState := PS_ST_OFF;
|
||||
FPSAAState := 0;
|
||||
PSSetFeedbackPath(False);
|
||||
if FWDSPReady then FDSPEngine.PSControl(1, 0, 0, 0);
|
||||
Changed(rfPureSignal);
|
||||
end;
|
||||
if FWDSPReady then FDSPEngine.SetTXRun(False);
|
||||
Changed(rfTransmitting); // MOX-кнопка off
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user