- TBackendCaps: MaxPans/IndependentPanFreq (HPSDR 4/True, Pluto 1/False); SetPanDDC/SetPanDDCFreq — виртуальные no-op в TRadioBackend - HPSDRNetwork: ConfigureDDCs → кэш главного DDC + RebuildDDCSpecific (главный + активные пан-DDC, каждый со своим rate/ADC); SendFullHP пишет частотные слова всех активных пан-DDC; хелпер DDCBaseIndex; сброс пан-состояния в Connect - RadioController: MAX_PANS=4, PAN_DDC_RATES 192/384/768; AddPanDDC/ RemovePanDDC/SetPanDDCFreq/PanDDC* (аллокатор hw-индексов от FActiveDDC+1, обратный маппинг FDDCPanMap); OnDDCIQ роутит пан-DDC → PushPanIQ; паны гасятся в StopRunning, пул сбрасывается в StartRunning - WDSPEngine: PushPanIQ — заглушка-дроп (аккумулятор/анализатор — этап 3.2) - фикс: StartRunning не включал Saturn (BoardType 10) в список плат с DDCBase=2 — на Saturn гейт FActiveDDC ждал бы IQ с DDC0 - doc/SLICES_PLAN.md: статус 3.1 + B1 проверена на экране Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
33 KiB
Мультислайсы и панадаптеры (как во 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-метр), маркеры полосы фильтра слайса в обоих рендерерах (GLDrawSliceFilterMarkers+ CPU), drag-ретюн несущей, колесо над флагом. Ctrl+ЛКМ по спектру = добавить слайс. Флаг главного (A) виден когда скрыта левая панель ЛИБО есть слайсы B+ (синхронизируется с правками с левой панели). - UI-полировка флагов (
30a6616,3049f91,a3ac459):MainForm.LayoutFlags— единый packing всех флагов (A+B+) в один ряд; идентификация уехавшего флага буквой по центру полосы (DrawBandLetterCPU +DrawBandLetterGLGL с кэшем текстур букв); палитраVfoOverlay.SliceColor(L); кнопки черезFlatButton.PaintFlatButton; полная тематизация изTAppTheme.
- Перф — S-метр развязан с кэшем «хрома» флага (
FMeterDirty+ meter-onlyRefreshMeterчерез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/звуковуха — уже реализовано)
Правила:
- Слайс всегда принадлежит панадаптеру (
PanId). Понятия «off-span слайс» больше нет: хочешь слайс вне захвата — создай панадаптер там. - Пан 0 (главный) существует всегда; его «слайс A» — текущий главный тракт
(WDSP-канал 0). Панадаптеры N>0 живут целиком на слайс-механике Этапа 1
(каналы
SLICE_CHAN_BASE+), при создании пана автоматически создаётся один слайс в его центре (поведение Flex). - ADC-источник — свойство панадаптера, не слайса (бейдж
A1/A2в шапке). В отличие от Flex показываем именно АЦП, а не антенну: на HPSDR-платах маппинг «вход→АЦП» жёсткий, антенная абстракция не нужна. - Буквы слайсов — глобальный пул A..G через все паны (как во Flex A-H): буква = личность слайса, пан = место жительства.
- Возможности бэкенда описывает запись 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
Статус: фазы A и B1 СДЕЛАНЫ (коммиты c125e59, cc9919c) и ПРОВЕРЕНЫ
пользователем на экране (A — 2026-07-10, B1 — 2026-07-11: создание/закрытие
флагов, видимость флага A, мышь/колесо/drag, тик S-метров — работает).
Создан PanafallPanel.pas:
- Фаза A: панель владеет view (CPU/GL), контролами (спектр/линейка/сплиттер/
водопад/пан-зум/кнопки зума), полной геометрией стека (
Layout), механикой сплиттера и мышью пан/зум-полосы. MainForm держит алиасы (FSpecView,PbSpectrum, …) на членыFPan; размеры областей — изFPan.SpectrumWidth/SpectrumHeight/WaterfallHeight. - Фаза B1: панель владеет коллекцией флагов слайсов (LayoutFlags, диспетчер
мыши флагов, Create/DestroySliceFlag, PushSliceFlagState, TickFlags,
UpdateMainFlagVisibility). Флаг A создаёт MainForm и отдаёт через
AttachMainFlag; семантические события флагов проводятсяOnWireFlag, роутинг поFPan.CurSliceId.
Решение по фазе B2 (мышь спектра/водопада, dirty-флаги/тик, SampleRate/
BandPlan-оверлеи): НЕ переносить сейчас. Семантика мыши (тюн VFO A/B,
XVTR-клампы, маяк, CTUN-drag) станет per-pan только в 3.3, когда появится
API «ретюн пана» — перенос сейчас потребовал бы хуков (ApplyVfoAHook,
BeaconSeedHook…), которые в 3.3 переделались бы заново. Мышь/тик переезжают
в панель В СОСТАВЕ 3.3 вместе с per-pan семантикой. Для 3.1/3.2 текущего
состояния панели достаточно.
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 %, дельты точечные:
- 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, обновляет при ретюне пана. - Per-DDC rate/ADC/enable в
ConfigureDDCs(HPSDRNetwork.pas:1460): сейчас один rate/ADCSource на все N. Принимать массив записей(Enabled, RateKHz, ADCSource)— протокол это позволяет (DDCConfig[ddc*6+0]=ADCSource, rate там же per-DDC). - Роутинг IQ по панам.
RadioController.OnDDCIQ(RadioController.pas:828) сейчас гейтитDDCIndex = FActiveDDC(:875), остальное выбрасывает. Заменить на маппингDDCIndex → PanId: пакет пана 0 — в существующий путь, пакеты панов N>0 — вWDSPEngine.PushPanIQ(PanId, Data)(см. 3.2). - Аллокатор DDC: пул hw-индексов от
DDCBase+1(DDCBase=2 на PureSignal-платах,HPSDRNetwork.pas:1485), захват при создании пана, освобождение при закрытии, слот-стабильность как у слайсов. - 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.1 (2026-07-11): РЕАЛИЗОВАНО, собирается (ewsdr qt6 + ewsdrd),
на эфире не проверено (проверяемо только с 3.2/3.3 — панов ещё никто не
создаёт). Отличие от текста плана: per-DDC состояние (freq/rate/ADC/enable)
живёт в THPSDRNetwork (FPanDDC*, пишет SetPanDDC/SetPanDDCFreq из
TRadioBackend), а контроллер держит только per-pan маппинг — эквивалентно
FDDCFreq[] из плана, но HP-кадр собирается из одного места. Сделано:
TBackendCaps.MaxPans/IndependentPanFreq(HPSDR 4/True, Pluto 1/False);SetPanDDC/SetPanDDCFreq— виртуальные no-op в базе, override в HPSDR.HPSDRNetwork:ConfigureDDCs→ кэш главного DDC +RebuildDDCSpecific(главный + активные пан-DDC, каждый со своим rate/ADC);SendFullHPпишет частотные слова всех активных пан-DDC (Buf[9+ddc*4]); хелперDDCBaseIndex(дедуп трёх списков плат); сброс пан-состояния вConnect.RadioController:MAX_PANS=4,PAN_DDC_RATES=(192,384,768);AddPanDDC/RemovePanDDC/SetPanDDCFreq/PanDDC{Active,Freq,RateKHz}(аллокатор hw-индексов отFActiveDDC+1, обратный маппингFDDCPanMap);OnDDCIQроутит: активный DDC → главный тракт, пан-DDC →WDSPEngine.PushPanIQ(пока заглушка-дроп, тело — 3.2); паны гасятся вStopRunning, пул сбрасывается вStartRunning.- ⚠ Попутный фикс:
StartRunningне включал Saturn (BoardType 10) в список плат с DDCBase=2 → на Saturn гейтFActiveDDCждал DDC0. Исправлено ([3,4,5,10], как вDDCBaseIndex). - Частоты панов — сырые Гц железа: XVTR-трансляция на паны НЕ распространяется (XVTR — свойство главного тракта; решить при 3.3/3.4, если понадобится).
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.
Дельты:
- Пан-анализаторы:
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 — можно стартовать с общих настроек, разделить позже). - Per-pan аккумулятор:
TPanChan = record(FPanAccI/Q,FPanAccPos, rate, disp id, активные слайсы).PushPanIQ(PanId, Data)из сетевого потока кладёт в аккумулятор пана; по накоплению блока в DSP-потоке: копия →Spectrum0(1, PAN_DISP_BASE+PanId, …)+ProcessSlicesпо слайсам ЭТОГО пана. - Слайс знает пана: в
TSliceChanдобавляетсяPanId; NCO shift слайса =TargetHz − PanCenterHz; rate канала слайса = rate его пана (для панов N>0 это 192-768 кГц — WDSP-каналу дешевле, чем полный rate!).RefreshSliceShiftsобобщается на «центр пана», гейтSliceFitsCapture— на «span пана». - Выдача пикселей:
FOnPanSpectrum(PanId, Pixels, N)(или существующий колбэк + PanId), MainForm-тик раздаёт поFPans[PanId]. - Пул 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)