Commit Graph
6 Commits
Author SHA1 Message Date
ew8bakandClaude Opus 4.8 6b695ce756 feat: unify sample-rate presets across desktop + web UIs
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>
2026-06-23 13:52:03 +03:00
ew8bakandClaude Opus 4.8 d78d835485 feat: Pluto support in headless daemon (connect/autostart/discover/dev_add) + headless audio
The headless daemon (ewsdrd) only ever built HPSDR devices, so Pluto
could not be used: backend is chosen by Dev.Kind and Pluto opens by URI,
neither of which the daemon propagated. Also local audio played on the
host and web Discover never probed network Plutos.

Shared, backend-agnostic helpers in TRadioController (used by GUI + daemon):
- ResolveDevice(IP): discovered->saved lookup restoring Kind/URI/Serial/
  BoardType/MAC. MainForm.ResolveDevice now delegates here.
- AddSavedDevice(name, addr): web dev_add saves Pluto when addr has a URI
  scheme (ip:/usb:/local:), else HPSDR. Both web hosts use it.
- SeedPlutoProbeFromSaved: seed network-probe URIs from saved Plutos
  (no mDNS -> a network Pluto is only found by direct URI probe).
- LocalAudioEnabled flag (GUI=True): when False the local sound card is
  neither opened nor written; RX audio goes only to web (OnAudioConsume).

DeviceStore.AutoStartDevice(out Dev): full autostart record (Kind/URI/
Serial) so a saved Pluto (URI-addressed, empty IPAddress on USB) can
autostart.

Daemon (ewsdrd.lpr): LocalAudioEnabled:=False; SyncConnect via
ResolveDevice; autostart via AutoStartDevice; SyncDiscover seeds probe
URIs then Discover; SyncDevAdd via AddSavedDevice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 13:32:05 +03:00
ew8bakandClaude Opus 4.8 0d82d1d8e6 fix: refresh S-meter in headless daemon
FLastSMeter is sourced from WDSP (GetSMeterDBm), not the network HP-status,
so only the GUI timer (MainForm) ever updated it. In headless mode nothing
did, leaving PushState to send web clients a frozen -130 dBm. Refresh it in
the daemon Run loop before each PushState, mirroring the GUI timer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:17:07 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 8fd45bc266 Phase 5 (D4 fix): daemon WDSP wisdom, spectrum width, shutdown order
Three runtime fixes found testing the daemon on real hardware:

- WDSP opened in ~47s (vs <1s GUI): WDSPEngine.Open never imports FFTW wisdom;
  the GUI does it separately in EnsureWDSPWisdom before Open. Daemon now calls
  WDSPwisdom(GetAppCfgDir) before Open (imports the existing wdspWisdom00 in ms;
  builds it once if absent). Verified: open is now instant.

- Web waterfall/spectrum filled only the left ~6%: the analyzer's pixel width
  defaults to DISPLAY_BLOCK_SIZE (64). The GUI calls SetSpectrumWidth(panel) every
  tick; headless never did, so only 64 of the 1024 web buffer points had data.
  Daemon now calls SetSpectrumWidth(1024) after Open (== SPECTRUM_PIXELS / web
  buffer). FLastSpectrumW survives channel recreation.

- SIGINT during the (blocking) autostart connect was lost because Run set
  FRunning:=True *after* the connect, overwriting RequestStop. Set FRunning:=True
  before autostart so a stop during connect exits the loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:04:25 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 db361a274b Phase 5 (D4 fix): daemon shares GUI config dir (EWSDR app name)
GetAppConfigDir derives the dir from ApplicationName, which defaulted to the
binary name 'ewsdrd' -> a separate empty ~/.config/ewsdrd/. With no per-device
settings the daemon never configured WDSP's FFT/spectrum/waterfall display (web
showed a white waterfall and no spectrum), never resolved a saved board type
(Board=0), and -- critically -- found no FFTW wisdom, so WDSP recomputed it from
scratch on Open (~48s vs <1s in the GUI). Set OnGetApplicationName := 'EWSDR'
(matching the GUI's Application.Title) before constructing the controller, so the
daemon reads the same ~/.config/EWSDR/ (settings, saved devices, wdspWisdom00).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 15:55:34 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 de234b56e5 Phase 5 (D4): headless daemon entry point (ewsdrd)
ewsdrd.lpr — a console daemon that runs TRadioController + TWebAdapter WITHOUT
TMainForm/LCL, proving the Phase-5 goal: web-only headless operation. THeadlessHost
wires the data-path callbacks to the controller, the web command handlers to the
adapter (lifecycle ones marshalled to the main thread via FCtrl.Invoke =
TThread.Synchronize), opens WDSP synchronously, autostarts the saved device, and
runs a CheckSynchronize + periodic PushState loop. SIGINT/SIGTERM → graceful
StopAndDisconnect (persist + Run=0).

To keep the controller graph truly LCL-free, PlatformUtils now guards its only
Forms dependency (Screen.PixelsPerInch in CurrentScreenDPI) behind {$IFNDEF
HEADLESS}; the GUI build is byte-identical (HEADLESS undefined). build-ewsdrd.sh
compiles the headless graph from source with -Mobjfpc (nested comments) -dHEADLESS
into a separate lib-headless/ output dir, untouching the GUI .ppu.

Verified: builds clean; runs headless, web server up (HTTP 401 = serving), no LCL;
SIGINT shuts down gracefully. Radio-connect + web round-trip need hardware to test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 15:26:44 +03:00