diff --git a/MainForm.pas b/MainForm.pas index 066d49a..6d675dc 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -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;