mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(tx): заводские EQ-кривые ESSB, честная АЧХ в редакторе, фикс 3-полосного EQ
Ключ ко всему — семантика EQ в WDSP (wdsp/eq.c, eq_impulse + TXA.c): F[1..n] это УЗЛЫ ломаной АЧХ, между ними интерполяция по децибелам, а за крайними узлами при ctfmode=0 (так создаётся eqp у TXA, мы его не меняем) идёт кумулятивный скат (f/f0)^4 на каждый бин — фактически обрыв. Значит включённый EQ работает как второй полосовой фильтр, и верхний узел обязан лежать за верхней кромкой TX-фильтра. Settings: заводские ESSB несут свою кривую (AddEQ) — ESSB 3.5k узлы 100..3600, ESSB 5k узлы 100..5100, преамп в минус (EQ стоит до компрессора и ALC). EQ у Default/SSB DX/SSB Wide сбрасывается в заводской ноль (FlatEQ), а не наследует живой, иначе «сброс к заводским» не сбрасывал бы тембр. Сетка узлов вынесена в модульную DEF_EQ_FREQS. WDSPEngine: единая точка пуша EQ (PushTXEQProfile). Починен 3-полосный режим: раньше в WDSP уходили первые три узла 10-полосной сетки (100/200/400) и включённый EQ убивал весь голос выше 400 Гц; теперь legacy-раскладка самого WDSP (150/400/1500/6000, как SetTXAGrphEQ). EqualizerControl: кривая — точный порт eq_impulse вместо гауссовых горбов (сверено с C численно, расхождение 0.0 дБ), рисуется без клампа ±15, чтобы обрыв за крайними узлами был виден. В 3-полосном режиме ручки стоят на 150/1500/6000 и по частоте не таскаются, сетка профиля при этом цела. RadioController/SettingsForm/MainForm: кнопка Factory — пересборка заводского набора с применением Default. Без неё обновлённые заводские профили не доедут до тех, у кого секция tx_profiles уже записана: она читается, а не создаётся. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+42
-17
@@ -554,6 +554,7 @@ type
|
||||
procedure ProcessTXBlock; // вызывается из TTXDSPThread
|
||||
procedure FeedTXDisplaySample(const I, Q: Double);
|
||||
procedure ApplyTXChainSettings; // прокидывает все FTX* поля в WDSP
|
||||
procedure PushTXEQProfile; // кривая EQ в WDSP (узлы + legacy 3 полосы)
|
||||
procedure ApplyPSParams; // прокидывает FPS*-параметры в WDSP calcc
|
||||
procedure ApplyRXAnalyzerSettings; // применяет RX FFT/Window/Det/Avg к RX_DISP_ID
|
||||
procedure ApplyTXAnalyzerSettings; // применяет TX FFT/Window/Det/Avg к TX_DISP_ID
|
||||
@@ -3542,8 +3543,6 @@ procedure TWDSPEngine.ApplyTXChainSettings;
|
||||
// Вызывается после OpenChannel(TXA) и при старте TX. Прокидывает все
|
||||
// FTX*-поля в WDSP. Идемпотентна, ничего не меняет если канал не открыт.
|
||||
var
|
||||
i: Integer;
|
||||
Gains, Freqs, Qs: array[0..10] of Double;
|
||||
BpLo, BpHi: Double;
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
@@ -3584,15 +3583,8 @@ begin
|
||||
SetTXAPHROTNstages(TXA_CHAN, FTXPhaseRotStages);
|
||||
SetTXAPHROTCorner(TXA_CHAN, FTXPhaseRotFreq);
|
||||
|
||||
// EQ TX (10-band: [0]=preamp, [1..10]=bands)
|
||||
for i := 0 to 10 do
|
||||
begin
|
||||
Gains[i] := FTXEQGains[i];
|
||||
Freqs[i] := FTXEQFreqs[i];
|
||||
Qs[i] := 0.707; // умолчание Q
|
||||
end;
|
||||
SetTXAEQProfile(TXA_CHAN, FTXEQNumBands, @Freqs[0], @Gains[0]);
|
||||
SetTXAEQRun(TXA_CHAN, Ord(FTXEQOn));
|
||||
// EQ TX ([0]=preamp, [1..10]=точки кривой)
|
||||
PushTXEQProfile;
|
||||
|
||||
// AM
|
||||
SetTXAAMCarrierLevel(TXA_CHAN, FTXAMCarrier);
|
||||
@@ -3791,11 +3783,48 @@ begin
|
||||
SetTXAPHROTRun(TXA_CHAN, Ord(Enable));
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PushTXEQProfile;
|
||||
// Единственная точка, отдающая кривую EQ в WDSP.
|
||||
// F[1..n] — УЗЛЫ ломаной АЧХ (eq.c/eq_impulse), между ними интерполяция по
|
||||
// децибелам, а за крайними узлами при ctfmode=0 (так создаётся eqp в TXA.c)
|
||||
// идёт кумулятивный скат (f/f0)^4 на бин — фактически обрыв. Поэтому:
|
||||
// • 10 полос — узлы берём из профиля как есть (за краями обязан быть запас,
|
||||
// см. заводские ESSB-профили в Settings.DefaultTXProfiles);
|
||||
// • 3 полосы — legacy-раскладка самого WDSP (SetTXAGrphEQ): 150/400/1500/
|
||||
// 6000, ручка Low держит полку 150..400, High тянет до 6 кГц. Отдавать
|
||||
// здесь первые три узла 10-полосной сетки (100/200/400) нельзя — весь
|
||||
// голос выше 400 Гц ушёл бы в ноль.
|
||||
var
|
||||
F, G: array[0..10] of Double;
|
||||
i, NF: Integer;
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
if FTXEQNumBands = 3 then
|
||||
begin
|
||||
NF := 4;
|
||||
F[0] := 0.0; G[0] := FTXEQGains[0]; // preamp
|
||||
F[1] := 150.0; G[1] := FTXEQGains[1]; // Low
|
||||
F[2] := 400.0; G[2] := FTXEQGains[1];
|
||||
F[3] := 1500.0; G[3] := FTXEQGains[2]; // Mid
|
||||
F[4] := 6000.0; G[4] := FTXEQGains[3]; // High
|
||||
end
|
||||
else
|
||||
begin
|
||||
NF := 10;
|
||||
for i := 0 to 10 do
|
||||
begin
|
||||
F[i] := FTXEQFreqs[i];
|
||||
G[i] := FTXEQGains[i];
|
||||
end;
|
||||
end;
|
||||
SetTXAEQProfile(TXA_CHAN, NF, @F[0], @G[0]);
|
||||
SetTXAEQRun(TXA_CHAN, Ord(FTXEQOn));
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetTXEQ(Enable: Boolean; NumBands: Integer;
|
||||
const Gains, Freqs: array of Double);
|
||||
var
|
||||
i, n: Integer;
|
||||
G, F: array[0..10] of Double;
|
||||
begin
|
||||
FTXEQOn := Enable;
|
||||
FTXEQNumBands := NumBands;
|
||||
@@ -3804,12 +3833,8 @@ begin
|
||||
begin
|
||||
FTXEQGains[i] := Gains[i];
|
||||
FTXEQFreqs[i] := Freqs[i];
|
||||
G[i] := Gains[i];
|
||||
F[i] := Freqs[i];
|
||||
end;
|
||||
if not FInitialized then Exit;
|
||||
SetTXAEQProfile(TXA_CHAN, NumBands, @F[0], @G[0]);
|
||||
SetTXAEQRun(TXA_CHAN, Ord(Enable));
|
||||
PushTXEQProfile;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetTXAMCarrierLevel(Level: Double);
|
||||
|
||||
Reference in New Issue
Block a user