Commit Graph

1264 Commits

Author SHA1 Message Date
sjg d3f958fc37 [fix](trx-server): uppercase DEFAULT_COMMAND_EXEC_TIMEOUT constant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-29 11:40:44 +02:00
sjg 944fd4a0fc [refactor](trx-rs): fix clippy too_many_arguments warnings
Bundle parameters into structs to reduce argument counts:
- geo.rs: find_passes_for_sat takes &TleEntry instead of individual fields
- listener.rs: handle_client takes ClientContext struct

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-29 11:38:32 +02:00
Claude 01d0b9efdd [fix](trx-server): fix compilation errors in audio, rig_task, and main
- 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>
2026-03-29 09:52:14 +02:00
Claude 6c08ff4776 [fix](trx-server): wire TimeoutsConfig into example config and fix test signatures
- Include TimeoutsConfig in --print-config example output
- Update run_listener() test call sites with new parameters

https://claude.ai/code/session_01P9G7QCWfiYbPVJ7cgiXznf
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-29 08:54:59 +02:00
Claude 16426548de [refactor](trx-rs): resolve all P1/P2 improvement areas
P1 (High Priority):
- Fix LIFO command batching in rig_task.rs (batch.pop→batch.remove(0))
- Add ±25% jitter to ExponentialBackoff to prevent thundering herd
- Add 10,000-entry capacity bounds to decoder history queues
- Add rig task crash detection with Error state broadcast
- Decompose FrontendRuntimeContext 50-field god-struct into 9 sub-structs
  (AudioContext, DecodeHistoryContext, HttpAuthConfig, HttpUiConfig,
   RigRoutingContext, OwnerInfo, VChanContext, SpectrumContext, PerRigAudioContext)
- Migrate std::sync::RwLock to tokio::sync::RwLock in background_decode.rs
- Extract find_input_device/find_output_device helpers from audio pipeline

P2 (Medium Priority):
- Introduce SoapySdrConfig builder struct (replaces 20+ positional params)
- Add define_command_mappings! macro for ClientCommand↔RigCommand mapping
- Replace silent lock poison recovery with lock_or_recover() warning logger
- Make timeouts configurable via RigTaskConfig/ListenerConfig and TOML
- Extract shared config types to trx-app/src/shared_config.rs

Documentation updated in CLAUDE.md, Architecture.md, Improvement-Areas.md.

https://claude.ai/code/session_01P9G7QCWfiYbPVJ7cgiXznf
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-29 08:54:59 +02:00
sjg 0a60684e28 [feat](trx-rs): remove NOAA APT decoder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 23:32:10 +01:00
Claude 804b0d8846 [fix](trx-frontend-http): wire wxsat and lrpt decoders into bookmark toggle
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>
2026-03-28 21:10:02 +01:00
Claude 9461ba2a85 [fix](trx-frontend-http): persist satellite pass preemption config
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>
2026-03-28 20:53:38 +01:00
Claude 4eac4458bf [fix](trx-frontend-http): add NOAA APT and Meteor LRPT to bookmark decode checkboxes
The Add Bookmark popup was missing NOAA APT (wxsat) and Meteor LRPT
decoder checkboxes. Added them to the HTML form, the read/write
functions, and the decoder toggle logic when applying bookmarks.

https://claude.ai/code/session_01FMcYoHGy5K21maudnntueB
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 20:53:38 +01:00
Claude 891141489c [refactor](trx-frontend-http): extract satellite scheduling UI into dedicated module
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>
2026-03-28 20:38:58 +01:00
Claude 3e3fdbcb30 [feat](trx-frontend-http): add satellite scheduler UI in web frontend
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>
2026-03-28 20:21:29 +01:00
Claude 8e700fb98a [feat](trx-frontend-http): optimize HTTP frontend performance
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>
2026-03-28 20:20:33 +01:00
Claude 731410a7e6 [fix](trx-frontend-http): skip redundant DOM writes in render() hot path
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>
2026-03-28 19:09:15 +01:00
Claude aacc7336d7 [fix](trx-frontend-http): optimize spectrum and waterfall rendering performance
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>
2026-03-28 19:00:58 +01:00
Claude 9920094008 [fix](trx-frontend-http): fix SAT prediction page degrading whole-page rendering
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>
2026-03-28 18:51:42 +01:00
Claude 842ee6f076 [fix](trx-frontend-http): stop fetching /bookmarks on every SSE state update
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>
2026-03-28 18:34:17 +01:00
Claude afaf19d2b4 [fix](trx-server): fix satellite pass computation degrading spectrum performance
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>
2026-03-28 18:25:40 +01:00
sjg 1da42f2442 [fix](trx-core): seed TLE store with hardcoded NOAA/Meteor TLEs at startup
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>
2026-03-28 17:56:38 +01:00
sjg 47a85d9832 [fix](trx-rs): add NOAA-15/18/19 TLEs and move sat pass refresh off main connection
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>
2026-03-28 17:39:02 +01:00
sjg 91f50ebb3f [fix](trx-rs): increase JSON line limit to 256KB for large sat pass responses
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>
2026-03-28 17:24:47 +01:00
sjg 2f7adf05c8 [feat](trx-rs): add GetSatPasses protocol command for server-side TLE management
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>
2026-03-28 15:38:39 +01:00
sjg e831dff85d [feat](trx-rs): rework satellite predictions with category filter and live countdown
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>
2026-03-28 15:38:27 +01:00
sjg 82529c54d4 [fix](trx-client): spawn TLE refresh task for satellite pass predictions
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>
2026-03-28 15:08:48 +01:00
sjg 171a1f4bbc [feat](trx-frontend-http): add F hotkey to focus frequency input
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 14:34:52 +01:00
sjg 3003cf0df6 [fix](trx-rs): don't use fabricated TLEs for satellite pass predictions
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>
2026-03-28 14:32:24 +01:00
sjg a0c92df86f [feat](trx-rs): show all satellites in predictions with filter bar
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>
2026-03-28 14:07:51 +01:00
sjg aab344b729 [feat](trx-rs): add NOAA/Meteor predictions; rename wxsat → sat
- 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>
2026-03-28 13:53:26 +01:00
sjg adec33708f [feat](trx-rs): add ham sat pass predictions; rename SAT tab
- 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>
2026-03-28 13:42:57 +01:00
Claude 27117a8de5 [feat](trx-core): add periodic TLE refresh from CelesTrak
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>
2026-03-28 12:41:31 +01:00
Claude 929f1d3fab [fix](trx-server): remove unused pass_start_ms variable in wxsat decoder
The variable was assigned in six places but never read, producing
compiler warnings. The other `pass_start_ms` in `run_wxsat_image_task`
is unaffected and still used.

https://claude.ai/code/session_01SWe5x4CWy1q6BXtX4Gt3Qd
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 12:35:29 +01:00
Claude 560b6ec912 [feat](trx-rs): add weather satellite map overlay integration
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>
2026-03-28 12:24:36 +01:00
Claude 3ff848a715 [docs](trx-wxsat): add README with architecture and API documentation
https://claude.ai/code/session_01Cm1JpWMDZanjwKg3r2S3VR
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 11:23:33 +01:00
Claude 83a6b27130 [fix](trx-frontend-http): fix missing wxsat/lrpt history functions and lrpt_decode_enabled field
Remove calls to non-existent clear_wxsat_history and clear_lrpt_history
functions from the client-side clear endpoints. These image-based decoders
don't maintain client-side history unlike text decoders. The server-side
reset command (already sent) handles the cleanup. Also add missing
lrpt_decode_enabled field to the fallback RigSnapshot initializer.

https://claude.ai/code/session_019FkSMWpGR3XpWBvUghCybe
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 11:06:20 +01:00
Claude 384e1597f6 [style](trx-wxsat): apply cargo fmt formatting
https://claude.ai/code/session_01JA13DHuzuHUL4nSBBRU83f
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 10:36:34 +01:00
Claude 6d0c01c6c4 [feat](trx-frontend-http): add Live/History views to Weather Satellites panel
Replace flat image list with two switchable views:
- Live: decoder state cards (Idle/Listening), descriptions, latest image
- History: filterable table with columns for time, type, satellite,
  channels, lines, and download link. Supports text filter, type filter
  (All/APT/LRPT), and sort order (newest/oldest).

https://claude.ai/code/session_01JA13DHuzuHUL4nSBBRU83f
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 10:36:34 +01:00
Claude 4d40c29e49 [refactor](trx-wxsat): unify image encoding to shared PNG module
Extract common image_enc module at crate root with encode_grayscale_png
and encode_rgb_png helpers. Both NOAA APT and Meteor-M LRPT now use PNG
as the output format through the shared encoder. Drop jpeg image feature
dependency.

https://claude.ai/code/session_01JA13DHuzuHUL4nSBBRU83f
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 10:36:34 +01:00
Claude 1a3b815ed8 [feat](trx-wxsat): add Meteor-M LRPT decoder and Weather Satellites frontend panel
Restructure trx-wxsat into noaa/ (APT) and lrpt/ (Meteor-M LRPT) submodules
with shared crate base. Add QPSK demodulator, CCSDS CADU framer, MCU channel
assembler for LRPT. Wire LRPT through full stack (core types, protocol, server
decoder task, client). Add Weather Satellites sub-tab in Digital Modes with
toggle buttons for NOAA APT and Meteor LRPT, descriptions, and image history.

https://claude.ai/code/session_01JA13DHuzuHUL4nSBBRU83f
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 10:36:34 +01:00
Claude d26ef6ca81 [feat](trx-wxsat): rename trx-noaa to trx-wxsat with full NOAA APT decode
Rename the crate from trx-noaa to trx-wxsat (weather satellite) across
the entire workspace. Add full NOAA satellite decode support:

- Telemetry frame parsing: extract 16-wedge calibration data from the
  128-line telemetry frames embedded in APT lines
- Radiometric calibration: piecewise-linear LUT built from wedges 1-8
  to correct pixel values against known reference levels
- Channel identification: detect AVHRR sensor channels (VIS, NIR, MIR,
  TIR) from wedge 9 values per APT sub-channel
- Satellite identification: heuristic NOAA-15/18/19 detection from
  channel A/B sensor pairings
- Histogram equalisation: per-channel contrast enhancement for improved
  image output
- WxsatImage now carries satellite name and channel labels in decoded
  message broadcasts

https://claude.ai/code/session_01JA13DHuzuHUL4nSBBRU83f
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-28 10:36:34 +01:00
sjg e0181c99da [fix](trx-client): handle NoaaImage in replay history sink
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 07:12:13 +01:00
sjg f148cc05fc [fix](trx-frontend-http): handle NoaaImage in snapshot and decode match
- Add noaa_decode_enabled to the fallback RigSnapshot initializer in api.rs
- Add NoaaImage arm (no-op) to the DecodedMessage match in audio.rs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 07:10:07 +01:00
sjg 450a5388bf [fix](trx-server): fix NOAA decoder warnings and Send bound
- Remove unused chrono::Local import (use fully-qualified path)
- Drop watch::Ref before .await in state-change branch to satisfy Send
- Remove unused pass_start_ms parameter from finalize_noaa_pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 07:06:39 +01:00
sjg 4b40d44814 [feat](trx-noaa): add NOAA APT satellite image decoder
New trx-noaa crate: FFT-based Hilbert transform (rustfft) for 2400 Hz
AM demodulation, sync A detection via cross-correlation, line assembly
at 4160 Hz, and JPEG output via the image crate.

- trx-core: NoaaImage type, DecodedMessage::NoaaImage variant,
  noaa_decode_enabled/noaa_decode_reset_seq on RigState/RigSnapshot,
  AUDIO_MSG_NOAA_IMAGE = 0x16
- trx-server: DecoderHistories::noaa, run_noaa_decoder task (activates
  on noaa_decode_enabled, auto-finalises after 30 s silence), saves
  JPEGs to ~/.cache/trx-rs/noaa/<YYYY-MM-DD_HH-MM-SS>.jpg, forwards
  events over TCP audio channel and history replay

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-28 07:00:24 +01:00
Claude cf79df110a [fix](trx-frontend-http): map filter affects statistics, tooltips show receiver rig
Statistics panels (longest paths, strongest/weakest signals) now respect
all active map filters — source type, rig selector, band, search, and
history. Locator tooltips display which rig received each decoded frame.

https://claude.ai/code/session_01LT7zBnb2kQiYpeTuWNXHsT
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 22:01:43 +01:00
Claude 12726f3d2c [feat](trx-frontend-http): add R hotkey to round frequency up to next step
Remap retune from R to T, and add a new R hotkey that rounds the
current frequency forward to the next jog-step boundary. Both the
new hotkey and the remapped retune are documented in the F1 help
overlay.

https://claude.ai/code/session_017neG2jL9uXFSRpmhyS1EqG
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 21:52:28 +01:00
Claude 7c33af3d83 [feat](trx-frontend-http): add keyboard hotkeys for radio controls
Map R=retune, B=previous state, [/]=bandwidth ±10kHz, arrows=tune/center,
M=mode picker, Z=mono/stereo, N=noise blanker, Q=squelch toggle.
Document all shortcuts in the F1 help overlay.

https://claude.ai/code/session_01WDC889uQGW9XoPQqSZ6bVt
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 21:44:05 +01:00
Claude 71fe884ae6 [fix](trx-frontend-http): show all rigs data on map panel regardless of active rig
Remove rig_id filtering from dispatchDecodeMessage and dispatchDecodeBatch
so that decode data from all rigs (including remote/non-primary) flows into
the map. Also remove the rig_filter query param from decode history fetch
so all history is loaded. The existing map rig filter dropdown handles
visibility filtering via marker.__trxRigIds.

https://claude.ai/code/session_01GGvdXKdEbRBnJa2BjAQuVB
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 21:21:25 +01:00
Claude 0905360ba5 [fix](trx-backend-soapysdr): remove noise/pilot dependency from WFM signal strength
The noise floor subtraction was over-aggressive: the bandwidth ratio
scaling between the 67 kHz baseband probe and the IQ domain amplified
the noise estimate excessively, causing weak stations to be subtracted
to nothing.  The pilot-referenced correction only worked for stereo
stations.

Strip the signal strength path back to what actually works universally:
mean IQ envelope power with asymmetric attack/decay smoothing.  This
always produces a reading for any FM signal — mono, stereo, with or
without RDS.

The baseband noise probe, CNR estimation, and pilot metrics remain in
the WfmStereoDecoder for their existing uses (RDS quality weighting,
CCI/ACI estimation) but no longer feed into the S-meter.

https://claude.ai/code/session_017URSDqSJ8TyZpDhV2vKZUe
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 20:50:06 +01:00
Claude 41fa9dc242 [feat](trx-backend-soapysdr): proper WFM signal strength algorithm
Replace the simple IQ power averaging with a proper WFM signal
strength measurement algorithm based on established RF engineering
practice:

1. Asymmetric attack/decay smoothing (τ_attack=2ms, τ_decay=300ms)
   per IARU Region 1 Technical Recommendation R.1 for professional
   S-meter behaviour.  Fast attack catches signal increases
   immediately; slow decay provides stable, readable meter movement.

2. Baseband noise floor estimation via a 67 kHz probe in the
   demodulated FM baseband.  FM demodulation noise follows an f²
   spectral shape, so energy above the useful baseband (audio +
   RDS ≤ 57 kHz) is dominated by channel noise and independent of
   program content.  Subtracting this noise estimate in the linear
   domain reveals the carrier-only power, preventing the meter from
   reading the noise floor on empty/weak channels.

3. Pilot-referenced quality correction.  The 19 kHz stereo pilot
   has a known fixed amplitude at the transmitter (±7.5 kHz
   deviation, 10% of ±75 kHz).  Near the FM threshold (~10 dB CNR)
   where noise dominates the IQ reading, the pilot tone power
   provides an independent quality-weighted correction.  The blend
   factor scales from 0.3 at low CNR down to 0 at high CNR where
   the raw IQ measurement is already accurate.

4. CNR estimation from the ratio of total baseband power to the
   above-band noise probe, enabling adaptive pilot correction and
   providing a signal quality metric for future use.

https://claude.ai/code/session_017URSDqSJ8TyZpDhV2vKZUe
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 20:33:09 +01:00
sjg 0efdb5e360 [fix](trx-rs): show signal strength with decimal precision
Change RigRxStatus.sig from i32 to f64 and add get_signal_strength_db
to RigCat trait so SDR backends can bypass the coarse 0..15 quantisation.
Compensate for decimation processing gain so the meter matches the
spectrum peak. Display with one decimal place in all units.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-27 20:06:36 +01:00
sjg d468a96448 [fix](trx-backend-soapysdr): stabilize WFM signal strength and speed up SDR polling
Smooth envelope power (I²+Q²) instead of filtering I/Q components
separately — eliminates ~6 dB modulation-dependent fluctuation caused
by FM carrier rotation in the IQ plane. Reset signal strength on
frequency change. Reduce SDR poll interval from 500ms to 100ms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-27 18:52:12 +01:00