From 17d0af4910da85f97086a0c9e8b98f4c33f057f4 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 19 Jun 2026 22:08:45 +0300 Subject: [PATCH] docs: update Pluto integration plan for recent TX fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the TX-path work landed this session: - §4.6: polyphase FIR interpolator + sample-rate-scaled TX buffer + SoapyPlutoSDR confirmation of 32768 full-scale. - §4.8 (new): power-linear drive→atten mapping, full WDSP digital level, TX LO powerdown gated by PTT. - Phase 4 status updated. - Related fixes: per-band FM settings (SQL/CTCSS/step/repeater) restore on startup — was dropped by LoadDevice; affects all backends. Co-Authored-By: Claude Opus 4.8 --- doc/PLUTO_INTEGRATION_PLAN.md | 67 +++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/doc/PLUTO_INTEGRATION_PLAN.md b/doc/PLUTO_INTEGRATION_PLAN.md index 045eb15..1b5c8f9 100644 --- a/doc/PLUTO_INTEGRATION_PLAN.md +++ b/doc/PLUTO_INTEGRATION_PLAN.md @@ -239,14 +239,22 @@ end; - **RX:** `TPlutoBackend` читает int16 из `iio_buffer`, конвертирует 16→24-бит, упаковывает в `TDDCIQPacket` (seq синтезируется), вызывает `OnDDCIQ`. -- **TX:** ✅ `SendDUCIQ` апсемплит 192k→device-rate (целочисленно, линейная - интерполяция; пресеты рейта кратны 192k), конвертирует 24→16-бит (`>>8`, - full-scale MSB-aligned), инвертирует Q (симметрично RX) и кладёт в кольцевой - FIFO. Отдельный `TPlutoTXThread` льёт пары в tx-буфер libiio и `iio_buffer_push` +- **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`). TODO: полифазный FIR вместо линейной интерполяции - для лучшего подавления образов на аплинке. + один `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 Усиление / телеметрия @@ -257,6 +265,23 @@ end; `ServicePlutoTelemetry` (контроллер, опрос ~2 Гц с фильтром выбросов) → поле 3 статус-бара (Temp/RSSI вместо Supply, т.к. у Pluto нет PA). +### 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. Поэтапный план (фазы) @@ -283,13 +308,16 @@ end; **Фаза 4 — TX-тракт Pluto** ✅ - ✅ `SendDUCIQ` 24→16 + апсемпл 192k→device-rate + пейсинг (FIFO + push-поток, §4.6). -- ✅ Управление мощностью через TX-аттенюацию (ADI-модель): непрерывный full-duplex - стрим, drive% → `hardwaregain` (потолок `PlutoTxMaxAttDb`, дефолт −10 дБ, - настраиваемый под внешний усилитель); на приёме −89.75 дБ (≈выкл). Маппинг/ - политика — в контроллере (`PlutoTxAttForDrive`/`ApplyPlutoTxAtten`), бэкенд — - тонкий `SetTxAtten`. Без PA → нет drive-байта/SWR/Alex/keyer. -- TODO: проверка на железе (ориентация SSB по Q-инверсии, уровни); полифазный - ресэмпл (см. §4.6); опц. зануление IQ на приёме. +- ✅ **Полифазный 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-спектре в простое. +- TODO: проверка на железе на rate ≥1536k (ориентация SSB по Q-инверсии, уровни); + опц. headroom −6 дБ для FM (full-scale const-envelope → нелинейность ЦАП); опц. + зануление IQ на приёме. Опц. поддержка F5OEO-прошивки (PTT внешнего PA через + `hardwaregain=−40`) — отложено, нужно только под конкретное железо. **Фаза 5 — Режим QO-100** ✅ - ✅ Калибровка LNB по маяку (beacon lock) — стабилизация дрейфа. @@ -307,6 +335,14 @@ end; - Web/CAT-фронтенды (команды уже агностичны) + гейтинг полей; демон `ewsdrd.lpr` (discovery/connect Pluto headless); кросс-платформенная сборка с/без libiio; доки. +**Сопутствующие исправления (найдены при тесте 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. Риски и открытые вопросы @@ -314,8 +350,9 @@ end; 1. **2 м / 70 см ниже 325 МГц** → требуется freq-range мод прошивки Pluto. Документировать; возможно гейтить эти кнопки при стоковой прошивке. 2. ✅ **TX sample-rate mismatch** (192k WDSP vs ≥521k Pluto) — решено host-апсемплом - 192k→device-rate в `SendDUCIQ` (целочисленно, пресеты кратны 192k). Качество - образов — линейная интерполяция; апгрейд до полифазного FIR в бэклоге. + 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` по маяку (ручная/полуавтомат).