mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
Add ADALM-Pluto / AD9361 support as a second hardware backend alongside openHPSDR, sharing the WDSP DSP pipeline and the existing controller API (UI stays decoupled from logic). - RadioBackend.pas: abstract TRadioBackend + TBackendCaps + TRadioDevice (Kind/URI/Serial). THPSDRNetwork now derives from it (state via virtual getters); TRadioController.FNetwork is the base type. - IIOBindings.pas: dynamic libiio loader (runs without libiio present). - PlutoBackend.pas: scan/probe-by-URI, connect, LO/rate/bandwidth/gain control, RX streaming thread (int16->24bit BE -> OnDDCIQ), Q conjugated to match WDSP IQ convention. Verified on LibreSDR (AD9361) over network. - Unified discovery: TDiscoverThread scans both backends; network Plutos found via direct ProbeURI (no mDNS needed). ConnectDevice dispatches by Dev.Kind (EnsureBackend swaps backend, preserving callbacks). - DeviceStore/DeviceForm: persist Kind/URI/Serial; save Pluto via AddSavedPluto so saved/autostart devices reconnect across restarts. - VHF/UHF band plan (BoardUtils, kind-aware): 6m..ADS-B for Pluto; fixes HF clamps (band detect, mouse-wheel 60 MHz cap, freq-display max). - SampleRateOverlay: configurable presets (Pluto 576k..5760k, >520 ksps), auto-width to fit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
2.6 KiB
ObjectPascal
84 lines
2.6 KiB
ObjectPascal
program test_pluto;
|
|
|
|
{$MODE Delphi}
|
|
|
|
// Ручной тест Pluto/AD936x-бэкенда против реального железа (без UI/DSP).
|
|
// Сборка: fpc -Mobjfpc -dHEADLESS -Fu. -FUlib-headless/<cpu-os> test_pluto.lpr
|
|
// Запуск: ./test_pluto ip:172.16.2.199
|
|
|
|
uses
|
|
{$IFDEF UNIX}cthreads,{$ENDIF}
|
|
SysUtils, Classes, ctypes, Math,
|
|
RadioBackend, IIOBindings, HPSDRProtocol, PlutoBackend;
|
|
|
|
type
|
|
// Приёмник OnDDCIQ: считает пакеты/пары и средний уровень I (dBFS).
|
|
THelper = class
|
|
Pkts: Integer;
|
|
Pairs: Int64;
|
|
SumSq: Double;
|
|
procedure OnIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
|
end;
|
|
|
|
procedure THelper.OnIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
|
const SCALE = 1.0 / 8388608.0;
|
|
var spf, i, pos, ir: Integer; fi: Double;
|
|
begin
|
|
spf := (Integer(Data.SamplesPerFrame[0]) shl 8) or Integer(Data.SamplesPerFrame[1]);
|
|
Inc(Pkts);
|
|
Pairs := Pairs + spf;
|
|
for i := 0 to spf - 1 do
|
|
begin
|
|
pos := i * 6;
|
|
ir := (Integer(Data.IQData[pos]) shl 16) or (Integer(Data.IQData[pos+1]) shl 8)
|
|
or Integer(Data.IQData[pos+2]);
|
|
if (ir and $800000) <> 0 then ir := ir or Integer($FF000000);
|
|
fi := ir * SCALE;
|
|
SumSq := SumSq + fi * fi;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
URI: string;
|
|
pl: TPlutoBackend;
|
|
dev: TRadioDevice;
|
|
h: THelper;
|
|
rms: Double;
|
|
begin
|
|
if ParamCount >= 1 then URI := ParamStr(1) else URI := 'ip:172.16.2.199';
|
|
if not IIOLoad then begin writeln('FAIL: libiio not loaded'); Halt(1); end;
|
|
writeln('libiio loaded OK');
|
|
|
|
pl := TPlutoBackend.Create;
|
|
h := THelper.Create;
|
|
try
|
|
if not pl.ProbeURI(URI, dev) then begin writeln('FAIL: probe'); Halt(1); end;
|
|
writeln('PROBE OK: ', dev.Model, ' serial=', dev.Serial, ' ip=', dev.IPAddress);
|
|
|
|
if not pl.Connect(dev) then begin writeln('FAIL connect: ', pl.LastError); Halt(1); end;
|
|
writeln('CONNECT OK');
|
|
|
|
pl.OnDDCIQ := h.OnIQ;
|
|
pl.ConfigureDDCs(1, 1536, 0, True, True); // 1.536 MS/s
|
|
pl.SetRunAndFreq(True, 100000000, 435000000, 100); // RX 100 MHz (FM band)
|
|
writeln('RUN: streaming 800 ms @ RX=100MHz, rate=1.536M ...');
|
|
Sleep(800);
|
|
pl.SetRunAndFreq(False, 100000000, 435000000, 0);
|
|
|
|
if h.Pairs > 0 then rms := Sqrt(h.SumSq / h.Pairs) else rms := 0;
|
|
writeln(' packets=', h.Pkts, ' pairs=', h.Pairs,
|
|
' (~', (h.Pairs / 0.8 / 1e6):0:3, ' MS/s)');
|
|
if rms > 0 then
|
|
writeln(' I level: rms=', rms:0:5, ' (', (20*Log10(rms)):0:1, ' dBFS)')
|
|
else
|
|
writeln(' I level: zero (no signal/clip)');
|
|
|
|
pl.Disconnect;
|
|
writeln('DISCONNECT OK');
|
|
finally
|
|
h.Free;
|
|
pl.Free;
|
|
end;
|
|
writeln('DONE');
|
|
end.
|