Visual 24h timeline bar, inline entry editor, interleave progress ring, filterable checkbox list for bookmarks, status cards moved to top, SVG dot state badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Audit codebase against previous improvement list — all P0/P1/P2 items from
the prior review are now resolved or dropped. Restructured document with
resolved items in a collapsed section and identified new areas: decoder task
duplication, missing tests, decode log error handling, api.rs size, and others.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add the three missing VDES decoder components per ITU-R M.2092-1:
- turbo.rs: Turbo FEC decoder with dual 8-state RSC constituent
encoders, BCJR/MAP iterative decoding (8 iterations), QPP
interleaver, and rate-1/2 depuncturing
- crc.rs: CRC-16-CCITT validation (poly 0x1021, init 0xFFFF) for
decoded link-layer frames
- link_layer.rs: Structured parsing of M.2092-1 link-layer frames
(Messages 0-6) including station addressing, ASM identification,
geographic bounding boxes, and ACK/NACK reporting
The main decode pipeline now attempts turbo decoding first with CRC
validation, falls back to Viterbi when turbo fails, and reports
crc_ok=true when either path validates. 27 tests covering all new
modules.
https://claude.ai/code/session_01SJSN7cv3zoL1xNcb8ex2zY
Signed-off-by: Claude <noreply@anthropic.com>
Fixes dead_code warning on HttpAuthConfig::session_ttl().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Show the interactive setup wizard as the primary way to generate
trx-server.toml and trx-client.toml, with --print-config as an alternative.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
JSON-TCP frontend is for debugging only, not worth showing in the overview.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Replace single-server Mermaid diagram with two trx-servers: one with two
SDRs, the other with an SDR and FT-817, both feeding a single trx-client.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Convert ASCII art and box-drawing diagrams to Mermaid fenced code blocks
across README.md, CLAUDE.md, Architecture.md, Wxsat-Map-Overlay.md, and
trx-wxsat/README.md. Add Mermaid-only policy to CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Streamline README with centered header, feature summary table, collapsible
install commands, compact data-flow diagram, and documentation table linking
to wiki pages instead of duplicating content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add per-library descriptions, platform audio table, and concrete install
commands for Debian/Ubuntu, Fedora, Arch Linux, and macOS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Add lifetime parameter to lock_or_recover and fix missing .lock() call
- Replace undefined COMMAND_EXEC_TIMEOUT constant with local command_exec_timeout variable
- Add explicit type annotations to closure parameters in history snapshot methods
- Remove unused HostTrait import
- Fix non-existent machine_state/error_message fields on RigState in crash recovery
https://claude.ai/code/session_01HAkST2gLsYDXPom3282ABY
Signed-off-by: Claude <noreply@anthropic.com>
The bookmark_decoder_state() and apply_scheduler_decoders() functions
only handled aprs, hf-aprs, ft8, ft4, ft2, and wspr decoder kinds.
The "wxsat" and "lrpt" entries from bookmark.decoders were silently
ignored, so toggling a bookmark with NOAA APT or Meteor LRPT ticked
never sent SetWxsatDecodeEnabled / SetLrptDecodeEnabled commands.
https://claude.ai/code/session_0198fyXkA3jooddgQyD9FpRZ
Signed-off-by: Claude <noreply@anthropic.com>
The SchedulerConfig struct was missing a `satellites` field, so the
frontend's satellite configuration (enabled flag, pretune seconds,
satellite entries) was silently dropped by serde on every PUT request,
causing the setting to reset immediately.
Added SatelliteConfig, SatelliteEntry structs and the `satellites`
field to SchedulerConfig.
https://claude.ai/code/session_01FMcYoHGy5K21maudnntueB
Signed-off-by: Claude <noreply@anthropic.com>
Move ~230 lines of satellite pass scheduling code from scheduler.js
into a new sat-scheduler.js plugin with cached DOM refs, createElement-
based rendering, and a clean bridge API. Refactor sat.js predictions
view to deduplicate row builders, extract countdown timer lifecycle
management, and cache all DOM references.
https://claude.ai/code/session_0144nUfHAKs7yRnYTsozNagw
Signed-off-by: Claude <noreply@anthropic.com>
Add HTML, JS, and CSS for the satellite pass scheduling overlay in the
scheduler settings panel. The satellite section is always visible
regardless of the base scheduler mode (Grayline/TimeSpan) since it
operates as a preemption overlay.
UI features:
- Enable/disable toggle for satellite pass preemption
- Configurable pre-tune seconds (time before AOS to start tuning)
- Satellite entry table with add/edit/remove (satellite name, NORAD ID,
bookmark, min elevation, priority)
- Preset dropdown for common weather satellites (NOAA 15/18/19,
Meteor-M2 3/4) that auto-fills name and NORAD ID
- Bookmark selector for each satellite (sets freq, mode, decoders)
- Live pass status badge showing active satellite from scheduler status
- Status card shows "[SAT: name]" label when satellite pass triggers
- Scheduler control row visible when satellites enabled (even with
base mode disabled)
https://claude.ai/code/session_01WzWvhFVhEP9Fqn4u6pXs3T
Signed-off-by: Claude <noreply@anthropic.com>
Server-side:
- Cache index_html() with OnceLock (avoids 3 string replacements per request)
- Pre-compress all static assets (JS/CSS/HTML) with gzip at startup, serve
cached bytes with ETag + Cache-Control headers for browser caching
- Add If-None-Match / 304 Not Modified support for conditional GETs
- Serialize SSE state+meta in single serde pass via SnapshotWithMeta,
eliminating the serialize → parse → flatten → re-serialize round-trip
- Add Cache-Control: immutable for favicon/logo (never change)
Client-side:
- Replace atob() + charCodeAt loop with direct base64 lookup-table decoder
that writes to a reusable Int8Array (avoids UTF-16 string allocation)
- Spectrum bins now flow as Int8Array throughout the pipeline, reducing
waterfall row memory from ~8 bytes/element to 1 byte/element
- Add isBinsArray() helper to support both Array and TypedArray in all
spectrum/waterfall guard checks
https://claude.ai/code/session_01J3VCWZeEPsyFJiHjJRBREo
Signed-off-by: Claude <noreply@anthropic.com>
The render() function runs on every SSE event (5-20×/sec) and was
unconditionally writing to decoder toggle buttons and About-tab
decoder status elements — 8 getElementById calls + 32 DOM property
writes per frame — even when values hadn't changed. This caused
unnecessary style recalculation overhead on every SSE frame,
contributing to spectrum stuttering.
Changes:
- Cache all 7 decoder toggle button elements at module init instead
of calling getElementById on every render() call
- Track last-written enabled state per button; skip DOM writes when
the value is unchanged (steady-state cost: 0 DOM writes per frame)
- Same pattern for 8 About-tab decoder status elements
- Gate updateSatLiveState className/textContent writes on value change
Net effect: eliminates ~50 unnecessary DOM operations per SSE frame
during normal operation (decoders rarely toggle).
https://claude.ai/code/session_01G6wuNCkckbHHsU7w5zCtW2
Signed-off-by: Claude <noreply@anthropic.com>
Six hot-path optimizations that reduce per-frame CPU cost:
1. Waterfall color LUT: Pre-compute a 256-entry RGBA lookup table
(bins are i8 = 256 possible values) instead of calling
waterfallColorRgba() per-pixel with HSL→RGB math + Math.pow().
Eliminates ~2000+ HSL conversions per frame across both waterfalls.
2. Noise floor O(N)→O(N log N): Replace .slice().sort() with an
in-place quickselect algorithm for 15th-percentile estimation.
For 1024 bins this is ~10× faster.
3. Reuse spectrum bin buffers: SSE handler and buildSpectrumRenderData
now reuse pre-allocated arrays instead of creating new Array(N)
and .map() allocations every frame. Reduces GC pressure.
4. Cache canvas dimensions: drawSpectrum and drawSpectrumWaterfall
read cached CSS dimensions instead of querying clientWidth/
clientHeight every frame (which forces layout recalculation).
Dimensions refreshed on resize and layout changes.
5. Cache DOM references: getElementById calls for zoom indicator and
minimap elements moved to module-level constants instead of
querying the DOM on every drawSpectrum call.
6. Efficient array trimming: Peak hold pruning uses in-place splice
from front instead of .filter() (which allocates a new array).
Waterfall row trimming uses splice instead of repeated .shift().
https://claude.ai/code/session_01G6wuNCkckbHHsU7w5zCtW2
Signed-off-by: Claude <noreply@anthropic.com>
Three issues in the satellite predictions view caused page-wide
rendering performance degradation:
1. Unbounded DOM nodes: All satellite passes (200+ satellites × multiple
passes = 500-1000 rows with 5 spans each) were rendered at once,
creating thousands of DOM nodes that slowed style recalculation and
layout across the entire page. Now caps at 50 visible rows with a
"Show more" button.
2. No DOM cleanup on view switch: Prediction rows persisted in the DOM
when navigating away from the predictions view or the SAT tab,
bloating the page DOM indefinitely. Now clears prediction DOM when
leaving the predictions view or switching decoder tabs.
3. Countdown timer never paused: The 1-second setInterval with
querySelectorAll kept running even when the predictions view was
hidden, wasting CPU on invisible DOM queries. Now only runs when
predictions view is active, caches element references instead of
querying the DOM each tick, and auto-pauses when the view is hidden.
Also caches prediction DOM element references at module init instead
of calling getElementById on every render invocation.
https://claude.ai/code/session_01G6wuNCkckbHHsU7w5zCtW2
Signed-off-by: Claude <noreply@anthropic.com>
applyRigList() was called on every SSE state update (since `remotes`
is always present in the payload), and it unconditionally called
bmFetch() which fires 2x GET /bookmarks (list + overlay). At the
default poll rate this generated ~20 bookmark fetches/second — visible
as constant GET /bookmarks traffic on each spectrum render cycle.
Now track the previous rig list + active rig as a key and only
re-fetch bookmarks (and re-init scheduler/background-decode) when
the rig list actually changes.
https://claude.ai/code/session_017g7VNMb6CChaiWrfzVBhbR
Signed-off-by: Claude <noreply@anthropic.com>
Two issues introduced with wxsat/satellite support caused indirect
performance degradation on the spectrum rendering path:
1. spawn_tle_refresh_task() was called inside spawn_rig_audio_stack(),
which runs per-rig. With N rigs this spawned N redundant TLE refresh
tasks, each making 3 concurrent HTTP requests to CelesTrak and
competing for write locks on the global TLE store. Moved to a single
global call after the per-rig loop.
2. compute_upcoming_passes() (SGP4 propagation for 200+ satellites over
24h = ~300K propagation steps) ran on every GetSatPasses request with
no caching. Multiple client connections could trigger concurrent
CPU-heavy computations, causing cache pollution and tokio runtime
contention that indirectly slowed spectrum frame processing. Added a
60-second server-side cache shared across all client connections.
https://claude.ai/code/session_017g7VNMb6CChaiWrfzVBhbR
Signed-off-by: Claude <noreply@anthropic.com>
compute_upcoming_passes requires the TLE store to be populated by
CelesTrak fetches. If a client requests passes before the async NOAA
group fetch completes, NOAA-15/18/19 are missing from predictions.
Seed the store with hardcoded fallback TLEs synchronously in
spawn_tle_refresh_task before spawning the async fetch. CelesTrak
data overwrites these entries once fetched. Also adds pass sanity
tests for NOAA-15 and NOAA-18.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
CelesTrak GROUP=weather does not include legacy NOAA POES satellites.
Added GROUP=noaa fetch so NOAA-15/18/19 appear in predictions. Moved
GetSatPasses to a dedicated TCP connection (client) and spawn_blocking
(server) so pass computation never blocks state polling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
GetSatPasses responses with 100+ satellites easily exceed the previous
16KB limit, causing the remote client to disconnect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
TLE refresh now happens only on trx-server (once at startup, then every
24h). Client fetches satellite predictions from server via new
GetSatPasses fast-path command and caches them locally, refreshing
every 5 minutes. Removes spawn_tle_refresh_task from trx-client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add category selector (All/Weather/Ham Radio/Other) to predictions panel.
Split predictions into currently receivable passes with live countdown
timer and upcoming passes table. Add SatCategory enum to geo types
for CelesTrak group classification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
The TLE store is process-local; only the server was fetching TLEs from CelesTrak, leaving the client store empty and predictions always unavailable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Hardcoded fallback TLEs had approximate orbital elements (round numbers for RAAN, arg of perigee, mean anomaly) producing pass times hours off. Return empty predictions with a clear error when CelesTrak data is not yet available. Add TLE source and satellite count to the API response.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Iterate all TLE store entries (weather + amateur) for pass predictions instead of a hardcoded list. Add name/elevation filter bar to the predictions UI. Fix pre-existing missing fields in remote_client test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Add NOAA-15/18/19 and Meteor-M N2-3/N2-4 to predictions list
- Rename PREDICTION_SATS (was HAM_SATS) to include weather + ham sats
- Rename all wxsat identifiers to sat throughout JS/HTML/CSS/Rust:
wxsat.js → sat.js, WXSAT_JS → SAT_JS, /wxsat.js route → /sat.js,
all #wxsat-* element IDs, .wxsat-* CSS classes, window.addWxsat* →
window.addSat*, window.onServerWxsatImage → window.onServerSatImage,
etc. (backend protocol strings unchanged)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Rename "Weather Satellites" sub-tab to "SAT"
- Add "Predictions" view: next 24 h flyby table for 13 ham sats
(ISS, AO-91, AO-92, SO-50, AO-73, JO-97, PO-101, LilacSat-2,
CAS-4B, EO-88, RS-44, SALSAT, GREENCUBE)
- trx-core/geo: add PassPrediction, HAM_SATS, compute_upcoming_passes(),
find_passes_for_sat(), compute_az_el() helpers; spawn_tle_refresh_task
now also fetches CelesTrak amateur group on startup and every 24 h
- trx-frontend-http: add GET /sat_passes endpoint
- app.js: locator tooltips now accumulate all receivers per station
via remotes Set; _detailPassesRigFilter checks the Set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Fetch fresh weather satellite TLEs from CelesTrak on startup and then
once every 24 hours. The dynamic TLE store is checked first in
tle_for_satellite(), falling back to the existing hardcoded TLEs when
the fetch has not yet completed or fails.
- Add global TLE_STORE (RwLock<HashMap<norad_id, (line1, line2)>>)
- Add parse_tle_response() to parse 3-line TLE format
- Add refresh_tles_from_celestrak() async fetch + store update
- Add spawn_tle_refresh_task() for startup + daily refresh loop
- Refactor tle_for_satellite() into norad_id lookup + store check
- Spawn refresh task in trx-server alongside wxsat decoder tasks
- Add reqwest (rustls-tls) dependency to trx-core
https://claude.ai/code/session_01RB19i93dnemDYLcfrhyhqc
Signed-off-by: Claude <noreply@anthropic.com>
Add SGP4-based geo-referencing for NOAA APT and Meteor LRPT decoded
satellite images, enabling them to be displayed as semi-transparent
overlays on the Leaflet map module with ground track polylines.
Changes:
- Add sgp4 crate dependency to trx-core for orbital propagation
- New trx-core/src/geo.rs module with TLE-based pass geo-referencing,
ECI-to-geodetic conversion, and station-location fallback estimation
- Extend WxsatImage and LrptImage structs with geo_bounds and
ground_track optional fields (backward compatible via serde defaults)
- Compute geo-bounds in finalize_wxsat_pass and finalize_lrpt_pass
using satellite identity, pass timestamps, and station coordinates
- Add 'wxsat' source filter to the map module (off by default)
- Add L.imageOverlay rendering with popup and ground track polyline
- Add "Show on Map" buttons in wxsat plugin live/history views
https://claude.ai/code/session_01DUCfb9CjGoViwBrznpfWyt
Signed-off-by: Claude <noreply@anthropic.com>