Commit Graph
4 Commits
Author SHA1 Message Date
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