mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
fix cpu
This commit is contained in:
+53
-86
@@ -145,6 +145,7 @@ type
|
||||
FANFEnabled: Boolean;
|
||||
FMuted: Boolean;
|
||||
FVolume: Double;
|
||||
FLastError: string;
|
||||
|
||||
function ModeToWDSP(Mode: Integer): Integer;
|
||||
procedure ApplyDefaultFilter;
|
||||
@@ -211,6 +212,7 @@ type
|
||||
property Mode: Integer read FMode;
|
||||
property FilterLow: Integer read FFilterLow;
|
||||
property FilterHigh: Integer read FFilterHigh;
|
||||
property LastError: string read FLastError;
|
||||
property SMeter: Double read FSMeter;
|
||||
property OnAudio: TOnAudioReady read FOnAudio write FOnAudio;
|
||||
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
||||
@@ -349,69 +351,31 @@ end;
|
||||
|
||||
procedure TWDSPEngine.OpenAnalyzer;
|
||||
var
|
||||
Success: Integer;
|
||||
MaxW: Integer;
|
||||
Ovrlp: Integer;
|
||||
Success: Integer;
|
||||
MaxW: Integer;
|
||||
Ovrlp: Integer;
|
||||
FRAME_RATE: Integer;
|
||||
begin
|
||||
if FAnalyzerOpen then Exit;
|
||||
|
||||
// ---- Шаг 1: создать анализатор ----
|
||||
// success = 0 означает УСПЕХ (WDSP Guide p.119)
|
||||
// m_size = максимальный FFT размер (должен быть степенью 2, ≤ 262144)
|
||||
// ОПТИМИЗАЦИЯ: 262144 → 16384. WDSP аллоцирует и инициализирует twiddle-таблицы
|
||||
// для ВСЕХ степеней 2 вплоть до m_size при каждом вызове XCreateAnalyzer.
|
||||
// Реальный рабочий FFT = 4096, запас ×4 = 16384 достаточен для будущих апгрейдов.
|
||||
// Эффект: время открытия WDSP ~10 сек → ~1 сек.
|
||||
// m_LO = 1 (нет spur elimination)
|
||||
// m_stitch = 1 (один sub-span)
|
||||
Success := -1;
|
||||
XCreateAnalyzer(DISP_ID, @Success, 16384, 1, 1, nil);
|
||||
if Success <> 0 then
|
||||
begin
|
||||
// Анализатор не создан — работаем без спектра, краша не будет
|
||||
Exit;
|
||||
end;
|
||||
if Success <> 0 then Exit;
|
||||
|
||||
// ---- Шаг 2: SetAnalyzer ----
|
||||
// Рассчитываем параметры по документации (WDSP Guide p.120-121)
|
||||
FRAME_RATE := 20; // 20 fps
|
||||
// max_w = fft_size + min(KEEP_TIME*sample_rate, KEEP_TIME*fft_size*frame_rate)
|
||||
// KEEP_TIME = 0.1
|
||||
// min(0.1*192000, 0.1*4096*20) = min(19200, 8192) = 8192
|
||||
MaxW := 4096 + 8192; // = 12288
|
||||
|
||||
// ovrlp = max(0, ceil(fft_size - sample_rate/frame_rate))
|
||||
// = max(0, ceil(4096 - 192000/20)) = max(0, 4096-9600) = 0
|
||||
FRAME_RATE := 20;
|
||||
// max_w: WDSP аллоцирует ring-buffer и FFT планы на max_w сэмплов.
|
||||
// Формула: fft_size + min(0.1*samplerate, 0.1*fft_size*fps)
|
||||
// = 4096 + min(19200, 8192) = 12288 → 1700ms инициализации!
|
||||
// Реально нам нужен только один FFT-кадр без усреднения по времени.
|
||||
// MaxW = sz = 4096 — минимально допустимое значение (должно быть ≥ sz).
|
||||
// piHPSDR использует именно sz без добавки keep_time.
|
||||
MaxW := 4096;
|
||||
Ovrlp := 0;
|
||||
|
||||
FFlp[0] := 0; // LO на нижней стороне, нет spur elimination
|
||||
SetAnalyzer(
|
||||
DISP_ID,
|
||||
1, // n_pixout: 1 (один выход — панадаптер и водопад одинаковые)
|
||||
1, // n_fft: 1 (без spur elimination)
|
||||
1, // typ: 1 = COMPLEX (I+Q)
|
||||
@FFlp[0], // flp: [0] — LO ниже сигнала
|
||||
4096, // sz: размер FFT
|
||||
FAudioBufSize, // bf_sz: блок данных @ AudioRate = dsp_size (1024 @ 48kHz)
|
||||
1, // win_type: 1 = 4-term Blackman-Harris
|
||||
14.0, // pi: Kaiser beta (не используется при BH)
|
||||
Ovrlp, // ovrlp: перекрытие = 0
|
||||
0, // clp: обрезка краёв = 0
|
||||
0.0, // fscLin: pan left = 0 (полный диапазон)
|
||||
0.0, // fscHin: pan right = 0 (полный диапазон)
|
||||
SPECTRUM_PIXELS, // n_pix: пикселей на выходе GetPixels
|
||||
1, // n_stch: 1 sub-span (нет stitching)
|
||||
0, // calset: нет калибровки
|
||||
0.0, // fmin
|
||||
0.0, // fmax
|
||||
MaxW // max_w: рассчитан выше = 12288
|
||||
);
|
||||
FFlp[0] := 0;
|
||||
SetAnalyzer(DISP_ID, 1, 1, 1, @FFlp[0], 4096, FAudioBufSize, 1, 14.0,
|
||||
Ovrlp, 0, 0.0, 0.0, SPECTRUM_PIXELS, 1, 0, 0.0, 0.0, MaxW);
|
||||
|
||||
// ---- Шаг 3: настройка детектора и усреднения ----
|
||||
// DETECTOR_MODE_AVERAGE + LOG_RECURSIVE — плавный, как в piHPSDR/Thetis
|
||||
// backmult ~0.45 = инерция ~2.2 кадра → плавно, без смазывания быстрых сигналов
|
||||
// NumAverage = FRAME_RATE * 0.12 ≈ 2..3 при 20fps — достаточно для LOG_RECURSIVE
|
||||
SetDisplayDetectorMode(DISP_ID, 0, DETECTOR_MODE_AVERAGE);
|
||||
SetDisplayAverageMode(DISP_ID, 0, AVERAGE_MODE_LOG_RECURSIVE);
|
||||
SetDisplayNumAverage(DISP_ID, 0, Max(2, Trunc(FRAME_RATE * 0.12)));
|
||||
@@ -434,66 +398,65 @@ end;
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function TWDSPEngine.Open: Boolean;
|
||||
var
|
||||
DllPath: string;
|
||||
begin
|
||||
Result := False;
|
||||
FLastError := '';
|
||||
if FInitialized then Exit;
|
||||
|
||||
// Проверяем что libwdsp загружена (WEAKEXTERNALSYMBOLS: nil если нет DLL)
|
||||
if not Assigned(@OpenChannel) then
|
||||
begin
|
||||
// wdsp.dll / libwdsp.so не найдена — демо-режим
|
||||
FInitialized := False;
|
||||
DllPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + WDSP_LIB;
|
||||
if FileExists(DllPath) then
|
||||
FLastError := Format('Symbols from %s are not resolved. Check architecture (x64/x86) and dependencies.', [DllPath])
|
||||
else
|
||||
FLastError := Format('WDSP library not found: %s', [DllPath]);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
// ОПТИМИЗАЦИЯ: Анализатор создаём ПЕРВЫМ — до OpenChannel.
|
||||
// Это соответствует порядку piHPSDR и гарантирует что DISP_ID готов
|
||||
// до того как DSP поток начнёт вызывать Spectrum0.
|
||||
// Анализатор ПЕРВЫМ — до OpenChannel (порядок как в piHPSDR)
|
||||
OpenAnalyzer;
|
||||
// НЕ используем SetRXASpectrum — кормим анализатор вручную
|
||||
// через Spectrum0 с Double буфером, чтобы избежать SIGSEGV
|
||||
// (fexchange0 работает с Single, а внутри WDSP Spectrum2 ждёт Double)
|
||||
if not FAnalyzerOpen then
|
||||
begin
|
||||
FLastError := 'XCreateAnalyzer failed (WDSP init failed).';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// ---- RXA channel 0 ----
|
||||
// ОПТИМИЗАЦИЯ: state=0 (не запускать сразу) — настраиваем все параметры
|
||||
// вхолостую, только потом стартуем через SetChannelState.
|
||||
// Канал не тратит CPU на обработку "мусорных" данных в процессе настройки.
|
||||
OpenChannel(
|
||||
RXA_CHAN,
|
||||
FBufSize, // in_size @ FSampleRate (4096 @ 192kHz)
|
||||
FAudioBufSize, // dsp_size @ AudioRate (1024 @ 48kHz) — как piHPSDR buffer_size
|
||||
FBufSize,
|
||||
FAudioBufSize,
|
||||
FSampleRate,
|
||||
FAudioRate, // dsp_rate = 48kHz
|
||||
FAudioRate, // out_rate = 48kHz
|
||||
0, // atype: 0=RX
|
||||
0, // state: 0=hold (запустим после настройки всех параметров)
|
||||
FAudioRate,
|
||||
FAudioRate,
|
||||
0,
|
||||
0, // state=0: создать но не запускать
|
||||
0.010, 0.010, 0.010, 0.010,
|
||||
0
|
||||
);
|
||||
|
||||
SetRXAMode(RXA_CHAN, ModeToWDSP(FMode));
|
||||
SetRXAShiftRun(RXA_CHAN, 1); // включаем NCO шифтер (нужен для CTUN)
|
||||
SetRXAShiftFreq(RXA_CHAN, 0.0); // по умолчанию сдвига нет
|
||||
SetRXAShiftRun(RXA_CHAN, 1);
|
||||
SetRXAShiftFreq(RXA_CHAN, 0.0);
|
||||
RXASetPassband(RXA_CHAN, FFilterLow, FFilterHigh);
|
||||
SetAGC(FAGCMode, 50.0); // настраиваем AGC с полными параметрами piHPSDR
|
||||
SetAGC(FAGCMode, 50.0);
|
||||
SetRXAPanelGain1(RXA_CHAN, FVolume);
|
||||
SetRXAPanelSelect(RXA_CHAN, 3);
|
||||
SetRXAPanelRun(RXA_CHAN, 1);
|
||||
SetChannelState(RXA_CHAN, 1, 0);
|
||||
|
||||
// Запускаем RXA канал только после полной настройки
|
||||
SetChannelState(RXA_CHAN, 1, 0); // run, no delay-wait
|
||||
|
||||
// ---- TXA channel 1 ----
|
||||
OpenChannel(
|
||||
TXA_CHAN,
|
||||
FBufSize,
|
||||
FBufSize,
|
||||
FAudioBufSize,
|
||||
FAudioBufSize,
|
||||
FAudioRate,
|
||||
48000,
|
||||
FSampleRate,
|
||||
1, // atype: 1=TX
|
||||
0, // state: 0=hold
|
||||
FAudioRate,
|
||||
FAudioRate,
|
||||
1,
|
||||
0,
|
||||
0.010, 0.010, 0.010, 0.010,
|
||||
0
|
||||
);
|
||||
@@ -519,8 +482,12 @@ begin
|
||||
FDSPThread.Priority := tpHighest; // DSP критический поток
|
||||
|
||||
except
|
||||
FInitialized := False;
|
||||
Result := False;
|
||||
on E: Exception do
|
||||
begin
|
||||
FLastError := E.ClassName + ': ' + E.Message;
|
||||
FInitialized := False;
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user