main
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c10b5faef4 |
[feat](trx-rs): redesign SDR noise blanker with tuning profiles
The old IQ noise blanker tracked a fast running RMS and, on a threshold crossing, replaced the sample with the last clean one. That hard sample-and-hold is a step discontinuity: it splatters energy back across the wideband passband, so after the narrow channel filter it often sounded worse than the noise it removed — especially on SSB, CW and digital. It also had no look-ahead (the impulse leading edge leaked through before the fast RMS reacted), blanked only single samples, and used a fixed 1/128 time constant that did not scale with capture rate. Redesign the blanker around accepted wideband-NB practice and add profiles matched to the interference source: - Noise-floor tracker updated only from clean samples and frozen while blanking, so a burst cannot desensitise detection. - Detection on instantaneous power vs threshold² × noise floor. - Look-ahead delay line so the gate closes *before* the impulse reaches the output, removing the leading edge. - Raised-cosine tapered gate (ramp 1→0→1) instead of a hard hold, which minimises blanker splatter. - Windowed blanking that re-arms on every detected sample to cover the full width of a burst. - All timings expressed in real time and converted to samples at the capture rate, so behaviour is consistent across SDR sample rates. Profiles (`NoiseBlankerProfile`, default `spike`): spike, ignition, powerline, broadband — each selects the blank window, look-ahead, taper and floor time constant. `threshold` stays orthogonal as the sensitivity knob. Core/protocol: - New `NoiseBlankerProfile` in trx-core (serde/parse/u8/TS), re-exported at the crate root; `RigFilterState.sdr_nb_profile` for state sync. - `profile` added to `RigCommand`/`ClientCommand::SetSdrNoiseBlanker`, the trait method, and the command mapping. Config: `[rig.sdr.noise_blanker] profile = "spike"` (regenerated trx-rs.toml.example). SDR backend: `NoiseBlanker` rewritten in the channel DSP; profile wired through `SoapySdrConfig`, the runtime setter, and `filter_state()`. Frontend: an "NB profile" selector in the SDR advanced controls (POST /set_sdr_noise_blanker&profile=…), reflecting server state; the profile rides along with the enable/threshold quick toggle so it is preserved. Regenerated generated.ts and app.js. Tests: profile u8/parse round-trips (trx-core); DSP tests for impulse suppression with no leading-edge leak, strong-steady-signal pass-through, and wider-profile-blanks-longer. Docs: User-Manual NB section rewritten for the new algorithm and profiles. Signed-off-by: Stan Grams <sjg@haxx.space> |
||
|
|
df7483fe30 |
[feat](trx-rs): configurable DIG sideband for SDR (auto/USB/LSB)
DIG has no inherent sideband; on the SDR backend it was always demodulated
as USB. Make it resolve to USB or LSB via a policy that defaults to the
amateur SSB/data convention (USB at/above 10 MHz, LSB below) and can be
overridden globally from the advanced radio controls or per-bookmark.
Design: the logical DIG mode is kept in RigState (display, decoder gating)
while the SDR pipeline is handed a concrete USB/LSB demodulator resolved from
(policy, dial frequency). Resolution happens at the boundary — the rig for
the primary channel and the virtual-channel manager for vchans — so the hot
DSP/demod path is untouched. The resolved sideband is only re-pushed when it
actually changes (e.g. tuning DIG/Auto across 10 MHz), keeping ordinary
tuning glitch-free.
Core/protocol:
- New `DigSidebandPolicy { Auto, Usb, Lsb }` with `resolve(freq)` and an
`effective_demod_mode()` helper (trx-core), re-exported at the crate root.
- `RigCommand::SetSdrDigSideband`, `RigSdr::set_sdr_dig_sideband`, and a
`RigFilterState.sdr_dig_sideband` field for state sync; wired through the
ClientCommand mapping.
Config: `[rig.sdr] dig_sideband = "auto"` (regenerated trx-rs.toml.example).
SDR backend: the vchan manager owns the shared policy (atomic); the rig
applies it to the primary channel and, on `set_sdr_dig_sideband`, re-resolves
all DIG virtual channels.
Frontend: a mode-gated "DIG sideband" selector in the SDR advanced controls
(POST /set_sdr_dig_sideband), reflecting server state; bookmarks gain an
optional `dig_sideband` field (form selector shown only for DIG) that, on
apply, sets the global policy before switching to DIG. The scheduler honours
it for automated bookmark activation too.
Tests: policy resolution / effective-mode / u8+parse round-trips (trx-core);
a vchan integration test asserting a DIG channel resolves to LSB below
10 MHz, flips with the policy, and still lists as DIG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UiK871ht2uPFBHtMbxy3wD
Signed-off-by: Stan Grams <sjg@haxx.space>
|
||
|
|
34507ffa17 |
Add composable HTTP access roles
CI / lint (pull_request) Successful in 2m24s
CI / test (pull_request) Successful in 9m24s
CI / frontend (pull_request) Successful in 5m12s
CI / reuse (pull_request) Successful in 6s
CI / lint (push) Successful in 2m24s
CI / test (push) Successful in 8m8s
CI / frontend (push) Successful in 4m15s
CI / reuse (push) Successful in 5s
|
||
|
|
3c3fc69542 |
Refactor HTTP account system
CI / frontend (pull_request) Successful in 5m13s
CI / reuse (pull_request) Successful in 29s
CI / frontend (push) Successful in 4m15s
CI / reuse (push) Successful in 5s
CI / lint (pull_request) Successful in 2m25s
CI / test (pull_request) Successful in 9m24s
CI / lint (push) Successful in 2m23s
CI / test (push) Successful in 8m19s
|
||
|
|
18b2d0efe6 |
[feat](trx-rs): keep a station log, and a layout to work the bands from
The logbook of issue #54, in the shape the proposal settled on. A new crate, trx-logbook, holds the contact, the ADIF reader and writer, the file, and the rules for telling one contact from two. ADIF because it is the only thing the ecosystem reads: LoTW, eQSL, Club Log, QRZ and every other logger take it and nothing else, so a log that cannot write .adi cannot be uploaded, confirmed or moved. The reader is forgiving in the ways real files are irregular -- lowercase tags, CRLF, missing header, unknown fields, a declared length that is the only thing ending a value -- and carries what it does not model through to the export, so a round trip does not strip what another program wrote. The file is JSON Lines, appended one line per contact. A log is the one thing here that cannot be regenerated, and the bookmark store's whole-file dump would rewrite megabytes to log one contact and lose all of them if the power went halfway; an append costs the record being written and no more, which a test tears a line in half to prove. Edits append revisions, deletes append tombstones, and the file compacts when the superseded outnumber the live. The panel is its own tab and stands in every layout. An entry opens with six fields and no more -- frequency, mode, rig name, time, and the callsign and locator of whatever decode it was started from. A report stays empty: an FT8 SNR is not what was sent. Times come from the server, because the browser may be a phone in another timezone, and the panel says so when the two disagree by more than a second. Worked-before answers as a callsign is typed. A decode is not a contact, so the Log button on an FT8 or APRS row opens an entry and logs nothing by itself. The ham layout is the fifth operator layout, opening on the logbook with the radio controls around it, offered only where the rig can transmit. Two bugs found on the way, both in code written here: a frequency of a whole number of megahertz ending in a zero rendered as a tenth of itself, in Rust and in TypeScript alike, because trimming trailing zeros from "20.000000" walks back through the point. The API also sits under /api/logbook rather than /logbook, so it cannot shadow its own page the way /bookmarks does. Closes #54 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space> |
||
|
|
b78c4a4dd4 |
[feat](trx-rs): make spectrum affordable over a slow link
CI / lint (pull_request) Successful in 2m22s
CI / test (pull_request) Successful in 8m36s
CI / frontend (push) Successful in 3m38s
CI / reuse (push) Successful in 6s
CI / frontend (pull_request) Successful in 4m27s
CI / reuse (pull_request) Successful in 6s
CI / lint (push) Successful in 2m21s
CI / test (push) Successful in 7m48s
Spectrum dominates the server↔client connection, and all three things that govern its cost were working against a poor link. **It was polled, one round trip per frame.** The client asked for a frame every 50 ms on a dedicated connection and waited for the reply, so the frame rate was capped at 1/RTT — on a 200 ms link, five frames a second no matter what was configured. Add SubscribeSpectrum alongside the existing SubscribeMeter: the server pushes frames from a per-rig broadcast that rig_task fills only while somebody is subscribed. A server too old to know the command answers with an error and leaves the connection usable, so the client falls back to polling on the same connection without reconnecting. **Bins were JSON floats.** 1024 bins spelled out as decimal text is around 10 KB a frame, ~200 KB/s at full rate — while the very next hop, client to browser, already sends the same information as base64 i8 in about 1.4 KB. Bins now travel base64-encoded whole dBFS, the resolution the display draws at anyway. Decoding still accepts the old array form. **Nothing was tunable.** [sdr].spectrum_fft_size and [sdr].spectrum_interval_ms replace the compile-time FFT size and cadence; [[remotes]].spectrum_interval_ms lets the client ask for less. 512 bins at 5 frames/s is roughly 3.5 KB/s against roughly 200 KB/s before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space> |
||
|
|
46c9827e8a |
[fix](trx-config): keep the generated example off the machine that made it
CI / lint (pull_request) Successful in 2m17s
CI / frontend (pull_request) Successful in 3m28s
CI / reuse (pull_request) Successful in 3s
CI / lint (push) Successful in 2m16s
CI / test (pull_request) Successful in 7m50s
CI / frontend (push) Successful in 4m11s
CI / reuse (push) Successful in 9s
CI / test (push) Failing after 17m15s
The example is generated from the config defaults, and [decode_logs].dir defaults to the running user's cache directory. So the file rendered /Users/sjg/Library/Caches/trx-rs/decoders on the machine that generated it and /root/.cache/trx-rs/decoders in CI, and the up-to-date test failed for everyone but its author. Pin dir to an illustrative /var/lib/trx-rs/decoders in the example config; omitting the key still falls back to the per-user directory. A new test asserts the rendered example contains none of this machine's home, cache or config directories, so the next environment-derived default cannot slip through the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space> |
||
|
|
cfaeb6ee15 |
[docs](trx-rs): generate the example config and correct the manual
trx-rs.toml.example was maintained by hand and had fallen well behind: no
[[rigs]], no [[remotes]], no [timeouts], no bandplan or decode-history
settings, and a [frontends.http].default_rig_id that had been renamed.
Generate it from the config structs instead, so a new field shows up the moment
it exists, and add a test that fails when the checked-in copy drifts:
cargo run -p trx-config --example generate_example
Section comments come from a small table; a section without an entry is still
emitted, so forgetting a comment can never drop a setting from the example.
The manual was wrong about the basics. It listed five config search paths, none
of which the loader has ever looked at (the real order is ./trx-rs.toml → XDG →
/etc), called --print-config output "fully commented" when it carries no
comments at all, and documented a TRX_PLUGIN_DIRS variable no code reads. It
also still described [frontends.rigctl].port as the bind port years after
rig_ports replaced it. Fixed, and the new configuration features are written
up alongside.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7
Signed-off-by: Stan Grams <sjg@haxx.space>
|
||
|
|
ba48de2d30 |
Initial commit
Sync docs to Wiki / wiki (push) Has been cancelled
Signed-off-by: Stan Grams <sjg@haxx.space> |