[fix](trx-frontend-http): start APRS on scheduler PKT channels
Carry bookmark decoder kinds through visible scheduler virtual channels so PKT/APRS scheduler entries start their decode workers instead of acting as audio-only channels. Verification: cargo test -p trx-frontend-http vchan Verification: cargo test -p trx-client (fails in existing config::tests::test_default_config) Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -327,13 +327,13 @@ async fn handle_audio_connection(
|
||||
}
|
||||
cmd = vchan_cmd_rx.recv() => {
|
||||
match cmd {
|
||||
Some(VChanAudioCmd::Subscribe { uuid, freq_hz, mode }) => {
|
||||
Some(VChanAudioCmd::Subscribe { uuid, freq_hz, mode, bandwidth_hz, decoder_kinds }) => {
|
||||
active_subs.insert(uuid, ActiveVChanSub {
|
||||
freq_hz,
|
||||
mode: mode.clone(),
|
||||
bandwidth_hz: 0,
|
||||
bandwidth_hz,
|
||||
hidden: false,
|
||||
decoder_kinds: Vec::new(),
|
||||
decoder_kinds: decoder_kinds.clone(),
|
||||
});
|
||||
// Skip if already re-sent during reconnect initialization.
|
||||
if resubscribed.remove(&uuid) {
|
||||
@@ -344,6 +344,8 @@ async fn handle_audio_connection(
|
||||
"freq_hz": freq_hz,
|
||||
"mode": mode,
|
||||
"hidden": false,
|
||||
"decoder_kinds": decoder_kinds,
|
||||
"bandwidth_hz": bandwidth_hz,
|
||||
});
|
||||
if let Ok(payload) = serde_json::to_vec(&json) {
|
||||
if let Err(e) = write_audio_msg(&mut writer, AUDIO_MSG_VCHAN_SUB, &payload).await {
|
||||
|
||||
@@ -29,7 +29,13 @@ pub enum VChanAudioCmd {
|
||||
/// Create the server-side DSP channel (if it does not exist) and subscribe
|
||||
/// to its Opus audio stream. `freq_hz` and `mode` are used if the server
|
||||
/// needs to create the channel.
|
||||
Subscribe { uuid: Uuid, freq_hz: u64, mode: String },
|
||||
Subscribe {
|
||||
uuid: Uuid,
|
||||
freq_hz: u64,
|
||||
mode: String,
|
||||
bandwidth_hz: u32,
|
||||
decoder_kinds: Vec<String>,
|
||||
},
|
||||
/// Create a hidden server-side DSP channel for background decoding.
|
||||
/// These channels are not enumerated as user-visible virtual channels and
|
||||
/// do not request an Opus audio stream back to the frontend.
|
||||
|
||||
@@ -402,8 +402,9 @@ fn sync_scheduler_vchannels(
|
||||
(
|
||||
bookmark_id.clone(),
|
||||
bookmark.freq_hz,
|
||||
bookmark.mode,
|
||||
bookmark.mode.clone(),
|
||||
bookmark.bandwidth_hz.unwrap_or(0) as u32,
|
||||
bookmark_decoder_kinds(&bookmark),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1740,6 +1741,33 @@ fn bookmark_decoder_state(
|
||||
(want_aprs, want_hf_aprs, want_ft8, want_wspr)
|
||||
}
|
||||
|
||||
fn bookmark_decoder_kinds(bookmark: &crate::server::bookmarks::Bookmark) -> Vec<String> {
|
||||
let mut out = Vec::new();
|
||||
for decoder in bookmark
|
||||
.decoders
|
||||
.iter()
|
||||
.map(|item| item.trim().to_ascii_lowercase())
|
||||
{
|
||||
if matches!(
|
||||
decoder.as_str(),
|
||||
"aprs" | "ais" | "ft8" | "wspr" | "hf-aprs"
|
||||
) && !out.iter().any(|existing| existing == &decoder)
|
||||
{
|
||||
out.push(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
if !out.is_empty() {
|
||||
return out;
|
||||
}
|
||||
|
||||
match bookmark.mode.trim().to_ascii_uppercase().as_str() {
|
||||
"AIS" => vec!["ais".to_string()],
|
||||
"PKT" => vec!["aprs".to_string()],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_selected_channel(
|
||||
rig_tx: &mpsc::Sender<RigRequest>,
|
||||
rig_id: &str,
|
||||
|
||||
@@ -85,6 +85,7 @@ struct InternalChannel {
|
||||
mode: String,
|
||||
/// Audio filter bandwidth in Hz (0 = mode default).
|
||||
bandwidth_hz: u32,
|
||||
decoder_kinds: Vec<String>,
|
||||
permanent: bool,
|
||||
scheduler_bookmark_id: Option<String>,
|
||||
/// Session UUIDs currently subscribed to this channel.
|
||||
@@ -169,6 +170,7 @@ impl ClientChannelManager {
|
||||
freq_hz,
|
||||
mode: mode.to_string(),
|
||||
bandwidth_hz: 0,
|
||||
decoder_kinds: Vec::new(),
|
||||
permanent: true,
|
||||
scheduler_bookmark_id: None,
|
||||
session_ids: Vec::new(),
|
||||
@@ -237,6 +239,7 @@ impl ClientChannelManager {
|
||||
freq_hz,
|
||||
mode: mode.to_string(),
|
||||
bandwidth_hz: 0,
|
||||
decoder_kinds: Vec::new(),
|
||||
permanent: false,
|
||||
scheduler_bookmark_id: None,
|
||||
session_ids: vec![session_id],
|
||||
@@ -266,6 +269,8 @@ impl ClientChannelManager {
|
||||
uuid: id,
|
||||
freq_hz,
|
||||
mode: mode.to_string(),
|
||||
bandwidth_hz: 0,
|
||||
decoder_kinds: Vec::new(),
|
||||
});
|
||||
|
||||
Ok(snapshot)
|
||||
@@ -515,7 +520,7 @@ impl ClientChannelManager {
|
||||
pub fn sync_scheduler_channels(
|
||||
&self,
|
||||
rig_id: &str,
|
||||
desired: &[(String, u64, String, u32)],
|
||||
desired: &[(String, u64, String, u32, Vec<String>)],
|
||||
) {
|
||||
let mut rigs = self.rigs.write().unwrap();
|
||||
let Some(channels) = rigs.get_mut(rig_id) else {
|
||||
@@ -523,10 +528,13 @@ impl ClientChannelManager {
|
||||
};
|
||||
|
||||
let mut changed = false;
|
||||
let desired_map: HashMap<String, (u64, String, u32)> = desired
|
||||
let desired_map: HashMap<String, (u64, String, u32, Vec<String>)> = desired
|
||||
.iter()
|
||||
.map(|(bookmark_id, freq_hz, mode, bandwidth_hz)| {
|
||||
(bookmark_id.clone(), (*freq_hz, mode.clone(), *bandwidth_hz))
|
||||
.map(|(bookmark_id, freq_hz, mode, bandwidth_hz, decoder_kinds)| {
|
||||
(
|
||||
bookmark_id.clone(),
|
||||
(*freq_hz, mode.clone(), *bandwidth_hz, decoder_kinds.clone()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let desired_ids: std::collections::HashSet<&str> =
|
||||
@@ -553,7 +561,7 @@ impl ClientChannelManager {
|
||||
let Some(bookmark_id) = channel.scheduler_bookmark_id.as_deref() else {
|
||||
continue;
|
||||
};
|
||||
let Some((freq_hz, mode, bandwidth_hz)) = desired_map.get(bookmark_id) else {
|
||||
let Some((freq_hz, mode, bandwidth_hz, decoder_kinds)) = desired_map.get(bookmark_id) else {
|
||||
continue;
|
||||
};
|
||||
if channel.freq_hz != *freq_hz {
|
||||
@@ -580,9 +588,20 @@ impl ClientChannelManager {
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
if channel.decoder_kinds != *decoder_kinds {
|
||||
channel.decoder_kinds = decoder_kinds.clone();
|
||||
self.send_audio_cmd(VChanAudioCmd::Subscribe {
|
||||
uuid: channel.id,
|
||||
freq_hz: channel.freq_hz,
|
||||
mode: channel.mode.clone(),
|
||||
bandwidth_hz: channel.bandwidth_hz,
|
||||
decoder_kinds: channel.decoder_kinds.clone(),
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (bookmark_id, freq_hz, mode, bandwidth_hz) in desired {
|
||||
for (bookmark_id, freq_hz, mode, bandwidth_hz, decoder_kinds) in desired {
|
||||
let exists = channels.iter().any(|channel| {
|
||||
channel.scheduler_bookmark_id.as_deref() == Some(bookmark_id.as_str())
|
||||
});
|
||||
@@ -598,6 +617,7 @@ impl ClientChannelManager {
|
||||
freq_hz: *freq_hz,
|
||||
mode: mode.clone(),
|
||||
bandwidth_hz: *bandwidth_hz,
|
||||
decoder_kinds: decoder_kinds.clone(),
|
||||
permanent: false,
|
||||
scheduler_bookmark_id: Some(bookmark_id.clone()),
|
||||
session_ids: Vec::new(),
|
||||
@@ -606,13 +626,9 @@ impl ClientChannelManager {
|
||||
uuid: channel_id,
|
||||
freq_hz: *freq_hz,
|
||||
mode: mode.clone(),
|
||||
bandwidth_hz: *bandwidth_hz,
|
||||
decoder_kinds: decoder_kinds.clone(),
|
||||
});
|
||||
if *bandwidth_hz > 0 {
|
||||
self.send_audio_cmd(VChanAudioCmd::SetBandwidth {
|
||||
uuid: channel_id,
|
||||
bandwidth_hz: *bandwidth_hz,
|
||||
});
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -655,7 +671,13 @@ mod tests {
|
||||
mgr.init_rig(rig_id, 14_074_000, "USB");
|
||||
mgr.sync_scheduler_channels(
|
||||
rig_id,
|
||||
&[("bm-ft8".to_string(), 14_074_000, "DIG".to_string(), 3_000)],
|
||||
&[(
|
||||
"bm-ft8".to_string(),
|
||||
14_074_000,
|
||||
"DIG".to_string(),
|
||||
3_000,
|
||||
vec!["ft8".to_string()],
|
||||
)],
|
||||
);
|
||||
|
||||
let channels = mgr.channels(rig_id);
|
||||
@@ -679,7 +701,13 @@ mod tests {
|
||||
.expect("allocate vchan");
|
||||
mgr.sync_scheduler_channels(
|
||||
rig_id,
|
||||
&[("bm-ft8".to_string(), 14_074_000, "DIG".to_string(), 3_000)],
|
||||
&[(
|
||||
"bm-ft8".to_string(),
|
||||
14_074_000,
|
||||
"DIG".to_string(),
|
||||
3_000,
|
||||
vec!["ft8".to_string()],
|
||||
)],
|
||||
);
|
||||
|
||||
mgr.release_session(session_id);
|
||||
@@ -699,7 +727,13 @@ mod tests {
|
||||
mgr.init_rig(rig_id, 14_074_000, "USB");
|
||||
mgr.sync_scheduler_channels(
|
||||
rig_id,
|
||||
&[("bm-aprs".to_string(), 144_800_000, "PKT".to_string(), 12_500)],
|
||||
&[(
|
||||
"bm-aprs".to_string(),
|
||||
144_800_000,
|
||||
"PKT".to_string(),
|
||||
12_500,
|
||||
vec!["aprs".to_string()],
|
||||
)],
|
||||
);
|
||||
|
||||
let channel_id = mgr.channels(rig_id)[1].id;
|
||||
|
||||
Reference in New Issue
Block a user