Commit Graph
248 Commits
Author SHA1 Message Date
ew8bakandClaude Opus 4.8 28ae832b71 feat(slices): per-pan tuning wheel and per-slice RX mute on TX
Mouse wheel over an extra panadapter now retunes that pan's primary
slice (first created on that receiver) instead of the main VFO; a slice
flag under the cursor still wins, pan 0 keeps driving the VFO.

Slice flags get a clickable RXM badge: RX audio of the slice is muted
while transmitting (default), so the operator no longer hears himself on
every slice in full duplex. State persists with the workspace.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:35:19 +03:00
ew8bak fa5a95ca5d fix(dmr): avoid stale and mislabeled talkgroups 2026-07-14 21:30:41 +03:00
ew8bak 8b28986fec fix(ui): show DMR status without replacing PLL 2026-07-14 21:05:59 +03:00
ew8bak a880ed0e6c fix(ui): close DMR step panel layout gap 2026-07-14 20:54:38 +03:00
ew8bak 62e663baf5 feat(dmr): default tuning step to 12.5 kHz 2026-07-14 20:51:10 +03:00
ew8bak 831045f37d chore(dmr): remove diagnostic capture probes 2026-07-14 20:42:48 +03:00
ew8bak d794805081 fix(dmr): block MOX and tune on receive mode 2026-07-14 20:35:20 +03:00
ew8bak 53aef502e9 fix(ui): sync main slice controls with panel 2026-07-14 20:28:10 +03:00
ew8bak 9755378586 fix(dmr): normalize voice audio and lock bandwidth 2026-07-14 20:14:50 +03:00
ew8bak 39b4627c2b feat(dmr): add diagnostic capture probes 2026-07-14 19:09:35 +03:00
ew8bak 70486d901b fix(dmr): track voice state per timeslot 2026-07-14 18:56:33 +03:00
ew8bak e0e74b5337 feat(dmr): decode full link control 2026-07-14 18:51:19 +03:00
ew8bak 23596d0565 feat(dmr): decode CACH and slot type 2026-07-14 18:46:49 +03:00
ew8bak 53a3681618 feat(dmr): add receive frontend and AMBE adapter 2026-07-14 18:38:39 +03:00
ew8bakandClaude Opus 4.8 f33a1fbd0a feat(pans): выбор аппаратного DDC для панадаптера
Клик по «NNN kHz · DDCn» в шапке пана — кроме сетки rate теперь есть пункты
«→ DDC N»: пан переезжает на другой аппаратный DDC (MovePanDDC), при этом
движок, анализатор и слайсы не пересоздаются — перепрошивается только
hw-индекс. Пригодилось при отладке: конкретный DDC платы может вести себя
хуже соседей, и пан можно просто увести на другой.

Диапазон ограничен реальными DDC железа: MaxDDCIndex = 7 (DDC8+ openHPSDR
enable-бит принимает, но поток не отдаёт). Ограничение действует и в меню,
и в DDCFree, и в самом MovePanDDC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:52:31 +03:00
ew8bakandClaude Opus 4.8 9dd38e1dd3 feat(pluto): выбор антенного разъёма RX/TX на диапазон (LibreSDR/AntSDR)
Второй разъём у AD936x-плат — это второй КАНАЛ чипа (не порт B первого), и в
1R1T-образе он выбирается мультиплексором AD9361: debug-параметры
adi,1rx-1tx-mode-use-rx-num/-tx-num + биты RX1/RX2, TX1/TX2 регистров 0x3/0x2
(как switch_rfinput.sh/switch_rfoutput.sh прошивки). rf_port_select тут ни при
чём — прошивка его к тому же блокирует.

- IIOBindings: debug-атрибуты + прямой доступ к регистрам.
- PlutoBackend: детект второго разъёма (2R2T-каналы либо мультиплексор + модель
  платы), SetAntPorts(RxPort,TxPort), ApplyRxMux/ApplyTxMux; Caps.HasAntSwitch.
  ⚠ApplyGain(Force): драйвер пропускает запись gain_control_mode, если считает
  режим неизменным, — а у нового канала AGC-регистры не запрограммированы (в
  железе крутится auto-AGC, hardwaregain отвергается EOPNOTSUPP). Форсируем
  перепрог через другой режим после смены разъёма и после ad9361_set_bb_rate.
- RadioBackend: TBackendCaps.HasAntSwitch + virtual SetAntPorts (HPSDR — no-op,
  там антеннами рулит Alex).
- Settings: секция ant936x (разъём RX/TX на диапазон и на слот трансвертера);
  hw-AGC (режим + manual gain) переехал в TBandSettings/TXvtrEntry — теперь он
  свой на каждом диапазоне и трансвертере.
- RadioController: FAnt936x + ApplyAntPorts (RestoreBand/ActivateXvtr/
  StartRunning); ApplyBandDSP восстанавливает hw-AGC диапазона.
- SettingsForm: вкладка Antenna — Alex-таблица (openHPSDR) либо таблица
  разъёмов AD936x по диапазонам/трансвертерам.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:26:18 +03:00
ew8bakandClaude Opus 4.8 a2e403af3d fix(slices): красная полоса фильтра при TX — у передающего слайса, не у главного
- MainForm: TXOverlay главной вьюхи взводился по FTransmitting без учёта
  источника TX → красной становилась полоса главного VFO даже при передаче
  со слайса. Теперь TXOverlay = FTransmitting and (FTxSliceId = 0).
- VfoOverlay: публичный TxActive (FTx) — вьюхи знают, какой слайс передаёт.
- SpectrumView/SpectrumViewOpengl: полоса/края/несущая передающего слайса
  красные (alpha 100, как у главного при TX), иначе цвет бейджа слайса.
- Фикс цвета TX-полосы в GL: $00E03030 при ColorToRGB (младший байт = R)
  давал синий вместо красного; общая константа CLR_TX_BAND = $003030E0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:48:30 +03:00
ew8bakandClaude Opus 4.8 2474821739 feat(slices): SPLIT только у главного флага
У слайса нет своего VFO B — кросс-частотный TX делается TX-бейджем слайса.
Бейдж SPLIT (и его хит-зона) рисуется лишь при IsMain; «×» флага слайса
переехал вплотную к TX. SPLIT главного гаснет, пока TX-источник — слайс
(ActiveTXFreqHz берёт частоту слайса, а не VFO B).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:32:54 +03:00
ew8bakandClaude Opus 4.8 8d65f4d6e8 feat(slices): мультислайс-TX — выбор TX-слайса, кросс-банд (Flex-паритет)
- RadioController: FTxSliceId (0=главный VFO, >0=слайс), SetTxSlice
  эксклюзивно; ActiveTXFreqHz/ActiveTXMode с TX-слайса, режим применяется
  в SetMOX(True) перед SetTXRun; TxBandIdx для PA-кал и safety DoNotTx
  при кросс-банде; RemoveSlice сбрасывает TX-слайс.
- HPSDRNetwork: TX-антенна выбиралась по RX-бэнду → по TX-частоте
  (TxAnt[FreqToBandIndex(TXFreqHz)]).
- MainForm/PanafallPanel: клик TX на флаге слайса → SetTxSlice, TX-бейджи
  (янтарь=выбран, красный=передаёт), RefreshTxIndicators по rfTransmitting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:15:52 +03:00
ew8bakandClaude Opus 4.8 9bb86b59b8 feat(slices): кастомные тематизированные popup-меню панов + фиксы позиционирования
Селекторы диапазона/samplerate панов N переведены с нативного TPopupMenu на
своё меню в стиле приложения (одинаково на всех платформах, в цветовой теме).

- FlatPopupMenu.pas (new): TFlatPopupMenu — owner-draw меню как ДОЧЕРНИЙ контрол
  формы (не top-level окно: Wayland центрирует свои top-level окна). Hover,
  галка текущего, клавиатура (↑↓/Enter/Esc), закрытие по клику-вне (MouseCapture)
  и потере фокуса. Позиционируется под лейблом-якорем в клиентских координатах.
- PanDisplayPopup: поповер «◑» тоже переведён с top-level формы на дочерний
  контрол (центрировался на Wayland) — открывается под кнопкой, закрытие «×»
  или повторным «◑».
- MainForm: band/rate-селекторы на FlatPopupMenu; свёртка повторяющегося
  выбора темы (9 инлайнов) в CurrentAppTheme.
- PanafallPanel: курсор-«палец» кликабельных лейблов шапки переприменяется в
  конце Build (флаги *Clickable могли выставляться до создания лейблов —
  samplerate-лейбл не получал crHandPoint).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:02:08 +03:00
ew8bakandClaude Opus 4.8 3ee996154a feat(slices): выбор КВ-диапазона на панах N (Flex-style band-селектор)
Кликабельный бейдж диапазона («20m») в шапке пана N → popup-меню КВ-бандов.
На выбор — по-флексовому: ретюн DDC на VfoA диапазона (из общего band-кэша,
как пан 0), активный приёмник садится на VfoA с mode/filter/AGC + zoom/pan
этого диапазона, прочие слайсы пана убираются (вне новой полосы). Только на
настоящих КВ (не XVTR, не Pluto).

- TPanafallPanel: FHeaderBandLabel + OnBandClick/BandClickable/SetHeaderBand;
  каскад left-лейблов шапки вынесен в LayoutHeaderLabels (band/rate/adc без
  дыр при скрытии).
- MainForm: OnPanBandClick/PanBandMenuItemClick/ApplyPanBand +
  PanBandSelectorVisible (гейт КВ); band-лейбл в UpdatePanHeaders (по
  FreqToBandIdx частоты DDC), тема в ApplyPanHeaderTheme.

Ограничение: per-slice AGC-Top в движке нет — подтягиваются
mode/filter/AGC-mode/zoom/pan, но не индивидуальный AGC-Top диапазона.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 14:27:17 +03:00
ew8bakandClaude Opus 4.8 3566f10191 feat(slices): per-pan дисплей — палитра/gamma/уровни на пан («◑»-поповер)
Кнопка «◑» в шапке каждого пана открывает компактный поповер настроек
отображения (палитра, gamma, ref/range спектра, авто-уровень водопада +
offset/ручные min-max). Модель «глобальный дефолт + override на пан»: пан 0
правит глобальные поля рендера (зеркалятся в контроллер), паны N ставят
DisplayOverride и персистятся в воркспейс раздельно КВ/трансвертер.

- PanDisplayPopup.pas (new): borderless fsStayOnTop форма, гаснет по
  OnDeactivate; Flat*-контролы; крестик «×» закрытия; дим-хинт «правки
  применяются и сохраняются сами»; кнопки «⟳ Reset»/«⇄ Copy to all».
- TPanafallPanel: FBtnDisplay + OnDisplayClick + DisplayOverride;
  PositionHeaderChildren каскадит [×][⧉][◑] справа по Visible.
- MainForm: OnPanDisplayClick/Applied/Reset/ApplyAll + ApplyGlobalRenderToView
  + MarkPanDisplayDirty. При смене палитры/сбросе чистим историю GL-водопада
  (TSpectrumView.ClearWaterfall) — GL хранит уже раскрашенные пиксели.
- Settings/TPanCfg: под-объект 'display' (палитра/gamma/ref/range/wf-уровни),
  пишется только при override.

Глобальными осознанно оставлены разрешение анализатора, MSAA, скорость
водопада, детектор/усреднение (движок/перф, не косметика).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:49:19 +03:00
ew8bakandClaude Opus 4.8 543453007e feat(slices): персист панов+слайсов, раздельно КВ/трансвертеры
Сохранение и восстановление панадаптеров, слайсов и раскладки per-device,
раздельно по контексту: КВ ('hf') и каждый трансвертер ('xvtr_N') — паны
одного контекста не имеют смысла в другом.

Settings: секция "pans"/<ctx> под MAC (grid-режим/доли, на пан —
freq/rate/adc/zoom/pan/доли стека+колонки+сплиттер спектр/водопад, на слайс —
freq/mode/фильтр/agc/vol/mute/squelch/аудиоустройство). TSliceCfg/TPanCfg/
TPansConfig + Default/Save/LoadPans(Ctx).

MainForm: CreatePanCore (вынос ядра из OnAddPanClick), SavePanWorkspaceCtx/
RestorePanWorkspaceCtx/TearDownWorkspace, PanContextKey. Хук контроллера
OnBeforeStop (снимок до гашения DDC в StopRunning). Переходы ActivateXvtrBand/
DeactivateXvtr обёрнуты save-старого/teardown/restore-нового (гейт FRunning);
restore XVTR-панов после ActivateXvtr (FCurrentXvtr целевой ⇒ XvtrTranslate
верный). PanafallPanel.DestroyAllSliceFlags. Смена КВ-банда контекст не меняет.

Round-trip сериализации и разделения контекстов проверены автономно; на
экране не проверено.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 23:06:12 +03:00
ew8bakandClaude Fable 5 b3edf3a4ad fix(slices): XVTR-трансляция частот панов N
Частота пана хранится ВИДИМОЙ (как FVfoA/FCenterFreq) и транслируется в
IF через XvtrTranslate только на границе с железом (AddPanDDC,
SetPanDDCFreq, SetPanDDCRate, SetPanADC) — раньше DDC пана получал
видимую частоту сырьём, а drag водопада пана N клампился пределами АЦП
(~61 МГц) вместо диапазона трансвертера.

Новое: controller.VisibleFreqBounds (диапазон активного XVTR | пределы
железа) для UI-клампа драга; RefreshPanDDCs — на входе/выходе XVTR и
правке LO/LOError клампит видимые центры живых панов, перешивает
freq-слова DDC и прижимает выпавшие слайсы к краям полосы захвата.

Подтверждено юзером на трансвертере.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 16:02:54 +03:00
ew8bakandClaude Fable 5 eac66100a8 feat(slices): грид-раскладка панов + фиксы минимумов стека, утечки кнопок, CPU
Грид: пан 0 сверху во всю ширину, доп. паны колонками снизу; тумблер
«▦/▤» в ряду зума пана 0, вертикальные сплиттеры-призраки колонок
(FPanColShare), доля ряда FPanGridRowShare; автоткат в стек при <2 доп.
панах. Регион пана обобщён до прямоугольника (StackLeft/StackWidth).

Фиксы по экранному тесту:
- наезд панов: раздача высот и драг сплиттеров клампятся минимумом
  содержимого пана (MinLayoutHeight/PanMinHeight, пан 0 += wideband);
- «блуждающие» кнопки после STOP: MakeFlatBtn даёт Owner=контейнер,
  TPanafallPanel.Destroy теперь явно освобождает −/⌂/+/⊞/▦;
- жор CPU ~40%/пан: убран безусловный Invalidate зум-бара из тика
  (SyncPanZoomUI), инвалидация только при реальной смене значений;
- пропорция спектр/водопад пана 0 не восстанавливалась после закрытия
  панов: кламп ratio минимумами больше не пишется в FSplitterRatio.

Подтверждено юзером на экране: грид/стек/STOP/pop-out работают,
CPU со всеми панами 17-20%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 15:51:11 +03:00
ew8bakandClaude Fable 5 64edf9e289 feat(slices): пан-зум панов N — свой бар/кнопки, span-clip анализатора пана
Движок: TPanChan.ZoomFactor/PanSlider, span-clip в ApplyPanAnalyzerSettings
(математика главного, ZOOM_LIMIT 100), SetPanZoomPan с no-op guard (SetAnalyzer
сбрасывает FFT-историю). Зум переживает смену rate/ADC пана.
Контроллер: формула окна вынесена в ViewWindowFor (главный GetViewWindow тоже
через неё), GetPanViewWindow (PanId=0 делегирует), PanZoomIn/Out по лестнице
кратностей с якорем на центре окна, PanZoomDefault, геттеры.
UI: TPanZoomEvent получил Sender; у панов N включён ряд зума, бар
диспетчеризуется по объекту, кнопки −/⌂/+ общие через PanOfZoomButton (матч по
идентичности — MakeFlatBtn создаёт кнопки с Owner=контейнер, PanFromSender их
не видит); SyncPanZoomUI/RefreshPanZoom, вся мышь панов зум-корректна через
GetPanViewWindow. Темизация баров панов N.
Смоук panzoomtest ALL PASS (пик тона совпадает с формулой окна ±0.6%),
подтверждено на экране.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:35:13 +03:00
ew8bakandClaude Fable 5 209e490dfa fix(slices): pop-out при wideband + читаемость шапки; бейдж АЦП → RX1/RX2
1) LayoutPanStack прятал шапку пана 0 по числу застекованных панов, а wideband
ставится со сдвигом Pan0HeaderH по общему числу (вкл. плавающие) — спектр
накрывал линейку частот wideband. Теперь видимость шапки по PanCount.
2) Лейблы шапки наследовали шрифт формы (ParentFont) — в pop-out окне текст
был дефолтно-тёмным на тёмном. Темизация шапки вынесена в ApplyPanHeaderTheme
(зовётся из UpdatePanHeaders: создание пана/pop-out/dock), окну — шрифт формы.
3) Бейдж ADC-источника переименован A1/A2 → RX1/RX2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:05:10 +03:00
ew8bakandClaude Fable 5 8bf8bcfc76 feat(slices): этап 3.4 ADC-селектор панов + этап 3.5 pop-out в отдельное окно
3.4 ADC-селектор:
- Caps.NumADCs (HPSDR: 2 для ORION/ORION2/SATURN, как Pkt.NumADCs; Pluto: 1)
- controller.PanADC/SetPanADC: пан 0 = FMainADCSrc + ConfigureDDCs (ADC
  прокинут и в штатные вызовы — смена rate не сбрасывает выбор), паны N =
  FPanADC[] + SetPanDDC (RebuildDDCSpecific)
- Бейдж «A1/A2» в шапке каждого пана (виден при NumADCs>1), клик = тумблер;
  пан 0 на ADC2 закрывает сценарий rx2-support

3.5 Pop-out:
- «⧉» в шапке панов N → PopOutPan: TForm.CreateNew + ReparentTo (перенос
  всех контролов панели), [×] окна = DockBackPan (возврат в стек), «×»
  шапки = закрыть пан; LayoutPanStack скипает плавающих; колесо окна =
  FormMouseWheel
- GL: reparent пересоздаёт контексты → ResetGLCache обеих канв без glDelete
  (по прецеденту MSAA); TWaterfallViewOpenGL.ResetGLCache новый, история
  водопада перезаливается из CPU-буфера

Проверено юзером на экране (беглый тест: селектор и pop-out работают).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 23:54:09 +03:00
ew8bakandClaude Fable 5 e5921b8196 feat(slices): независимый rate панадаптеров (селектор в шапке)
- Создание пана: rate наследуется от главного с клампом ≤768к
  (вне сетки → 384к); за сменой rate главного паны не следуют
- Клик по «NNN kHz» в шапке пана N → меню полной сетки железа
  (PanRateAllowed = Caps.RatePresets ∩ кратные 48 кГц; захардкоженный
  PAN_DDC_RATES удалён)
- WDSPEngine.SetPanRate: под FSliceLock пересбор аккумулятора/анализатора
  пана + переоткрытие слайс-каналов на новом rate (state сохраняется)
- RadioController.SetPanDDCRate: движок → реконфиг DDC (SetPanDDC) →
  кламп слайсов к новой полосе; FPanADC[] хранится (база под 3.4)
- Шапка: rate отдельным лейблом (у панов N кликабелен, акцент/рука)

Смоук-тест без железа ALL PASS (тон: спектр+аудио слайса живы через
384→192→768). Проверено юзером на экране.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 23:28:25 +03:00
ew8bakandClaude Fable 5 95b4f749be feat(slices): этап 3.3 — UI стек панадаптеров
Несколько панадаптеров в главном окне (кнопка «⊞», до MAX_PANS):
- TPanafallPanel: регион в стеке (StackTop/Height), шапка с «×»,
  pan-aware ViewWindow, FirstSliceId; явный crArrow GL-канвам
  (WA_NativeWindow-поверхность на Wayland навсегда наследует курсор
  момента создания — прилипала «рука» кнопки «⊞»)
- MainForm: FPans[]/LayoutPanStack (доли + межпанные сплиттеры с живым
  призраком), создание/закрытие панов (DDC+слайс+вьюха), пиксели панов
  из display-потока под FPanCbLock, рендер по dirty в тике
- Мышь панов N: Ctrl+ЛКМ = слайс, drag маркера = ретюн слайса,
  drag фона = live-ретюн DDC (гистерезис 4px), клик = тюн слайса пана
  (PanClickTune, снап 100 Гц), флаг-диспетчер только на спектре
- Сплиттер спектр/водопад: drag-математика от региона пана
  (FSplitTopOff/FSplitAvailH из Layout), OnSplitterMoved у панов N
- Паны живут в Run-сессии, умирают на STOP

Проверено юзером на экране (3 раунда фидбека) + e2e против hpsdrsim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:56:49 +03:00
ew8bakandClaude Fable 5 cc9919c35a refactor(slices): этап 3.0B1 — флаги слайсов переехали в TPanafallPanel
Панель владеет коллекцией флагов B+ (создание/закрытие/заливка состояния из
контроллера), раскладкой LayoutFlags, диспетчером мыши флагов, тиком S-метров
(TickFlags) и видимостью флага A (UpdateMainFlagVisibility). Главный флаг A
создаёт MainForm (проводка семантических событий у него) и отдаёт через
AttachMainFlag; новые флаги проводятся через OnWireFlag. Роутинг по слайсу —
FPan.CurSliceId; закрытие «×» — RequestSliceClose/OnSliceClosed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 22:51:47 +03:00
ew8bakandClaude Fable 5 c125e5978e refactor(slices): этап 3.0A — панадаптер выделен в TPanafallPanel
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>
2026-07-10 22:41:32 +03:00
ew8bakandClaude Opus 4.8 cdbdfec792 feat(bandplan): диапазон OIRT 65-74 МГц в плане Pluto (кнопка перед FM)
VU-план (Pluto) расширен до 12 слотов: OIRT (65..74 МГц, дефолт 69 МГц)
вставлен перед FM. HF-план (openHPSDR) остаётся 11-диапазонным — 12-й слот
пустой, кнопка прячется (RelabelBands). CAT индексный (OIRT = band 08 на
Pluto), SetBand игнорирует пустой слот. Web bandSel строится динамически из
BandPlan; пустые слоты пропускаются.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:40:05 +03:00
ew8bakandClaude Opus 4.8 ba2a75bb04 feat(dsp): режимы WFM, DIGI-U и DIGI-L
WFM использует новые wfmd/wfmmod из нашего форка WDSP. Демодулятор —
квадратурный дискриминатор на dsp_rate, поэтому при штатных 48 кГц
девиация 75 кГц не помещается в полосу. В WFM цепь RXA поднимается до
WFMDSPRate (наибольший FAudioRate*2^k <= 192 кГц, делящий SampleRate
нацело), rsmpin децимирует вход сам. Выше 192 кГц смысла нет: энергия
демодулированного тона выходит на полку (33.5k при 192 кГц против 31.8k
при 576 кГц), а FFT дорожает.

Две ловушки WDSP, найденные по дороге:

  * firmin.c: nfor = nc / size, idxmask = nfor - 1. Все fircore создаются
    с nc = max(2048, dsp_size), поэтому смена dsp_size без RXASetNC даёт
    nfor = 0 — пустой массив планов и падение в fftw_execute.

  * fir.c/bandpass.c планируют с FFTW_PATIENT, а wisdom.c пишет в
    wdspWisdom00 только степени двойки. При SampleRate 576 кГц наивный
    dsp_size = in_size = 12288 требовал FFT на 24576 точек — FFTW мерил
    план 25 секунд. Отсюда потолок 192 кГц и dsp_size = степень двойки.

RXA и TXA гейтятся раздельно: рейты TXA (48 кГц вход, 192 кГц выход) от
SampleRate не зависят, поэтому wfmmod уходит на широкий dsp_rate даже
когда приёмник остался на 48 кГц.

Заодно исправлен WDSP_SAM: было 12, а RXA_SAM = 10. Раньше 12 было вне
диапазона и WDSP молча игнорировал (SAM работал как DSB); после появления
RXA_WFM = 12 это включало бы WFM-демодулятор вместо SAM. txaMode
расходится с rxaMode после DRM (WFM: RXA 12, TXA 14) — отсюда ModeToWDSPTX.

DIGI-U/DIGI-L получили свою таблицу фильтров (3.0k…300) и полосу от нуля,
а не от среза на 100 Гц, как у SSB. В CAT они замаплены на коды Kenwood 6
(FSK) и 9 (FSK-R) — раньше эти коды сваливались в USB/LSB.

Новые индексы дописаны в конец (DIGU=8, DIGL=9, WFM=10): индекс режима
уходит в settings.json, band-cache, CAT и web-API. В VfoOverlay пришлось
развести хит-коды — режимы кодировались как -(I+1) и налезли на HIT_NR/
HIT_NB/HIT_SNB, а полосы кодировались значением BW, и WFM-овские
120000/160000/200000 налезли на HIT_AGC_PICK_BASE/HIT_DEV_BASE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:59:27 +03:00
ew8bakandClaude Opus 4.8 b2b3339a2a feat(vfo): кнопка SQL + ползунок FM-squelch в DSP-меню флага (main + слайсы)
В fly-out DSP появляется ряд SQL (тумблер + порог 0..100) только в режиме FM.
Squelch работает per-slice: у слайсов свои WDSP-каналы, поэтому добавлены
поля/сеттеры FMSQ в TSliceChan/TCtrlSlice, ApplyFMSQToChan и восстановление
при переоткрытии канала. Роутинг события по SliceId (0=RXA, иначе слайс).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 12:51:02 +03:00
ew8bak ce9cfd730f Revert "fix(opengl): use Lazarus OpenGLContext"
This reverts commit 004c363df8.
2026-07-08 11:56:09 +03:00
ew8bak 004c363df8 fix(opengl): use Lazarus OpenGLContext
Replace OpenGLContextEx with OpenGLContext in MainForm, SpectrumViewOpengl, WaterfallViewOpengl, and WidebandView.

Switch the Lazarus project/program dependencies from LazOpenGLContextEx to LazOpenGLContext in ewsdr.lpi and ewsdr.lpr.

PixelScale was introduced in 092a202; restore the previous viewport/texture path: glViewport(0, 0, W, H), EnsureTexture(W, H), and UploadCurrentRow(W).
2026-07-07 23:30:35 +03:00
ew8bakandClaude Opus 4.8 e13d610a23 feat(audio): раздельная громкость RX и TX-монитора + фикс синка слайдера
Две сохраняемые громкости: FVolume (RX) и FTxMonVolume (self-monitor
даунлинка на передаче, дефолт 40). Контекст монитора = TX + DUP + RX MUTE
off (тот же гейт, что аудио в движке). В этом контексте слайдер/SetVolume
правят FTxMonVolume, иначе FVolume; ActiveVolume — активная под контекст.
Автопереключение громкости RX<->TX-монитор на T/R (SetMOX) и при смене
RX MUTE / DUP во время передачи (ApplyActiveVolume). Обе персистятся
(tx_mon_volume в конфиге). Веб выровнен на ActiveVolume.

Фикс: слайдер громкости не синхронизировался с загруженным значением после
подключения (в rfDevice синкался только drive, volume пропущен) → слайдер
оставался на 70, движок брал сохранённое → «прыжок» при первом касании.
Добавлен TrkVolume.Position := ActiveVolume в rfDevice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:24:57 +03:00
ew8bakandClaude Opus 4.8 ff2082ab04 feat(tx): self-monitor RX-аудио на TX в любом DUP + меньше TX pre-roll
RX MUTE (self-monitor даунлинка на передаче): кнопка теперь доступна на
ВСЕХ бэкендах, а не только Pluto/QO-100. Активна только в DUP; без DUP
кнопка disabled и звук на TX не проходит (движок гейтит по
FKeepRXDuringTX=FDisplayDuplex — двойная защита: аудио-гейт + RXA не
кормится). Добавлен RadioController.DisplayDuplex. TFlatButton теперь
рисует disabled-состояние приглушённым (текст/рамка к фону).

TX pre-roll 8144->6096 (2 блока подушки вместо 3): обоснование «нужно 3
блока» опиралось на щелчок ~0.5с после старта, который на деле оказался
bfo-глитчом тона (устранён f2d0519), а не underrun'ом подушки. Латентность
старта TX ~-10мс; startup-щелчков под нагрузкой DUP не слышно (подтверждено).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:09:26 +03:00
ew8bakandClaude Opus 4.8 98493f08b3 perf(tr): устранение GUI-фриза ~100мс на TX→RX (живой TX-DSP поток)
TX-DSP поток (TTXDSPThread) больше не пересоздаётся на каждом T/R — он
создаётся один раз и живёт весь сеанс, на приём/передачу переключается
флагом FTXActive (поток при False просто Continue, не трогает канал/ring).
Тердаун перенесён в TWDSPEngine.Close. Раньше Terminate+WaitFor+Free на
каждом отпускании PTT давал фиксированные ~100мс пустого join-оверхеда
pthread_join, блокируя GUI-поток. Итог: 206→106мс, крупные фризы ушли.
Остаток ~106мс = плавный WDSP fade-out TXA (SetChannelState dmode=1) —
оставлен как есть (dmode=0 дал бы trailing key-click в эфир).

Попутно:
- MainForm: убраны прямые PbSpectrum.Invalidate из drag/wheel VFO/центра —
  кадр рисует таймер по FSpectrumDirty (FPS-гейт; прямой Invalidate давал
  GL-кадр на каждое событие мыши мимо гейта → треск в HDMI-звуке AMD APU).
- AudioOutput: const-секция после {$ENDIF} блока PA-констант.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 16:32:35 +03:00
ew8bakandClaude Fable 5 e260995218 feat(display): скорость водопада с max-hold накоплением + MSAA спектра
- настройка Speed (wf_speed_div 1/2/4/8, Waterfall→Rendering): замедление
  накапливает кадры max-hold'ом в SetWaterfallData — короткие посылки (CW)
  не выпадают из строки; двойная децимация MainForm убрана
  (EffectiveWfInterval, CPU-путь не быстрее полукадровой)
- настройка Anti-aliasing (spec_msaa 1/2/4/8, дефолт 4x, Spectrum→Rendering):
  MultiSampling на GL-контроле спектра; смена в рантайме пересоздаёт
  контекст → ResetGLCache обнуляет текстурные кэши без glDelete

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:48:55 +03:00
ew8bakandClaude Fable 5 092a202f5f feat(waterfall): палитры Inferno/Turbo + gamma, разрешение до 4096, HiDPI
- SPECTRUM_PIXELS 1024→4096 + настройка display_pixels (лимит точек
  анализатора, SetDisplayResLimit); web-зеркало остаётся 1024 —
  max-децимация в SnapshotPixels
- палитры водопада Classic/Inferno/Turbo + регулятор gamma (в LUT),
  настройки wf_palette/wf_gamma, страница Waterfall → группа Rendering
- GL-водопад переведён на общий LUT/AGC-уровни базового класса
  (убраны дубли ColorForDB/UpdateLevels)
- явные glViewport в GL-вьюхах умножаются на PixelScale (HiDPI),
  текстура водопада — в физической ширине

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:11:58 +03:00
ew8bakandClaude Fable 5 4733ddab58 feat(opengl): переход на LazOpenGLContextEx — GL-рендер в нативном Wayland
Пакет LazOpenGLContext заменён на LazOpenGLContextEx (форк в
../lazOpengl): Qt6-бэкенд работает поверх настоящего QOpenGLWidget
(своя C++-либа libqlclglwidget) вместо GLX-контекста на winId(),
поэтому --opengl теперь работает в нативной Wayland-сессии, а под
ws=qt6 открывается путь на Windows/macOS.

- uses OpenGLContext -> OpenGLContextEx (класс TOpenGLControl тот же)
- удалена заглушка QtPlatformAllowsOpenGLControls с советом про
  XWayland: больше не нужна
- рантайм-зависимость: libqlclglwidget.so рядом с бинарником
  (сборка: make -C ../lazOpengl/csrc install-app APPDIR=bin/x86_64-linux)

Проверено на живом эфире (QO-100): спектр/водопад через GL под
Wayland и xcb, прозрачность FBO-альфы починена в обёртке (forceOpaque).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:56:46 +03:00
ew8bakandClaude Fable 5 01d78d148f chore: strip temporary PerfLog instrumentation
Профилирование завершено (кадр спектра 8.3мс -> 1.7мс, UI 45% -> 26% ядра):
удаляем PerfLog.pas, doc/PERF_PROFILING.md и все // PERF-замеры из
WDSPEngine/HPSDRNetwork/PlutoBackend/AudioOutput/SpectrumView(+GL)/
WaterfallViewOpengl/WidebandView/MainForm. Сами оптимизации (full-raw
рендерер, дворд-блендеры, порог бэндплана) остаются.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 00:36:03 +03:00
ew8bakandClaude Fable 5 0b4d126dac perf: zone CPU profiler (PerfLog) + phase-restructured CPU spectrum render
Инструментация (ВРЕМЕННАЯ, не мержить в main):
- PerfLog.pas: зонные таймеры, отчёт раз в 5с в stderr (core% = доля ядра),
  EWSDR_PERF=0 выкл, EWSDR_PERF_FILE=путь — дублировать в файл
- Замеры // PERF: сеть (DDC IQ/wideband), Pluto (rx_conv/tx_resamp/tx_fill),
  DSP (push_iq/fexchange/slices/tx), аудио, UI (тики/draw/paint/gl_swap,
  разбивка draw_spectrum по фазам)
- doc/PERF_PROFILING.md: карта зон, методика, выводы

Оптимизация CPU-рендера спектра (по результатам замеров на эфире):
диагноз — дорога не пиксельная работа, а чередование raw(ScanLine)/
canvas(QPainter) доступа к битмапу на Qt6 (~6 переходов за кадр, каждый
синкает весь битмап; полоса фильтра в тысячи пикселей стоила 4x дороже
полного memcpy кадра). DrawSpectrum перестроен в фазы calc->raw->canvas->raw
(2 перехода): FillBandRaw вместо Canvas.FillRect, DrawSliceFilterMarkers
разделён на raw-полосы + canvas-линии, композиты флагов и альфа-OR в
финальной raw-фазе. Кадр 8.3мс -> 6.1мс; spec_bands 1.87мс -> 0.03мс.
Альфа-OR проход на Qt6 подтверждён нужным (без него canvas-пиксели теряют
альфу и пропадают при блите), сам цикл ~0.08мс — оставлен; тумблер
EWSDR_ALPHA_FIX=0 только для экспериментов. Кисть перед AGC-подписями
теперь ставится явно (была остаточной после перестройки фаз).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 22:07:36 +03:00
ew8bakandClaude Opus 4.8 a3ac459abc feat(overlay): flat themed buttons + full theme support for slice flags
Share one button paint routine: extract TFlatButton.Paint into
PaintFlatButton(canvas, rect, colors) and call it from both TFlatButton
and VfoOverlay.DrawButton — flag buttons now render identically to the
left panel (flat fill + border, no gloss), using theme Btn* colors.

Theme the whole flag card: the structural CLR_* palette (card bg,
border, text, dim, accents, freq, meter bg) is now derived from the
active TAppTheme via ApplyThemeColors, so flags follow light/dark like
the rest of the UI. Status accents (TX/SPLIT) stay fixed on purpose and
use a fixed dark text over their bright badges. Theme is pushed to
flags from ApplyDarkTheme and on creation via WireOverlayEvents.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:04:21 +03:00
ew8bakandClaude Opus 4.8 30a6616dbe feat(slices): non-overlapping flag packing + per-slice colored letters
Flags (A + B+) now lay out in one pass: each sits beside its own filter
band as before, but a left neighbour pushes the next flag right so they
never cross (forward pack + backward pull-in on right-edge overflow).
LayoutFlags replaces the per-flag positioning; PositionVfoOverlay/
PositionSliceFlags are thin wrappers.

Identity is shown by a letter drawn at the top of each filter band in
the spectrum (both CPU + GL renderers), so a flag pushed away from its
band is still traceable. Letters and the flag letter-badge share one
per-slice palette (SliceColor); F is violet, not red, to avoid clashing
with the TX indicator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:19:50 +03:00
ew8bakandClaude Opus 4.8 3fbde19b9f fix(slices): align all VFO flags to same top (Top=35)
Slice flags (B+) were staggered by i*12 which barely helped with the
96px-tall card and looked untidy. Put every flag — main A and slices —
on one row. Overlap of near-frequency flags to be handled next (leader
lines).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:02:23 +03:00
ew8bakandClaude Opus 4.8 1527b0f849 feat(slices): multi-slice receivers (FlexRadio-style) — soft fan-out + per-slice flag/audio
Independent RX slices sharing one panadapter: each has its own
freq/mode/filter/AGC/NR-NB-SNB-ANF, S-meter, volume and sound card.
VFO A = main receiver; B+ are soft fan-out WDSP channels within the
captured span (ideal for Pluto/QO-100; HW DDC path is Stage 3).

- WDSPEngine: soft fan-out (TSliceChan, ProcessSlices in DSP thread,
  per-slice NCO/mode/filter/AGC/NR/NB/SNB/ANF + S-meter, OnSliceAudio),
  FSliceLock serialises add/remove/set vs DSP; reopened on rate change.
- RadioController: TCtrlSlice with its own TAudioOutput/device,
  Add/Remove/SetSlice*, RefreshSliceShifts keeps slices on target Hz
  across centre changes. Slice letters start at 'B' (A = main receiver).
- UI: per-slice flags (VfoOverlay) with letter badge/close, filter-band
  markers in BOTH GL and CPU renderers, drag-retune, wheel; flags placed
  beside the filter band with flip, like the main flag. Ctrl+click adds.
- Main flag (slice A) visible whenever the left panel is hidden OR any
  slice exists; synced with panel-driven DSP/mode/AGC changes.
- Perf: S-meter decoupled from flag chrome cache (FMeterDirty +
  meter-only RefreshMeter via EnsureRendered); InvalidateVfoOverlay
  avoids global overlay-cache churn on meter ticks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:46:30 +03:00
ew8bakandClaude Opus 4.8 08c71c47ad feat(overlay): clickable TX badge with slice-select groundwork
TX-индикатор теперь кликабельный (хит HIT_TX → колбэк OnTxSelect) — задел
под выбор TX-слайса в мультислайсах. Три состояния цвета:
- передача идёт → красный;
- слайс выбран для TX → янтарный (CLR_TX_SEL);
- не выбран → нейтральный.

Новое состояние FTxSel прокидывается через SetExtState (пока True — один
слайс всегда TX-слайс). MainForm.OnVfoOverlayTxSelect — заглушка с TODO под
мультислайсы.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 17:10:34 +03:00