mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
fix TX meter readout: правильная SWR-формула + EMA + троттлинг UI
- SWR: было 1+sqrt(Pr/Pf) — теперь (1+ρ)/(1-ρ). Считается только при FwdW>=1W и HPS_PTT=1, иначе 1.0. Cap 9.9. - На RX FwdW=0 (ADC даёт шум). - EMA-сглаживание (α=0.30/0.20/0.05) в DoUpdateStatus на UI-потоке. - LblFwdPwr/LblSWRVal и StatusBar Supply/FWD/SWR обновляются из MeterTimerTick (10 Гц), а не на rate HP Status (~150/с). - PbFwdPower/PbSWR теперь Invalidate в таймере — бары двигаются.
This commit is contained in:
+80
-19
@@ -203,6 +203,10 @@ type
|
||||
FLastDDCIndex: Integer; // последний DDC index
|
||||
FLastFwdW: Double;
|
||||
FLastSWR: Double;
|
||||
// Эти значения приходят с HP Status пакетами (50..200 раз/с). UI обновляется
|
||||
// из MeterTimerTick (10 Гц), чтобы цифры/полоски не дёргались.
|
||||
FLastSupplyV: Double;
|
||||
FLastPLLLock: Boolean;
|
||||
|
||||
// ---- Spectrum / Waterfall view ----
|
||||
FSpecView: TSpectrumView;
|
||||
@@ -391,6 +395,7 @@ type
|
||||
// Public UI update (called from sync objects)
|
||||
procedure DoAddDevice(const Dev: THPSDRDevice; const Entry: string);
|
||||
procedure DoUpdateStatus(FwdW, SWRV, SupplyV: Double; PLLLock, HWPTT: Boolean);
|
||||
procedure UpdateTXMeters;
|
||||
procedure DoUpdateDDCSeq(DDCIdx: Integer; Seq: LongWord);
|
||||
procedure DoUpdateDDCSeqOrNoDevice(DDCIdx: Integer; Seq: LongWord);
|
||||
|
||||
@@ -1106,6 +1111,8 @@ begin
|
||||
FLastDDCIndex := 0;
|
||||
FLastFwdW := 0;
|
||||
FLastSWR := 1;
|
||||
FLastSupplyV:= -1;
|
||||
FLastPLLLock:= False;
|
||||
FDeviceCount := 0;
|
||||
FDeviceDialog := TDeviceDialog.Create(Self);
|
||||
FDeviceDialog.OnDiscover := BtnDiscoverFromDialog;
|
||||
@@ -2584,6 +2591,9 @@ begin
|
||||
FSpecView.LastSWR := FLastSWR;
|
||||
|
||||
if PbSMeterRight <> nil then PbSMeterRight.Invalidate;
|
||||
// PWR/SWR метки и бары обновляем здесь (10 Гц), а не в DoUpdateStatus —
|
||||
// HP Status пакеты идут ~150 раз/с, при таком rate цифры дёргаются.
|
||||
UpdateTXMeters;
|
||||
|
||||
// Диагностика TX mic-пути — в отдельной панели [5], чтобы её не перетирали
|
||||
// другие источники (DDC seq, audio-логи и т.п.).
|
||||
@@ -2791,9 +2801,15 @@ begin
|
||||
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, FwdW, SWRV: Double;
|
||||
SupplyV, FwdW, SWRV, Rho: Double;
|
||||
IsTx: Boolean;
|
||||
Sync: TStatusUISync;
|
||||
M: TThreadMethod;
|
||||
begin
|
||||
@@ -2813,10 +2829,24 @@ begin
|
||||
// FLastSMeter обновляется только из WDSP (GetSMeterDBm) в SpectrumTimerTick —
|
||||
// ExciterPwr это мощность TX, не уровень принятого сигнала.
|
||||
FwdW := ADCToWatts100(FwdPwr);
|
||||
if FwdW > 0 then
|
||||
SWRV := 1 + Sqrt(RevPwr / FwdPwr)
|
||||
else
|
||||
IsTx := (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
|
||||
Rho := Sqrt(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(Self, FwdW, SWRV, SupplyV,
|
||||
(Status.StatusBits and HPS_PLL_LOCKED) <> 0,
|
||||
(Status.StatusBits and HPS_PTT) <> 0);
|
||||
@@ -2829,24 +2859,30 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainForm.DoUpdateStatus(FwdW, SWRV, SupplyV: Double; PLLLock, HWPTT: Boolean);
|
||||
var
|
||||
PLLStr: string;
|
||||
const
|
||||
// EMA-сглаживание (Thetis/piHPSDR-style): значения с ADC шумят, особенно
|
||||
// RevPwr на низкой мощности. Эти константы подобраны на rate ~150 пакетов/с.
|
||||
ALPHA_FWD = 0.30; // быстрый отклик для текущей мощности
|
||||
ALPHA_SWR = 0.20; // SWR более инерционный
|
||||
ALPHA_SUPPLY = 0.05; // напряжение питания меняется медленно
|
||||
begin
|
||||
FLastFwdW := FwdW;
|
||||
FLastSWR := SWRV;
|
||||
LblFwdPwr.Caption := Format('%.0fW', [FwdW]);
|
||||
LblSWRVal.Caption := Format('SWR:%.1f', [SWRV]);
|
||||
if PLLLock then PLLStr := 'PLL OK' else PLLStr := 'PLL?';
|
||||
// Запоминаем значения; вывод в UI (метки, бары, статусбар) делает
|
||||
// MeterTimerTick на 10 Гц — иначе цифры дёргаются по частоте HP Status (~150/с).
|
||||
// EMA-сглаживание здесь же (на UI-потоке через Synchronize, безопасно для
|
||||
// чтения из MeterTimerTick).
|
||||
FLastFwdW := ALPHA_FWD * FwdW + (1.0 - ALPHA_FWD) * FLastFwdW;
|
||||
FLastSWR := ALPHA_SWR * SWRV + (1.0 - ALPHA_SWR) * FLastSWR;
|
||||
if SupplyV >= 0 then
|
||||
StatusBar1.Panels[2].Text :=
|
||||
Format('Supply: %.1fV | FWD: %.0fW | SWR: %.1f | %s',
|
||||
[SupplyV, FwdW, SWRV, PLLStr])
|
||||
else
|
||||
StatusBar1.Panels[2].Text :=
|
||||
Format('FWD: %.0fW | SWR: %.1f | %s',
|
||||
[FwdW, SWRV, PLLStr]);
|
||||
begin
|
||||
if FLastSupplyV < 0 then
|
||||
FLastSupplyV := SupplyV // первое значение — без сглаживания
|
||||
else
|
||||
FLastSupplyV := ALPHA_SUPPLY * SupplyV + (1.0 - ALPHA_SUPPLY) * FLastSupplyV;
|
||||
end;
|
||||
FLastPLLLock := PLLLock;
|
||||
|
||||
// Hardware PTT (foot switch / mic PTT) — обнаружение фронта
|
||||
// Hardware PTT (foot switch / mic PTT) — обнаружение фронта (нужно делать
|
||||
// на rate HP Status, а не таймера, чтобы не пропустить короткое нажатие).
|
||||
if HWPTT <> FHWPTTActive then
|
||||
begin
|
||||
FHWPTTActive := HWPTT;
|
||||
@@ -2857,6 +2893,31 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateTXMeters;
|
||||
// Вызывается из MeterTimerTick (10 Гц). Использует FLastFwdW/FLastSWR/
|
||||
// FLastSupplyV/FLastPLLLock — они обновляются на rate HP Status (~150/с)
|
||||
// в DoUpdateStatus.
|
||||
var
|
||||
PLLStr: string;
|
||||
begin
|
||||
if LblFwdPwr <> nil then
|
||||
LblFwdPwr.Caption := Format('%.0fW', [FLastFwdW]);
|
||||
if LblSWRVal <> nil then
|
||||
LblSWRVal.Caption := Format('SWR:%.1f', [FLastSWR]);
|
||||
if PbFwdPower <> nil then PbFwdPower.Invalidate;
|
||||
if PbSWR <> nil then PbSWR.Invalidate;
|
||||
|
||||
if FLastPLLLock then PLLStr := 'PLL OK' else PLLStr := 'PLL?';
|
||||
if FLastSupplyV >= 0 then
|
||||
StatusBar1.Panels[2].Text :=
|
||||
Format('Supply: %.1fV | FWD: %.0fW | SWR: %.1f | %s',
|
||||
[FLastSupplyV, FLastFwdW, FLastSWR, PLLStr])
|
||||
else
|
||||
StatusBar1.Panels[2].Text :=
|
||||
Format('FWD: %.0fW | SWR: %.1f | %s',
|
||||
[FLastFwdW, FLastSWR, PLLStr]);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnDDCIQCB(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
var
|
||||
SamplesPerFrame: Integer;
|
||||
|
||||
Reference in New Issue
Block a user