Commit Graph

1188 Commits

Author SHA1 Message Date
sjg cd045d353c [feat](trx-rs): tighten spectrum refresh and header layout
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:49:55 +01:00
sjg eb920ce138 [fix](trx-frontend-http): clean spectrum stream warning
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:44:56 +01:00
sjg c703a5bafd [perf](trx-rs): optimize spectrum streaming and header overlay
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:43:32 +01:00
sjg adc17507ce [feat](trx-frontend-http): refine top bar and overview controls
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:37:37 +01:00
sjg b5cac4be41 [feat](trx-frontend-http): wrap and layer main controls
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:34:50 +01:00
sjg b976465e37 [feat](trx-frontend-http): align overview strip with spectrum view
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:31:22 +01:00
sjg cf7b2cb3a5 [fix](trx-frontend-http): restore overview startup order
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:25:13 +01:00
sjg 54c6320f73 [fix](trx-frontend-http): show auth gate before startup checks
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:22:12 +01:00
sjg e422dc0b89 [fix](trx-frontend-http): restore auth-gated startup
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:18:37 +01:00
sjg 258258920d [refactor](trx-frontend-http): align logo header naming
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:14:34 +01:00
sjg d9b73479c7 [fix](trx-frontend-http): remove boxed shell and fix auth gate
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:11:20 +01:00
sjg 2154279d78 [feat](trx-frontend-http): restructure top frontend bars
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:08:34 +01:00
sjg c010073e0c [feat](trx-frontend-http): replace header graph with adaptive strip
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 00:04:17 +01:00
sjg fffc4c6b90 [feat](trx-rs): add WFM RDS and playback controls
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:57:46 +01:00
sjg f77d0b0bb1 [perf](trx-frontend-http): smooth SDR spectrum rendering
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:43:48 +01:00
sjg e212283d3c [feat](trx-frontend-http): refine SDR bandwidth control
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:42:51 +01:00
sjg e392e7ffa5 [feat](trx-backend-soapysdr): decode WFM stereo in SDR pipeline
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:40:25 +01:00
sjg 8827b2fa21 [feat](trx-frontend-http): add editable SDR bandwidth control
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:34:51 +01:00
sjg b8c154af60 [feat](trx-frontend-http): add WFM mono and stereo playback switch
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:32:09 +01:00
sjg 14cefd1198 [fix](trx-frontend-http): raise WFM bandwidth cap to 300 kHz
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:30:05 +01:00
sjg c5f1c5308b [fix](trx-server): duplicate mono SDR frames for stereo opus
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:29:23 +01:00
sjg dee165008d [fix](trx-frontend-http): hide transmit power controls on RX-only rigs
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 23:21:08 +01:00
sjg 7f222eaf10 [chore](trx-rs): refine pending SDR frontend and backend changes
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 23:09:21 +01:00
sjg 071157e5bf [feat](trx-frontend-http): tune SDR spectrum with ctrl-scroll
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 23:05:20 +01:00
sjg bc206e8f6b [fix](trx-frontend-http): hide TX audio controls on RX-only rigs
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 23:03:46 +01:00
sjg 5730ce2991 [fix](trx-frontend-http): keep spectrum SSE alive on idle ticks
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 22:56:34 +01:00
sjg c23f1a4b4d [feat](trx-frontend-http): stream spectrum updates over SSE
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 22:53:22 +01:00
sjg 54fb107d3b [fix](trx-rs): keep SDR center frequency stable in-band
Signed-off-by: Stan Grams <sjg@haxx.space>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-02-27 22:48:12 +01:00
sjg 07ad26b54a fix(trx-server): Opus-encode SDR PCM for TCP audio clients
The SDR audio bridge was forwarding PCM to server-side decoders but
never encoding it to Opus for rx_audio_tx, so TCP audio clients
(browser RX button) received nothing. Add Opus encoder initialised
from the rig's audio config and encode each PCM frame alongside the
pcm_tx broadcast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 22:31:40 +01:00
sjg 600830ba3d fix(http-frontend): extend PKT bandwidth max to 25 kHz
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 22:27:44 +01:00
sjg a29264093a feat(http-frontend): BW bookmark on spectrum, dB floor control, remove FIR taps
Replace the BW slider + FIR taps filter panel with a visual bandwidth
bookmark drawn directly on the spectrum canvas:
- Semi-transparent amber gradient strip spanning dialFreq ± BW/2
- Rounded-top bookmark tab at the top of the strip showing the current BW
- Draggable left/right edge handles (cursor: ew-resize) that adjust bandwidth
  live and send set_bandwidth on mouse-up; range clamped per-mode defaults
- Y-axis now labeled with dB values (floor to ceiling) drawn on canvas
- Configurable floor level via number input below spectrum (default -100 dB)
- Auto button fits floor/range to current noise floor and peak level
- Remove FIR taps selector (internal DSP implementation detail)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 22:25:14 +01:00
sjg 146516c52e feat(http-frontend): per-mode bandwidth defaults with live DSP apply
Add MODE_BW_DEFAULTS table mapping each mode to [default, min, max, step]
in Hz. When mode changes (via picker or SSE), applyBwDefaultForMode
updates the filter bandwidth slider range and sends set_bandwidth to the
server, so the DSP filter is rebuilt automatically for the new mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 22:09:35 +01:00
sjg af9f2e092e fix(trx-backend-soapysdr,trx-server): fix hardware tuning and add live filter update
- Compute hardware center as dial - center_offset_hz (was ignoring offset)
- Pass configured bandwidth_hz to device instead of hardcoded 1.5 MHz
- Add retune_cmd channel so set_freq repoints SDR hardware in real time
- Auto-add default channel with mode-appropriate bandwidth when [[sdr.channels]]
  is empty, preventing silent audio with minimal config
- Add ChannelDsp::set_filter to rebuild FIR LPFs at runtime; wire
  set_bandwidth and set_fir_taps to call it so UI filter changes take effect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 22:09:26 +01:00
sjg 547f253837 feat(http-frontend): move spectrum above controls, add zoom/pan/tooltip
Spectrum panel is now placed above the freq/mode/jog row, spanning the
full card width. Key improvements:

- Scroll wheel zooms in/out at the cursor position (up to 64x); double-
  click resets to full bandwidth view.
- Mouse drag pans the visible window; click-to-tune is suppressed when a
  drag has occurred.
- Touch pinch-to-zoom and single-finger drag-to-pan supported.
- Hover tooltip shows the frequency under the cursor, formatted to the
  currently selected unit (MHz/kHz/Hz, matching the jog-step selection).
- Frequency axis labels update to reflect the zoomed visible range.
- Canvas height increased to 160 px; axis bar styled with card bg.
- A small hint line below the panel explains the controls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 21:49:07 +01:00
sjg 952961b9fd feat(trx-client,http-frontend): spectrum waveform with frequency picker
Poll GetSpectrum every 200 ms in remote_client via a dedicated timer that
bypasses the main state-watch channel (no SSE noise). The resulting
SpectrumData is stored in FrontendRuntimeContext::spectrum and served by
a new GET /spectrum endpoint (JSON or 204 when unavailable).

HTTP frontend shows a spectrum panel (canvas + frequency axis) only when
the rig reports filter_controls=true (i.e. SoapySDR). The canvas renders:
- dark background with dBFS grid lines
- green FFT spectrum line with semi-transparent fill
- red dashed vertical marker at the currently tuned frequency
- frequency axis labels (MHz/kHz) below the canvas

Clicking the canvas tunes the rig to the clicked frequency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 21:36:05 +01:00
sjg 76969b5499 feat(trx-core,trx-protocol,trx-backend-soapysdr): add spectrum data pipeline
Add SpectrumData struct (bins, center_hz, sample_rate) to RigState and
RigSnapshot. Add GetSpectrum RigCommand and ClientCommand plumbed through
the protocol layer. SoapySDR DSP pipeline now computes a 1024-bin FFT
(Hann window, FFT-shifted, dBFS) every 4 IQ blocks (~10 Hz update rate)
and exposes it via RigCat::get_spectrum(). The rig_task handles
GetSpectrum without persisting spectrum data in ongoing state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 21:35:53 +01:00
sjg df79f06ff0 fix(trx-backend-soapysdr): implement real IQ streaming and fix PKT demodulation
Three root causes prevented APRS decoding at 144.800 MHz with PKT/FM mode:

1. `RealIqSource::read_into` returned zeros — the SoapySDR streaming API
   was never wired up.  `RxStream<Complex<f32>>` is `Send` and
   `StreamSample` is implemented for `num_complex::Complex<f32>` in the
   soapysdr 0.3 crate, so the stream can read directly into the IQ buffer.
   Now creates and activates an `RxStream` in `new()` and calls
   `stream.read` in `read_into`.

2. PKT mode used `Passthrough` (take `.re`) demodulation.  VHF/UHF packet
   radio (APRS, AX.25) is FM-encoded AFSK — it must be FM-demodulated
   before the APRS decoder sees the audio tones.  Changed PKT to `Fm`.

3. `iq_read_loop` always slept `block_duration_ms` after each read.  Real
   hardware already blocks inside `read_into`; the extra sleep doubled
   latency.  Added `IqSource::is_blocking()` (default `false`; `true` for
   `RealIqSource`) and skip the throttle sleep for blocking sources.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 01:25:34 +01:00
sjg 1a3cc0def7 style(http-frontend): make rig subtitle bold 2026-02-27 01:22:01 +01:00
sjg ca097efb33 feat(http-frontend): link header callsigns to qrzcq 2026-02-27 01:21:41 +01:00
sjg d4cb390721 fix(http-frontend): use configured rig display name in header 2026-02-27 01:19:22 +01:00
sjg 0a7195c93c fix(trx-server): add shutdown signal to audio capture/playback threads
Pass `watch::Receiver<bool>` into `run_capture` and `run_playback` so
both threads check for shutdown at the top of their outer recovery loop
and inner monitoring loop. Without this, restarting the process (e.g.
via systemd after an ALSA error) left the old threads stalled forever
while new ones were created alongside them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 01:15:57 +01:00
sjg 223c01392a feat(http-frontend): add rig and owner header lines 2026-02-27 01:14:30 +01:00
sjg 600257a7c4 [perf](trx-backend-soapysdr): replace FIR with FFT overlap-save via rustfft
Replace the per-sample ring-buffer FIR convolution with block-level
overlap-save convolution using rustfft. For a block of M samples and
N taps the old approach costs O(N·M); the new one costs O(M log M),
with rustfft using SIMD (AVX2/SSE4) internally.

Key changes:
- Add rustfft = "6" dependency
- Add BlockFirFilter: overlap-save filter with pre-computed H(f) and
  a single forward+inverse FFT pair per block (no per-sample multiply)
- ChannelDsp.process_block() now:
  1. Batch-mixes entire block to baseband in one vectorisable loop
  2. Applies BlockFirFilter to I and Q (one FFT pair each)
  3. Decimates and demodulates as before
- Keep the old FirFilter for unit tests (sample-by-sample interface)
- Add BlockFirFilter unit tests (DC passthrough, length preservation)
- IQ_BLOCK_SIZE promoted to pub const for use in filter sizing

For the default config (4096-sample blocks, 64 taps, decim=40):
  Old: ~262144 multiply-adds per FIR × 2 components = ~524k per block
  New: ~2 × (3 × 8192 × log2(8192)) ops, all SIMD-vectorised by rustfft

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 01:05:49 +01:00
sjg 0cfbf22c18 fix(http-frontend): prevent rig switch picker population crash 2026-02-27 00:59:13 +01:00
sjg 6c213369da [fix](trx-ft8): fix const qualifier warning in ft8_lib C code
Fix compiler warning about discarded const qualifier in strchr() call.
The result of strchr(const char*, ...) should be assigned to const char*,
not char*.

This resolves:
  warning: initialization discards 'const' qualifier from pointer target type

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:42:29 +01:00
sjg bc270834e0 [chore](trx-rs): enable CPU optimizations for better performance
Add .cargo/config.toml to enable native CPU features (AVX2, SSE4.2, etc.)
for maximum performance during compilation. This allows rustc and
dependencies to use SIMD instructions and other CPU optimizations.

This should reduce CPU usage of the DSP backend by allowing:
- Vectorized floating-point operations
- Better compiler optimizations for complex number math
- SIMD acceleration in dependencies like num-complex

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:36:37 +01:00
sjg 2bdc23400a [fix](trx-frontend-http): hide rig picker when auth is required
Hide the header rig picker when showing the authentication gate,
since no rigs are accessible until the user authenticates.
Show the picker again when auth gate is hidden (after login).

This improves UX by not showing an empty picker to unauthenticated users.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:26:57 +01:00
sjg efd2c53754 [fix](trx-server): auto-generate rig IDs when not specified in config
When rigs are configured without explicit IDs in TOML, they now
receive auto-generated IDs based on model name and index (e.g.,
"ft817_0", "soapysdr_1"). Previously, the default empty ID prevented
auto-generation from triggering.

Changes:
- Set RigInstanceConfig default id to empty string (was "default")
- This allows resolved_rigs() auto-generation to trigger for all rigs
- Legacy single-rig path still gets explicit "default" ID
- Auto-generated IDs now appear in HTTP API /rigs endpoint
- Rig picker now displays auto-IDs correctly

The fix ensures that rigs without explicit ids get proper identifiers
that appear in the protocol and frontend selectors.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:24:15 +01:00
sjg a370742d2b [chore](trx-rs): remove test config file
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:19:05 +01:00
sjg 12d967154c [fix](trx-backend-soapysdr): improve device initialization diagnostics
Enhance error handling and messaging for SoapySDR device initialization:
- Add fallback logic to try empty args if device-specific args fail
- Provide detailed multi-line error messages with troubleshooting guidance
- Include suggestions to check SoapySDR plugins and run SoapySDRUtil --probe
- Clarify device lifetime management in struct
- Document why actual streaming not yet implemented (soapysdr 0.3 limitations)
- Note that sdr 0.3 requires FFI or upgrade for streaming support

This helps users diagnose device initialization failures and understand
the current architectural limitations.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:18:07 +01:00