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; const // Признак «значение телеметрии недоступно» (ReadTelemetry). Заведомо вне // физического диапазона температур/RSSI. TELEMETRY_NONE = -1.0E9; type // Колбэки данных (вызываются из рабочих потоков бэкенда). 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; // RX-усиление (Pluto/AD936x). ModeIdx: 0=manual, 1=fast_attack, 2=slow_attack, // 3=hybrid. GainDb применяется только в manual. HPSDR — no-op. procedure SetRxGain(ModeIdx: Integer; GainDb: Integer); virtual; // TX-аттенюация выходного тракта (Pluto/AD936x): hardwaregain на voltage0 // (output), дБ в диапазоне ~ -89.75..0 (0 = макс. мощность). Контроллер ставит // максимум на приёме (≈выкл) и значение от drive на передаче. HPSDR — no-op // (мощность там через DriveLevel/DUC). Db < 0. procedure SetTxAtten(Db: Double); 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; // Аппаратная телеметрия, не приходящая в потоке (опрашивается контроллером). // Pluto/AD936x: температура чипа (°C) и RSSI приёмника (дБ). HPSDR — нет такой // (использует HP-status), возвращает False. Недоступные значения = TELEMETRY_NONE. function ReadTelemetry(out TempC, RSSIdB: Double): 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.SetRxGain(ModeIdx: Integer; GainDb: Integer); 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; function TRadioBackend.ReadTelemetry(out TempC, RSSIdB: Double): Boolean; begin TempC := TELEMETRY_NONE; RSSIdB := TELEMETRY_NONE; Result := False; end; procedure TRadioBackend.SetTxAtten(Db: Double); begin end; end.