fix(tx): полоса ЧМ на передаче — занимаемая полоса, а не кромки bp0

TXFilterEdgesHz была тонкой обёрткой над TXSignedEdges, то есть отдавала
в отрисовку кромки аудио-полосовика перед модулятором. У SSB/CW/DIGI/AM
это то же самое, что полоса вокруг несущей, а у ЧМ — нет: FM RAW рисовался
как (20..7000), WFM как (20..15000), то есть полоса уезжала вверх от
несущей и визуально «переключалась на USB».

Теперь у экранных кромок своя семантика — Карсон ±(девиация + верхний срез
аудио) для FM/WFM/FM RAW, остальные режимы как были. TXSignedEdges и
SetTXABandpassFreqs не тронуты: bp0 остаётся портом Thetis, в эфире ничего
не меняется. Попутно 20.0/7000.0 из трёх мест FM RAW сведены в
RAW_AF_LOW/RAW_AF_HIGH.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 19:09:31 +03:00
co-authored by Claude Opus 5
parent 7c9064659b
commit 6195c80ce3
+28 -5
View File
@@ -78,6 +78,10 @@ const
WFM_AF_HIGH = 15000.0;
WFM_TAU_DE_US = 50.0e-6; // Европа/Россия (Америка — 75 мкс)
DMR_DEVIATION = 2500.0;
// FM RAW: плоский baseband для внешних декодеров — одна величина на RX-AF,
// TX-AF и bp0 (раньше 20/7000 стояли числами в трёх местах).
RAW_AF_LOW = 20.0;
RAW_AF_HIGH = 7000.0;
// Потолок dsp_rate для WFM. Вещательному каналу (200 кГц) хватает 192 кГц,
// а гнать цепь на полном IQ-rate нельзя: при 576 кГц dsp_size стал бы 12288,
// и fircore спланировал бы FFT на 2*12288 = 24576 точек. Это не степень
@@ -1167,7 +1171,7 @@ procedure TWDSPEngine.ApplyRXRawToChan(Chan: Integer);
begin
// Decoder-grade discriminator: no broadcast de-emphasis or limiter.
SetRXAWFMDeviation (Chan, FRawFMDeviation);
SetRXAWFMAFFilter (Chan, 20.0, 7000.0);
SetRXAWFMAFFilter (Chan, RAW_AF_LOW, RAW_AF_HIGH);
SetRXAWFMDeemphRun (Chan, 0);
SetRXAWFMLimRun (Chan, 0);
end;
@@ -1221,7 +1225,7 @@ begin
// Плоский baseband для внешних декодеров (совпадает с RX AF в ApplyRXRawToChan
// и с явным SetTXABandpassFreqs в ApplyTXModeSettings). РЧ-ширина FILT_RAW_BW
// (6..25 кГц) — селективность RX, к аудио-полосовику отношения не имеет.
begin Lo := 20.0; Hi := 7000.0; end;
begin Lo := RAW_AF_LOW; Hi := RAW_AF_HIGH; end;
else
begin Lo := L; Hi := H; end; // прочее — как есть
end;
@@ -1236,9 +1240,9 @@ begin
// selected peak deviation. Keep only a broad linear-phase AF bandpass;
// APRS and other external modems must not see voice EQ, dynamics or emphasis.
SetTXAWFMDeviation (TXA_CHAN, FRawFMDeviation);
SetTXAWFMAFFreqs (TXA_CHAN, 20.0, 7000.0);
SetTXAWFMAFFreqs (TXA_CHAN, RAW_AF_LOW, RAW_AF_HIGH);
SetTXAWFMPreEmphRun (TXA_CHAN, 0);
SetTXABandpassFreqs (TXA_CHAN, 20.0, 7000.0);
SetTXABandpassFreqs (TXA_CHAN, RAW_AF_LOW, RAW_AF_HIGH);
SetTXAPanelGain1 (TXA_CHAN, 1.0);
SetTXACompressorRun (TXA_CHAN, 0);
SetTXALevelerSt (TXA_CHAN, 0);
@@ -3715,8 +3719,27 @@ begin
end;
procedure TWDSPEngine.TXFilterEdgesHz(out Lo, Hi: Integer);
var L, H: Double;
// ЭКРАННЫЕ кромки — занимаемая полоса вокруг несущей, а НЕ bp0. У SSB/CW/DIGI/
// AM/DSB это одно и то же, поэтому здесь и стояла обёртка над TXSignedEdges.
// Но у ЧМ bp0 — АУДИО-полосовик ПЕРЕД модулятором: у FM RAW он (20..7000), у
// WFM (20..15000), и полоса на спектре уезжала вверх от несущей — визуально
// «переключалась на USB». Для ЧМ считаем по Карсону: ±(девиация + верхний срез
// аудио), симметрично несущей. У MODE_FM TXSignedEdges отдаёт ровно эту
// величину и без нас (порт Thetis), но пусть формула будет в одном месте.
var L, H, Half: Double;
begin
Half := 0.0;
case FTXMode of
MODE_FM: Half := FTXFMDeviation + FTXFMHighCut;
MODE_WFM: Half := WFM_DEVIATION + WFM_AF_HIGH;
MODE_FMRAW: Half := FRawFMDeviation + RAW_AF_HIGH;
end;
if Half > 0.0 then
begin
Lo := -Round(Half);
Hi := Round(Half);
Exit;
end;
TXSignedEdges(FTXMode, L, H);
Lo := Round(L);
Hi := Round(H);