mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
PanafallPanel.pas: view (CPU/GL), контролы стека (спектр/линейка/сплиттер/ водопад/пан-зум/кнопки зума), геометрия Layout, drag сплиттера и мышь пан/зум-полосы. MainForm работает через алиасы (FSpecView/PbSpectrum/… = FPan.*), размеры областей — FPan.SpectrumWidth/SpectrumHeight/WaterfallHeight. Семантика мыши/флаги/тик пока в MainForm (фаза B). GL-аудит per-instance пройден (глобальных GL-ресурсов нет). План: doc/SLICES_PLAN.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
383 lines
30 KiB
Markdown
383 lines
30 KiB
Markdown
# Мультислайсы и панадаптеры (как во FlexRadio)
|
||
|
||
Статус: **Этапы 1 + 2 РЕАЛИЗОВАНЫ и ЗАКОММИЧЕНЫ** (ветка `feature/multislice`,
|
||
коммит `1527b0f`; проверено на эфире) + UI-полировка флагов (`30a6616`,
|
||
`3049f91`, `a3ac459`).
|
||
|
||
**2026-07-10: Этап 3 ПЕРЕПРОЕКТИРОВАН.** Вместо старой идеи «off-span слайс с
|
||
edge-маркером» принята Flex-модель: **аппаратный DDC = отдельный панадаптер**
|
||
(свой спектр+водопад), а слайсы ВСЕГДА живут внутри какого-то панадаптера.
|
||
Старый черновик (edge-маркеры + slices-рейл) перенесён в «Отвергнутые
|
||
альтернативы» в конце файла. Реализация Этапа 3 не начата.
|
||
|
||
---
|
||
|
||
## Что реализовано (Этапы 1 + 2)
|
||
|
||
- **WDSPEngine** — софтовый fan-out: `TSliceChan` + `FSlices[0..MAX_SLICES-1]`
|
||
(`MAX_SLICES=6`), свои WDSP-каналы (`SLICE_CHAN_BASE=2`), `ProcessSlices` в
|
||
DSP-потоке (свежая копия IQ на канал, свой NCO/mode/filter/AGC/NR/NB/SNB/ANF +
|
||
S-метр → `OnSliceAudio`), `FSliceLock` сериализует add/remove/set vs DSP,
|
||
переоткрытие после смены sample rate.
|
||
- **RadioController** — `TCtrlSlice` (абс. `TargetHz`, буква, свой `TAudioOutput`/
|
||
звуковуха), `Add/Remove/SetSlice*`, `RefreshSliceShifts` держит слайсы на
|
||
`TargetHz` при смене центра, гейт `SliceFitsCapture` (вне полосы = Этап 3).
|
||
Буквы доп. слайсов начинаются с `'B'` (A = главный приёмник).
|
||
- **UI** — по флагу `TVfoOverlay` на слайс (буква-бейдж, «×», fly-out MODE/DSP/AUD,
|
||
vol/mute/split/TX, S-метр), маркеры полосы фильтра слайса в **обоих** рендерерах
|
||
(GL `DrawSliceFilterMarkers` + CPU), drag-ретюн несущей, колесо над флагом.
|
||
Ctrl+ЛКМ по спектру = добавить слайс. Флаг главного (A) виден когда скрыта
|
||
левая панель ЛИБО есть слайсы B+ (синхронизируется с правками с левой панели).
|
||
- **UI-полировка флагов** (`30a6616`, `3049f91`, `a3ac459`):
|
||
- `MainForm.LayoutFlags` — единый packing всех флагов (A+B+) в один ряд;
|
||
идентификация уехавшего флага буквой по центру полосы (`DrawBandLetter`
|
||
CPU + `DrawBandLetterGL` GL с кэшем текстур букв); палитра
|
||
`VfoOverlay.SliceColor(L)`; кнопки через `FlatButton.PaintFlatButton`;
|
||
полная тематизация из `TAppTheme`.
|
||
- **Перф** — S-метр развязан с кэшем «хрома» флага (`FMeterDirty` + meter-only
|
||
`RefreshMeter` через `EnsureRendered`); `InvalidateVfoOverlay` не дёргает
|
||
глобальный перезалив оверлеев на тик метра.
|
||
|
||
Остаток Этапов 1-2 (не сделано): персист слайсов в `Settings` (список +
|
||
устройство); per-slice AGC-Top (сейчас общий с главным); полноценный
|
||
мультислайс-TX (пока TX только главный).
|
||
|
||
---
|
||
|
||
## Модель FlexRadio (итог разбора 2026-07-10)
|
||
|
||
Во Flex **две независимые сущности**, и это ключ ко всему дизайну:
|
||
|
||
- **Панадаптер (panafall)** — спектр+водопад. Самостоятельный объект: свой
|
||
широкополосный захват в железе, свой центр/span/зум, свои настройки водопада.
|
||
- **Слайс** — узкополосный приёмник, ВСЕГДА прикреплён к какому-то панадаптеру
|
||
и рисуется на нём флагом. Слайс никогда не имеет собственного спектра.
|
||
Тянешь слайс за край — панорама скроллится за ним.
|
||
|
||
Хочешь слушать другой участок — открываешь **второй панадаптер** (радио выделяет
|
||
ещё один захват) и создаёшь слайсы в нём. Лимиты: 6400 — 2 пана/2 слайса,
|
||
6600 — 4/4, 6700 — 8/8.
|
||
|
||
**Второй приёмник (второй АЦП)** у Flex называется SCU (Spectral Capture Unit):
|
||
полный тракт «антенный вход → АЦП → захват». 6600/6700 — два SCU, 6400 — один.
|
||
У панадаптера НЕТ переключателя «RX1/RX2» — есть селектор **антенны**
|
||
(ANT1/ANT2/RXA/RXB/XVTR), и радио само сажает панадаптер на SCU под выбранную
|
||
антенну. Слайсы наследуют антенну своего пана. Diversity (когерентное сложение
|
||
двух SCU) — отдельный режим поверх.
|
||
|
||
**Окна:** все панафоллы тайлятся ВНУТРИ одного главного окна (сплиты с
|
||
перетаскиваемыми разделителями). Вынос панафолла в отдельное OS-окно штатный
|
||
SmartSDR НЕ умеет — наш pop-out будет улучшением поверх Flex-модели.
|
||
|
||
---
|
||
|
||
## Итоговая модель EWSDR
|
||
|
||
```
|
||
Панадаптер (спектр+водопад; свой захват IQ; свой ADC-источник; свой span/зум/
|
||
│ палитра/wf-AGC; свой WDSP-анализатор)
|
||
└─ Слайсы (флаги внутри панорамы; софт-fan-out IQ ЭТОГО панадаптера;
|
||
свой WDSP-канал/режим/фильтр/AGC/DSP/звуковуха — уже реализовано)
|
||
```
|
||
|
||
Правила:
|
||
1. Слайс всегда принадлежит панадаптеру (`PanId`). Понятия «off-span слайс»
|
||
больше нет: хочешь слайс вне захвата — создай панадаптер там.
|
||
2. Пан 0 (главный) существует всегда; его «слайс A» — текущий главный тракт
|
||
(WDSP-канал 0). Панадаптеры N>0 живут целиком на слайс-механике Этапа 1
|
||
(каналы `SLICE_CHAN_BASE+`), при создании пана автоматически создаётся
|
||
один слайс в его центре (поведение Flex).
|
||
3. ADC-источник — свойство **панадаптера**, не слайса (бейдж `A1`/`A2` в шапке).
|
||
В отличие от Flex показываем именно АЦП, а не антенну: на HPSDR-платах
|
||
маппинг «вход→АЦП» жёсткий, антенная абстракция не нужна.
|
||
4. Буквы слайсов — глобальный пул A..G через все паны (как во Flex A-H):
|
||
буква = личность слайса, пан = место жительства.
|
||
5. Возможности бэкенда описывает запись capabilities (см. таблицу) — UI по ней
|
||
прячет/блокирует кнопки, а не хардкодит «if Pluto».
|
||
|
||
Матрица бэкендов:
|
||
|
||
| Бэкенд | Панадаптеров | Незав. частоты панов | АЦП | Примечание |
|
||
|-------------------------|--------------|----------------------|-----|------------|
|
||
| HPSDR 1 ADC (Hermes) | до 4 (DDC) | да | 1 | ADC-селектор скрыт |
|
||
| HPSDR 2 ADC (Angelia/Orion/Saturn) | до 4 (DDC, DDCBase=2) | да | 2 | селектор A1/A2 на пан |
|
||
| Pluto (обычный) | 1 | — | 1 | как сейчас, ничего не меняется |
|
||
| Pluto+ (2r2t) | 2 | **нет — общий LO** | 2 | пан RX2 частотно связан с RX1 |
|
||
|
||
```
|
||
openHPSDR (Protocol 2, MAX_DDCS=80):
|
||
ADC0 ─┬─ DDC2 (широкий) → Пан 0 → анализатор + слайсы A,B…
|
||
└─ DDC3 (узкий) → Пан 1 → анализатор + слайсы C,D…
|
||
ADC1 ──── DDC4 → Пан 2 (другая антенна) → …
|
||
• Каждый DDC тюнится куда угодно, свой rate. DDC0/1 заняты PureSignal
|
||
на Angelia/Orion/Saturn (DDCBase=2, HPSDRNetwork.pas:1485).
|
||
|
||
Pluto+ (AD9363 2r2t, libiio):
|
||
RX LO ─┬─ RX1 → псевдо-DDC0 → Пан 0
|
||
(один!)└─ RX2 → псевдо-DDC1 → Пан 1 (та же частота/rate, другая антенна)
|
||
• Оба канала на ОДНОМ LO/rate, фазокогерентны (база под diversity/пеленгацию).
|
||
• Обычный Pluto: один RX разведён на плате — Пан 1 недоступен.
|
||
```
|
||
|
||
---
|
||
|
||
## Этап 3 (переработан): «DDC = панадаптер»
|
||
|
||
Подэтапы упорядочены так, чтобы каждый собирался и работал сам по себе.
|
||
3.0 — чистый рефакторинг без новых фич, дальше функциональность наращивается.
|
||
|
||
### 3.0 — TPanafallPanel: выделить панадаптер из MainForm
|
||
|
||
**Статус 2026-07-10: фаза A СДЕЛАНА (собирается, стартует; на экране не
|
||
проверено).** Создан `PanafallPanel.pas`: панель владеет view (CPU/GL),
|
||
контролами (спектр/линейка/сплиттер/водопад/пан-зум/кнопки зума), полной
|
||
геометрией стека (`Layout`), механикой сплиттера и мышью пан/зум-полосы.
|
||
MainForm держит алиасы (`FSpecView`, `PbSpectrum`, …) на члены `FPan` — весь
|
||
остальной код не тронут; размеры областей читаются из
|
||
`FPan.SpectrumWidth/SpectrumHeight/WaterfallHeight`. Семантика мыши
|
||
спектра/водопада, флаги слайсов, оверлеи, тик — ПОКА в MainForm (фаза B).
|
||
**GL-аудит per-instance ПРОЙДЕН**: в `SpectrumViewOpengl`/`WaterfallViewOpengl`
|
||
все текстуры (`FGridLabelTex`…`FLetterTex`, `FSliceTex`, `FTexture`,
|
||
`FMarkerTex`) и LUT водопада — поля экземпляра, глобальных/class var GL-ресурсов
|
||
нет; палитры — read-only const (шарить безопасно).
|
||
|
||
Самый большой и самый важный кусок. Сейчас панадаптер размазан по MainForm:
|
||
`FSpecView: TSpectrumView` создаётся и прошивается ~20 свойствами напрямую
|
||
(`MainForm.pas:1018-1035`), канва `PbSpectrum: TControl` (TOpenGLControl,
|
||
`MainForm.pas:2220`), `FPanZoomBar` (`MainForm.pas:312`), мышиные хендлеры
|
||
`PbSpectrumMouse*` (`MainForm.pas:439-445`), `LayoutFlags`, тик обновления.
|
||
|
||
Сделать composite `TPanafallPanel` (новый модуль `PanafallPanel.pas`,
|
||
наследник TCustomControl/TPanel), который владеет:
|
||
|
||
- канвой (`TOpenGLControl` или `TPaintBox` по `USE_OPENGL` — как сейчас);
|
||
- своим `TSpectrumView`/`TSpectrumViewOpenGL`;
|
||
- своим `TPanZoomBar` и линейкой;
|
||
- коллекцией флагов слайсов этого пана (`FVfoOverlay` пана + `FSliceFlags`)
|
||
и `LayoutFlags` (packing переезжает сюда);
|
||
- мышиной логикой канвы (тюн/drag/колесо/Ctrl+ЛКМ/drag маркера слайса);
|
||
- шапкой пана (см. 3.3) — в пане 0 шапка скрыта до появления пана 1.
|
||
|
||
Наружу: события `OnTune(PanId, Hz)`, `OnAddSlice(PanId, Hz)`, `OnSlice*`
|
||
(существующие слайс-события + PanId), `OnPanClose/OnPanPopOut/OnPanADC`;
|
||
входные сеттеры состояния (центр/span/mode/wf-настройки) — то, что сейчас
|
||
раздаётся FSpecView напрямую.
|
||
|
||
MainForm после рефакторинга держит `FPans: array of TPanafallPanel`
|
||
(пан 0 всегда), общий тик раздаёт кадры по панам, весь остальной код
|
||
(левая панель, FreqDisplay, контроллер) работает с «активным слайсом» —
|
||
механика `FActiveFlag/CurSliceId` уже есть, добавляется лишь `ActivePan`.
|
||
|
||
Критерий готовности 3.0: поведение приложения ИДЕНТИЧНО текущему
|
||
(один пан, все фичи слайсов работают), diff в MainForm — только вынос.
|
||
|
||
Отдельная задача внутри 3.0 — **аудит GL-ресурсов** `SpectrumViewOpengl` на
|
||
per-instance: каждое окно/канва = свой GL-контекст, текстуры между контекстами
|
||
НЕ шарятся. Проверить: текстуры водопада, LUT палитры, кэш текстур букв
|
||
(`DrawBandLetterGL`), текстуры флагов (`FSliceTex`), шейдеры/списки — ничего
|
||
static/глобального. (CPU-LUT палитры общий — это ок.)
|
||
|
||
### 3.1 — Бэкенд HPSDR: per-DDC частота/rate/ADC + роутинг
|
||
|
||
Плита готова ~на 70 %, дельты точечные:
|
||
|
||
1. **Per-DDC частота в High Priority.** `SendFullHP`
|
||
(`HPSDRNetwork.pas:1552`) пишет частотное слово только активного DDC
|
||
(`Buf[9 + DDCBase*4]`). Сделать цикл по активным DDC:
|
||
`Buf[9+ddc*4] := FreqToPhaseWord(FDDCFreq[ddc])`. Контроллер держит
|
||
`FDDCFreq: array[0..MAX_DDCS-1] of Double`, обновляет при ретюне пана.
|
||
2. **Per-DDC rate/ADC/enable в `ConfigureDDCs`** (`HPSDRNetwork.pas:1460`):
|
||
сейчас один rate/ADCSource на все N. Принимать массив записей
|
||
`(Enabled, RateKHz, ADCSource)` — протокол это позволяет
|
||
(`DDCConfig[ddc*6+0]=ADCSource`, rate там же per-DDC).
|
||
3. **Роутинг IQ по панам.** `RadioController.OnDDCIQ`
|
||
(`RadioController.pas:828`) сейчас гейтит `DDCIndex = FActiveDDC`
|
||
(`:875`), остальное выбрасывает. Заменить на маппинг
|
||
`DDCIndex → PanId`: пакет пана 0 — в существующий путь, пакеты
|
||
панов N>0 — в `WDSPEngine.PushPanIQ(PanId, Data)` (см. 3.2).
|
||
4. **Аллокатор DDC**: пул hw-индексов от `DDCBase+1` (DDCBase=2 на
|
||
PureSignal-платах, `HPSDRNetwork.pas:1485`), захват при создании пана,
|
||
освобождение при закрытии, слот-стабильность как у слайсов.
|
||
5. **Rate слайс-панов**: выбор из 192/384/768 кГц (дефолт 384). Бюджет
|
||
Ethernet: поток DDC ≈ rate × 6 байт (24-бит I+Q) → 384 кГц ≈ 18 Мбит/с;
|
||
3 доп. пана + главный на 1.536 МГц укладываются даже в 100 Мбит с запасом,
|
||
но кап «до 4 панов» держим жёстко (и по CPU тоже).
|
||
|
||
Ретюн пана N>0 = запись `FDDCFreq[ddc]` + `SendFullHP` (без реконфига DDC);
|
||
смена rate/ADC = `ConfigureDDCs` (как сейчас при смене sample rate).
|
||
|
||
### 3.2 — WDSPEngine: анализатор и IQ-тракт на панадаптер
|
||
|
||
Сейчас: анализаторы `RX_DISP_ID=0` / `TX_DISP_ID=1` (`WDSPEngine.pas:133`),
|
||
создание `XCreateAnalyzer` (`:1140`), настройки+span-clip
|
||
`ApplyRXAnalyzerSettings` (`:1163-1211`), снапшот спектра
|
||
`Spectrum0(1, RX_DISP_ID, …)` из DSP-потока (`:1597`), выдача пикселей
|
||
через `FOnSpectrum` (`:2850`). Слайсы едят копию ГЛАВНОГО аккумулятора
|
||
в `ProcessSlices`.
|
||
|
||
Дельты:
|
||
|
||
1. **Пан-анализаторы**: `PAN_DISP_BASE = 8` (не 4/5 — они заняты в ветке
|
||
`feature/qo100-zoom-panel`, при будущем merge не столкнёмся);
|
||
`XCreateAnalyzer(PAN_DISP_BASE+PanId)` при создании пана,
|
||
`ApplyPanAnalyzerSettings(PanId)` — параметризованная копия
|
||
`ApplyRXAnalyzerSettings` (свой rate, свой span-clip/zoom, свои
|
||
FFT/avg/detector — можно стартовать с общих настроек, разделить позже).
|
||
2. **Per-pan аккумулятор**: `TPanChan = record` (`FPanAccI/Q`, `FPanAccPos`,
|
||
rate, disp id, активные слайсы). `PushPanIQ(PanId, Data)` из сетевого
|
||
потока кладёт в аккумулятор пана; по накоплению блока в DSP-потоке:
|
||
копия → `Spectrum0(1, PAN_DISP_BASE+PanId, …)` + `ProcessSlices` по
|
||
слайсам ЭТОГО пана.
|
||
3. **Слайс знает пана**: в `TSliceChan` добавляется `PanId`; NCO shift
|
||
слайса = `TargetHz − PanCenterHz`; rate канала слайса = rate его пана
|
||
(для панов N>0 это 192-768 кГц — WDSP-каналу дешевле, чем полный rate!).
|
||
`RefreshSliceShifts` обобщается на «центр пана», гейт `SliceFitsCapture` —
|
||
на «span пана».
|
||
4. **Выдача пикселей**: `FOnPanSpectrum(PanId, Pixels, N)` (или существующий
|
||
колбэк + PanId), MainForm-тик раздаёт по `FPans[PanId]`.
|
||
5. **Пул WDSP-каналов** общий: 0=RXA (слайс A пана 0), 1=TX, слайсы 2..7
|
||
(`SLICE_CHAN_BASE`, `MAX_SLICES=6`) — на все паны суммарно. Этого хватает:
|
||
6 доп. слайсов + главный = 7 приёмников.
|
||
|
||
Блокировки: `FSliceLock` уже сериализует слайсы vs DSP; добавить такую же
|
||
дисциплину для add/remove пана (сетевой поток пишет в аккумулятор пана —
|
||
создание/удаление пана только под локом, как переоткрытие слайсов при
|
||
ChangeSampleRate).
|
||
|
||
### 3.3 — UI: второй панадаптер в главном окне
|
||
|
||
- **Стек панов**: область спектра = вертикальный стек `TPanafallPanel` с
|
||
`TSplitter` между ними (Flex-стиль). Пан 0 сверху. Высоты в персист.
|
||
- **Шапка пана** (тонкая полоска над спектром, темизирована как флаги):
|
||
частота центра · диапазон · rate · бейдж ADC `A1/A2` (3.4) ·
|
||
кнопки `⧉` (pop-out, 3.5) и `×` (закрыть; у пана 0 нет). Шапка пана 0
|
||
появляется только когда панов ≥ 2.
|
||
- **Добавить пан**: кнопка `+` в шапке/тулбаре (только если
|
||
`caps.MaxPans > 1`): диалог/дефолт — частота (ввод или «текущая ±»),
|
||
rate, ADC. Создание пана автоматически создаёт его первый слайс в центре.
|
||
- **Активный пан** = пан активного слайса. Клик по канве пана делает его
|
||
слайс активным (существующая механика `FActiveFlag`), левая панель /
|
||
FreqDisplay / CAT правят активный слайс — как сейчас.
|
||
- **Ctrl+ЛКМ** на любом пане = добавить слайс в этот пан (буква из
|
||
глобального пула).
|
||
- **Тик/FPS**: общий таймер, кадры раздаются каждому пану; FPS-гейт из
|
||
перф-работ применяется per-pan. Свёрнутый/скрытый пан кадры не рендерит.
|
||
- **TX**: TX-бейдж на флаге работает с любого слайса любого пана — DUC
|
||
независим от DDC, TX-частота = частота слайса. (Полный мультислайс-TX —
|
||
отдельная старая задача, тут ничего нового не ломаем.)
|
||
|
||
### 3.4 — ADC-селектор панадаптера
|
||
|
||
- `TPanafall.ADCSource: 0/1`; в шапке пана селектор/бейдж `A1`/`A2`.
|
||
Показывается только при `NumADCs=2` (детект платы уже есть — `NumADCs`
|
||
в ConfigureDDCs-пути).
|
||
- Переключение = `ConfigureDDCs` с новым ADCSource для DDC этого пана.
|
||
- Пан 0 тоже можно перекинуть на ADC1 — это закрывает сценарий старой ветки
|
||
`feature/rx2-support` (вторая антенна) бесплатно, как частный случай.
|
||
- Diversity (sync-DDC, оба АЦП когерентно в один канал) — НЕ здесь; протокол
|
||
умеет, дизайну панов не мешает, отдельная будущая фича.
|
||
|
||
### 3.5 — Pop-out: панадаптер в отдельном OS-окне
|
||
|
||
Улучшение поверх Flex (SmartSDR так не умеет). Кнопка `⧉` в шапке.
|
||
|
||
- `TPanFloatForm` — простая TForm-обёртка, в которую переезжает
|
||
`TPanafallPanel`; `×` окна возвращает пан в стек главного окна.
|
||
- **Риск №1 — GL + reparent**: перенос живого `TOpenGLControl` между
|
||
окнами на Qt6/Wayland может убить контекст. Безопасный путь: НЕ
|
||
репарентить канву, а пересоздать вьюху в новом окне (панель отдаёт
|
||
state: центр/span/wf-историю можно не сохранять — водопад начнётся
|
||
заново; слайсы/флаги живут в контроллере и переподключаются).
|
||
Сначала попробовать честный reparent, fallback — пересоздание.
|
||
- **Риск №2 — общие GL-ресурсы**: закрывается аудитом из 3.0.
|
||
- Персист: флаг «пан N в отдельном окне» + геометрия окна.
|
||
|
||
### 3.6 — Pluto+ (2r2t): второй пан на втором антенном входе
|
||
|
||
Опционально, после 3.0-3.3. Обычного Pluto НЕ касается (там один RX разведён).
|
||
|
||
- **Детект**: число RX-каналов в iio-контексте (`voltage0/1` + `voltage2/3`
|
||
у `cf-ad9361-lpc`); прошивка должна быть с `adi,2rx-2tx-mode-enable`
|
||
(на Pluto+ обычно так). Если каналов 2 → `caps.MaxPans=1`, как сейчас.
|
||
- **PlutoBackend** (`PlutoBackend.pas:222-236`): сейчас RX-поток пушит
|
||
`FOnDDCIQ(0, pkt)`. В 2r2t буфер отдаёт 4 канала интерливом —
|
||
деинтерлив на два пакета → `FOnDDCIQ(0, …)` и `FOnDDCIQ(1, …)`.
|
||
Дальше это обычные псевдо-DDC, роутинг из 3.1/3.2 работает без изменений.
|
||
- **Частотная связка (главное отличие)**: оба RX на одном LO и одном rate.
|
||
`caps.IndependentPanFreq = False` → пан 1 создаётся ТОЛЬКО с центром/rate
|
||
пана 0; ретюн любого пана ретюнит общий LO (двигает оба); в шапке пана 1 —
|
||
признак связки (`🔗 LO`), контролы центра либо скрыты, либо честно тюнят
|
||
общий LO. Зум/span-clip у каждого пана свой (анализаторы независимы).
|
||
- **Пропускная способность**: ×2 потока. Через гигабитный Ethernet Pluto+
|
||
(наш libiio network context) проходит свободно; на USB2 пришлось бы резать
|
||
rate — не наш кейс.
|
||
- **QO-100 beacon-lock**: лок правит общий LO — оба пана едут вместе,
|
||
инвариант «всё стоит на месте» сохраняется, конфликта нет.
|
||
- **Бонус на потом**: каналы фазокогерентны — готовая база под честный
|
||
diversity (сложение с фазовращателем) и пеленгацию. Не в этом этапе.
|
||
- TX2 (второй выход, общий TX LO) — вне этого плана.
|
||
|
||
---
|
||
|
||
## Персист (собрать в один заход после 3.3)
|
||
|
||
В `Settings.pas`:
|
||
- список панов: центр, rate, ADC, span/зум, высота в стеке, pop-out+геометрия;
|
||
- список слайсов: PanId, TargetHz, mode/filter/AGC/DSP, звуковуха
|
||
(DeviceIndex + имя для повторного матчинга), громкость/mute;
|
||
- восстановление на старте: сначала паны (ConfigureDDCs), потом слайсы.
|
||
Это закрывает и старый долг «персист слайсов» из Этапа 2.
|
||
|
||
---
|
||
|
||
## Оговорки / риски
|
||
|
||
- **CPU**: слайс на пане N>0 дешевле слайса на пане 0 (WDSP-канал жуёт
|
||
192-768 кГц вместо полного rate). Дорогое место — рендер: каждый пан =
|
||
свой спектр+водопад; FPS-гейт и перф-дисциплина из [[project_perf_profiling]]
|
||
обязательны per-pan.
|
||
- **Не портить входной IQ-буфер** между каналами (fexchange0 in-place) —
|
||
правило Этапа 1, действует и для пан-аккумуляторов (копия до Spectrum0,
|
||
как `FSpecBuf`, см. `WDSPEngine.pas:235`).
|
||
- **Старт/стоп WDSP-каналов на лету** — задержки RXA стоп/старт
|
||
(`WDSPEngine.pas:~230`), создание/закрытие пана держать вне аудио-глитчей.
|
||
- **Ethernet**: кап 4 пана суммарно; следить за суммарным rate при выборе
|
||
rate панов.
|
||
- **DDCBase=2** на PureSignal-платах: доп. панам достаются DDC3+ — при
|
||
4 панах на Angelia/Orion/Saturn это DDC2..5, ок.
|
||
- **Qt6/Wayland + GL**: reparent канвы (3.5) и per-instance ресурсы (3.0) —
|
||
два главных технических риска всего этапа; аудит 3.0 делать первым.
|
||
- Смена sample rate главного пана переоткрывает его слайсы (уже реализовано) —
|
||
панов N>0 это касаться не должно (их rate независим).
|
||
|
||
## Открытые вопросы (решить по ходу, не блокеры)
|
||
|
||
- CAT: что репортить как VFO A/B при активном слайсе на пане N>0
|
||
(сейчас CAT смотрит на главный тракт; вероятно «активный слайс = VFO A»).
|
||
- Перетаскивание слайса МЕЖДУ панами (drag флага на другой пан): вкусно,
|
||
но не в первую итерацию — сначала слайс живёт в своём пане (кламп по
|
||
span пана, как сейчас по захвату).
|
||
- Авто-promotion (слайс уехал за край пана → предлагать новый пан) — нет,
|
||
осознанно: только явное создание пана (решение «сначала статика» остаётся).
|
||
- Настройки водопада (палитра/AGC/avg) per-pan или глобально — стартуем с
|
||
наследования глобальных, разделим по запросу.
|
||
|
||
## Отвергнутые альтернативы (для истории)
|
||
|
||
Старый черновик Этапа 3 предлагал «off-span слайсы»: слайс вне видимой
|
||
панорамы на своём hard-DDC, представленный edge-маркером у края спектра
|
||
(цветная буква + ◄/► + частота) и/или «slices-рейлом» (полоса-менеджер с
|
||
чипами слайсов). Отвергнуто 2026-07-10 в пользу Flex-модели «DDC =
|
||
панадаптер»: off-span слайс без своего спектра слепой (не видно обстановку
|
||
вокруг), сущности расползаются (слайс с DDC ≠ слайс без DDC), а рейл дублирует
|
||
флаги. Идея рейла может вернуться позже как компактный обзор всех слайсов —
|
||
но не как замена панадаптеру. Палитра «цвет+буква = личность слайса во всех
|
||
местах» из черновика — остаётся в силе.
|
||
|
||
## Связанные заметки памяти
|
||
|
||
- project_slices_plan (статус), project_pluto_integration, project_perf_profiling
|
||
- project_spectrum_zoom (GetViewWindow / span-clip / маппинг мыши)
|
||
- project_qo100_zoom_panel (прецедент доп. анализаторов WDSP, disp id 4/5!)
|
||
- project_lazopengl_component (QOpenGLWidget/контексты на Qt6 — база для 3.5)
|