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>
- 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>
- 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>
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>
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>
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>
Replace the DC-component approach (which underreads FM due to carrier deviation) with peak |s|² on the filtered+decimated IQ before AGC is applied. Works correctly for both constant-envelope FM and narrowband modes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
EMA (α=0.4) smooths the carrier power estimate across DSP blocks. Custom PartialEq on VchanRdsEntry excludes signal_db so rapidly-changing levels do not trigger main state SSE updates that overwrite the frequency input.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use the DC component of the baseband-mixed IQ (before LPF/decimation) as a narrow-band carrier power estimate. This correlates with the spectrum FFT peak instead of measuring wideband channel power which inflates the reading for WFM.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- RRC span 4→6 chips: better ISI rejection and pulse energy capture
- PI_ACC_THRESHOLD 3→5: more Block A votes before committing PI at weak signal
- OSD(3): add C(26,3)=2600 triple-bit search under same cost gate as OSD(2)
- Tech 11 Gardner TED: closed-loop symbol timing PI loop per Candidate;
replaces open-loop NCO with mid-chip capture, power-normalised error signal,
anti-windup integrator, and ±1% pull-in range (±23.75 Hz at 2375 chips/s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- RRC_ALPHA 0.75→0.50: narrower noise BW, ~0.6 dB SNR gain
- COSTAS_KI 3.5e-7: maintain ζ≈0.68 (1e-6 caused loop instability)
- Soft confidence: use biphase_i.abs() instead of full vector magnitude
so OSD confidence is aligned with bit-decision sign; suppresses
false groups under noise with residual Costas phase error
- OSD(2) in locked mode: corrects ≤2-bit errors after block sync
- Search mode: hard decode only for Block A; OSD(1) in search yielded
~13% false Block A rate per bit, letting wrong clock candidates
accumulate false groups as fast as the correct candidate
- Incumbent candidate tracking (best_candidate_idx): the winning
candidate updates best_state at equal score; challengers need strictly
higher score; best_score tracks incumbent even on no-state-change
groups so challengers can't leapfrog on a single false group
- blocks_to_chips: add NRZI (NRZ-Mark) pre-encoding so the differential
biphase decoder recovers actual data bits rather than XOR-of-pairs
- Add blocks_to_chips_round_trips_all_groups test: verifies all 16 blocks
across 4 PS segments round-trip correctly without BPSK modulation
[fix](trx-backend-soapysdr): lower pilot lock threshold for weak-signal RDS
- PILOT_LOCK_THRESHOLD 0.25→0.20, add PILOT_LOCK_ONSET=0.30 constant
- Pilot reference engages at coherence ≥0.36 (was ≥0.45)
WIP: end_to_end_clean_signal_decodes_ps still failing (13/15 pass).
Decoder skips segment 2 due to ISI from rectangular test chips through
RRC receive filter. chips_to_rds_signal needs RRC pulse shaping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Lower PILOT_LOCK_THRESHOLD 0.5 -> 0.25 so the accurate 57 kHz pilot-derived
carrier reference is handed to the RDS decoder even with a weaker pilot tone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Add single-bit flip fallback in search mode (push_bit_soft) so Block A
can be acquired with one bit error, matching locked-mode OSD(1) behaviour
- Lower MIN_PUBLISH_QUALITY 0.38 -> 0.20 for earlier publish on noisy signals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Replace FirFilter (ring-buffer FIR) with FftRrcFilter using overlap-save
FFT convolution; I and Q are processed together as a single complex FFT,
halving filter cost (~10x fewer operations than direct convolution)
- Reduce PHASE_CANDIDATES 16 -> 8 (reasonable, double the original)
- Lower MIN_PUBLISH_QUALITY 0.55 -> 0.38 (more permissive acquisition)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
The IQ prefilter cutoff was audio_bandwidth_hz/2, so any setting below
~120 kHz would cut off the 57 kHz RDS subcarrier before FM demod.
- Clamp IQ prefilter cutoff to >= 60 kHz for WFM in both new() and
rebuild_filters() — audio quality is unaffected since WfmStereoDecoder
applies its own 18 kHz lowpass internally
- Ensure pipeline target rate >= 120 kHz for WFM so the decimated IQ
sample rate can represent the 60 kHz cutoff
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Downgrade OSD from distance-2 to distance-1 (removes 325-iteration
double-bit flip loop per block, main source of both false positives
and excess CPU)
- Reduce phase candidates from 8 to 4 (halves per-sample work)
- Raise MIN_PUBLISH_QUALITY from 0.45 to 0.65 (requires stronger
signal confidence before emitting decoded state)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Track per-rig server connection state in `rig_server_connected` so that when
one trx-server drops, only the rig(s) it serves are marked disconnected. Other
rigs with active connections remain fully interactive. The SSE `server_connected`
field is now resolved from the per-rig map for the session's active rig, falling
back to the global flag for backward compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Tech 1: replace one-pole baseband LPF with FIR RRC matched filter
(alpha=0.75, 4-chip span) — largest single measured improvement per
empirical comparison (gr-rds RRC vs plain FIR: 32/38 vs 18/38 stations).
Tech 2: 19 kHz pilot x3 -> 57 kHz coherent carrier reference via the
triple-angle formula; fed from the WFM pilot Costas PLL when
pilot_lock_level > 0.5, clearing to NCO fallback otherwise.
Tech 3/7/8: OSD(2) soft-decision block decoder replaces hard CRC check.
Per-bit soft magnitudes accumulated in Candidate::block_soft[26].
decode_block_soft() searches Hamming distance 0/1/2 (352 trials total)
and returns the minimum Euclidean-cost valid codeword; ~2-3 dB gain.
Tech 4: 8th-order 57 kHz BPF (4 cascaded biquads at Q=5) in wfm.rs
replaces the previous single Q=10 biquad; ~6x steeper ACI stopband.
Tech 5: Costas loop with tanh soft phase detector drives the RDS carrier
NCO when no pilot reference is available (P+I, B_L ~20 Hz).
Tech 6: Block A PI field LLR accumulation — signed per-bit LLR summed
over 3 independent Block A observations before committing the PI value,
correcting weak-signal false locks without delaying strong-signal lock.
Tech 9: 8-tap complex CMA blind equalizer applied to IQ samples before
FM discrimination; constant-modulus error (|y|^2 - R^2) drives tap
adaptation without a training sequence, suppressing adjacent-channel
interference at the source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use saturating CAS loop in adjust_total_count to prevent AtomicUsize underflow, and cap history estimate at 500k entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Remove overkill peak frequency labels from spectrum view. Set waterfall
height to match spectrum height (1:1 split) instead of fixed 120px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add 8 enhancements to the spectrum display:
1. Noise floor reference line — dashed horizontal line at estimated
noise floor (15th-percentile heuristic)
2. Peak frequency labels — top 5 strongest peaks labeled with
frequency text on the spectrum canvas
3. Crosshair lines — vertical + horizontal guide lines follow
cursor on hover for precise frequency/dB reading
4. Zoom indicator + minimap — shows current zoom level (e.g. "4.0x")
and a minimap showing the visible window within the full span
5. dB range control — new Range input alongside Floor, with Auto
button updating both; allows direct control of vertical span
6. Keyboard shortcuts — Arrow Left/Right to pan, +/- to zoom,
0 to reset zoom; documented in hint bar
7. Full waterfall panel — WebGL waterfall canvas below the spectrum
plot, synchronized with zoom/pan, with scroll/click/drag support
8. Signal overlay extended — overlay height now includes waterfall
canvas for consistent BW/bookmark/freq marker coverage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add 8 enhancements to the spectrum display:
1. Noise floor reference line — dashed horizontal line at estimated
noise floor (15th-percentile heuristic)
2. Peak frequency labels — top 5 strongest peaks labeled with
frequency text on the spectrum canvas
3. Crosshair lines — vertical + horizontal guide lines follow
cursor on hover for precise frequency/dB reading
4. Zoom indicator + minimap — shows current zoom level (e.g. "4.0x")
and a minimap showing the visible window within the full span
5. dB range control — new Range input alongside Floor, with Auto
button updating both; allows direct control of vertical span
6. Keyboard shortcuts — Arrow Left/Right to pan, +/- to zoom,
0 to reset zoom; documented in hint bar
7. Full waterfall panel — WebGL waterfall canvas below the spectrum
plot, synchronized with zoom/pan, with scroll/click/drag support
8. Signal overlay extended — overlay height now includes waterfall
canvas for consistent BW/bookmark/freq marker coverage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Nudge state watch when server_connected goes false so SSE delivers the change. Frontend applies a desaturated frost + banner instead of a blocking overlay, keeping the last-known state visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Decoder bar overlays (AIS, VDES, FT8, APRS, RDS) use backdrop-filter
blur for a frosted-glass look in the browser, but this can't be
replicated on canvas — resulting in opaque blocks covering the spectrum
in screenshots. Cap their background alpha to 0.35 when rendering to
the snapshot canvas.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Press F1 to toggle a help overlay listing available keyboard shortcuts.
Dismiss with F1, Escape, or clicking the backdrop. Refactored the
global keydown handler to route all shortcuts through one listener.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Validates existing TOML config files for syntax correctness, unknown
keys, and structural issues. Auto-detects config type (server, client,
combined) and checks known sections against expected schema.
Validates: log levels, coordinate ranges, port ranges, access types,
lat/lon pairing, and unknown key warnings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
New binary crate that generates trx-server.toml, trx-client.toml, or
trx-rs.toml via interactive prompts or --defaults mode. Produces
commented TOML using toml_edit with per-field descriptions.
Supports server config (general, rig, listen, audio, behavior) and
client config (general, remote, frontends). Hardware detection is
stubbed for future iteration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Order bookmark mode and bandwidth updates so WFM bookmarks do\nnot race against the backend mode default.\n\nAlso apply saved bookmark bandwidth in the scheduler path so\nscheduled bookmark replays keep the configured filter width.\n\nTested with:\n- cargo test -p trx-frontend-http\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Radio paths now originate from the rig that decoded the message rather
than the currently selected rig. Bookmark locators no longer draw radio
paths. Rig switch no longer tears down decode pipeline since it is
rig-independent. Mobile spectrum controls use flex-wrap for better layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add latitude/longitude to /rigs API response. Map now displays receiver
markers for all configured rigs, de-duplicated by location. Decode history
is no longer filtered to the active rig so all remotes contribute to the map.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use the merged bookmarks endpoint instead of separate fetches so general
bookmarks are always visible alongside rig-specific ones.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
WebCodecs AudioDecoder does not support Opus on Safari. Fall back to
opus-decoder WASM library (loaded from CDN) for browsers without
WebCodecs Opus support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Scope picker filters the bookmarks table for editing. Spectrum and map
always show merged general + active rig bookmarks via bmOverlayList.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Fix clippy warning by adding Default impl for BookmarkStoreMap. Scheduler
bookmark picker now fetches both general and rig-specific bookmarks and
merges them so all available bookmarks are shown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Two-tier bookmark system: general bookmarks shared across all rigs plus
rig-specific bookmarks with scope picker in the Bookmarks tab. Scheduler
storage split into per-rig files with migration from legacy single file.
Decode history tagged with rig_id and filterable via ?remote= on
/decode/history endpoint. Decode SSE reconnects on rig switch to refresh
filtered history.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
watch::Sender::send() silently discards the value when receiver_count
is zero. The per-rig info channel is created with watch::channel().0
which drops the only Receiver, so the first send(Some(info)) after TCP
connect returns early without storing the value. Later subscribers
see None forever. send_replace() stores unconditionally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use only the per-rig stream info when a remote is specified on the
channel audio path; do not fall back to the global channel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
The audio client was clearing per_rig_info_tx to None every time the
TCP connection dropped, even during transient reconnect cycles. This
caused WebSocket clients subscribing to per-rig audio to stall
indefinitely waiting for stream info that would only arrive after the
next successful reconnect.
Move the None send to the permanent shutdown path only. The last-known
stream info remains valid for the same rig across reconnects.
Also revert the global info_rx fallback from 6e4c5e3 since the root
cause is now fixed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Per-rig info_rx watch channel is transiently None when the rig's audio
TCP connection is between reconnect cycles. This caused the WebSocket
handler to hang indefinitely waiting for stream info that might never
arrive, regressed in 7d76606.
Prefer the per-rig info_rx when it holds a value, otherwise fall back to
the global info channel (the pre-regression behaviour).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Trace per-rig audio subscription lookup, stream info availability, and
session lifecycle to diagnose multi-rig audio regression.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Send the selected remote together with virtual-channel audio requests and use per-rig stream info when /audio is opened with channel_id.
This keeps browser channel audio aligned with the requested remote after the recent multi-rig client changes.
Add coverage for parsing channel_id together with remote.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Create and resume the RX AudioContext from the audio button click so Chromium does not leave playback suspended until a later interaction.
Reuse that context when stream metadata arrives instead of recreating it from the WebSocket message path.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Remove the remaining legacy rig_id aliases across the HTTP frontend and use remote consistently for scheduler and audio requests.
Disable browser caching for the HTML, CSS, and JS assets so clients pick up the parameter rename immediately instead of running stale frontend code.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Parse the renamed `remote` query parameter on `/audio` while keeping
`rig_id` as a compatibility alias, and use per-rig stream info for
rig-scoped audio subscriptions.
Add tests covering both query names.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Keep global state and spectrum updates scoped to the server group that
owns the selected short name, so other servers cannot overwrite the UI
or clear the active spectrum stream.
Add regression tests for cross-server selection and spectrum ownership.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Merge per-server GetRigs updates into the shared remote rig cache
instead of replacing it, so audio tasks from other servers are not
torn down on each poll cycle.
Add a regression test covering the multi-server cache update path.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use full audio endpoint URLs for trx-server audio connections while
preserving server-advertised ports and legacy port-based fallback for
backward compatibility.
Add `server_url` and per-remote `rig_urls` config entries, plus
validation and tests for audio URL parsing and address resolution.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Rename HTTP query params, JSON fields, and scheduler payloads to
use remote names consistently while still accepting legacy `rig_id`
inputs through serde aliases.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use the remote short name for the HTTP frontend default selection and
keep `default_rig_id` as a serde alias for backward compatibility.
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Introduce [[remotes]] config array where each entry maps a user-chosen
short name to a (server URL, rig_id) pair. Short names replace raw
rig_ids as the universal key throughout frontends, audio routing, and
state management, allowing rig_ids to safely collide across servers.
Entries sharing the same server URL and token share a single TCP
connection. A request routing dispatcher forwards frontend commands to
the correct per-server channel based on the short name.
Legacy [remote] config and CLI --url are preserved via automatic
fallback to a single-entry remotes list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Include active rig display name and nearest city in the browser tab
title: "freq - rig name - city, country - trx-rs v<version>".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add checkbox to enable/disable NB and number input for threshold (1-100).
Controls are hidden by default and shown when the server reports NB support
via SSE filter state updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>