From 51e8fc4782624be84b59ae8487304cbb9f4aafde Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 24 Jul 2026 19:42:06 +0300 Subject: [PATCH] =?UTF-8?q?fix(tx):=20=D0=B1=D0=BE=D0=BA=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D1=83=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=BF=D1=83=D1=82?= =?UTF-8?q?=D1=8F=D1=85=20+=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BE=D1=80=D0=BA=D0=B5=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20TX/RX=20(=D1=8D=D1=82=D0=B0=D0=BB=D0=BE=D0=BD=20Thetis?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Боковая TX теперь строится ЕДИНЫМ конвертером знака TXSignedEdges (порт Thetis UpdateTXLowHighFilterForMode): магнитуда TX-полосы → знаковые края bp0 по боковой режима. Через него идут ApplyTXChainSettings и SetTXFilterFull — LSB/DIGL больше не уходят в USB, когда пуш оказывается последним (MOX). Убраны оба SetTXABandpassRun(1): этот сеттер дёргает bp1 (aux), а не bp0. При выкл. компрессоре bp1 держал стартовую полосу (−5000,−100) и глушил вход модулятора — FM/WFM/FMRAW не заводились без «тычка» компрессором. bp1 отдан компрессору через TXASetupBPFilters, как в Thetis. Оркестрация сведена: ApplyTXMode (SetTXAMode+ApplyTXModeSettings) — единая точка TX-перехода для Open/SetMode/SetTXMode (в т.ч. слайс-TX); ApplyRXModeSettings (WFM/DMR/FMRAW) — для главного RXA и слайсов. Убраны ручной DMR-инлайн в SetMode и двойной вызов WFM; ApplyWFMSettings→ApplyTXWFMSettings. Дефолт TX-полосы 100/2800 → 200/3100 (Thetis-паритет). Co-Authored-By: Claude Opus 4.8 --- Settings.pas | 4 +- SettingsForm.pas | 4 +- WDSPEngine.pas | 112 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 85 insertions(+), 35 deletions(-) diff --git a/Settings.pas b/Settings.pas index 6f98b36..33108f2 100644 --- a/Settings.pas +++ b/Settings.pas @@ -1253,8 +1253,8 @@ begin T.TUNLevel := 10; T.TUNFreq := 750; // SSB-фильтр по умолчанию - T.FilterLow := 100; - T.FilterHigh := 2800; + T.FilterLow := 200; + T.FilterHigh := 3100; T.FilterNC := 1024; T.FilterMP := 1; // Low Latency (как в piHPSDR) T.FilterWindow := 1; // BH4 / Blackman-Harris diff --git a/SettingsForm.pas b/SettingsForm.pas index 0a7f1cc..452c33b 100644 --- a/SettingsForm.pas +++ b/SettingsForm.pas @@ -1390,9 +1390,9 @@ begin Grp := MakeGroupPanel(FPageTransmit, 'TX Filter (bandpass)', MARGIN, Y, GRP_W, 220); MakeLbl(Grp, 'Low cut (Hz)', PAD, R1 + 5, LW); - FEdTXFiltLow := MkSpin(Grp, CX, R1, 0, 8000, 100); + FEdTXFiltLow := MkSpin(Grp, CX, R1, 0, 8000, 200); MakeLbl(Grp, 'High cut (Hz)', PAD + 320, R1 + 5, 90); - FEdTXFiltHigh := MkSpin(Grp, PAD + 320 + 100, R1, 100, 12000, 2800); + FEdTXFiltHigh := MkSpin(Grp, PAD + 320 + 100, R1, 100, 12000, 3100); MakeLbl(Grp, 'FIR length', PAD, R1 + STEP + 5, LW); FCmbTXFiltNC := MkCmb(Grp, CX, R1 + STEP, 140); diff --git a/WDSPEngine.pas b/WDSPEngine.pas index 5593579..a32ad64 100644 --- a/WDSPEngine.pas +++ b/WDSPEngine.pas @@ -493,10 +493,20 @@ type procedure ApplyRXWFMToChan(Chan: Integer); procedure ApplyRXDMRToChan(Chan: Integer); procedure ApplyRXRawToChan(Chan: Integer); - procedure ApplyWFMSettings; + procedure ApplyTXWFMSettings; + // Единая RX-режимная надстройка канала (WFM/DMR/FM RAW). Одна точка для + // главного RXA и для слайсов — иначе дублирование по трём местам. + procedure ApplyRXModeSettings(Chan, Mode: Integer); + // Магнитуда TX-полосы (FTXFilter*) → знаковые края bp0 по боковой режима. + // Порт Thetis UpdateTXLowHighFilterForMode: единственный конвертер знака, + // через который обязан идти ЛЮБОЙ пуш TX-bandpass, иначе LSB уходит в USB. + procedure TXSignedEdges(Mode: Integer; out Lo, Hi: Double); // Mode-dependent TX overlay. FM RAW uses the flat WFM modulator and // bypasses every speech processor without overwriting the saved TX setup. procedure ApplyTXModeSettings(Mode: Integer); + // ЕДИНАЯ точка перевода TX-тракта в режим (модулятор + режимная цепь). + // Через неё идут ВСЕ переходы: Open, SetMode, SetTXMode (в т.ч. слайс-TX). + procedure ApplyTXMode(Mode: Integer); // --- Мультислайсы --- function FindSliceIdx(Id: Integer): Integer; // -1 если нет function SliceInRate(Idx: Integer): Integer; // входной rate слайса (его пан) @@ -1159,15 +1169,50 @@ begin SetRXAWFMLimRun (Chan, 0); end; -procedure TWDSPEngine.ApplyWFMSettings; +procedure TWDSPEngine.ApplyTXWFMSettings; +// TX-часть широкого ЧМ. RX-сторона WFM ставится через ApplyRXModeSettings. begin - ApplyRXWFMToChan(RXA_CHAN); SetTXAWFMDeviation (TXA_CHAN, WFM_DEVIATION); SetTXAWFMAFFreqs (TXA_CHAN, WFM_AF_LOW, WFM_AF_HIGH); SetTXAWFMPreEmphRun (TXA_CHAN, 1); SetTXAWFMPreEmphTau (TXA_CHAN, WFM_TAU_DE_US); end; +procedure TWDSPEngine.ApplyRXModeSettings(Chan, Mode: Integer); +// Режимная RX-надстройка канала. Для «нормальных» режимов ничего не нужно — +// всё делает SetRXAMode; спец-обвязку требуют только FM-семейства. +begin + if Mode = MODE_WFM then ApplyRXWFMToChan(Chan) + else if Mode = MODE_DMR then ApplyRXDMRToChan(Chan) + else if Mode = MODE_FMRAW then ApplyRXRawToChan(Chan); +end; + +procedure TWDSPEngine.TXSignedEdges(Mode: Integer; out Lo, Hi: Double); +// Порт Thetis UpdateTXLowHighFilterForMode. Вход — магнитуда TX-аудио-полосы +// (FTXFilterLow/High, всегда положительная); выход — знаковые края bp0 под +// боковую режима. Нижняя боковая зеркалит полосу в отрицательные частоты. +var + L, H, Half: Double; +begin + L := FTXFilterLow; + H := FTXFilterHigh; + case Mode of + MODE_LSB, MODE_CWL, MODE_DIGL: + begin Lo := -H; Hi := -L; end; // нижняя боковая + MODE_USB, MODE_CWU, MODE_DIGU: + begin Lo := L; Hi := H; end; // верхняя боковая + MODE_DSB, MODE_AM, MODE_SAM: + begin Lo := -H; Hi := H; end; // симметрично + MODE_FM: + begin // Карсон: ±(девиация + верхний срез) + Half := FTXFMDeviation + FTXFMHighCut; + Lo := -Half; Hi := Half; + end; + else + begin Lo := L; Hi := H; end; // прочее — как есть + end; +end; + procedure TWDSPEngine.ApplyTXModeSettings(Mode: Integer); begin if not FInitialized then Exit; @@ -1180,7 +1225,6 @@ begin SetTXAWFMAFFreqs (TXA_CHAN, 20.0, 7000.0); SetTXAWFMPreEmphRun (TXA_CHAN, 0); SetTXABandpassFreqs (TXA_CHAN, 20.0, 7000.0); - SetTXABandpassRun (TXA_CHAN, 1); SetTXAPanelGain1 (TXA_CHAN, 1.0); SetTXACompressorRun (TXA_CHAN, 0); SetTXALevelerSt (TXA_CHAN, 0); @@ -1192,12 +1236,22 @@ begin else begin // Restore the user's normal voice/data chain after leaving FM RAW. - SetTXABandpassRun(TXA_CHAN, 1); + // bp1 (aux bandpass) НЕ трогаем вручную — им управляет компрессор через + // TXASetupBPFilters (как в Thetis). bp0 всегда активен сам. ApplyTXChainSettings; - if Mode = MODE_WFM then ApplyWFMSettings; + if Mode = MODE_WFM then ApplyTXWFMSettings; end; end; +procedure TWDSPEngine.ApplyTXMode(Mode: Integer); +// ЕДИНАЯ точка перевода TX-тракта в режим. SetTXAMode пересобирает модулятор и +// bp0/bp1 под режим (TXASetupBPFilters), ApplyTXModeSettings кладёт режимную цепь. +begin + if not FInitialized then Exit; + SetTXAMode(TXA_CHAN, ModeToWDSPTX(Mode)); + ApplyTXModeSettings(Mode); +end; + function TWDSPEngine.ClampToNyquist(Hz, Rate: Integer): Integer; var Limit: Integer; begin @@ -1340,8 +1394,8 @@ begin FillChar(FTXMicRing, SizeOf(FTXMicRing), 0); // TX-цепь — дефолты, согласуются с Settings.DefaultTX - FTXFilterLow := 100; - FTXFilterHigh := 2800; + FTXFilterLow := 200; + FTXFilterHigh := 3100; FTXFilterNC := 1024; FTXFilterMP := 1; FTXFilterWindow := 1; @@ -1741,7 +1795,6 @@ begin ); FTXMode := FMode; - SetTXAMode(TXA_CHAN, ModeToWDSPTX(FTXMode)); SetTXAPanelRun(TXA_CHAN, 1); // PatchPanel: 1=Q, 2=I. Mic подаётся в I (FTXIn[i*2]), Q=0 — выбираем I. SetTXAPanelSelect(TXA_CHAN, 2); @@ -1753,12 +1806,14 @@ begin // SetFilter пересчитает полосу уже под новый dsp_rate. if FMode = MODE_WFM then begin - SetMode(FMode); + SetMode(FMode); // внутри: ApplyRXModeSettings + ApplyTXMode SetFilter(FFilterLow, FFilterHigh); end; // Прокидываем все TX-параметры из FTX*-полей: фильтр, mic gain, EQ, // Leveler, ALC, Compressor, Phase Rotator, AM Carrier, FM, CTCSS. ApplyTXChainSettings; + // Перевод TX-тракта в режим через единую точку (для WFM уже сделал SetMode). + if FMode <> MODE_WFM then ApplyTXMode(FTXMode); ApplyNoiseFilterState; // Переоткрываем WDSP-каналы активных слайсов (например после ChangeSampleRate, @@ -2382,9 +2437,7 @@ begin create_nobEXT(Chan, 1, 0, InSize, InRate, 0.0001, 0.0001, 0.0001, 0.05, 20); SetRXABandpassWindow(Chan, 1); SetRXAMode(Chan, ModeToWDSP(FSlices[Idx].Mode)); - if FSlices[Idx].Mode = MODE_WFM then ApplyRXWFMToChan(Chan); - if FSlices[Idx].Mode = MODE_DMR then ApplyRXDMRToChan(Chan); - if FSlices[Idx].Mode = MODE_FMRAW then ApplyRXRawToChan(Chan); + ApplyRXModeSettings(Chan, FSlices[Idx].Mode); SetRXAShiftRun(Chan, 1); SetRXAShiftFreq(Chan, FSlices[Idx].ShiftHz); RXASetPassband(Chan, ClampToNyquist(FSlices[Idx].FilterLo, DSPRate), @@ -2688,9 +2741,7 @@ begin else if FSlices[idx].Opened then begin SetRXAMode(FSlices[idx].Chan, ModeToWDSP(Mode)); - if Mode = MODE_WFM then ApplyRXWFMToChan(FSlices[idx].Chan); - if Mode = MODE_DMR then ApplyRXDMRToChan(FSlices[idx].Chan); - if Mode = MODE_FMRAW then ApplyRXRawToChan(FSlices[idx].Chan); + ApplyRXModeSettings(FSlices[idx].Chan, Mode); if Mode in [MODE_DMR, MODE_FMRAW] then SetRXAPanelGain1(FSlices[idx].Chan, 1.0) else @@ -2895,16 +2946,8 @@ begin // а SetRXAMode затем пересчитает bpsnba под уже актуальный dsp_rate. ApplyDSPRate(Mode); SetRXAMode(RXA_CHAN, ModeToWDSP(Mode)); - SetTXAMode(TXA_CHAN, ModeToWDSPTX(Mode)); - if Mode = MODE_WFM then ApplyWFMSettings - else if Mode = MODE_DMR then - begin - SetRXAWFMDeviation (RXA_CHAN, DMR_DEVIATION); - SetRXAWFMAFFilter (RXA_CHAN, 20.0, 7000.0); - SetRXAWFMDeemphRun (RXA_CHAN, 0); - end - else if Mode = MODE_FMRAW then ApplyRXRawToChan(RXA_CHAN); - ApplyTXModeSettings(FTXMode); + ApplyRXModeSettings(RXA_CHAN, Mode); + ApplyTXMode(FTXMode); // НЕ вызываем ApplyDefaultFilter — фильтр устанавливается явно из UI end; @@ -3202,8 +3245,7 @@ procedure TWDSPEngine.SetTXMode(Mode: Integer); begin FTXMode := Mode; if not FInitialized then Exit; - SetTXAMode(TXA_CHAN, ModeToWDSPTX(FTXMode)); - ApplyTXModeSettings(FTXMode); + ApplyTXMode(FTXMode); end; procedure TWDSPEngine.SetTXFilter(Low, High: Integer); @@ -3498,12 +3540,17 @@ procedure TWDSPEngine.ApplyTXChainSettings; var i: Integer; Gains, Freqs, Qs: array[0..10] of Double; + BpLo, BpHi: Double; begin if not FInitialized then Exit; - // Bandpass + // Bandpass — магнитуду TX-полосы прогоняем через знак боковой режима + // (иначе LSB/DIGL уходят в USB, когда этот пуш оказывается последним, напр. MOX). if (FTXFilterLow > 0) and (FTXFilterHigh > FTXFilterLow) then - SetTXABandpassFreqs(TXA_CHAN, FTXFilterLow, FTXFilterHigh); + begin + TXSignedEdges(FTXMode, BpLo, BpHi); + SetTXABandpassFreqs(TXA_CHAN, BpLo, BpHi); + end; if FTXFilterWindow >= 0 then SetTXABandpassWindow(TXA_CHAN, FTXFilterWindow); if FTXFilterNC > 0 then @@ -3672,6 +3719,7 @@ begin end; procedure TWDSPEngine.SetTXFilterFull(Low, High, NC, MP, Window: Integer); +var BpLo, BpHi: Double; begin FTXFilterLow := Low; FTXFilterHigh := High; @@ -3681,7 +3729,9 @@ begin FFilterLow := Low; FFilterHigh := High; if not FInitialized then Exit; - SetTXABandpassFreqs(TXA_CHAN, Low, High); + // Магнитуда диалога → знаковые края под боковую режима (см. TXSignedEdges). + TXSignedEdges(FTXMode, BpLo, BpHi); + SetTXABandpassFreqs(TXA_CHAN, BpLo, BpHi); SetTXABandpassWindow(TXA_CHAN, Window); if NC > 0 then TXASetNC(TXA_CHAN, EffectiveTXNC); if MP >= 0 then TXASetMP(TXA_CHAN, MP);