Files
ewsdr/pluto_integration.MD
T
Uladzimir KarpenkaandClaude Opus 4.8 f122079b83 Phase 5 (D2): move spectrum/waterfall raw frames into the controller
OnSpectrumReady/OnWaterfallReady move into TRadioController. The controller now
owns the last spectrum/waterfall frame (FSpectrumBuf/FWaterfallBuf) for the web
mirror and emits raw pixels via OnSpectrumData/OnWaterfallData. MainForm
subscribes for FSpecView rendering (+ waterfall scroll decimation, a UI concern).
TWebAdapter.PushState now reads the frame from the controller (no buffer params).
Behavior-preserving; removes the last spectrum/waterfall data-path coupling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:46:39 +03:00

519 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Интеграция ADALM-PLUTO (libiio) в EWSDR — полный план работ
> Цель: добавить поддержку **PlutoSDR** (AD9363/AD9364) через **libiio** как
> второй тип «железа» наряду с трансиверами **openHPSDR**. Discovery-форма
> остаётся прежней, но дополняется board-специфическими параметрами Pluto.
> В UI скрываются функции, которых у Pluto нет, и появляются те, что есть
> только у Pluto. Весь DSP по-прежнему идёт через **WDSP**. Платформенно-
> специфичный код выносится в отдельные файлы, чтобы не загромождать
> существующую логику openHPSDR.
---
## 0. Краткое резюме решения
1. Ввести **тонкий слой абстракции backend'а** (`SDRBackend.pas`,
`TSDRBackend`), описывающий ровно тот API, которым `MainForm` сегодня
пользуется у `THPSDRNetwork`. Существующий `THPSDRNetwork` обворачивается
адаптером `THPSDRBackend` (код самого `HPSDRNetwork.pas` не трогаем).
2. Новый backend Pluto — `PlutoBackend.pas` (`TPlutoBackend`), и низкоуровневые
биндинги libiio — `IIO.pas` (по образцу `WDSP.pas`).
3. Поток IQ от Pluto (int16 LE) **переупаковывается в тот же 24-битный
big-endian формат**, что и openHPSDR, и подаётся в существующий
`TWDSPEngine.PushDDCPacket` — DSP-конвейер не меняется.
4. Возможности железа описываются записью **`TBoardCaps`** (`BoardCaps.pas`).
`MainForm`/`SettingsForm` гейтят UI по `Caps`, а не по жёстким спискам
`BoardType in [...]`.
5. Discovery-форма расширяется панелью Pluto-параметров (URI, gain mode,
gain, RF bandwidth, sample rate, диапазон LO) — раскладка прежняя.
6. Минимальный sample rate Pluto = **≈521 кS/s** (без программируемого FIR)
учитывается в `SampleRateOverlay` и в валидации.
---
## 1. Текущая архитектура (контекст)
Стек: **Lazarus / Free Pascal** (FPC 3.2 / Lazarus 2.x), LCL, кросс-платформенно
(Windows / Linux / macOS). DSP — внешняя **WDSP** (`wdsp.dll` / `libwdsp.so` /
`libwdsp.dylib`). Аудио — PortAudio (`AudioInput.pas` / `AudioOutput.pas`).
### 1.1 Путь данных RX
```
THPSDRNetwork (UDP, HPSDRNetwork.pas)
→ receive thread → MainForm.OnDDCIQCB(DDCIndex, TDDCIQPacket)
→ FDSPEngine.PushDDCPacket(IQData, 0, SamplesPerFrame) // сырые 24-bit BE байты
→ WDSP DSP-поток: декод 24-bit BE → double (scale 1/8388608),
RXA-цепь → OnAudio (AudioOutput) + OnSpectrum/OnWaterfall
```
Декод IQ зашит в `TWDSPEngine.PushIQItemToDSP` (`WDSPEngine.pas:1199-1209`):
3 байта I + 3 байта Q, big-endian, `SCALE = 1/8388608.0`.
### 1.2 Путь данных TX
```
Mic: OnMicPacketCB (HW-mic) или AudioInput (звуковая карта)
→ FDSPEngine.PushTXMicSamples*
→ WDSP TXA-цепь → OnTXIQReady(double IQ @ 192k, MainForm.pas:6795)
→ батчинг по 240 пар → FNetwork.SendDUCIQ → UDP (порт 1029)
```
Масштаб TX: `V * 8388607.0`, кламп в 24-bit (`MainForm.pas:6801`).
### 1.3 Discovery / устройство
- `DeviceForm.pas` (`TDeviceDialog`): слева сохранённые устройства
(`TSavedDevice`: Name / IPAddress / BoardType / AutoStart → `hpsdr_devices.ini`),
справа найденные. Кнопка DISCOVER → callback `OnDiscover`
`MainForm.BtnDiscoverFromDialog``TDiscoverThread``FNetwork.Discover`
`OnDeviceFound``FDeviceDialog.AddDiscovered(IP, name, BoardType)`.
- Устройство в сети идентифицируется по **IP**, а настройки хранятся по **MAC**
(`Settings.pas`, JSON по `MacToStr`).
### 1.4 Поверхность API `THPSDRNetwork`, которой пользуется MainForm
(`HPSDRNetwork.pas:204-249`) — именно это станет интерфейсом backend'а:
- `Discover`, `Connect(THPSDRDevice)`, `Disconnect`
- `ConfigureDDCs(NumDDCs, SampleRate, ADCSource, Dither, Random)`
- `SetRunAndFreq(Run, RXFreq, TXFreq, Drive)`
- `UpdateState(RXFreq, TXFreq, Drive, TX, PAEnabled, AlexEnabled)`
- `SendDUCIQ(I, Q)`, `SendDDCAudio`, `SendDUCSpecific`, `SetStepAtten`,
`SetAlexConfig`, `SetXvtrMode`, `SendPTT`, `SendFullHP`
- Свойства: `Connected`, `Running`, `Device`, `LastError`
- Колбэки: `OnDeviceFound`, `OnDDCIQ`, `OnMicPacket`, `OnHPStatus`, `OnWideband`
### 1.5 Board-специфичные места (по `BoardType`)
- `BoardUtils.BoardTypeName`, `FreqToBandIdx`.
- `HPSDRProtocol`: `BoardSupportsSupplyVoltage/Current`, `ADCToWatts*`,
`ADCToSupplyVolts/Current`.
- `HPSDRNetwork.ConfigureDDCs`: число ADC и базовый DDC (`BoardType in [3,4,5,10]`).
- `MainForm`: телеметрия (supply V/A, fwd/rev power, SWR) в `OnHPStatusCB`,
step-attenuator (`FAtten`), Alex/XVTR, wideband-панель, dither/random.
- `SettingsForm`: вкладки Antenna/Alex, Transverter, Tip/Ring (Orion),
ADC dither/random, PA-калибровка.
### 1.6 Линковка внешних библиотек
- `WDSP.pas`: `WDSP_LIB` по платформе; на macOS `{$linklib wdsp}`; weak —
если библиотеки нет, указатели `nil`, проверяется `Assigned(@WDSPwisdom)`.
- `ewsdr.lpi`: `CustomOptions = -k-L/usr/local/lib`.
- Сборка: `lazbuild --ws=qt6 ewsdr.lpr` (`BUILD.MD`).
---
## 2. Pluto SDR / libiio — факты и ограничения
**Железо:** ADALM-PLUTO на AD9363 (заводской), прошивкой разблокируется до
возможностей AD9364/AD9361. **Один RX и один TX** канал (один ADC / один DAC).
**libiio API (нужный минимум):**
- Контекст: `iio_create_context_from_uri("ip:192.168.2.1" | "ip:pluto.local"
| "usb:1.x.x" | "local:")`.
- Discovery: `iio_create_scan_context(NULL,0)` →
`iio_scan_context_get_info_list` → `iio_context_info_get_uri/_description`.
- Устройства: `ad9361-phy` (управление), `cf-ad9361-lpc` (RX-стрим),
`cf-ad9361-dds-core-lpc` (TX-стрим).
- Каналы phy: `voltage0` (RX, input) / `voltage0` (TX, output) — gain,
rf_bandwidth, sampling_frequency; `altvoltage0` (RX LO), `altvoltage1` (TX LO).
- Стрим: `iio_device_create_buffer`, `iio_buffer_refill` (RX),
`iio_buffer_push` (TX); каналы `voltage0`(I)+`voltage1`(Q), формат **int16 LE**,
интерливинг I/Q. Полезных бит — 12 (знаковые, в младших битах).
- Атрибуты (строки): `frequency` (LO, Hz), `sampling_frequency` (Hz),
`rf_bandwidth` (Hz), `hardwaregain` (dB), `gain_control_mode`
(`manual|slow_attack|fast_attack|hybrid`), `rssi`, `temp0` (xadc, mC).
**Платформенные ограничения Pluto (важно для UI/валидации):**
| Параметр | Значение |
|---|---|
| Sample rate (min) | **≈520.83 кS/s** без FIR; ~65 кS/s только с загруженным FIR |
| Sample rate (max) | 61.44 МS/s (по USB 2.0 реально ~5–6 МS/s непрерывно) |
| Частота LO (AD9363) | 325 МГц 3.8 ГГц |
| Частота LO (AD9364, разблок.) | 70 МГц – 6 ГГц |
| RX gain | 073 дБ (manual) + режимы AGC |
| TX «gain» | аттенюатор −89.75…0 дБ |
| RF bandwidth | 200 кГц 56 МГц |
| Транспорт | USB / Ethernet-gadget; интерфейс — URI, не broadcast |
**Чего у Pluto НЕТ:** нескольких DDC/ADC, dither/random, Alex-фильтров/реле
антенн, аппаратного wideband-ADC, PureSignal, измерения напряжения/тока питания,
fwd/rev power и КСВ, аппаратного step-attenuator, HW-CW-keyer. XVTR-логика
возможна только как программный сдвиг частоты (без реле).
---
## 3. openHPSDR vs Pluto — матрица возможностей
| Возможность | openHPSDR | Pluto | Действие в UI |
|---|---|---|---|
| Несколько DDC | да | нет (1 RX) | скрыть мульти-DDC |
| Dither / Random ADC | да | нет | скрыть в Advanced |
| Step attenuator (RX) | да | нет (есть RX gain dB) | заменить контрол |
| RX gain / AGC mode | косвенно | да (manual/AGC) | показать для Pluto |
| Drive level (TX) | 0255 | TX atten dB | переинтерпретировать |
| Alex антенны/фильтры | да | нет | скрыть вкладку |
| XVTR (реле) | да | только сдвиг | показать упрощённо |
| Wideband ADC панель | да | нет | скрыть |
| Supply V / A | часть плат | нет (есть temp) | заменить на temp |
| Fwd/Rev power, SWR | да | нет | скрыть SWR/PWR |
| RF bandwidth | фикс. | настраиваемый | показать для Pluto |
| Диапазон частот | 0–61.44М (HF/6м) | 70М–6Г | расширить ввод/бэнд-план |
| Sample rates | 48к–1536к | 521к–~6М | свой набор в overlay |
---
## 4. Архитектурное решение
### 4.1 Слой абстракции backend'а — `SDRBackend.pas` (НОВЫЙ)
Абстрактный класс `TSDRBackend`, повторяющий публичный контракт
`THPSDRNetwork` (раздел 1.4). MainForm работает с `FBackend: TSDRBackend`
вместо прямого `FNetwork`. Колбэки переносятся на backend.
```pascal
type
TSDRKind = (sdrHPSDR, sdrPluto);
TSDRBackend = class
protected
FOnDDCIQ: TOnDDCIQPacket; // (повторно используем типы из HPSDRNetwork)
FOnMic: TOnMicPacket;
FOnHPStatus: TOnHPStatus; // для Pluto заполняем синтетически (RSSI/temp)
FOnWideband: TOnWidebandFrame;// Pluto: не вызывается
FOnDeviceFound: TOnDeviceFound;
public
function Kind: TSDRKind; virtual; abstract;
function Caps: TBoardCaps; virtual; abstract;
function Discover(TimeoutMs: Integer): THPSDRDeviceArray; virtual; abstract;
function Connect(const Dev: THPSDRDevice): Boolean; virtual; abstract;
procedure Disconnect; virtual; abstract;
procedure ConfigureDDCs(NumDDCs: Byte; SampleRate: Word; ...); virtual; abstract;
procedure SetRunAndFreq(Run: Boolean; RXFreqHz, TXFreqHz: Double; Drive: Byte); virtual; abstract;
procedure UpdateState(...); virtual; abstract;
procedure SendDUCIQ(const I, Q: array of Integer); virtual; abstract;
// ... остальные методы из 1.4; для неподдерживаемых — пустая реализация в Pluto
property Connected: Boolean read FConnected;
property Running: Boolean read FRunning;
property Device: THPSDRDevice read FDevice;
property LastError: string read FLastError;
property OnDDCIQ: ... ; // + остальные колбэки
end;
```
**Почему не интерфейс (`interface`/IUnknown):** класс проще для FPC-кода без
ARC-сюрпризов, повторяет существующий стиль и не требует менять
`HPSDRNetwork.pas`.
### 4.2 `THPSDRBackend` (адаптер) — в `SDRBackend.pas` или отдельно
Тонкая обёртка: внутри `FNet: THPSDRNetwork`, делегирует все методы и
пробрасывает колбэки. `Caps` возвращает capabilities по `BoardType`.
`HPSDRNetwork.pas` остаётся **без изменений**.
### 4.3 `TPlutoBackend` — `PlutoBackend.pas` (НОВЫЙ)
Реализует `TSDRBackend` поверх libiio. Внутри:
- RX-поток: `iio_buffer_refill` → int16→24-bit BE repack → синтез
`TDDCIQPacket` (seq, SamplesPerFrame) → `FOnDDCIQ(0, pkt)`.
- TX-поток: очередь как в HPSDR (`DUC_TX_QUEUE_SIZE`), пары I/Q (24-bit)
→ int16 → `iio_buffer_push`.
- Телеметрия-поток (~5–10 Гц): читает `rssi`/`temp0` → синтетический
`THighPriorityStatus` → `FOnHPStatus` (S-метр + температура).
- `Discover` → `iio_create_scan_context`; маппинг URI/serial → `THPSDRDevice`
(см. 7.3 про идентификацию).
### 4.4 `IIO.pas` — биндинги libiio (НОВЫЙ)
По образцу `WDSP.pas`. **Рекомендация: динамическая загрузка через `dynlibs`**
(`LoadLibrary`/`GetProcedureAddress`) с `function IIOAvailable: Boolean` —
тогда приложение запускается даже без установленной libiio, а Pluto просто
недоступен (как сейчас weak-WDSP). Имена: `libiio.so.0`/`libiio.so` (Linux),
`iio.dll` (Windows), `libiio.dylib`/framework (macOS).
### 4.5 `BoardCaps.pas` — описание возможностей (НОВЫЙ)
```pascal
type
TBoardCaps = record
Kind: TSDRKind;
DisplayName: string;
HasMultiDDC, HasDitherRandom, HasStepAtten, HasRXGain,
HasAGCModes, HasAlex, HasXvtrRelays, HasWideband,
HasSupplyVolts, HasSupplyCurrent, HasPower, HasSWR,
HasRFBandwidth, HasHWMic, HasPureSignal: Boolean;
MinSampleRate, MaxSampleRate: Integer;
MinFreqHz, MaxFreqHz: Double;
SampleRates: array of Integer; // что показывать в overlay
end;
function BoardCapsFor(Kind: TSDRKind; BoardType: Integer): TBoardCaps;
```
Единый источник правды для гейтинга UI — заменяет разбросанные
`if BoardType in [...]`.
---
## 5. Список новых и изменяемых файлов
**Новые:**
- `IIO.pas` — биндинги libiio (динамическая загрузка).
- `PlutoBackend.pas` — `TPlutoBackend` (RX/TX/telemetry потоки, repack).
- `SDRBackend.pas` — `TSDRBackend` + `THPSDRBackend`-адаптер + `TSDRKind`.
- `BoardCaps.pas` — `TBoardCaps` + `BoardCapsFor`.
- `PlutoUtils.pas` (опц.) — частотные бэнд-планы/пресеты для VHF/UHF/SHF.
**Изменяемые:**
- `MainForm.pas` — `FNetwork: THPSDRNetwork` → `FBackend: TSDRBackend`
(+ фабрика по `Kind`); гейтинг UI по `FBackend.Caps`; телеметрия;
drive/gain; START/STOP.
- `DeviceForm.pas` — `TSavedDevice` += поля Pluto; панель Pluto-параметров;
`AddDiscovered` += URI/Kind.
- `SettingsForm.pas` — показывать/прятать вкладки и контролы по Caps;
новые контролы Pluto (RX gain, AGC mode, RF bandwidth).
- `BoardUtils.pas` — имя для Pluto, бэнд-план VHF/UHF (или вынести в PlutoUtils).
- `Settings.pas` — ключ устройства (MAC vs serial), новые поля (gain mode,
gain, rf_bandwidth, pluto sample rate), set частот для Pluto.
- `SampleRateOverlay.pas` — набор скоростей зависит от backend/Caps.
- `ewsdr.lpr` — добавить новые units в `uses`.
- `ewsdr.lpi` — пути/линковка libiio (если link-time), новые файлы в проекте.
- `bundle_app.sh` — включать libiio в macOS-бандл.
- `BUILD.MD` — отметить зависимость libiio (опциональная).
---
## 6. Детальный разбор по подсистемам
### 6.1 libiio биндинги (`IIO.pas`)
Объявить нужные типы-указатели (`p_iio_context`, `p_iio_device`,
`p_iio_channel`, `p_iio_buffer`, `p_iio_scan_context`, `p_iio_context_info`)
и функции:
- контекст: `iio_create_context_from_uri`, `iio_context_destroy`,
`iio_context_get_device`, `iio_context_find_device`.
- scan: `iio_create_scan_context`, `iio_scan_context_get_info_list`,
`iio_context_info_get_uri`, `iio_context_info_get_description`,
`iio_context_info_list_free`, `iio_scan_context_destroy`.
- каналы: `iio_device_find_channel`, `iio_channel_enable`,
`iio_channel_attr_write_longlong/_read_longlong`, `..._write/_read` (строки),
`iio_channel_attr_write_double` (для gain).
- буферы: `iio_device_create_buffer`, `iio_buffer_destroy`,
`iio_buffer_refill`, `iio_buffer_push`, `iio_buffer_first`,
`iio_buffer_step`, `iio_buffer_end`, `iio_channel_read/_write` (или прямой
доступ к памяти буфера).
- `iio_strerror`.
Все `cdecl`. Использовать `TIIO_*` typedef'ы. Реализовать `InitIIO/IIOAvailable`
(грузим библиотеку, резолвим символы; при отсутствии — `False`).
### 6.2 Backend-абстракция и подключение в MainForm
- Завести фабрику: `function CreateBackend(Kind: TSDRKind): TSDRBackend;`.
- В `MainForm` заменить тип поля `FNetwork` на `FBackend: TSDRBackend`
(или оставить имя `FNetwork`, сменив тип — меньше правок в тексте).
- Колбэки (`OnDeviceFound`, `OnDDCIQCB`, `OnHPStatusCB`, `OnMicPacketCB`,
`OnWidebandCB`) переключить на backend.
- При выборе устройства в discovery узнаём `Kind` (Pluto vs HPSDR) и создаём
нужный backend перед `Connect`.
- `BoardTypeName`-вывод в статус-баре и Web (`MainForm:3172`,
`3257`) — через `FBackend.Caps.DisplayName`.
### 6.3 Discovery-форма (`DeviceForm.pas`)
- `TSavedDevice` расширить: `Kind: TSDRKind; URI: string;
PlutoGainMode: Integer; PlutoGainDB: Double; PlutoRFBW: Integer;
PlutoSampleRate: Integer;` (BoardType остаётся для HPSDR).
- INI (`hpsdr_devices.ini`): дописать поля; обратная совместимость —
отсутствующие читать с дефолтами, `Kind` по умолчанию `sdrHPSDR`.
- DISCOVER запускает **обе** ветки: HPSDR broadcast и libiio-scan (если
`IIOAvailable`). В списке найденных помечать тип: `[HPSDR]` / `[PLUTO]`.
`AddDiscovered` получает доп. параметры (URI, Kind, отображаемое имя).
- Раскладка прежняя; снизу/справа добавить **сворачиваемую панель
«Pluto parameters»**, активную только когда выбранное/добавляемое
устройство — Pluto: поля URI, Gain mode (combo), Gain (dB), RF bandwidth,
Sample rate. Для HPSDR-устройств панель скрыта/неактивна.
- `BtnConnect` отдаёт в результат не только IP, но и `Kind`/URI/Pluto-параметры.
### 6.4 Идентификация устройства и Settings
- HPSDR: ключ настроек — **MAC**. Pluto: MAC нет → использовать **serial
number** Pluto (атрибут контекста `hw_serial`/usb serial) как ключ.
В `Settings.pas` обобщить `MacToStr` до строкового device-id, либо завести
отдельную ветку ключей `pluto:<serial>`.
- `THPSDRDevice` дополнить (или ввести `TSDRDeviceInfo`) полями `Kind`,
`URI`, `Serial`. Чтобы не плодить типы, можно положить serial в поле `MAC`
как хэш — но честнее расширить запись. (Решить в Фазе 1, см. открытые вопросы.)
- Новые per-device поля в `TGlobalSettings` (или отдельная секция `pluto`):
`PlutoGainMode`, `PlutoGainDB`, `PlutoRFBW`, `PlutoLOOffset`.
### 6.5 Sample rate (`SampleRateOverlay.pas` + валидация)
- Текущий `SPAN_RATES = (48k..1536k)` сделать зависимым от `Caps.SampleRates`.
- Для Pluto предложить набор: `521000, 768000, 1000000, 1536000, 2000000,
3000000` (верх — с оглядкой на USB-троттлинг; пометить «USB limit»).
- Жёсткая валидация: для Pluto `SampleRate >= MinSampleRate(521000)`.
При попытке выставить ниже — клампить + статус-предупреждение.
- `FSpanHz` = выбранный rate (как сейчас). WDSP rate-agnostic; пересоздание
движка через существующий `RecreateDSPEngine/ChangeSampleRate`.
- **CPU/латентность:** при 521к–3М `FBufSize = BufSize*Rate/48000` большой
(≈5.5k–32k). Проверить нагрузку; при необходимости — уменьшить базовый
`BufSize` для Pluto или включить программируемый FIR Pluto для понижения
rate. (Замер в Фазе 4.)
### 6.6 RX-поток IQ и формат сэмплов (ключевой момент)
- Pluto отдаёт **int16 LE** (12 значащих бит). openHPSDR/WDSP ждёт **24-bit BE**,
scale `1/8388608`.
- **Решение (минимальная инвазивность):** в `TPlutoBackend` RX-потоке
переупаковывать каждый int16-сэмпл в 3-байтовый big-endian с тем же
динамическим диапазоном: `v24 = v16 << 12` (12→24 бита, знак сохраняется),
затем разложить в 3 байта BE. Это даёт корректный масштаб для существующего
декодера `PushIQItemToDSP` без изменений в WDSPEngine.
- Альтернатива: добавить в `TWDSPEngine` метод `PushIQDoubles(constI,Q)` и
кормить double напрямую (чище, но трогает WDSPEngine — оставить как опцию).
- Размер «пакета»: эмулируем `TDDCIQPacket` — заполняем `Seq` (монотонно),
`SamplesPerFrame` (например 238, как у HPSDR, чтобы переиспользовать ветку
`MainForm.OnDDCIQCB`), и режем буфер `iio_buffer_refill` на такие кадры.
- `FActiveDDC`/seq-контроль в `OnDDCIQCB` продолжают работать (DDCIndex=0).
### 6.7 TX-поток
- `OnTXIQReady` (MainForm:6795) уже даёт 24-bit-клампленные `I/Q` пары →
`FBackend.SendDUCIQ`. В `TPlutoBackend.SendDUCIQ` конвертировать
24-bit → int16 (`v16 = v24 >> 12` или scale), писать в TX-буфер,
`iio_buffer_push` в отдельном потоке (равномерная подача, как DUC-sender).
- TX-частота: Pluto имеет отдельный TX LO (`altvoltage1`). Поддержать
split/duplex; при не-дуплексе LO RX=TX.
- «Drive level» (0100 слайдер) маппить в TX `hardwaregain` (аттенюатор
0…−89.75 дБ): 100%→0 дБ, 0%→−89.75 дБ (или разумный практический диапазон,
напр. 0…−40 дБ). См. `CalcDriveByte`/`TrkDriveChange` в MainForm.
### 6.8 Gain / AGC модель
- HPSDR: step-attenuator (`FAtten`, 0/10/20 дБ) + WDSP-AGC.
- Pluto: RX `gain_control_mode` (manual/slow/fast/hybrid) + `hardwaregain` (дБ).
- В UI: если `Caps.HasRXGain` — показать RX-gain слайдер (073 дБ) и combo
AGC-mode вместо step-attenuator. Когда mode≠manual — слайдер gain disable.
- WDSP-AGC RXA оставить включённым для пост-обработки (как у HPSDR), но
основной gain — аппаратный Pluto.
### 6.9 Телеметрия (S-meter / temp вместо supply/SWR)
- `OnHPStatusCB` (MainForm:3286) и панель статуса завязаны на supply V/A,
fwd/rev power, SWR.
- Pluto: `Caps.HasSupplyVolts/Current/Power/SWR = False`. В этих местах
показывать прочерк/скрывать. Вместо supply показать `temp0` (°C) Pluto.
- S-метр: WDSP `GetSMeterDBm` остаётся основным (по IQ); RSSI Pluto можно
показывать как доп. индикатор.
- TX-метр (см. memory `project_smeter_tx`: мощность+КСВ): для Pluto скрыть КСВ
и показывать только относительный уровень / TX gain (нет датчиков мощности).
### 6.10 Частотный диапазон / LO / XVTR
- Расширить допустимый ввод частоты до 70 МГц–6 ГГц (по `Caps.MinFreqHz/Max`).
- Бэнд-план: `BoardUtils.FreqToBandIdx` сейчас только HF/6м. Добавить
VHF/UHF/SHF бэнды (2м, 70см, 23см, и т.д.) для Pluto (в `PlutoUtils.pas`
или расширить `BoardUtils`), чтобы per-band настройки/калибровка работали.
- XVTR-реле у Pluto нет → вкладка Transverter в упрощённом виде (только
программный сдвиг частоты), либо скрыть, оставив встроенную перестройку LO.
### 6.11 Гейтинг UI (`MainForm` + `SettingsForm`)
- Везде, где сейчас `BoardType in [...]` или прямые предположения о HPSDR —
заменить на `FBackend.Caps.*`.
- `SettingsForm`: вкладки `Antenna/Alex`, `Transverter`, контролы Tip/Ring,
ADC dither/random, PA-калибровка показывать только при соответствующих Caps.
Для Pluto добавить секцию «Pluto RF» (gain mode, gain, RF bandwidth, LO offset).
- Главная форма: кнопки/оверлеи Wideband, ATT, SWR-индикатор — скрывать по Caps.
---
## 7. Сборка и зависимости
- **Линковка libiio:** предпочтительно **рантайм-загрузка** (`dynlibs`) —
приложение работает без libiio (Pluto просто не появляется в discovery).
Имена: Linux `libiio.so.0`, Windows `iio.dll` (libiio MSVC/MinGW сборка),
macOS `libiio.dylib` или `/Library/Frameworks/iio.framework`.
- Если выбрать link-time (как WDSP `{$linklib}`) — добавить путь в `ewsdr.lpi`
(`-k-L...`) и `{$linklib iio}` под `{$IFDEF DARWIN}`/нужные платформы.
- `ewsdr.lpr`: добавить `IIO, SDRBackend, PlutoBackend, BoardCaps` в `uses`.
- `ewsdr.lpi`: внести новые `.pas` в список файлов проекта.
- `bundle_app.sh`: при сборке macOS-бандла копировать `libiio.dylib` и его
зависимости (как уже делается для прочих не-системных dylib).
- `BUILD.MD`: отметить опциональную зависимость и где взять libiio
(пакет `libiio` / Analog Devices релизы).
- Документация: dev-права на USB (Linux udev-правило для Pluto
`0456:b673`), доступ к `ip:192.168.2.1`.
---
## 8. План работ по фазам
### Фаза 0 — Подготовка и каркас абстракции (без поведения Pluto)
1. `BoardCaps.pas`: `TBoardCaps`, `BoardCapsFor` (заполнить для всех HPSDR-плат).
2. `SDRBackend.pas`: `TSDRBackend` (абстракт), `THPSDRBackend` (обёртка над
существующим `THPSDRNetwork`), фабрика, `TSDRKind`.
3. `MainForm`: перевести `FNetwork` на `FBackend: TSDRBackend` (= `THPSDRBackend`),
все вызовы — через backend. Колбэки — через backend.
4. **Критерий приёмки:** поведение с openHPSDR полностью идентично текущему
(регресс-проверка RX/TX/discovery/settings). Pluto ещё нет.
### Фаза 1 — libiio биндинги и discovery Pluto
1. `IIO.pas`: рантайм-загрузка + нужные функции; `IIOAvailable`.
2. `TPlutoBackend.Discover` (scan-context) → список Pluto-URI.
3. `DeviceForm`: тип устройства в списке найденных; расширить `TSavedDevice`
и INI; панель Pluto-параметров; результат с `Kind`/URI.
4. Идентификация Pluto по serial; ключ Settings.
5. **Критерий:** Pluto находится в discovery, сохраняется/загружается,
подключение `iio_create_context_from_uri` успешно (без стрима).
### Фаза 2 — RX-стрим Pluto → WDSP
1. `TPlutoBackend`: конфигурация phy (LO, sampling_frequency, rf_bandwidth,
gain mode/gain), создание RX-буфера, RX-поток.
2. Repack int16→24-bit BE → синтез `TDDCIQPacket` → `OnDDCIQ`.
3. `SampleRateOverlay`/валидация по `Caps` (min 521к).
4. MainForm START/STOP-ветка для Pluto (вместо `ConfigureDDCs`/`SetRunAndFreq`
— вызовы backend, которые у Pluto означают set-rate + start-stream).
5. **Критерий:** живой спектр/водопад и аудио с Pluto на FM/AM/SSB.
### Фаза 3 — TX-стрим Pluto
1. TX-буфер, TX-поток (`iio_buffer_push`), 24-bit→int16.
2. `SendDUCIQ`/`SendPTT` для Pluto; drive→TX hardwaregain.
3. Split/duplex LO RX/TX.
4. **Критерий:** передача SSB/FM, корректный уровень, отсутствие underrun.
### Фаза 4 — UI-гейтинг и Pluto-специфика
1. Заменить все `BoardType in [...]` на `Caps`.
2. Скрыть Alex/XVTR/Wideband/Dither/SWR для Pluto; показать RX gain / AGC mode /
RF bandwidth; temp вместо supply.
3. VHF/UHF/SHF бэнд-план; расширенный ввод частоты до 6 ГГц.
4. Замер CPU/латентности на высоких rate; тюнинг буферов/FIR.
5. **Критерий:** UI согласован с возможностями железа для обоих типов.
### Фаза 5 — Полировка, сборка, документация
1. `bundle_app.sh`/`lpi`/`BUILD.MD`/udev.
2. Обработка ошибок libiio (`iio_strerror`), реконнект, выдёргивание USB.
3. Регресс-тесты openHPSDR + Pluto.
---
## 9. Риски и открытые вопросы
1. **WDSP на высоких sample rate (521к–3М):** проверить CPU/латентность;
возможно нужен меньший `BufSize` для Pluto или FIR-децимация Pluto.
2. **Идентификация Pluto (нет MAC):** окончательно выбрать ключ Settings —
расширять `THPSDRDevice` (поле `Serial`/`URI`) или вводить новый device-info
тип. Влияет на форму и Settings — решить в Фазе 1.
3. **USB-троттлинг:** верхняя граница реального непрерывного rate по USB 2.0
(~5–6 МS/s); пометить «USB limit» в overlay.
4. **libiio на Windows/macOS:** наличие готовых сборок `iio.dll`/`libiio.dylib`;
рантайм-загрузка снижает риск отсутствия.
5. **Масштаб/нормировка IQ:** подтвердить, что `<<12` даёт правильный уровень
S-метра (калибровка по WDSP); при необходимости — поправочный коэффициент.
6. **TX gain маппинг:** диапазон слайдера drive → TX atten подобрать на железе.
7. **Discovery-таймаут:** libiio-scan может блокировать; запускать в потоке
(как `TDiscoverThread`) и не вешать UI.
---
## 10. Тест-план
- **Регресс openHPSDR:** discovery, connect, RX все режимы, TX SSB/CW/FM,
Alex/XVTR/wideband, supply/SWR — без изменений после Фазы 0.
- **Pluto RX:** discovery (USB и IP), connect, спектр/водопад/аудио на разных
rate (521к/768к/1.5М), смена LO в пределах диапазона, gain manual/AGC.
- **Pluto TX:** SSB/FM, уровень по drive, split RX/TX, отсутствие
drop/underrun (логи seq/drops).
- **UI-гейтинг:** при Pluto скрыты Alex/XVTR/Wideband/SWR/dither; показаны
RX gain/AGC/RF bandwidth/temp.
- **Устойчивость:** выдёргивание USB / разрыв сети → корректный Disconnect,
reconnect, отсутствие крэшей; запуск без установленной libiio.
- **Кросс-платформа:** Linux (qt6) обязательно; Windows/macOS — по мере
доступности libiio.
---
## 11. Принципы, которых держимся
- `HPSDRNetwork.pas`, `WDSPEngine.pas`, `WDSP.pas` — **не меняем** (или
минимально: опц. `PushIQDoubles`). Pluto-логика — в новых файлах.
- Один источник правды о возможностях железа — `TBoardCaps`.
- Pluto переиспользует существующий DSP-конвейер за счёт repack в 24-bit BE.
- Discovery-форма сохраняет раскладку; Pluto-параметры — дополнительная панель.