[feat](trx-protocol): add centralised decoder registry

Add DECODER_REGISTRY in trx-protocol::decoders as the single source of
truth for all decoder metadata (activation mode, supported rig modes,
background-decode capability). Replace duplicated resolver functions in
background_decode.rs and sse.rs with shared resolve_bookmark_decoders().
Add GET /decoders endpoint to expose the registry to the frontend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-30 20:47:12 +02:00
parent 5c43bac42b
commit e6dbfd1edb
17 changed files with 677 additions and 91 deletions
+9 -9
View File
@@ -1183,7 +1183,7 @@ fn run_playback(
}
}
/// Run the APRS decoder task. Only processes PCM when rig mode is FM or PKT.
/// Run the APRS decoder task. Only processes PCM when rig mode is PKT.
pub async fn run_aprs_decoder(
sample_rate: u32,
channels: u16,
@@ -1257,9 +1257,9 @@ async fn run_aprs_decoder_inner(
let mode_match = |state: &RigState| -> bool {
if is_hf {
matches!(state.status.mode, RigMode::USB | RigMode::DIG)
matches!(state.status.mode, RigMode::DIG)
} else {
matches!(state.status.mode, RigMode::FM | RigMode::PKT)
matches!(state.status.mode, RigMode::PKT)
}
};
let get_reset_seq = |state: &RigState| -> u64 {
@@ -1405,14 +1405,14 @@ pub async fn run_ais_decoder(
let mut decoder_a = AisDecoder::new(sample_rate);
let mut decoder_b = AisDecoder::new(sample_rate);
let mut was_active = false;
let mut active = matches!(state_rx.borrow().status.mode, RigMode::AIS | RigMode::FM | RigMode::PKT);
let mut active = matches!(state_rx.borrow().status.mode, RigMode::AIS);
loop {
if !active {
match state_rx.changed().await {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::AIS | RigMode::FM | RigMode::PKT);
active = matches!(state.status.mode, RigMode::AIS);
if active {
pcm_a_rx = pcm_a_rx.resubscribe();
pcm_b_rx = pcm_b_rx.resubscribe();
@@ -1476,7 +1476,7 @@ pub async fn run_ais_decoder(
match changed {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::AIS | RigMode::FM | RigMode::PKT);
active = matches!(state.status.mode, RigMode::AIS);
if !active && was_active {
decoder_a.reset();
decoder_b.reset();
@@ -1505,14 +1505,14 @@ pub async fn run_vdes_decoder(
info!("VDES decoder started ({}Hz complex baseband)", sample_rate);
let mut decoder = VdesDecoder::new(sample_rate);
let mut was_active = false;
let mut active = matches!(state_rx.borrow().status.mode, RigMode::VDES | RigMode::FM);
let mut active = matches!(state_rx.borrow().status.mode, RigMode::VDES);
loop {
if !active {
match state_rx.changed().await {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::VDES | RigMode::FM);
active = matches!(state.status.mode, RigMode::VDES);
if active {
iq_rx = iq_rx.resubscribe();
}
@@ -1550,7 +1550,7 @@ pub async fn run_vdes_decoder(
match changed {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::VDES | RigMode::FM);
active = matches!(state.status.mode, RigMode::VDES);
if !active && was_active {
decoder.reset();
was_active = false;
@@ -158,8 +158,7 @@ impl SoapySdrRig {
fn default_bandwidth_for_mode(mode: &RigMode) -> u32 {
match mode {
RigMode::LSB | RigMode::USB | RigMode::DIG => 3_000,
RigMode::AIS => 25_000,
RigMode::PKT => 25_000,
RigMode::PKT | RigMode::AIS => 25_000,
RigMode::VDES => 100_000,
RigMode::CW | RigMode::CWR => 500,
RigMode::AM | RigMode::SAM => 9_000,
@@ -318,6 +317,8 @@ impl SoapySdrRig {
RigMode::FM,
RigMode::AIS,
RigMode::VDES,
RigMode::DIG,
RigMode::PKT,
],
num_vfos: 1,
lock: false,
@@ -504,7 +504,7 @@ mod tests {
mgr.add_channel(14_074_000, &RigMode::USB).unwrap();
let hidden_id = Uuid::new_v4();
mgr.ensure_background_channel_pcm(hidden_id, 14_075_000, &RigMode::USB)
mgr.ensure_background_channel_pcm(hidden_id, 14_075_000, &RigMode::DIG)
.unwrap();
let visible = mgr.channels();