mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Phase 5 (batch 25b): move HP-status callback into the controller
OnHPStatusCB + DoUpdateStatus move into TRadioController as OnHPStatus (network thread: board-specific supply/power/SWR decode -> pending fields -> Invoke) and ApplyHPStatus (marshalled: PEP-style fwd/SWR ballistics + supply EMA into FLast*, ADC-overload via Changed(rfADCOverload), and the HW-PTT edge -> SetMOX). FNetwork.OnHPStatus is wired to the controller; the ADC-overload indicator renders via OnControllerState(rfADCOverload). The HWPTT->TX path is now headless-capable and reads an always-current FWebClientActive for mic-source. TStatusUISync is left dead in UISync.pas (unit still used for TDDCSeqSync) pending a separate cleanup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9d2324cc6c
commit
c1da506f73
+10
-165
@@ -351,7 +351,6 @@ type
|
||||
|
||||
// Network callbacks
|
||||
procedure OnDeviceFound(const Dev: THPSDRDevice);
|
||||
procedure OnHPStatusCB(const Status: THighPriorityStatus);
|
||||
procedure OnWidebandCB(ADCIndex: Integer; const Samples: array of SmallInt;
|
||||
Count: Integer);
|
||||
procedure OnWDSPOpenDone(Success: Boolean);
|
||||
@@ -364,8 +363,6 @@ type
|
||||
|
||||
// Public UI update (called from sync objects)
|
||||
procedure DoAddDevice(const Dev: THPSDRDevice; const Entry: string);
|
||||
procedure DoUpdateStatus(FwdW, SWRV, SupplyV, SupplyA: Double; PLLLock, HWPTT: Boolean;
|
||||
ADCOverload: Byte);
|
||||
procedure UpdateTXMeters;
|
||||
procedure DoUpdateDDCSeq(DDCIdx: Integer; Seq: LongWord);
|
||||
procedure DoUpdateDDCSeqOrNoDevice(DDCIdx: Integer; Seq: LongWord);
|
||||
@@ -1139,7 +1136,7 @@ begin
|
||||
// startup-preview выше, поэтому FDSPEngine рождается с нужной частотой.
|
||||
FController.CreateEngines(FController.FSampleRate);
|
||||
FController.FNetwork.OnDeviceFound := OnDeviceFound;
|
||||
FController.FNetwork.OnHPStatus := OnHPStatusCB;
|
||||
FController.FNetwork.OnHPStatus := FController.OnHPStatus;
|
||||
FController.FNetwork.OnDDCIQ := FController.OnDDCIQ;
|
||||
FController.FNetwork.OnMicPacket := FController.OnMicPacket;
|
||||
FController.FNetwork.OnWideband := OnWidebandCB;
|
||||
@@ -1317,6 +1314,12 @@ begin
|
||||
end;
|
||||
rfTuning:
|
||||
StyleButton(BtnTUN, FController.FTuning);
|
||||
rfADCOverload:
|
||||
if FSpecView <> nil then
|
||||
begin
|
||||
FSpecView.ADCOverloadVisible := FController.FADCOverloadActive;
|
||||
if PbSpectrum <> nil then PbSpectrum.Invalidate;
|
||||
end;
|
||||
rfBand:
|
||||
// В XVTR-режиме HF-кнопки гасим (активна XVTR-кнопка, см. rfXvtr).
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
@@ -3281,169 +3284,11 @@ begin
|
||||
SetStatusText(2, Format('Found: %s', [Dev.IPAddress]));
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnHPStatusCB(const Status: THighPriorityStatus);
|
||||
const
|
||||
// Порог fwd-мощности (W), ниже которого SWR не вычисляем — там всё ADC-шум.
|
||||
SWR_MIN_FWD_W = 1.0;
|
||||
// Верхняя граница SWR (выше — это всё равно «беда»).
|
||||
SWR_CAP = 9.9;
|
||||
var
|
||||
ExcPwr, FwdPwr, RevPwr: Word;
|
||||
SupplyV, SupplyA, FwdW, SWRV, Rho: Double;
|
||||
IsTx: Boolean;
|
||||
Sync: TStatusUISync;
|
||||
M: TThreadMethod;
|
||||
begin
|
||||
ExcPwr := (Status.ExciterPwr0Hi shl 8) or Status.ExciterPwr0Lo;
|
||||
FwdPwr := (Status.FwdPwrAlex0Hi shl 8) or Status.FwdPwrAlex0Lo;
|
||||
RevPwr := (Status.RevPwrAlex0Hi shl 8) or Status.RevPwrAlex0Lo;
|
||||
SupplyV := -1.0;
|
||||
SupplyA := -1.0;
|
||||
|
||||
// Board 5 (Orion MkII): voltage on UserADC0 (bytes 57-58),
|
||||
// current on UserADC1 (bytes 55-56) — matching Thetis behaviour.
|
||||
// Other supported boards report voltage in SupplyVolts bytes and no current.
|
||||
if BoardSupportsSupplyVoltage(FController.FNetwork.Device.BoardType) then
|
||||
begin
|
||||
if FController.FNetwork.Device.BoardType = 5 then
|
||||
SupplyV := ADCToSupplyVolts(
|
||||
(Status.UserADC0Hi shl 8) or Status.UserADC0Lo,
|
||||
FController.FNetwork.Device.BoardType)
|
||||
else
|
||||
SupplyV := ADCToSupplyVolts(
|
||||
(Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo,
|
||||
FController.FNetwork.Device.BoardType);
|
||||
|
||||
if BoardSupportsSupplyCurrent(FController.FNetwork.Device.BoardType) then
|
||||
SupplyA := ADCToSupplyCurrent(
|
||||
(Status.UserADC1Hi shl 8) or Status.UserADC1Lo,
|
||||
FController.FNetwork.Device.BoardType);
|
||||
end;
|
||||
// FController.FLastSMeter обновляется только из WDSP (GetSMeterDBm) в SpectrumTimerTick —
|
||||
// ExciterPwr это мощность TX, не уровень принятого сигнала.
|
||||
FwdW := ADCToWatts100(FwdPwr);
|
||||
// FController.FTransmitting — наш программный флаг (MOX/TUN/HW-PTT через ApplyMOX).
|
||||
// HPS_PTT-бит в StatusBits — это вход аппаратного PTT (footswitch/mic),
|
||||
// а не «трансивер сейчас передаёт», поэтому только на него полагаться нельзя.
|
||||
IsTx := FController.FTransmitting or ((Status.StatusBits and HPS_PTT) <> 0);
|
||||
// SWR: правильная формула (1+ρ)/(1-ρ), считаем только когда есть значимая
|
||||
// прямая мощность — иначе ADC-шум RevPwr даёт случайные «прыжки» SWR.
|
||||
// Не на передаче — SWR не показываем (фиксируем 1.0), Fwd принудительно 0.
|
||||
if IsTx and (FwdW >= SWR_MIN_FWD_W) and (FwdPwr > 0) then
|
||||
begin
|
||||
// ρ = sqrt(Pr/Pf). Поскольку P=V²/R и V пропорционально ADC, то
|
||||
// sqrt(Pr/Pf) = adc_r/adc_f напрямую — лишнего sqrt не нужно.
|
||||
Rho := RevPwr / FwdPwr;
|
||||
if Rho >= 0.99 then
|
||||
SWRV := SWR_CAP
|
||||
else
|
||||
begin
|
||||
SWRV := (1.0 + Rho) / (1.0 - Rho);
|
||||
if SWRV > SWR_CAP then SWRV := SWR_CAP;
|
||||
if SWRV < 1.0 then SWRV := 1.0;
|
||||
end;
|
||||
end else
|
||||
SWRV := 1.0;
|
||||
if not IsTx then FwdW := 0;
|
||||
Sync := TStatusUISync.Create(DoUpdateStatus, FwdW, SWRV, SupplyV, SupplyA,
|
||||
(Status.StatusBits and HPS_PLL_LOCKED) <> 0,
|
||||
(Status.StatusBits and HPS_PTT) <> 0,
|
||||
Status.ADCOverload);
|
||||
try
|
||||
M := Sync.Execute;
|
||||
TThread.Synchronize(nil, M);
|
||||
finally
|
||||
Sync.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.DoUpdateStatus(FwdW, SWRV, SupplyV, SupplyA: Double; PLLLock, HWPTT: Boolean;
|
||||
ADCOverload: Byte);
|
||||
const
|
||||
// Thetis/piHPSDR PEP-style баллистика для FwdW и SWR: атака мгновенная
|
||||
// (peak-hold), спад медленный — стрелка «висит» на пике и плавно опускается.
|
||||
// Это маскирует огибающую SSB-голоса, которая на ADC прыгает на 10-20 дБ
|
||||
// внутри слога. На rate ~150 пакетов/с эти α дают τ_decay ≈ 350 мс / 500 мс.
|
||||
DECAY_FWD = 0.02;
|
||||
DECAY_SWR = 0.015;
|
||||
// Напряжение питания меняется медленно — обычное симметричное EMA.
|
||||
ALPHA_SUPPLY = 0.05;
|
||||
begin
|
||||
// Запоминаем значения; вывод в UI делает MeterTimerTick на 10 Гц.
|
||||
// Сама обработка здесь идёт на UI-потоке через Synchronize.
|
||||
// FwdW=0 / SWRV=1 — это «не на передаче»: снапим без decay,
|
||||
// чтобы метры моментально падали при отпускании PTT.
|
||||
if FwdW <= 0 then
|
||||
FController.FLastFwdW := 0
|
||||
else if FwdW > FController.FLastFwdW then
|
||||
FController.FLastFwdW := FwdW // attack: instant
|
||||
else
|
||||
FController.FLastFwdW := DECAY_FWD * FwdW + (1.0 - DECAY_FWD) * FController.FLastFwdW;
|
||||
if SWRV <= 1.0 then
|
||||
FController.FLastSWR := 1.0
|
||||
else if SWRV > FController.FLastSWR then
|
||||
FController.FLastSWR := SWRV // attack: instant
|
||||
else
|
||||
FController.FLastSWR := DECAY_SWR * SWRV + (1.0 - DECAY_SWR) * FController.FLastSWR;
|
||||
if SupplyV >= 0 then
|
||||
begin
|
||||
if FController.FLastSupplyV < 0 then
|
||||
FController.FLastSupplyV := SupplyV // первое значение — без сглаживания
|
||||
else
|
||||
FController.FLastSupplyV := ALPHA_SUPPLY * SupplyV + (1.0 - ALPHA_SUPPLY) * FController.FLastSupplyV;
|
||||
end else
|
||||
FController.FLastSupplyV := -1.0;
|
||||
|
||||
if SupplyA >= 0 then
|
||||
begin
|
||||
if FController.FLastSupplyA < 0 then
|
||||
FController.FLastSupplyA := SupplyA
|
||||
else
|
||||
FController.FLastSupplyA := ALPHA_SUPPLY * SupplyA + (1.0 - ALPHA_SUPPLY) * FController.FLastSupplyA;
|
||||
end else
|
||||
FController.FLastSupplyA := -1.0;
|
||||
FController.FLastPLLLock := PLLLock;
|
||||
|
||||
if (FSpecView <> nil) and (FSpecView.ADCOverloadVisible <> (ADCOverload <> 0)) then
|
||||
begin
|
||||
FSpecView.ADCOverloadVisible := ADCOverload <> 0;
|
||||
if PbSpectrum <> nil then
|
||||
PbSpectrum.Invalidate;
|
||||
end;
|
||||
|
||||
// Hardware PTT (foot switch / mic PTT) — обнаружение фронта (нужно делать
|
||||
// на rate HP Status, а не таймера, чтобы не пропустить короткое нажатие).
|
||||
if HWPTT <> FController.FHWPTTActive then
|
||||
begin
|
||||
FController.FHWPTTActive := HWPTT;
|
||||
if not FController.FTuning then
|
||||
begin
|
||||
if HWPTT then
|
||||
begin
|
||||
// Press: не перекрываем ручной MOX; запоминаем что мы включили TX.
|
||||
if not BtnMOX.Active then
|
||||
begin
|
||||
ApplyMOX(True);
|
||||
FController.FHWPTTStartedTX := True;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
// Release: выключаем TX только если он был включён через HWPTT.
|
||||
// Нельзя проверять BtnMOX.Active — ApplyMOX(True) сам его выставляет.
|
||||
if FController.FHWPTTStartedTX then
|
||||
begin
|
||||
FController.FHWPTTStartedTX := False;
|
||||
ApplyMOX(False);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateTXMeters;
|
||||
// Вызывается из MeterTimerTick (10 Гц). Использует FController.FLastFwdW/FController.FLastSWR/
|
||||
// FController.FLastSupplyV/FController.FLastSupplyA обновляются на rate HP Status (~150/с)
|
||||
// в DoUpdateStatus.
|
||||
// Вызывается из MeterTimerTick (10 Гц). FController.FLastFwdW/FLastSWR/
|
||||
// FLastSupplyV/FLastSupplyA обновляются на rate HP Status (~150/с)
|
||||
// в TRadioController.ApplyHPStatus.
|
||||
begin
|
||||
if not FController.FRunning then
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user