TWebAdapter.SyncMode и TCATAdapter.SyncMode клампили индекс режима по
MODE_SAM (7), тогда как новые режимы дописаны в конец: DIGU=8, DIGL=9,
WFM=10. Команда на WFM/DIGI молча выходила из SyncMode, SetMode не
вызывался — приёмник оставался в прежнем режиме. У web-клиента окно
pend("mode") (600мс) истекало и UI откатывался на старый режим, что
выглядело как «переключение не успело по таймауту». Дело не в самплрейте.
Верхняя граница поднята до MODE_WFM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Две сохраняемые громкости: 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>
Прокинул в 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>
Pluto exposes two temperature sensors: the AD9361 transceiver
(ad9361-phy temp0/input, milli-°C) and the Zynq SoC/FPGA (xadc temp0,
raw+offset+scale → C = (raw+offset)*scale/1000). Only the former was
read. Now both are read and shown as "Temp 37/53C" (chip/SoC) in the
desktop status bar and web UI.
- ReadTelemetry gains an out SocTempC param (base + Pluto override);
Pluto reads xadc via the IIO context.
- Controller stores FLastPlutoSocTempC; SoC temp validated with a
plausibility window (no slew-limit — stable local sensor, unlike the
AD9361 sensor that spikes during mid-conversion).
- Status formatting handles one or both temps in MainForm and WebAdapter.
- Widen desktop status-bar field 3 (150→190) so both temps + RSSI fit;
web layout unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the desktop QO-100 beacon lock into the web interface and make it
work in the headless daemon.
Backend:
- WebServer: set_beacon/beacon_seed commands (+OnBeacon/OnBeaconSeed
events), beacon state fields, SetBeaconStatus, and beacon_visible/on/
text/ref_hz/track_hz in BuildStateJson. beacon_seed carries 'frac'
(0..1 click position); absolute Hz is computed controller-side from
live FCenterFreq/FSpanHz (mirrors desktop PixelToFreq).
- WebAdapter: OnBeacon->SetBeaconLock, OnBeaconSeed->BeaconSeedAtHz;
PushState mirrors compact status (matches MainForm.BeaconStatusText)
into the PLL status cell when visible (Pluto + active XVTR).
- MainForm/ewsdrd: wire the two callbacks.
- ewsdrd: call ServiceBeaconLock in the main loop @~10Hz (FRunning+
FWDSPReady gated). Without this the lock loop never ran headless.
Frontend (WebPageHtml):
- BCN button (visible only on Pluto+XVTR), highlighted while locked.
- Seed-on-mousedown with immediate return (like desktop FormMouseDown):
no drag/set_center so the gesture never moves the center / tears IQ.
Armed (first click after BCN) or Shift, mirroring desktop arm/Shift.
- PLL status cell shows beacon status (field-7 parity with desktop).
- Ref (green) + tracked (orange) beacon markers on spectrum/waterfall.
Known issue (unresolved): after lock the beacon can slowly drift off and
drop to "BCN sync". Suspected residual-sign anti-drift or retune-timing
on the web/daemon path; needs on-air diagnostics. See memory
project_web_beacon for the investigation state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sample-rate (span) presets were duplicated in three places and none was
authoritative: HPSDR set hardcoded in the desktop overlay (SPAN_RATES),
Pluto set in backend caps, and a separate hardcoded list in the web JS.
The web showed HPSDR rates even on Pluto, and the web adapter silently
dropped any rate outside a hardcoded HPSDR whitelist (so Pluto-only rates
like 960k/2304k never switched).
Single source of truth = backend caps -> controller:
- HPSDRNetwork.Caps now fills RatePresets [48k..1536k] (was nil).
- RadioBackend: named type TBackendRateArray.
- TRadioController.SampleRatePresets returns BackendCaps.RatePresets.
Both UIs derive from it:
- Desktop: ApplyBackendCapsToUI always feeds the overlay from
SampleRatePresets (overlay no longer owns the HPSDR list).
- Web: WebServer.SetRatePresets + rate_presets in state JSON; hosts
(daemon OnState/startup, GUI ApplyBackendCapsToUI/startup) push
SampleRatePresets; JS builds span buttons dynamically from it.
- WebAdapter.SyncSpan validates against SampleRatePresets instead of a
hardcoded whitelist, so any backend rate is accepted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Status bar / web now show Pluto/LibreSDR model + serial instead of
"Unknown Board" (BoardDisplayName in controller; UI just renders it).
- Field 3 shows AD936x chip temperature + RX RSSI for Pluto (no PA →
Supply only for openHPSDR). Polled via backend.ReadTelemetry (~2 Hz),
filtered for sensor glitches (plausibility window + slew-limit, with
anti-stick) — fixes rare bogus -15°C readings.
- Add libiio binding iio_channel_attr_read_double.
- Remove stale root pluto_integration.MD (superseded by
doc/PLUTO_INTEGRATION_PLAN.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OnSpectrumReady/OnWaterfallReady move into TRadioController. The controller now
owns the last spectrum/waterfall frame (FSpectrumBuf/FWaterfallBuf) for the web
mirror and emits raw pixels via OnSpectrumData/OnWaterfallData. MainForm
subscribes for FSpecView rendering (+ waterfall scroll decimation, a UI concern).
TWebAdapter.PushState now reads the frame from the controller (no buffer params).
Behavior-preserving; removes the last spectrum/waterfall data-path coupling.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OnAudioReady moves from MainForm into TRadioController (radio-speaker push +
local AudioOut). The web steal becomes a route hook: controller fires
OnAudioConsume; TWebAdapter.AudioConsume mixes to mono and pushes to the web
client, returning True to skip local AudioOut. Behavior-preserving; removes the
last audio-path coupling to MainForm.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SetBand was a skeleton (FCurrentBand := Idx + Changed), leaving BandUp/BandDown
non-functional. Fill it with the band-change logic (XVTR-exit + persist + restore)
that WebAdapter.SyncBand had inline; SyncBand now just calls FController.SetBand.
Gives CAT band up/down/by-index a controller command to call (Phase 4) instead of
routing through the web adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With ApplyVfoA now a thin SetVfoA delegate (channel orchestration on the
OnAfterTune hook), the web Freq/FreqA handlers carry no MainForm logic — move
them to TWebAdapter.OnFreq (active-VFO routing → SetVfoA/SetVfoB) and OnFreqA
(SetVfoA). CAT freq (CATSetVfoA/SyncCATVfoA) left as-is — moves with the CAT
adapter (Phase 4).
Remaining host-coupled web handlers: Run / Connect / device CRUD (daemon entry).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that RestoreBand and XVTR enter/exit are full controller commands, the web
Band and XvtrBand handlers carry no MainForm orchestration — move them to
TWebAdapter.OnBand/OnXvtrBand (validation + SaveCurrentBand/persist + command).
CAT Ctx.DoBandByIndex and CAT band up/down repointed to FWebAdapter.OnBand.
Remaining host-coupled web handlers: Freq/FreqA (ApplyVfoA channel orchestration),
Run/Connect/device CRUD (daemon entry).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that global-settings persistence is controller-owned, fold the persist into
SetSampleRate (sample rate is global; self-gated on FDevConnected). OnSampleRateSelect
becomes a thin wrapper. Web Span moves to TWebAdapter.OnSpan (keeps the valid-span
whitelist), so the last span coupling to MainForm is gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-tick PushSpectrum mirror (status strings + ~40 state values + spectrum/
waterfall buffers) moves out of SpectrumTimerTick into TWebAdapter.PushState,
which reads FController for everything and takes the pixel buffers as params.
MainForm's timer now just calls FWebAdapter.PushState(FSpectrumBuf, FWaterfallBuf).
Server lifecycle/ownership stays in MainForm for now — it's still driven by the
remaining host-coupled handlers (PushXvtrToWeb, device CRUD); ownership transfer
completes when those move.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that SetDrive is a complete command, the web/CAT Drive handler moves into
TWebAdapter.OnDrive (the one handler WA1 left behind). CAT context SetDriveLevel
repointed to it too.
Makes the channel-power-clear reactive: ChannelController.OnDriveChanged fires on
the rfDrive event and clears FPreChannelPower for any external drive change
(slider/web/encoder), guarded by FRestoringDrive for the unit's own apply/restore.
This removes the imperative ClearPreChannelPower coupling so SetDrive callers need
no channel knowledge.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WebOnMic only feeds TX mic samples to the controller's WDSP engine
(no UI, thread-safe like the other engine data callbacks), so it moves
verbatim into TWebAdapter.OnMic. FWebServer.OnWebMic now points there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the pure controller-command web handlers out of MainForm into a
new TWebAdapter (bridge between TWebServer and TRadioController),
marshalling via FController.Invoke so it is daemon-ready.
- 20 handlers moved: Mode/Filter/AGC/AGCTop/Volume/WfAGC/WfNF/Mute/
NR/NB/SNB/ANF/FreqB/Center/Attn/ActiveVfo/Ctun/MOX/Tun/FMStep +
ClientActiveChanged. Each calls a controller command directly.
- CAT context (Ctx.Set*) and CATSetFilterIdx repointed to the adapter,
since CAT reuses these as its command layer.
- Host-dependent handlers (Freq/Band/XvtrBand/Span/Run/Drive/connect/
disconnect/CRUD/mic) and the outgoing PushSpectrum mirror stay in
MainForm for now (WA2/WA3); IWebHost is the placeholder seam.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>