mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat: Pluto/AD936x SDR backend (discovery, RX, VHF band plan)
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>
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
unit RadioBackend;
|
||||
|
||||
{
|
||||
Абстракция бэкенда железа. Базовый класс TRadioBackend, который реализуют
|
||||
конкретные транспорты:
|
||||
- THPSDRNetwork (openHPSDR Ethernet protocol) — HPSDRNetwork.pas
|
||||
- TPlutoBackend (libiio / AD936x) — PlutoBackend.pas (позже)
|
||||
|
||||
TRadioController держит FNetwork: TRadioBackend и работает только через этот
|
||||
интерфейс. Возможности бэкенда описывает TBackendCaps (контроллер гейтит по
|
||||
ним UI и поведение).
|
||||
|
||||
Здесь же — общая запись устройства (TRadioDevice) и типы колбэков данных,
|
||||
чтобы оба бэкенда и контроллер ссылались на один источник правды.
|
||||
TDDCIQPacket/THighPriorityStatus/TMicDataPacket берём из HPSDRProtocol —
|
||||
это сетевой формат HPSDR, а Pluto-бэкенд синтезирует те же структуры, поэтому
|
||||
WDSP-вход остаётся неизменным.
|
||||
}
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE Delphi}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, HPSDRProtocol, Settings;
|
||||
|
||||
type
|
||||
TBackendKind = (bkHPSDR, bkPluto);
|
||||
TSampleRateMode = (srmDiscrete, srmContinuous);
|
||||
|
||||
// Описание возможностей бэкенда. Контроллер гейтит UI/поведение по этим полям.
|
||||
TBackendCaps = record
|
||||
Kind: TBackendKind;
|
||||
HasTX: Boolean; // есть передатчик
|
||||
HasPA: Boolean; // fwd power / SWR / supply (HP status)
|
||||
HasAlex: Boolean; // Alex фильтры / антенный коммутатор
|
||||
HasWideband: Boolean; // wideband ADC дисплей
|
||||
HasDitherRandom: Boolean; // dither / random на ADC
|
||||
HasHWMic: Boolean; // mic over network / HW keyer / sidetone
|
||||
HasPLLStatus: Boolean; // PLL lock в статусе
|
||||
HasHWGain: Boolean; // manual gain / hw-AGC (Pluto)
|
||||
HasRFBandwidth: Boolean; // настройка аналоговой RF-полосы (Pluto)
|
||||
HasFullDuplex: Boolean; // одновременный RX+TX (Pluto / QO-100)
|
||||
MinSampleRate: Integer;
|
||||
MaxSampleRate: Integer;
|
||||
SampleRateMode: TSampleRateMode;
|
||||
RatePresets: array of Integer; // пресеты для SampleRateOverlay
|
||||
MinFreqHz: Double;
|
||||
MaxFreqHz: Double;
|
||||
end;
|
||||
|
||||
// Общая запись найденного/подключённого устройства. Поля HPSDR (MAC/BoardType/
|
||||
// NumDDCs…) и Pluto (URI/Serial) сосуществуют; актуальность по Kind.
|
||||
TRadioDevice = record
|
||||
Kind: TBackendKind;
|
||||
IPAddress: string;
|
||||
Port: Word;
|
||||
MAC: array[0..5] of Byte; // HPSDR; для Pluto — synthetic из Serial
|
||||
BoardType: Byte;
|
||||
ProtocolVersion: Byte;
|
||||
FirmwareVersion: Byte;
|
||||
NumDDCs: Byte;
|
||||
FreqOrPhase: Byte;
|
||||
EndianModes: Byte;
|
||||
URI: string; // Pluto: 'usb:1.5.5' / 'ip:192.168.2.1'
|
||||
Serial: string; // Pluto serial (hw_serial)
|
||||
Model: string; // Pluto hw_model (для отображения)
|
||||
InUse: Boolean;
|
||||
Valid: Boolean;
|
||||
end;
|
||||
PRadioDevice = ^TRadioDevice;
|
||||
TRadioDeviceArray = array of TRadioDevice;
|
||||
|
||||
// Колбэки данных (вызываются из рабочих потоков бэкенда).
|
||||
TOnDeviceFound = procedure(const Dev: TRadioDevice) of object;
|
||||
TOnDDCIQPacket = procedure(DDCIndex: Integer;
|
||||
const Data: TDDCIQPacket) of object;
|
||||
TOnMicPacket = procedure(const Data: TMicDataPacket) of object;
|
||||
TOnHPStatus = procedure(const Status: THighPriorityStatus) of object;
|
||||
TOnWidebandFrame = procedure(ADCIndex: Integer;
|
||||
const Samples: array of SmallInt;
|
||||
Count: Integer) of object;
|
||||
|
||||
{ TRadioBackend — абстрактный бэкенд железа }
|
||||
TRadioBackend = class
|
||||
protected
|
||||
// Колбэки — общее хранилище для всех бэкендов (свойства ниже).
|
||||
FOnDeviceFound: TOnDeviceFound;
|
||||
FOnDDCIQ: TOnDDCIQPacket;
|
||||
FOnMic: TOnMicPacket;
|
||||
FOnHPStatus: TOnHPStatus;
|
||||
FOnWideband: TOnWidebandFrame;
|
||||
|
||||
function GetConnected: Boolean; virtual; abstract;
|
||||
function GetRunning: Boolean; virtual; abstract;
|
||||
function GetDevice: TRadioDevice; virtual; abstract;
|
||||
function GetLastError: string; virtual; abstract;
|
||||
function GetDirectIP: string; virtual;
|
||||
procedure SetDirectIP(const V: string); virtual;
|
||||
public
|
||||
// ---- Возможности ----
|
||||
function Caps: TBackendCaps; virtual; abstract;
|
||||
|
||||
// ---- Жизненный цикл / discovery ----
|
||||
function Discover(TimeoutMs: Integer = 3000): TRadioDeviceArray; virtual; abstract;
|
||||
function Connect(const Dev: TRadioDevice): Boolean; virtual; abstract;
|
||||
procedure Disconnect; virtual; abstract;
|
||||
|
||||
// ---- Конфигурация потока RX ----
|
||||
procedure ConfigureDDCs(NumDDCs: Byte; SampleRate: Word; ADCSource: Byte = 0;
|
||||
DitherEnabled: Boolean = True;
|
||||
RandomEnabled: Boolean = True); virtual; abstract;
|
||||
|
||||
// ---- Частота / drive / TX-состояние ----
|
||||
procedure SetRunAndFreq(Run: Boolean; DDC0FreqHz, DUCFreqHz: Double;
|
||||
DriveLevel: Byte = 100); virtual; abstract;
|
||||
procedure UpdateState(RXFreqHz, TXFreqHz: Double; DriveLevel: Byte;
|
||||
Transmitting, PAEnabled, AlexEnabled: Boolean); virtual; abstract;
|
||||
procedure SendFullHP; virtual; abstract;
|
||||
procedure SendPTT(Active: Boolean); virtual; abstract;
|
||||
|
||||
// ---- TX IQ ----
|
||||
procedure SendDUCIQ(const IData, QData: array of Integer); virtual;
|
||||
procedure ClearDUCIQQueue; virtual;
|
||||
|
||||
// ---- Опциональные (HPSDR-специфичные): no-op по умолчанию ----
|
||||
procedure SendGeneralPacket(const Pkt: TGeneralPacket); virtual;
|
||||
procedure ConfigureWideband(ADCIndex: Integer; Enabled: Boolean;
|
||||
SamplesPerPacket: Integer = 512; SampleBits: Integer = 16;
|
||||
UpdateRateMS: Integer = 70; PacketsPerFrame: Integer = 32); virtual;
|
||||
procedure SendDDCSpecific(const Pkt: TDDCSpecificPacket); virtual;
|
||||
procedure SendDUCSpecific(const Pkt: TDUCSpecificPacket); virtual;
|
||||
procedure SendHighPriority(const Pkt: THighPriorityPacket); virtual;
|
||||
procedure SetStepAtten(dB: Byte); virtual;
|
||||
procedure SetAlexConfig(const A: TAlexSettings); virtual;
|
||||
procedure SetXvtrMode(AEnable, ADisablePA: Boolean; ARxAnt: Byte); virtual;
|
||||
procedure SendDDCAudio(const LeftRight: array of SmallInt); virtual;
|
||||
procedure SendSpeakerAudio(const Left, Right: array of Single; Count: Integer); virtual;
|
||||
procedure SetSpeakerAudio(AEnabled: Boolean); virtual;
|
||||
|
||||
// ---- Свойства состояния (полиморфные через виртуальные геттеры) ----
|
||||
property Connected: Boolean read GetConnected;
|
||||
property Running: Boolean read GetRunning;
|
||||
property Device: TRadioDevice read GetDevice;
|
||||
property LastError: string read GetLastError;
|
||||
property DirectIP: string read GetDirectIP write SetDirectIP;
|
||||
|
||||
// ---- Колбэки данных ----
|
||||
property OnDeviceFound: TOnDeviceFound read FOnDeviceFound write FOnDeviceFound;
|
||||
property OnDDCIQ: TOnDDCIQPacket read FOnDDCIQ write FOnDDCIQ;
|
||||
property OnMicPacket: TOnMicPacket read FOnMic write FOnMic;
|
||||
property OnHPStatus: TOnHPStatus read FOnHPStatus write FOnHPStatus;
|
||||
property OnWideband: TOnWidebandFrame read FOnWideband write FOnWideband;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TRadioBackend — пустые реализации опциональных методов (no-op).
|
||||
Конкретные бэкенды переопределяют то, что поддерживают. }
|
||||
|
||||
function TRadioBackend.GetDirectIP: string;
|
||||
begin
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SetDirectIP(const V: string);
|
||||
begin
|
||||
// no-op по умолчанию (unicast discovery — фича HPSDR)
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendDUCIQ(const IData, QData: array of Integer);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.ClearDUCIQQueue;
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendGeneralPacket(const Pkt: TGeneralPacket);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.ConfigureWideband(ADCIndex: Integer; Enabled: Boolean;
|
||||
SamplesPerPacket: Integer; SampleBits: Integer;
|
||||
UpdateRateMS: Integer; PacketsPerFrame: Integer);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendDDCSpecific(const Pkt: TDDCSpecificPacket);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendDUCSpecific(const Pkt: TDUCSpecificPacket);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendHighPriority(const Pkt: THighPriorityPacket);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SetStepAtten(dB: Byte);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SetAlexConfig(const A: TAlexSettings);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SetXvtrMode(AEnable, ADisablePA: Boolean; ARxAnt: Byte);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendDDCAudio(const LeftRight: array of SmallInt);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SendSpeakerAudio(const Left, Right: array of Single; Count: Integer);
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure TRadioBackend.SetSpeakerAudio(AEnabled: Boolean);
|
||||
begin
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user