Serve decode history as gzipped CBOR and decode it in the frontend.
Defer map materialization until replay completes to avoid replay-time stutter,
and include the pending longest-QSO style adjustment.
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Let users click longest-QSO cards to isolate a single contact path on the map and click again to restore all visible contact paths. Also remove the extra inner panel styling from decode map tooltips so the popup renders as a single container.
Verification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Verification: git diff --check -- src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Keep map history data cached when the history window is reduced so older APRS, AIS, VDES, FT8, and WSPR items can be shown again when the user expands the window, and add a global decode-history replay overlay with progress updates across the UI. Also update the longest QSO summary to render bidirectional contacts with <-> labels.
Verification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Verification: git diff --check -- src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add a map summary section below the map that lists the five longest directed FT8 and WSPR contacts in the current view, including distance, band, age, and locator details.
Verification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Verification: git diff --check -- src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add a map filter-panel history picker with 15 minute through 24 hour retention options and prune dynamic APRS, AIS, VDES, FT8, and WSPR overlays to the selected age window.
Verification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Write compressed audio-history replay directly into the local frontend history buffers so large APRS and AIS replays survive trx-client restart instead of overrunning the live decode broadcast channel.
Verification: cargo test -p trx-client --no-run
Verification: cargo test -p trx-frontend-http --no-run
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Automatically return control to the scheduler after using the Previous or Next entry controls so manual stepping does not leave the session latched in takeover mode.
Verification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Apply scheduler-backed virtual channels as real manual selections so they take control, retune the rig, and restore bookmark decoder state including APRS/PKT. Also remove the inner border from the map decode locator tooltip.\n\nVerification: cargo test -p trx-frontend-http vchan\nVerification: node --check src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Keep scheduler-managed virtual channels reconciled while\nclients remain connected, instead of only materializing\nthem during the initial connect path.\n\nVerification: cargo test -p trx-frontend-http vchan\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Materialize scheduler-managed virtual channels before the\ninitial channels SSE event when the scheduler currently\ncontrols the rig.\n\nVerification: cargo test -p trx-frontend-http vchan\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use the currently tuned virtual channel for the website title\ninstead of always showing channel 0 metadata.\n\nVerification: node --check assets/web/app.js\nVerification: node --check assets/web/plugins/vchan.js\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Track the last applied scheduler entry so previous/next\ncycles correctly across active entries and resets the\ncountdown after manual entry changes.\n\nVerification: cargo test -p trx-frontend-http scheduler\nVerification: node --check assets/web/plugins/scheduler.js\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add previous/next scheduler entry controls for overlapping\ntime-span slots and fix interleave timing calculations so\nthe active slot and countdown align with the overlap window.\n\nVerification: cargo test -p trx-frontend-http scheduler\nVerification: node --check assets/web/plugins/scheduler.js\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Reduce main-thread stalls while decode history replays.\n\nCoalesce decoder list redraws and map maintenance so spectrum\nand controls stay responsive during history import.\n\nVerification: node --check on modified frontend JS files.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Treat the SPA entry routes as public so direct requests to /map,\n/decoders, /settings, and /about return the app shell and let\nthe frontend show the login screen instead of a 403.\n\nMove the map filter overlay to the bottom-right corner and color\ndecode contact paths by their decoded band so they match the band\nlegend and locator overlays.\n\nVerified with cargo test -p trx-frontend-http.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Append the APRS-IS position beacon comment as\n`trx-rs v<version> by SP2SJG` so IGate beacons identify\nthe running software version.\n\nUpdate the APRS beacon formatter tests to assert the exact\npayload including the generated version string.\n\nCo-Authored-By: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Skip decoded candidates where ftx_message_decode() returns a non-OK
status instead of forwarding a synthetic error string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Radio paths and decode contact paths now use the same color as the
marker they belong to, respecting the active filter mode:
- Band mode: color follows the band (golden-angle HSL hue)
- Mode/source mode: color follows the source type (FT8/WSPR/bookmark)
APRS, AIS, and VDES paths use their fixed source colors unchanged.
Decode contact paths sync color when the filter mode is switched.
CSS stroke/stroke-opacity removed from path classes so Leaflet's
color option takes effect; dasharray and flow animation are retained.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
The score from ft8_lib is an averaged uint8 difference between Costas
sync tones and their neighbours (each unit = 0.5 dB). The previous
score * 0.5 gave the signal-above-adjacent-noise in dB relative to a
single 3.125 Hz waterfall bin, yielding values of +5 to +50 dB —
all wrong.
Subtract 10*log10(2500/3.125) ≈ 29 dB to normalise to the 2500 Hz
reference bandwidth used by WSJT-X and expected by PSKReporter:
snr = score * 0.5 - 29.0
This maps score 10 (minimum decodable) → -24 dB and score 60 → +1 dB,
matching typical WSJT-X SNR report ranges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Replace the two-character beacon_symbol string with separate
beacon_symbol_table (char) and beacon_symbol_code (char) fields to
avoid TOML backslash escaping issues with the alternate symbol table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add periodic IGate position beacon support to the APRS-IS uplink.
New AprsFiConfig fields: beacon (bool), beacon_interval_secs (default
1200), beacon_symbol (default "/-"), latitude/longitude overrides.
A beacon is sent immediately on connect then every beacon_interval_secs.
Coordinates fall back from [aprsfi] to [general].latitude/longitude.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Allow specifying the IGate callsign directly in [aprsfi] instead of
relying on [general].callsign. The aprsfi-specific callsign takes
precedence; [general].callsign is used as fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Move aprsfi and pskreporter modules from trx-server into a new
standalone trx-reporting library crate. Config types (AprsFiConfig,
PskReporterConfig) move to trx-reporting and are re-exported from
trx-server::config for backwards compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
- Append mandatory q-construct (,qAR,<callsign>) to all forwarded
TNC2 packets via updated format_tnc2(pkt, igate_call)
- Add TCPIP/TCPXX loop-prevention check before forwarding
- Drain server-sent data in select! loop to prevent TCP backpressure
- Enable TCP_NODELAY for low-latency packet forwarding
- Guard against history replays: skip packets older than 2 minutes
- Use "trx-rs" in login string and keepalive comment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Add [workspace.package] version = "0.1.0" to the root Cargo.toml and
switch all 21 member crates to version.workspace = true so the entire
workspace is versioned from a single place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Two bugs introduced by 60697bb:
1. dsp.rs passed `channel_idx == 0` as force_mono_pcm, which forced the
primary pipeline channel to output mono samples. The Opus encoder was
configured for stereo, so it received half the expected frame data,
causing distortion for all connected audio clients.
Fixed by passing `false` — hidden virtual channels already set
force_mono_pcm=true via set_force_mono_pcm() in vchan_impl.rs.
2. main.rs short-circuited channel conversion when no audio clients were
connected, sending raw frames to pcm_tx (decoders). When clients then
connected, decoders switched to receiving stereo-interleaved frames,
making decoder input format dependent on client presence.
Fixed by always performing the channel conversion before sending to
pcm_tx; the no-client skip now only bypasses Opus encode + rx_audio_tx.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Skip APRS packets whose ts_ms is older than 120 seconds. Live RF-decoded
packets arrive within milliseconds; history replay items can be up to 24
hours old and must not be re-uploaded to APRS-IS as live traffic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Rewrite the PSKReporter uplink to match the protocol spec exactly:
- Fix template FlowSetIDs: receiver uses 0x0003 (Options Template Set),
sender uses 0x0002 (Template Set); previously both used 0x9992/0x9993
- Add missing enterprise numbers (0x0000768F = 30351) to all enterprise
field specifiers in both template blocks
- Fix sender template field IDs: use correct attributes (senderCallsign
30351.1, frequency 30351.5, sNR 30351.6, iMD 30351.7, mode 30351.10,
informationSource 30351.11, senderLocator 30351.3, flowStartSeconds 150)
- Fix sender data field order to match the template declaration
- Add iMD byte (0) required by the 8-field template
- Add 4-byte null padding on receiver and sender data records
- Batch spots into one UDP packet per 5-minute window (spec requirement)
- Deduplicate by callsign within each window (keep most-recent spot)
- Send template descriptors only in first 3 packets then once per hour
- Increment sequence number by report count, not packet count
- Guard against history replays: drop any spot older than the flush
window (live FT8/WSPR is seconds old; history can be 24 h old)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Rebuild the visible-source chips when live APRS, AIS, or VDES markers are first added so the map filter list updates without a page refresh.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
Use an unbounded virtual-channel command queue so background decode and scheduler transitions do not silently drop subscribe or remove commands.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>