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:
Uladzimir Karpenka
2026-06-05 23:35:35 +03:00
co-authored by Claude Opus 4.8
parent 9d2324cc6c
commit c1da506f73
2 changed files with 155 additions and 165 deletions
+145
View File
@@ -69,6 +69,7 @@ type
rfFMStep, rfFMStepIdx, rfFMRpt,
rfChannel,
rfSMeter, rfPower, rfSWR, rfSupply, rfPLL, // телеметрия
rfADCOverload, // ADC overload (рендер индикатора)
rfDevice // подключённое устройство сменилось
);
@@ -213,6 +214,12 @@ type
FLastSeqErrorDDC: Integer;
FLastSeqErrorDelta: Int64;
FSeqOkStreak: Integer;
FADCOverloadActive: Boolean; // ADC overload (рендерится по rfADCOverload)
// Pending-значения декода HP-статуса (сетевой поток → ApplyHPStatus через
// Invoke; Synchronize блокирующий и последовательный, гонок нет).
FHPpFwdW, FHPpSWR, FHPpSupplyV, FHPpSupplyA: Double;
FHPpPLL, FHPpHWPTT: Boolean;
FHPpOverload: Byte;
constructor Create;
destructor Destroy; override;
@@ -230,6 +237,8 @@ type
procedure OnMicPacket(const Data: TMicDataPacket);
function PullSoundCardMic(MaxN: Integer): Integer;
procedure OnDDCIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
procedure OnHPStatus(const Status: THighPriorityStatus);
procedure ApplyHPStatus; // маршалится из OnHPStatus (сглаживание/overload/HWPTT)
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
procedure Invoke(M: TThreadMethod);
@@ -499,6 +508,142 @@ begin
FLastDDCIndex := DDCIndex;
end;
procedure TRadioController.OnHPStatus(const Status: THighPriorityStatus);
// Сетевой поток: декод HP-статуса (board-specific supply/power/SWR) → pending →
// маршалинг в ApplyHPStatus (сглаживание + overload + HWPTT уже на потоке UI/ядра).
const
SWR_MIN_FWD_W = 1.0; // ниже — fwd это ADC-шум, SWR не считаем
SWR_CAP = 9.9;
var
FwdPwr, RevPwr: Word;
SupplyV, SupplyA, FwdW, SWRV, Rho: Double;
IsTx: Boolean;
begin
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): напряжение на UserADC0, ток на UserADC1 (как Thetis).
// Прочие платы — в SupplyVolts-байтах, без тока.
if BoardSupportsSupplyVoltage(FNetwork.Device.BoardType) then
begin
if FNetwork.Device.BoardType = 5 then
SupplyV := ADCToSupplyVolts(
(Status.UserADC0Hi shl 8) or Status.UserADC0Lo,
FNetwork.Device.BoardType)
else
SupplyV := ADCToSupplyVolts(
(Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo,
FNetwork.Device.BoardType);
if BoardSupportsSupplyCurrent(FNetwork.Device.BoardType) then
SupplyA := ADCToSupplyCurrent(
(Status.UserADC1Hi shl 8) or Status.UserADC1Lo,
FNetwork.Device.BoardType);
end;
FwdW := ADCToWatts100(FwdPwr);
// FTransmitting — программный флаг; HPS_PTT — вход HW-PTT (footswitch/mic).
IsTx := FTransmitting or ((Status.StatusBits and HPS_PTT) <> 0);
// SWR = (1+ρ)/(1-ρ), ρ = adc_r/adc_f; только при значимой fwd-мощности.
if IsTx and (FwdW >= SWR_MIN_FWD_W) and (FwdPwr > 0) then
begin
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;
FHPpFwdW := FwdW;
FHPpSWR := SWRV;
FHPpSupplyV := SupplyV;
FHPpSupplyA := SupplyA;
FHPpPLL := (Status.StatusBits and HPS_PLL_LOCKED) <> 0;
FHPpHWPTT := (Status.StatusBits and HPS_PTT) <> 0;
FHPpOverload := Status.ADCOverload;
Invoke(ApplyHPStatus);
end;
procedure TRadioController.ApplyHPStatus;
// Маршалится из OnHPStatus. PEP-style баллистика (мгновенная атака, медленный
// спад) для FwdW/SWR; EMA для supply. + overload-событие + HWPTT-фронт → SetMOX.
const
DECAY_FWD = 0.02;
DECAY_SWR = 0.015;
ALPHA_SUPPLY = 0.05;
begin
if FHPpFwdW <= 0 then
FLastFwdW := 0
else if FHPpFwdW > FLastFwdW then
FLastFwdW := FHPpFwdW // attack: instant
else
FLastFwdW := DECAY_FWD * FHPpFwdW + (1.0 - DECAY_FWD) * FLastFwdW;
if FHPpSWR <= 1.0 then
FLastSWR := 1.0
else if FHPpSWR > FLastSWR then
FLastSWR := FHPpSWR // attack: instant
else
FLastSWR := DECAY_SWR * FHPpSWR + (1.0 - DECAY_SWR) * FLastSWR;
if FHPpSupplyV >= 0 then
begin
if FLastSupplyV < 0 then
FLastSupplyV := FHPpSupplyV // первое значение — без сглаживания
else
FLastSupplyV := ALPHA_SUPPLY * FHPpSupplyV + (1.0 - ALPHA_SUPPLY) * FLastSupplyV;
end else
FLastSupplyV := -1.0;
if FHPpSupplyA >= 0 then
begin
if FLastSupplyA < 0 then
FLastSupplyA := FHPpSupplyA
else
FLastSupplyA := ALPHA_SUPPLY * FHPpSupplyA + (1.0 - ALPHA_SUPPLY) * FLastSupplyA;
end else
FLastSupplyA := -1.0;
FLastPLLLock := FHPpPLL;
// ADC overload — индикатор рисует UI по rfADCOverload (только на изменении).
if FADCOverloadActive <> (FHPpOverload <> 0) then
begin
FADCOverloadActive := FHPpOverload <> 0;
Changed(rfADCOverload);
end;
// Hardware PTT (footswitch / mic PTT): фронт на rate HP Status — не пропустить
// короткое нажатие. HWPTT не перекрывает ручной MOX; выключаем TX только если
// его включил именно HWPTT.
if FHPpHWPTT <> FHWPTTActive then
begin
FHWPTTActive := FHPpHWPTT;
if not FTuning then
begin
if FHPpHWPTT then
begin
if not FTransmitting then
begin
SetMOX(True);
FHWPTTStartedTX := True;
end;
end
else
begin
if FHWPTTStartedTX then
begin
FHWPTTStartedTX := False;
SetMOX(False);
end;
end;
end;
end;
end;
procedure TRadioController.OnMicPacket(const Data: TMicDataPacket);
// HW mic-стрим уходит в WDSP только если выбран источник Radio; иначе сэмплы
// игнорируются (mic берётся со звуковой карты).