Files
ewsdr/doc/PLUTO_INTEGRATION_PLAN.md
ew8bakandClaude Opus 4.8 a885488243 feat: web RF-AGC + QO-100 RX MUTE controls (Pluto)
Прокинул в web-интерфейс управление, которого там не хватало (раньше
только отображение):

- RF-AGC (Pluto hw-gain): дропдаун режима FAST/SLOW/HYB/MAN + слайдер
  GAIN 0–73 дБ (активен только в MAN, драг переводит в manual). Виден
  по BackendCaps.HasHWGain (= Pluto подключён). Команды set_rx_gain_mode
  / set_rx_gain → SetRxGainMode / SetRxGain.
- RX MUTE (QO-100 self-monitor): toggle-кнопка рядом с BCN, видна по
  InQO100, подсветка по состоянию. Команда set_rx_mute → SetRxMuteOnTx.

Гейтинг зеркалит десктоп. Работает в GUI-web-сервере и headless-демоне.
Проверено на железе пользователем.

WebServer: команды + state-поля (rf_agc_visible/rx_gain_mode/rx_gain_db,
rx_mute_visible/rx_mute_on) + SetRxGainStatus/SetRxMuteStatus.
WebAdapter: колбэки OnRxGain*/OnRxMute (Invoke на поток контроллера) +
публикация в PushState. MainForm + ewsdrd.lpr: привязка колбэков.
WebPageHtml: виджеты, JS-обработчики, updRfAgc/updRxMute, PEND_MAP.

Также актуализирован doc/PLUTO_INTEGRATION_PLAN.md: TX-тракт и режим
QO-100 помечены проверенными на железе; раскрыт TODO «XO ppm-калибровка»
(ручной TxLOOffset как текущий обходной путь, xo_correction/GPSDO как
постоянное решение).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:02:34 +03:00

30 KiB
Raw Permalink Blame History

План интеграции ADALM-PLUTO (PlutoSDR) в EWSDR

Ветка: feature/pluto-sdr-integration (от feature/radio-controller-refactor).

Документ — результат анализа текущей кодовой базы и предлагаемый поэтапный план. Цель: добавить второй бэкенд железа (Pluto, libiio/AD936x) рядом с openHPSDR так, чтобы оба находились в одном окне discovery, а различия возможностей корректно отражались в UI (включая оверлей samplerate с другими значениями для Pluto).

Ключевые решения по продукту:

  • Pluto — только VHF/UHF, HF исключён. Свой band-план: 2 м, 70 см, 23 см, 13 см (и т.д.).
  • Отдельный режим QO-100 (геостационарный транспондер): RX через LNB, TX напрямую на 2400 МГц, раздельные RX/TX LO, full-duplex.

1. Анализ текущей архитектуры

1.1 Слои и владение

TMainForm (GUI)  ─┐
ewsdrd.lpr (демон)─┤── владеют ─→ TRadioController  (backend-агностичное ядро)
WebServer/Adapter ─┘                   │
                                       ├─ FNetwork:   THPSDRNetwork   ← ЕДИНСТВЕННАЯ точка железа
                                       ├─ FDSPEngine: TWDSPEngine
                                       ├─ FAudioOut / FAudioIn
                                       ├─ FSettings:  TSettingsManager (per-device по MAC)
                                       └─ FDeviceStore: TDeviceStore  (saved + discovered)

TRadioController уже спроектирован как backend-агностичное ядро — в коде есть прямые комментарии-намёки на Pluto (RadioController.pas:541, :603). Но FNetwork объявлен конкретным типом THPSDRNetwork, а не интерфейсом, и используется в 52 местах контроллера.

1.2 Путь данных (ключевое для бэкенда)

RX (вход IQ):

THPSDRNetwork (RX-поток, UDP 1035) → OnDDCIQ(DDCIndex, TDDCIQPacket)
  → FDSPEngine.PushDDCPacket(Data.IQData, 0, SamplesPerFrame)

IQ — 24-битные интерливнутые пары I/Q (6 байт/пара), формат зашит в PushDDCPacket (WDSPEngine.pas:1136) и в декодере DSP-потока.

TX (выход IQ):

WDSP TXA → OnTXIQ(Buf, Count) → накопление 240 пар (24-бит, clamp)
  → FNetwork.SendDUCIQ(I[], Q[])

Телеметрия: OnHPStatus(THighPriorityStatus) → fwd power / SWR / supply V/A / PLL lock / ADC overload. S-meter считается отдельно в WDSP (FLastSMeter).

1.3 Discovery

FController.Discover → TDiscoverThread → FNetwork.Discover(2000)
  (UDP broadcast :1024, CMD_DISCOVERY)
  → per device: OnDeviceFound(THPSDRDevice)
  → FDeviceStore.AddDiscovered(...) → Changed(rfDeviceList)
  → DeviceForm.RefreshFound (один список «DISCOVERED DEVICES»)
  • THPSDRDevice: IP, Port, MAC[6], BoardType, ProtocolVersion, NumDDCs…
  • TDeviceStore: TSavedDevice (Name/IP/BoardType/AutoStart) и TDiscoveredDevice (IP/DisplayName/BoardType/MAC[6]).
  • Per-device настройки (Settings.pas) ключуются по MAC через MacToStr(MAC) → строковая секция JSON. LoadDevice/LoadTX/LoadAlex/LoadXvtr все принимают MAC: array of Byte.

1.4 Sample rate

  • SampleRateOverlay.pasжёстко зашитый массив SPAN_RATES = (48000,96000,192000,384000,768000,1536000) и SPAN_NAMES.
  • SetSampleRate(Hz) (RadioController.pas:1791): span ≡ sample rate; FDSPEngine.ChangeSampleRate(Hz) + FNetwork.ConfigureDDCs(1, Hz div 1000, …).
  • Дискретные значения HPSDR кратны DSP-clock 122.88 МГц.

1.5 Частота / диапазоны / XVTR

  • FreqToPhaseWord (HPSDRProtocol.pas): phase = 2³² · f / 122.88 МГц.
  • FreqToBandIdx / band-план — HF/6m (1.8–54 МГц, 11 диапазонов).
  • Есть полноценная XVTR-подсистема (8 слотов, LO offset/error, трансляция видимой частоты в IF, XvtrTranslate) — но один LO-offset на слот (RX=TX). Для QO-100 нужны раздельные RX/TX LO → отдельная модель (см. §4).
  • SetDuplex (RadioController.pas:1851) — сейчас заглушка ({TODO wiring}). Для QO-100 full-duplex это нужно довести.

2. Pluto: что отличается (capability-матрица)

Возможность openHPSDR Pluto (AD936x)
Транспорт UDP/Ethernet (свой) libiio: USB / IP / local
Discovery UDP broadcast :1024 iio_scan_context (USB/network/local)
Идентификатор MAC[6] serial / context URI
Кол-во приёмников (DDC) до 80, sync 1 RX (2×2 только с хаком прошивки)
Sample rate дискрет 48k…1536k непрерывный, ≥ ~521 ksps, до 61.44 MSPS (по USB2 практически ~5–6 MSPS)
RF-полоса (аналог. фильтр) нет (фикс CIC) отдельная настройка RX/TX LPF
Диапазон частот 061.44 МГц (HF/6m) 325 МГц–3.8 ГГц (сток), 70 МГц–6 ГГц с хаком. HF не используем
Разрядность IQ 24-бит 12-бит ADC, по шине 16-бит
Усиление RX step atten + dither/random manual gain 073 дБ / AGC (slow/fast/hybrid/manual)
TX DUC + PA + Alex T/R 1 DAC, TX atten 89…0 дБ, без PA/SWR
Full-duplex (зависит) да (раздельные RX/TX LO) — нужно для QO-100
S-meter WDSP (есть) WDSP (есть) + аппаратный RSSI
Fwd power / SWR / supply V/A HP status нет (нет PA)
PLL lock HP status нет (другой механизм)
ADC overload HP status косвенно (gain/RSSI)
Wideband ADC дисплей есть нет
Alex / антенный коммутатор есть нет
Mic over network / HW CW keyer / sidetone / open collector есть нет (TX-аудио через звуковую карту/web)
10 МГц reference / PA config есть нет; есть XO ppm-калибровка, темп.сенсор

Только у Pluto (нужно в UI): RF bandwidth, manual gain / режим AGC железа, XO ppm-коррекция, выбор транспорта (USB/IP), buffer size/timeout, температура.


3. Band-план Pluto (VHF/UHF) и режим QO-100

3.1 VHF/UHF band-план

HF исключён. Для Pluto — отдельная таблица диапазонов (используется, когда подключён бэкенд bkPluto); FreqToBandIdx/band-кнопки переключаются на неё:

Band Частоты Примечание
2 м 144146 МГц требует freq-range хак Pluto (<325 МГц)
70 см 430440 МГц требует freq-range хак Pluto
23 см 12401300 МГц нативно
13 см 23002450 МГц нативно (включает QO-100 uplink 2400)
(опц.) FM bcast / ADS-B / прочие — позже

⚠️ 2 м и 70 см ниже стокового минимума 325 МГц → нужен AD9363→AD9361 frequency-range мод прошивки Pluto. В UI/доках это отметить.

3.2 Режим QO-100 (геостационар Es'hail-2 NB-транспондер)

Геометрия сигнала:

  • Downlink (RX): 10489.5010489.99 МГц (10 ГГц). Принимается через LNB: LNB LO (обычно 9750 МГц) → Pluto RX ≈ 739.5739.99 МГц. Нативный диапазон Pluto.
  • Uplink (TX): 2400.052400.54 МГц. Pluto TX напрямую (нативно).
  • Связь частот: uplink = downlink 8089.5 МГц (offset транспондера).
  • Full-duplex — стандартный режим работы (слышишь себя через спутник).

Отсюда QO-100 — это трансвертер с раздельными RX/TX LO, чего нынешняя XVTR-модель (один offset) не даёт. Предлагаемая модель — отдельная подсистема/слот:

TQO100Settings = record
  Enabled:        Boolean;
  DisplayBase:    Double;   // отображаем downlink (10489.x ГГц)
  LnbLoHz:        Double;   // LO LNB, по умолч. 9750e6
  LnbPpm:         Double;   // калибровка дрейфа LNB (по маяку)
  TransponderOfs: Double;   // 8089.5e6 (downlink - uplink)
  TxAttenDb:      Double;   // -89..0
  FullDuplex:     Boolean;  // RX во время TX
end;

Трансляция:

  • RX: PlutoRxLO = DisplayFreq LnbLoHz (с учётом LnbPpm).
  • TX: PlutoTxLO = DisplayFreq TransponderOfs.
  • Дисплей/VFO/курсор работают в терминах downlink (10 ГГц).
  • Калибровка по маяку: подстройка LnbPpm, чтобы наблюдаемый маяк совпал с опорной частотой.

Требует довести SetDuplex (сейчас TODO) до реального full-duplex (одновременно RX-поток refill и TX-поток push в libiio — Pluto это поддерживает аппаратно).


4. Предлагаемая архитектура интеграции

4.1 Абстракция бэкенда (ядро плана)

Базовый класс/интерфейс TRadioBackend, который реализуют оба: THPSDRNetwork и новый TPlutoBackend. Контроллер держит FBackend: TRadioBackend (сохранить имя поля FNetwork, сменив только тип на базовый — минимизирует дифф). Методы, которых у Pluto нет, — пустые no-op (Alex, wideband, step atten, speaker audio, DUC specific и т.д.).

Дескриптор возможностей:

TBackendKind   = (bkHPSDR, bkPluto);
TSampleRateMode = (srmDiscrete, srmContinuous);

TBackendCaps = record
  Kind:            TBackendKind;
  HasTX, HasPA, HasAlex, HasWideband: Boolean;
  HasDitherRandom, HasHWMic, HasPLLStatus: Boolean;
  HasHWGain:       Boolean;   // manual gain / hw AGC (Pluto)
  HasRFBandwidth:  Boolean;
  HasFullDuplex:   Boolean;
  MinSampleRate, MaxSampleRate: Integer;
  SampleRateMode:  TSampleRateMode;
  RatePresets:     array of Integer;  // для оверлея
  MinFreqHz, MaxFreqHz: Double;
end;

Контроллер по FBackend.Caps: гейтит UI, подставляет пресеты samplerate, выбирает band-план (VHF/UHF), путь телеметрии.

4.2 libiio binding

  • FPC-биндинг к libiio (и опц. libad9361) через динамическую загрузку (dynlibs): libiio.so / libiio.dll / libiio.dylib. Приложение запускается без libiio (Pluto просто не появляется в discovery).
  • Новые юниты: IIOBindings.pas (типы/прототипы) + PlutoBackend.pas (TPlutoBackend: scan/connect, RX/TX потоки, gain/freq/rate/bandwidth).

4.3 Discovery в одном окне

  • Расширить записи устройств: Kind: TBackendKind, URI: string (Pluto: usb:1.5.5 / ip:192.168.2.1), Serial: string.
  • TDiscoverThread запускает оба скана параллельно, сливает в один store: HPSDR (FNetwork.Discover) + Pluto (iio_create_scan_context).
  • DeviceForm структурно не меняется: тот же список, лишь метка типа в строке (PlutoSDR usb:1.5.5). BoardTypeName/рендер дополнить веткой Pluto.
  • ConnectDevice диспетчеризует по Kind: создаёт нужный бэкенд перед Connect.

4.4 Ключ per-device настроек

Минимальное изменение: для Pluto синтезировать 6-байтовый ключ из serial (хэш serial → 6 байт) и подавать в существующие LoadDevice/LoadTX/.... Settings.pas не трогаем. (Альтернатива — обобщить ключ до строки; дороже, отложить.)

4.5 Sample rate overlay

  • TSampleRateOverlay получает список значений/имён от контроллера (SetRatePresets). HPSDR — текущие 48k…1536k. Pluto — пресеты > 560 кбит, напр. 768k / 1536k / 2304k / 3072k / 3840k / 5760k.
  • SetSampleRate для Pluto зовёт FBackend (set sampling_frequency + rf_bandwidth), без ConfigureDDCs.

4.6 IQ-конвертация (формат WDSP не меняем)

  • RX: TPlutoBackend читает int16 из iio_buffer, конвертирует 16→24-бит, упаковывает в TDDCIQPacket (seq синтезируется), вызывает OnDDCIQ.
  • TX: SendDUCIQ апсемплит 192k→device-rate полифазным FIR-интерполятором ×L (BuildTxResampler: оконный sinc-ФНЧ, окно Блэкмана ~−58 дБ, TX_FIR_TAPS=16 отводов/фазу, срез TX_FIR_CUT=40 кГц, нормировка на единичное DC-усиление фазы; пресеты рейта кратны 192k → L целое), конвертирует 24→16-бит (>>8, full-scale MSB-aligned), инвертирует Q (симметрично RX) и кладёт в кольцевой FIFO. Линия задержки FTxDlI/Q живёт между вызовами, сбрасывается в StartTX/ClearDUCIQQueue. Отдельный TPlutoTXThread льёт пары в tx-буфер libiio и iio_buffer_push (блокирующий push = пейсинг на device-rate); при underrun — нули (непрерывный full-duplex). Решение rate-mismatch (риск §6.2) — апсемпл в host (RX/TX делят один sampling_frequency).
  • Размер TX-буфера масштабируется от device-rate (модель SoapyPlutoSDR): round(rate/60) → следующая степень 2, кламп [4096..65536] (576k→16384, 1536k→32768). Фикс 4096 был мал для full-duplex выше ~1 Msps → дропы/щелчки. FIFO держит ~4 таких буфера запаса (TxBufPairsForRate, пересчёт в StartTX).
  • Подтверждено по каноничному драйверу (SoapyPlutoSDR): TX full-scale = 32768, MSB-aligned → наш >>8 (24→16) верен; занижения уровня по дизайну нет.

4.7 Усиление / телеметрия

  • RX gain: блок RF AGC (десктоп) — кнопки FAST/SLOW/HYB/MAN (4 режима AD9361, подтверждены на железе gain_control_mode_available) + слайдер manual 0–73 дБ при Caps.HasHWGain; персист per-device. WDSP AGC поверх.
  • RF-AGC в web (управление): дропдаун режима RFagc-F/S/H/M + слайдер GAIN 0–73 дБ (активен только в MAN), виден при BackendCaps.HasHWGain (Pluto подключён). Команды set_rx_gain_mode/set_rx_gainSetRxGainMode/SetRxGain; state-поля rf_agc_visible/rx_gain_mode/rx_gain_db. GUI-web-сервер + headless-демон. Собрано, live-проверка на железе за пользователем.
  • Телеметрия: S-meter (WDSP) — для обоих. fwd/SWR/supply/PLL/overload — скрыть при not Caps.HasPA. RSSI/температура Pluto: ReadTelemetry (backend) → ServicePlutoTelemetry (контроллер, опрос ~2 Гц с фильтром выбросов) → поле 3 статус-бара (Temp/RSSI вместо Supply, т.к. у Pluto нет PA). Две температуры: трансивер AD9361 (ad9361-phy temp0) + Zynq SoC/FPGA (xadc temp0, (raw+offset)*scale/1000) — формат Temp 37/53C в десктопе и web.

4.8 TX-мощность / drive (Pluto)

  • Мощность задаётся ОДИН раз — железной аттенюацией (hardwaregain), а не цифрой WDSP. ApplyWDSPDrive (контроллер): на Pluto SetDriveLevel(1.0) (полная цифра), HPSDR — Pct/100. Раньше drive резался дважды (цифра × атт).
  • PlutoTxAttForDrive — power-linear: дБ = потолок + 20·log10(pct/100) (100% → потолок, 50% → −6, 10% → −20). Старый dB-линейный маппинг от −89.75 уводил низкие % в −80 дБ — из-за этого TUN (TUNLevel 10%) давал −81.8 дБ → тон не слышен. Потолок — FPlutoTxMaxAttDb (дефолт −10 дБ), на приёме −89.75 (≈выкл).
  • TX LO (altvoltage1) гасится по PTT (SetTxLOPowerdown): на приёме powerdown=1 → нет утечки несущей TX на частоте VFO в RX-спектре и нет излучения в простое; на передаче powerdown=0. ApplyTxLO ставит powerdown по FTransmitting (заодно возвращает LO, если стороннее ПО его погасило). RX LO (altvoltage0) — всегда вкл (приём непрерывен, QO-100 full-duplex не ломается). Цена: повторное включение LO запускает калибровку синтезатора AD9361 (единицы–десятки мс) → первый слог SSB может чуть подрезаться.

5. Поэтапный план (фазы)

Фаза 0 — Абстракция (без поведения Pluto)

  • TRadioBackend (база) + TBackendCaps; THPSDRNetwork реализует, заполняет caps. Сменить тип FNetwork/вызовы. Регрессия: HPSDR как раньше.

Фаза 1 — libiio binding + discovery

  • IIOBindings.pas, PlutoBackend.pas (skeleton: scan/connect/disconnect/caps).
  • Kind/URI/Serial в store, объединённый discovery, диспетчер по Kind.
  • UI: оба типа в одном окне. (видим Pluto, ещё без RX).

Фаза 2 — RX-тракт Pluto + VHF/UHF band-план

  • RX-поток libiio → 16→24-бит → OnDDCIQ → WDSP. Set freq (Hz в LO), gain, sample rate, RF bandwidth.
  • VHF/UHF band-план (§3.1) при bkPluto; samplerate-оверлей с Pluto-пресетами.
  • Гейтинг UI по caps. приём на Pluto (спектр/водопад/аудио на 2м/70см/23см/13см).

Фаза 3 — Управление/калибровка

  • RX gain / hw-AGC, транспорт (USB/IP), persist per-device по synthetic-MAC.
  • Имя платы (Pluto/LibreSDR + serial) и телеметрия в статус-бар: RSSI + две температуры (трансивер AD9361 + Zynq SoC xadc, формат Temp 37/53C).
  • TODO: XO ppm-калибровка — корень частотной ошибки опорника Pluto. Сейчас обходится вручную через TxLOOffset QO-100-слота (подстройка под фактический uplink, см. Фаза 5). Правильный фикс — xo_correction AD9361 (правит RX и TX в ppm на всех частотах разом) или внешний 40 МГц GPSDO-реф (золотой стандарт для QO-100). На 2.4 ГГц 1 ppm ≈ 2.4 кГц, поэтому TX без GPSDO плывёт с прогревом.
  • TODO: buffer/timeout-контролы.

Фаза 4 — TX-тракт Pluto

  • SendDUCIQ 24→16 + апсемпл 192k→device-rate + пейсинг (FIFO + push-поток, §4.6).
  • Полифазный FIR-интерполятор вместо линейной интерполяции (§4.6) — образы на кратных 192k давятся ~−58 дБ (раньше только sinc²), чище аплинк, громкость не меняется.
  • TX-буфер по sample-rate (§4.6) — устранены щелчки/дропы на full-duplex >1 Msps.
  • Управление мощностью через TX-аттенюацию (ADI-модель), маппинг power-linear
    • полная цифра WDSP (§4.8). Без PA → нет drive-байта/SWR/Alex/keyer.
  • TX LO powerdown по PTT (§4.8) — нет утечки несущей TX в RX-спектре в простое.
  • Проверено на железе (передача QO-100 через спутник, downlink виден в full-duplex self-monitor; SSB-ориентация и уровни в норме).
  • TODO: опц. headroom 6 дБ для FM (full-scale const-envelope → нелинейность ЦАП); опц. зануление IQ на приёме. Опц. поддержка F5OEO-прошивки (PTT внешнего PA через hardwaregain=40) — отложено, нужно только под конкретное железо.

Фаза 5 — Режим QO-100

  • Калибровка LNB по маяку (beacon lock) — стабилизация дрейфа.
  • Hardware full-duplex + display-duplex (SetDuplex доведён: живой KeepRXDuringTX; авто-вкл при входе в QO-100 — слышим свой downlink).
  • Раздельные RX/TX LO (§3.2): QO-100 = XVTR-слот с флагом FullDuplexTx + TxLOOffset; XvtrTranslateTX даёт TX-LO = visibleTxLOOffset (без LOError). RX-путь (LNB LO + beacon-дрейф) не тронут. Дисплей/VFO в downlink-терминах.
  • Отдельная страница «QO-100» в SettingsForm (LNB LO / транспондер-offset / границы downlink / RX gain / TX-потолок); зарезервирован последний XVTR-слот (QO100_SLOT), не засоряет таблицу трансвертеров openHPSDR.
  • TX↔RX связка с фиксированным офсетом: TX-VFO в downlink-домене, XvtrTranslateTX = visible TxLOOffset (≈8089.5 МГц) — крутишь приём, аплинк едет следом, офсет держится сам (через общий VFO). Split — через VFO-B.
  • Аудио self-monitor («слышу себя со спутника»): кнопка RX MUTE в TX-панели рядом с DUP — видна только Pluto + full-duplex (QO-100). По умолчанию ON (RX-аудио глушится на TX, как раньше); OFF → RX-аудио идёт на колонки и во время TX (свой downlink через транспондер, ~250 мс). Реализация: гейт WDSPEngine (not FTXActive or (FKeepRXDuringTX and FMonitorRXDuringTX)) + флаг FRxMuteOnTx в контроллере, персист в глобале (rx_mute_on_tx). В web: toggle-кнопка RX MUTE рядом с BCN, видна по InQO100, подсветка по rx_mute_on; команда set_rx_muteSetRxMuteOnTx. GUI-web-сервер + headless-демон.
  • Bandplan-оверлей NB-транспондера (BandPlanOverlay.pas): полоска внизу спектра (над ruler) с сегментами CW ONLY / NB DIGI / DIGI / SSB / MIXED MODES + маяки CW/PSK/EXP. Модель VfoOverlay (кэш-битмап + const-alpha), CPU и GL, виден по InQO100. DPI-устойчив (высота и шрифт по Screen.PixelsPerInch).
  • TODO (визуальный self-monitor / навигация по транспондеру):
    • Индикатор «не громче маяка» — сравнение уровня своего downlink с маяком (этикет QO-100); амплитуда маяка уже известна декодеру.
    • RIT/XIT-клариффайер — опц., на геостационаре нужен редко.
  • Проверено на железе (QSO через QO-100: TX uplink 2400 + RX downlink 739.5 МГц, full-duplex self-monitor). Нюанс калибровки: TxLOOffset пришлось подстроить руками под фактический uplink — это ошибка опорника Pluto (TCXO), а не геометрия (геом. offset точно 8089.5 МГц). Постоянное решение — XO ppm / GPSDO, см. Фаза 3 TODO.
  • ОТВЕРГНУТО: TX-маркер на RX-спектре в DUP — в обычной работе RX=TX (один VFO), маркер ложится на RX-курсор, информации не даёт; полезен только в split (редко).

Фаза 6 — Полировка (в основном)

  • Web/CAT-фронтенды (команды уже агностичны) + гейтинг полей; демон ewsdrd.lpr (discovery/connect Pluto headless, beacon-lock @10 Гц); кросс-платформенная сборка с/без libiio.
  • Проброс в web RF-AGC (gain/режимы) и QO-100 RX MUTE (self-monitor) — см. §4.7 / Фаза 5. Доки актуализируются по ходу.

Сопутствующие исправления (найдены при тесте Pluto VHF/UHF FM, но баги общие):

  • Восстановление per-band FM-настроек на старте (Settings.LoadDevice): bulk- загрузчик бэндов читал только freq/mode/filter/AGC/CTun/center/span и пропускал FM-поля (fmsq_on/fmsq_level/ctcss_*/fmstep_*/fmrpt_*), хотя SaveBand их пишет, а LoadBand читает. Итог: SQL/CTCSS/шаг/репитер-сдвиг сохранялись на выходе, но сбрасывались в дефолт при следующем запуске (смена бэндов работала — идёт через in-memory band-cache). Затрагивало все бэкенды, не только Pluto.

6. Риски и открытые вопросы

  1. 2 м / 70 см ниже 325 МГц → требуется freq-range мод прошивки Pluto. Документировать; возможно гейтить эти кнопки при стоковой прошивке.
  2. TX sample-rate mismatch (192k WDSP vs ≥521k Pluto) — решено host-апсемплом 192k→device-rate в SendDUCIQ (целочисленно, пресеты кратны 192k). Качество образов — полифазный FIR-интерполятор (окно Блэкмана ~−58 дБ). Щелчки на full-duplex >1 Msps — TX-буфер масштабируется от rate (модель SoapyPlutoSDR).
  3. QO-100 display-duplex — hardware full-duplex есть (Фаза 4), остаётся довести SetDuplex (водопад RX во время TX); проверить латентность/буферы libiio.
  4. LNB дрейф — калибровка LnbPpm по маяку (ручная/полуавтомат).
  5. Ключ настроек — synthetic-MAC из serial (быстро) vs обобщение до строки.
  6. libiio как зависимость — динамическая загрузка, поведение при отсутствии.
  7. USB-латентность/пропускная — реальный максимум sample-rate по USB2.

7. Затрагиваемые файлы (ориентир)

Файл Изменение
RadioController.pas тип FBackend, диспетчер по Kind, гейтинг по caps, QO-100 трансляция
HPSDRNetwork.pas реализовать TRadioBackend + заполнить caps
IIOBindings.pas (нов) биндинг libiio (dynamic)
PlutoBackend.pas (нов) TPlutoBackend (scan/connect/RX/TX/gain/rate/bw)
DeviceStore.pas Kind/URI/Serial в записях
DeviceForm.pas метка типа в списке (структурно без изменений)
SampleRateOverlay.pas пресеты из контроллера вместо констант
BoardUtils.pas имя Pluto, VHF/UHF band-план
Settings.pas без изменений (synthetic-MAC) / QO-100 persist
MainForm.pas / ewsdrd.lpr гейтинг виджетов по caps, wiring, QO-100 UI