From 7a999c95ee37ed67605ada51e6cbdd983a002a72 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 17 Jul 2026 23:01:08 +0300 Subject: [PATCH] =?UTF-8?q?fix(puresignal):=20=D1=86=D0=B5=D0=BB=D1=8C=20f?= =?UTF-8?q?eedback=2022=20=D0=B4=D0=BB=D1=8F=20Orion=20MkII=20+=20outlier-?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=20calcc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Разбор «IMD3 хуже, чем в Thetis»: юзерская плата (Orion MkII/Anvelina PRO3) при стоковой цели feedback 152 перегружает входные каскады ADC2208+кодека — фидбек сам интермодулирует, PS корректирует по искажённому наблюдению. Замеры eu2av (Thetis-Enhanced): оптимум FB≈22 при ATT≈10 дБ. - PSFBTarget: цель уровня feedback, авто по плате (board 5 → 22, иначе 152), настройка ps_fb_target (0 = авто) - авто-ATT: пороги от цели (FB > 1.5×цели / FB < 0.7×цели при ATT>0), ddB = 20*log10(FB/цель). Заодно чинит «ATT всегда 0»: старый порог >181 на этой плате никогда не достигался - PSOutlierSigmaEff → SetPSOutlierSigma (новый экспорт libwdsp): MAD- фильтр выбросов перед фитом calcc, авто σ=5.0 для Orion MkII, настройка ps_outlier_sigma (-1 = авто) - лампа FB в поповере: зоны от цели (0.7–1.3× зелёная), подпись «FB: тек/цель» Требует libwdsp с патчами eu2av (форк wdsp, коммит d27684c). Co-Authored-By: Claude Fable 5 --- PureSignalPopup.pas | 22 +++++++++++++-------- RadioController.pas | 48 +++++++++++++++++++++++++++++++++++++-------- Settings.pas | 12 ++++++++++++ WDSP.pas | 2 ++ WDSPEngine.pas | 9 +++++++-- 5 files changed, 75 insertions(+), 18 deletions(-) diff --git a/PureSignalPopup.pas b/PureSignalPopup.pas index 7ba5724..3a8f546 100644 --- a/PureSignalPopup.pas +++ b/PureSignalPopup.pas @@ -97,8 +97,9 @@ const CLR_INPUT = TColor($00FFFFFF); CLR_INPUT_TEXT = TColor($00202020); - // Лампа feedback (пороги Thetis: >181 горячо, 129..181 оптимум, 91..128 - // слабовато, <=90 нет сигнала). TColor = BGR. + // Лампа feedback — пороги от цели PSFBTarget (Thetis-Enhanced eu2av): + // >1.5×цели горячо, 0.7..1.3×цели оптимум, 0.5×..0.7× слабовато, ниже — нет + // сигнала. TColor = BGR. CLR_FB_HOT = TColor($00FF9030); // голубой: слишком горячо (атт. мал) CLR_FB_OK = TColor($0000FF00); // зелёный: оптимум CLR_FB_LOW = TColor($0000FFFF); // жёлтый: слабовато @@ -382,7 +383,7 @@ end; procedure TPureSignalPopup.RefreshStatus; var - FB: Integer; + FB, FBTarget: Integer; LampClr: TColor; begin if (FController = nil) or not Visible then Exit; @@ -394,17 +395,22 @@ begin FBtn2Ton.Active := FController.FTwoTone; FB := FController.PSFeedbackLevel; + FBTarget := FController.PSFBTarget; if FController.FPSFeedbackOn then begin - if FB > 181 then LampClr := CLR_FB_HOT - else if FB > 128 then LampClr := CLR_FB_OK - else if FB > 90 then LampClr := CLR_FB_LOW - else LampClr := CLR_FB_NONE; + if FB > Round(FBTarget * 1.5) then LampClr := CLR_FB_HOT + else if FB >= Round(FBTarget * 0.7) then + begin + if FB <= Round(FBTarget * 1.3) then LampClr := CLR_FB_OK + else LampClr := CLR_FB_LOW // 1.3..1.5×: желтовато + end + else if FB >= Round(FBTarget * 0.5) then LampClr := CLR_FB_LOW + else LampClr := CLR_FB_NONE; end else LampClr := CLR_LAMP_OFF; if FFbLamp.Color <> LampClr then FFbLamp.Color := LampClr; - FLbFbVal.Caption := Format('FB: %d', [FB]); + FLbFbVal.Caption := Format('FB: %d/%d', [FB, FBTarget]); if FController.PSCorrecting then begin diff --git a/RadioController.pas b/RadioController.pas index d41a7d8..b43a867 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -507,6 +507,7 @@ type procedure ApplyNoiseFilterButtonsToDSP; // NR/NB/SNB/ANF → WDSP procedure SendDUCSpecificFromSettings; // DUC Specific (mic/keyer/atten) в сеть procedure ApplyTXSettingsToDSP; // TX-цепь (фильтр/gain/EQ/comp/...) → WDSP + function PSOutlierSigmaEff: Double; // σ outlier-фильтра calcc (авто по плате) // ---- Управление диапазонами (состояние + DSP; рендер через события) ---- // MakeBandSettings — снимок текущего состояния в запись диапазона. @@ -756,7 +757,11 @@ type // Вооружение/снятие feedback-тракта (сеть + движок), идемпотентно. procedure PSSetFeedbackPath(En: Boolean); // Статус для UI (кэш последнего PureSignalTick) - function PSFeedbackLevel: Integer; // info[4], оптимум ~140-160 + function PSFeedbackLevel: Integer; // info[4], оптимум ~PSFBTarget + // Эффективная цель уровня feedback: настройка ps_fb_target, либо авто по + // плате (Orion MkII/ANAN-7000/8000 → 22 — выше перегружается вход ADC, + // замеры eu2av на Anvelina PRO3; остальные → 152, классика Thetis). + function PSFBTarget: Integer; function PSCalCount: Integer; // info[5] function PSCorrecting: Boolean; // info[14]=1 — коррекция применяется function PSMaxTXVal: Double; // GetPSMaxTX (пик TX для SetPk) @@ -4412,6 +4417,30 @@ begin Changed(rfPureSignal); end; +function TRadioController.PSFBTarget: Integer; +begin + Result := FTXSettings.PSFBTarget; + if Result <= 0 then + begin + if FDevConnected and (FNetwork <> nil) and (FNetwork.Device.BoardType = 5) then + Result := 22 // Orion MkII: замеры eu2av — при 152 фидбек-АЦП сам даёт IMD + else + Result := 152; // классическая цель Thetis + end; +end; + +function TRadioController.PSOutlierSigmaEff: Double; +begin + Result := FTXSettings.PSOutlierSigma; + if Result < 0 then + begin + if FDevConnected and (FNetwork <> nil) and (FNetwork.Device.BoardType = 5) then + Result := 5.0 // Orion MkII: дефолт Thetis-Enhanced eu2av + else + Result := 0.0; // выключен + end; +end; + procedure TRadioController.ApplyPSSettingsToDSP; var Ints, Spi: Integer; @@ -4426,7 +4455,7 @@ begin FTXSettings.PSTXDelayNs, FTXSettings.PSRelaxPtol, FTXSettings.PSPin, FTXSettings.PSMap, FTXSettings.PSStabilize, Ints, Spi, - FTXSettings.PSHWPeak); + FTXSettings.PSHWPeak, PSOutlierSigmaEff); // Правка частот двухтональника при активном 2TON — применяем живьём. if FTwoTone then FDSPEngine.SetTXTwoTone(True, FTXSettings.PSTTFreq1, FTXSettings.PSTTFreq2); @@ -4464,6 +4493,7 @@ procedure TRadioController.PureSignalTick; // + auto-attenuate (timer2code): подстройка AttOnTX по уровню feedback. var NewAtt: Integer; + FBTarget: Integer; ddB: Double; CalAttemptsChanged: Boolean; begin @@ -4555,21 +4585,23 @@ begin end; end; - // --- Auto-attenuate (Thetis timer2code) --- + // --- Auto-attenuate (Thetis-Enhanced eu2av timer2code) --- // По завершении очередной попытки калибровки: если feedback слишком горячий - // (>181) или слабый при ненулевом атт. (<=128), подстроить AttOnTX так, - // чтобы уровень лёг к оптимуму 152. + // (>1.5×цели) или слабый при ненулевом атт. (<0.7×цели), подстроить AttOnTX + // так, чтобы уровень лёг к цели PSFBTarget (22 на Orion MkII — выше вход + // фидбек-АЦП интермодулирует сам, и PS корректирует по искажённому сигналу). + FBTarget := PSFBTarget; 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 + and ((FPSInfo[4] > Round(FBTarget * 1.5)) or + ((FPSInfo[4] < Round(FBTarget * 0.7)) 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); + ddB := 20.0 * Log10(FPSInfo[4] / FBTarget); if ddB < -100.0 then ddB := -100.0; if ddB > +100.0 then ddB := +100.0; end diff --git a/Settings.pas b/Settings.pas index a2ac336..0011af8 100644 --- a/Settings.pas +++ b/Settings.pas @@ -234,6 +234,12 @@ type PSHWPeak: Double; // SetPk (0.2899 для P2, 0.6121 Saturn) PSTTFreq1: Integer; // Гц — тон 1 двухтональника (700) PSTTFreq2: Integer; // Гц — тон 2 двухтональника (1900) + // Калибровка фидбек-тракта (Thetis-Enhanced eu2av): на Orion MkII цель 152 + // перегружает входные каскады ADC → IMD-«юбка», оптимум FB≈22 при ATT≈10дБ. + PSFBTarget: Integer; // цель уровня feedback info[4]; 0 = авто по плате + // (Orion MkII → 22, иначе 152) + PSOutlierSigma: Double; // σ outlier-фильтра calcc; <0 = авто по плате + // (Orion MkII → 5.0, иначе выкл.), 0 = выкл. end; // Web-сервер — глобальные настройки (не привязаны к устройству, секция "web"). @@ -1214,6 +1220,8 @@ begin T.PSHWPeak := 0.2899; T.PSTTFreq1 := 700; T.PSTTFreq2 := 1900; + T.PSFBTarget := 0; // авто по плате + T.PSOutlierSigma := -1.0; // авто по плате end; function TSettingsManager.LoadTX(const MAC: array of Byte; @@ -1293,6 +1301,8 @@ begin T.PSHWPeak := JD(O,'ps_hw_peak', T.PSHWPeak); T.PSTTFreq1 := JI(O,'ps_tt_freq1', T.PSTTFreq1); T.PSTTFreq2 := JI(O,'ps_tt_freq2', T.PSTTFreq2); + T.PSFBTarget := EnsureRange(JI(O,'ps_fb_target', T.PSFBTarget), 0, 256); + T.PSOutlierSigma := JD(O,'ps_outlier_sigma', T.PSOutlierSigma); end; procedure TSettingsManager.SaveTX(const MAC: array of Byte; @@ -1364,6 +1374,8 @@ begin JW(O,'ps_hw_peak', T.PSHWPeak); JW(O,'ps_tt_freq1', T.PSTTFreq1); JW(O,'ps_tt_freq2', T.PSTTFreq2); + JW(O,'ps_fb_target', T.PSFBTarget); + JW(O,'ps_outlier_sigma',T.PSOutlierSigma); end; class procedure TSettingsManager.DefaultAlex(out A: TAlexSettings); diff --git a/WDSP.pas b/WDSP.pas index 21e57c7..c47992d 100644 --- a/WDSP.pas +++ b/WDSP.pas @@ -281,6 +281,8 @@ procedure SetPSPinMode(channel: Integer; pin: Integer); cdecl; external WDSP_LIB procedure SetPSMapMode(channel: Integer; map: Integer); cdecl; external WDSP_LIB; procedure SetPSStabilize(channel: Integer; stbl: Integer); cdecl; external WDSP_LIB; procedure SetPSIntsAndSpi(channel: Integer; ints: Integer; spi: Integer); cdecl; external WDSP_LIB; +// eu2av: медианный (MAD) outlier-фильтр перед кубическим фитом calcc; 0 = выкл. +procedure SetPSOutlierSigma(channel: Integer; sigma: Double); cdecl; external WDSP_LIB; // --------------------------------------------------------------------------- // cblock.c diff --git a/WDSPEngine.pas b/WDSPEngine.pas index 9058828..8a79bfa 100644 --- a/WDSPEngine.pas +++ b/WDSPEngine.pas @@ -404,6 +404,7 @@ type FPSInts: Integer; FPSSpi: Integer; FPSHWPeak: Double; + FPSOutlierSigma: Double; // eu2av MAD-фильтр перед фитом; 0 = выкл. // Кэшированные TX-параметры — применяются после OpenChannel(TXA) и при изменении. // Для Filter/Compressor/Leveler/ALC/PhaseRot/EQ/AM/FM/CTCSS. @@ -668,7 +669,8 @@ type function PSGetMaxTX: Double; procedure PSSetParams(MoxDelay, LoopDelay: Double; TXDelayNs: Integer; RelaxPtol: Boolean; Pin, Map, Stabilize: Boolean; - Ints, Spi: Integer; HWPeak: Double); + Ints, Spi: Integer; HWPeak: Double; + OutlierSigma: Double); // Расширенное TX управление (применяются мгновенно если канал открыт) procedure SetTXFilterFull(Low, High, NC, MP, Window: Integer); @@ -1318,6 +1320,7 @@ begin FPSInts := 16; FPSSpi := 256; FPSHWPeak := 0.2899; // протокол 2 (Saturn: 0.6121) + FPSOutlierSigma := 0.0; // выкл. до ApplyPSSettingsToDSP (авто по плате) FMonitorRXDuringTX := False; FPostTXMuteUntil := 0; FActiveDisplayID := RX_DISP_ID; @@ -3549,11 +3552,12 @@ begin SetPSStabilize(TXA_CHAN, Ord(FPSStabilize)); SetPSIntsAndSpi(TXA_CHAN, FPSInts, FPSSpi); SetPSHWPeak(TXA_CHAN, FPSHWPeak); + SetPSOutlierSigma(TXA_CHAN, FPSOutlierSigma); end; procedure TWDSPEngine.PSSetParams(MoxDelay, LoopDelay: Double; TXDelayNs: Integer; RelaxPtol: Boolean; Pin, Map, Stabilize: Boolean; Ints, Spi: Integer; - HWPeak: Double); + HWPeak: Double; OutlierSigma: Double); begin FPSMoxDelay := MoxDelay; FPSLoopDelay := LoopDelay; @@ -3565,6 +3569,7 @@ begin if Ints > 0 then FPSInts := Ints; if Spi > 0 then FPSSpi := Spi; if HWPeak > 0 then FPSHWPeak := HWPeak; + if OutlierSigma >= 0 then FPSOutlierSigma := OutlierSigma; ApplyPSParams; end;