mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
fix cpu
This commit is contained in:
+406
-376
File diff suppressed because it is too large
Load Diff
+48
-81
@@ -145,6 +145,7 @@ type
|
|||||||
FANFEnabled: Boolean;
|
FANFEnabled: Boolean;
|
||||||
FMuted: Boolean;
|
FMuted: Boolean;
|
||||||
FVolume: Double;
|
FVolume: Double;
|
||||||
|
FLastError: string;
|
||||||
|
|
||||||
function ModeToWDSP(Mode: Integer): Integer;
|
function ModeToWDSP(Mode: Integer): Integer;
|
||||||
procedure ApplyDefaultFilter;
|
procedure ApplyDefaultFilter;
|
||||||
@@ -211,6 +212,7 @@ type
|
|||||||
property Mode: Integer read FMode;
|
property Mode: Integer read FMode;
|
||||||
property FilterLow: Integer read FFilterLow;
|
property FilterLow: Integer read FFilterLow;
|
||||||
property FilterHigh: Integer read FFilterHigh;
|
property FilterHigh: Integer read FFilterHigh;
|
||||||
|
property LastError: string read FLastError;
|
||||||
property SMeter: Double read FSMeter;
|
property SMeter: Double read FSMeter;
|
||||||
property OnAudio: TOnAudioReady read FOnAudio write FOnAudio;
|
property OnAudio: TOnAudioReady read FOnAudio write FOnAudio;
|
||||||
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
||||||
@@ -356,62 +358,24 @@ var
|
|||||||
begin
|
begin
|
||||||
if FAnalyzerOpen then Exit;
|
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;
|
Success := -1;
|
||||||
XCreateAnalyzer(DISP_ID, @Success, 16384, 1, 1, nil);
|
XCreateAnalyzer(DISP_ID, @Success, 16384, 1, 1, nil);
|
||||||
if Success <> 0 then
|
if Success <> 0 then Exit;
|
||||||
begin
|
|
||||||
// Анализатор не создан — работаем без спектра, краша не будет
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// ---- Шаг 2: SetAnalyzer ----
|
FRAME_RATE := 20;
|
||||||
// Рассчитываем параметры по документации (WDSP Guide p.120-121)
|
// max_w: WDSP аллоцирует ring-buffer и FFT планы на max_w сэмплов.
|
||||||
FRAME_RATE := 20; // 20 fps
|
// Формула: fft_size + min(0.1*samplerate, 0.1*fft_size*fps)
|
||||||
// max_w = fft_size + min(KEEP_TIME*sample_rate, KEEP_TIME*fft_size*frame_rate)
|
// = 4096 + min(19200, 8192) = 12288 → 1700ms инициализации!
|
||||||
// KEEP_TIME = 0.1
|
// Реально нам нужен только один FFT-кадр без усреднения по времени.
|
||||||
// min(0.1*192000, 0.1*4096*20) = min(19200, 8192) = 8192
|
// MaxW = sz = 4096 — минимально допустимое значение (должно быть ≥ sz).
|
||||||
MaxW := 4096 + 8192; // = 12288
|
// piHPSDR использует именно sz без добавки keep_time.
|
||||||
|
MaxW := 4096;
|
||||||
// ovrlp = max(0, ceil(fft_size - sample_rate/frame_rate))
|
|
||||||
// = max(0, ceil(4096 - 192000/20)) = max(0, 4096-9600) = 0
|
|
||||||
Ovrlp := 0;
|
Ovrlp := 0;
|
||||||
|
|
||||||
FFlp[0] := 0; // LO на нижней стороне, нет spur elimination
|
FFlp[0] := 0;
|
||||||
SetAnalyzer(
|
SetAnalyzer(DISP_ID, 1, 1, 1, @FFlp[0], 4096, FAudioBufSize, 1, 14.0,
|
||||||
DISP_ID,
|
Ovrlp, 0, 0.0, 0.0, SPECTRUM_PIXELS, 1, 0, 0.0, 0.0, MaxW);
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
// ---- Шаг 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);
|
SetDisplayDetectorMode(DISP_ID, 0, DETECTOR_MODE_AVERAGE);
|
||||||
SetDisplayAverageMode(DISP_ID, 0, AVERAGE_MODE_LOG_RECURSIVE);
|
SetDisplayAverageMode(DISP_ID, 0, AVERAGE_MODE_LOG_RECURSIVE);
|
||||||
SetDisplayNumAverage(DISP_ID, 0, Max(2, Trunc(FRAME_RATE * 0.12)));
|
SetDisplayNumAverage(DISP_ID, 0, Max(2, Trunc(FRAME_RATE * 0.12)));
|
||||||
@@ -434,66 +398,65 @@ end;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function TWDSPEngine.Open: Boolean;
|
function TWDSPEngine.Open: Boolean;
|
||||||
|
var
|
||||||
|
DllPath: string;
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
|
FLastError := '';
|
||||||
if FInitialized then Exit;
|
if FInitialized then Exit;
|
||||||
|
|
||||||
// Проверяем что libwdsp загружена (WEAKEXTERNALSYMBOLS: nil если нет DLL)
|
|
||||||
if not Assigned(@OpenChannel) then
|
if not Assigned(@OpenChannel) then
|
||||||
begin
|
begin
|
||||||
// wdsp.dll / libwdsp.so не найдена — демо-режим
|
|
||||||
FInitialized := False;
|
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;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
try
|
try
|
||||||
// ОПТИМИЗАЦИЯ: Анализатор создаём ПЕРВЫМ — до OpenChannel.
|
// Анализатор ПЕРВЫМ — до OpenChannel (порядок как в piHPSDR)
|
||||||
// Это соответствует порядку piHPSDR и гарантирует что DISP_ID готов
|
|
||||||
// до того как DSP поток начнёт вызывать Spectrum0.
|
|
||||||
OpenAnalyzer;
|
OpenAnalyzer;
|
||||||
// НЕ используем SetRXASpectrum — кормим анализатор вручную
|
if not FAnalyzerOpen then
|
||||||
// через Spectrum0 с Double буфером, чтобы избежать SIGSEGV
|
begin
|
||||||
// (fexchange0 работает с Single, а внутри WDSP Spectrum2 ждёт Double)
|
FLastError := 'XCreateAnalyzer failed (WDSP init failed).';
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
// ---- RXA channel 0 ----
|
|
||||||
// ОПТИМИЗАЦИЯ: state=0 (не запускать сразу) — настраиваем все параметры
|
|
||||||
// вхолостую, только потом стартуем через SetChannelState.
|
|
||||||
// Канал не тратит CPU на обработку "мусорных" данных в процессе настройки.
|
|
||||||
OpenChannel(
|
OpenChannel(
|
||||||
RXA_CHAN,
|
RXA_CHAN,
|
||||||
FBufSize, // in_size @ FSampleRate (4096 @ 192kHz)
|
FBufSize,
|
||||||
FAudioBufSize, // dsp_size @ AudioRate (1024 @ 48kHz) — как piHPSDR buffer_size
|
FAudioBufSize,
|
||||||
FSampleRate,
|
FSampleRate,
|
||||||
FAudioRate, // dsp_rate = 48kHz
|
FAudioRate,
|
||||||
FAudioRate, // out_rate = 48kHz
|
FAudioRate,
|
||||||
0, // atype: 0=RX
|
0,
|
||||||
0, // state: 0=hold (запустим после настройки всех параметров)
|
0, // state=0: создать но не запускать
|
||||||
0.010, 0.010, 0.010, 0.010,
|
0.010, 0.010, 0.010, 0.010,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
SetRXAMode(RXA_CHAN, ModeToWDSP(FMode));
|
SetRXAMode(RXA_CHAN, ModeToWDSP(FMode));
|
||||||
SetRXAShiftRun(RXA_CHAN, 1); // включаем NCO шифтер (нужен для CTUN)
|
SetRXAShiftRun(RXA_CHAN, 1);
|
||||||
SetRXAShiftFreq(RXA_CHAN, 0.0); // по умолчанию сдвига нет
|
SetRXAShiftFreq(RXA_CHAN, 0.0);
|
||||||
RXASetPassband(RXA_CHAN, FFilterLow, FFilterHigh);
|
RXASetPassband(RXA_CHAN, FFilterLow, FFilterHigh);
|
||||||
SetAGC(FAGCMode, 50.0); // настраиваем AGC с полными параметрами piHPSDR
|
SetAGC(FAGCMode, 50.0);
|
||||||
SetRXAPanelGain1(RXA_CHAN, FVolume);
|
SetRXAPanelGain1(RXA_CHAN, FVolume);
|
||||||
SetRXAPanelSelect(RXA_CHAN, 3);
|
SetRXAPanelSelect(RXA_CHAN, 3);
|
||||||
SetRXAPanelRun(RXA_CHAN, 1);
|
SetRXAPanelRun(RXA_CHAN, 1);
|
||||||
|
SetChannelState(RXA_CHAN, 1, 0);
|
||||||
|
|
||||||
// Запускаем RXA канал только после полной настройки
|
|
||||||
SetChannelState(RXA_CHAN, 1, 0); // run, no delay-wait
|
|
||||||
|
|
||||||
// ---- TXA channel 1 ----
|
|
||||||
OpenChannel(
|
OpenChannel(
|
||||||
TXA_CHAN,
|
TXA_CHAN,
|
||||||
FBufSize,
|
FAudioBufSize,
|
||||||
FBufSize,
|
FAudioBufSize,
|
||||||
FAudioRate,
|
FAudioRate,
|
||||||
48000,
|
FAudioRate,
|
||||||
FSampleRate,
|
FAudioRate,
|
||||||
1, // atype: 1=TX
|
1,
|
||||||
0, // state: 0=hold
|
0,
|
||||||
0.010, 0.010, 0.010, 0.010,
|
0.010, 0.010, 0.010, 0.010,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
@@ -519,9 +482,13 @@ begin
|
|||||||
FDSPThread.Priority := tpHighest; // DSP критический поток
|
FDSPThread.Priority := tpHighest; // DSP критический поток
|
||||||
|
|
||||||
except
|
except
|
||||||
|
on E: Exception do
|
||||||
|
begin
|
||||||
|
FLastError := E.ClassName + ': ' + E.Message;
|
||||||
FInitialized := False;
|
FInitialized := False;
|
||||||
Result := False;
|
Result := False;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TWDSPEngine.Close;
|
procedure TWDSPEngine.Close;
|
||||||
|
|||||||
+5
-8
@@ -1046,13 +1046,13 @@ var
|
|||||||
i, n, Enc: Integer;
|
i, n, Enc: Integer;
|
||||||
Pkt: array of Byte;
|
Pkt: array of Byte;
|
||||||
begin
|
begin
|
||||||
if not FRunning then begin AudioLog('EXIT: FRunning=false'); Exit; end;
|
if not FRunning then Exit;
|
||||||
FClientLock.Enter;
|
FClientLock.Enter;
|
||||||
HasWs := FClientCount > 0;
|
HasWs := FClientCount > 0;
|
||||||
FClientLock.Leave;
|
FClientLock.Leave;
|
||||||
if not HasWs then begin AudioLog('EXIT: FClientCount=0'); Exit; end;
|
if not HasWs then Exit;
|
||||||
if (Samples = nil) or (Count <= 0) then begin AudioLog('EXIT: Samples nil/Count=0'); Exit; end;
|
if (Samples = nil) or (Count <= 0) then Exit;
|
||||||
if not FOpusReady then begin AudioLog('EXIT: FOpusReady=false'); Exit; end;
|
if not FOpusReady then Exit;
|
||||||
|
|
||||||
i := 0;
|
i := 0;
|
||||||
while i < Count do
|
while i < Count do
|
||||||
@@ -1073,10 +1073,7 @@ begin
|
|||||||
Pkt[0] := WS_MSG_AUDIO;
|
Pkt[0] := WS_MSG_AUDIO;
|
||||||
Move(FOpusOut[0], Pkt[1], Enc);
|
Move(FOpusOut[0], Pkt[1], Enc);
|
||||||
BroadcastBinary(Pkt[0], Length(Pkt));
|
BroadcastBinary(Pkt[0], Length(Pkt));
|
||||||
AudioLog('SENT opus bytes=' + IntToStr(Enc) + ' clients=' + IntToStr(FClientCount));
|
end;
|
||||||
end
|
|
||||||
else
|
|
||||||
AudioLog('ENCODE FAILED Enc=' + IntToStr(Enc));
|
|
||||||
FOpusBufPos := 0;
|
FOpusBufPos := 0;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ unit WebUtils;
|
|||||||
- SHA-1 (минимальная реализация для WebSocket handshake)
|
- SHA-1 (минимальная реализация для WebSocket handshake)
|
||||||
- Base64 (кодирование)
|
- Base64 (кодирование)
|
||||||
- JSON helpers (JsonGetStr, JsonGetFloat, JsonGetInt, JsonGetBool)
|
- JSON helpers (JsonGetStr, JsonGetFloat, JsonGetInt, JsonGetBool)
|
||||||
- AudioLog (отладочное логирование аудио)
|
|
||||||
|
|
||||||
ИСПРАВЛЕНИЯ:
|
ИСПРАВЛЕНИЯ:
|
||||||
- (Windows build fix) Порядок uses: стандартные RTL-юниты первыми,
|
- (Windows build fix) Порядок uses: стандартные RTL-юниты первыми,
|
||||||
@@ -76,10 +75,6 @@ function JsonGetFloat(const Json, Key: string; Def: Double): Double;
|
|||||||
function JsonGetInt(const Json, Key: string; Def: Integer): Integer;
|
function JsonGetInt(const Json, Key: string; Def: Integer): Integer;
|
||||||
function JsonGetBool(const Json, Key: string; Def: Boolean): Boolean;
|
function JsonGetBool(const Json, Key: string; Def: Boolean): Boolean;
|
||||||
|
|
||||||
{ ── Отладочное логирование аудио ─────────────────────────────────────────── }
|
|
||||||
|
|
||||||
procedure AudioLog(const S: string);
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{ ═══════════════════════════════════════════════════════════════════════════
|
{ ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -318,20 +313,4 @@ begin
|
|||||||
else Result := (S = 'true') or (S = '1');
|
else Result := (S = 'true') or (S = '1');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
Отладочное логирование аудио
|
|
||||||
═══════════════════════════════════════════════════════════════════════════ }
|
|
||||||
|
|
||||||
procedure AudioLog(const S: string);
|
|
||||||
var F: TextFile;
|
|
||||||
begin
|
|
||||||
try
|
|
||||||
AssignFile(F, 'audio_debug.log');
|
|
||||||
if FileExists('audio_debug.log') then Append(F) else Rewrite(F);
|
|
||||||
WriteLn(F, FormatDateTime('hh:nn:ss.zzz', Now) + ' ' + S);
|
|
||||||
CloseFile(F);
|
|
||||||
except
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+3
-3
@@ -285,7 +285,7 @@
|
|||||||
"span_hz" : 1.9200000000000000E+005
|
"span_hz" : 1.9200000000000000E+005
|
||||||
},
|
},
|
||||||
"1" : {
|
"1" : {
|
||||||
"vfo_a" : 3.6210000000000000E+006,
|
"vfo_a" : 3.6870000000000000E+006,
|
||||||
"vfo_b" : 3.7500000000000000E+006,
|
"vfo_b" : 3.7500000000000000E+006,
|
||||||
"mode" : 0,
|
"mode" : 0,
|
||||||
"filter_idx" : 5,
|
"filter_idx" : 5,
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
"span_hz" : 1.9200000000000000E+005
|
"span_hz" : 1.9200000000000000E+005
|
||||||
},
|
},
|
||||||
"3" : {
|
"3" : {
|
||||||
"vfo_a" : 7.1620000000000000E+006,
|
"vfo_a" : 7.1410000000000000E+006,
|
||||||
"vfo_b" : 7.1000000000000000E+006,
|
"vfo_b" : 7.1000000000000000E+006,
|
||||||
"mode" : 0,
|
"mode" : 0,
|
||||||
"filter_idx" : 5,
|
"filter_idx" : 5,
|
||||||
@@ -329,7 +329,7 @@
|
|||||||
"span_hz" : 1.9200000000000000E+005
|
"span_hz" : 1.9200000000000000E+005
|
||||||
},
|
},
|
||||||
"5" : {
|
"5" : {
|
||||||
"vfo_a" : 1.4163000000000000E+007,
|
"vfo_a" : 1.4189000000000000E+007,
|
||||||
"vfo_b" : 1.4200000000000000E+007,
|
"vfo_b" : 1.4200000000000000E+007,
|
||||||
"mode" : 1,
|
"mode" : 1,
|
||||||
"filter_idx" : 5,
|
"filter_idx" : 5,
|
||||||
|
|||||||
Reference in New Issue
Block a user