[feat](trx-frontend-http): expand background decode selection
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -358,6 +358,8 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="bm-label">Decoders
|
<div class="bm-label">Decoders
|
||||||
<div class="bm-decoder-checks">
|
<div class="bm-decoder-checks">
|
||||||
|
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-aprs" value="aprs" /> APRS</label>
|
||||||
|
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-ais" value="ais" /> AIS</label>
|
||||||
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-ft8" value="ft8" /> FT8</label>
|
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-ft8" value="ft8" /> FT8</label>
|
||||||
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-wspr" value="wspr" /> WSPR</label>
|
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-wspr" value="wspr" /> WSPR</label>
|
||||||
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-hf-aprs" value="hf-aprs" /> HF APRS</label>
|
<label class="bm-decoder-check"><input type="checkbox" id="bm-dec-hf-aprs" value="hf-aprs" /> HF APRS</label>
|
||||||
|
|||||||
+2
-1
@@ -5,7 +5,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const SUPPORTED_DECODERS = ["ft8", "wspr", "hf-aprs"];
|
const SUPPORTED_DECODERS = ["aprs", "ais", "ft8", "wspr", "hf-aprs"];
|
||||||
|
|
||||||
let backgroundDecodeRole = null;
|
let backgroundDecodeRole = null;
|
||||||
let currentRigId = null;
|
let currentRigId = null;
|
||||||
@@ -303,6 +303,7 @@
|
|||||||
case "no_supported_decoders": return "Unsupported";
|
case "no_supported_decoders": return "Unsupported";
|
||||||
case "disabled": return "Disabled";
|
case "disabled": return "Disabled";
|
||||||
case "handled_by_scheduler": return "Scheduler";
|
case "handled_by_scheduler": return "Scheduler";
|
||||||
|
case "handled_by_virtual_channel": return "VChan";
|
||||||
default: return "Inactive";
|
default: return "Inactive";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ function bmRender(list) {
|
|||||||
// Read decoder checkboxes and return an array of selected decoder names.
|
// Read decoder checkboxes and return an array of selected decoder names.
|
||||||
function bmReadDecoders() {
|
function bmReadDecoders() {
|
||||||
const decoders = [];
|
const decoders = [];
|
||||||
|
if (document.getElementById("bm-dec-aprs").checked) decoders.push("aprs");
|
||||||
|
if (document.getElementById("bm-dec-ais").checked) decoders.push("ais");
|
||||||
if (document.getElementById("bm-dec-ft8").checked) decoders.push("ft8");
|
if (document.getElementById("bm-dec-ft8").checked) decoders.push("ft8");
|
||||||
if (document.getElementById("bm-dec-wspr").checked) decoders.push("wspr");
|
if (document.getElementById("bm-dec-wspr").checked) decoders.push("wspr");
|
||||||
if (document.getElementById("bm-dec-hf-aprs").checked) decoders.push("hf-aprs");
|
if (document.getElementById("bm-dec-hf-aprs").checked) decoders.push("hf-aprs");
|
||||||
@@ -157,6 +159,8 @@ function bmReadDecoders() {
|
|||||||
// Set decoder checkboxes to match the given array.
|
// Set decoder checkboxes to match the given array.
|
||||||
function bmWriteDecoders(decoders) {
|
function bmWriteDecoders(decoders) {
|
||||||
const list = decoders || [];
|
const list = decoders || [];
|
||||||
|
document.getElementById("bm-dec-aprs").checked = list.includes("aprs");
|
||||||
|
document.getElementById("bm-dec-ais").checked = list.includes("ais");
|
||||||
document.getElementById("bm-dec-ft8").checked = list.includes("ft8");
|
document.getElementById("bm-dec-ft8").checked = list.includes("ft8");
|
||||||
document.getElementById("bm-dec-wspr").checked = list.includes("wspr");
|
document.getElementById("bm-dec-wspr").checked = list.includes("wspr");
|
||||||
document.getElementById("bm-dec-hf-aprs").checked = list.includes("hf-aprs");
|
document.getElementById("bm-dec-hf-aprs").checked = list.includes("hf-aprs");
|
||||||
|
|||||||
@@ -3559,7 +3559,8 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
.bgd-status-state[data-state="waiting_for_spectrum"],
|
.bgd-status-state[data-state="waiting_for_spectrum"],
|
||||||
.bgd-status-state[data-state="waiting_for_user"],
|
.bgd-status-state[data-state="waiting_for_user"],
|
||||||
.bgd-status-state[data-state="inactive"],
|
.bgd-status-state[data-state="inactive"],
|
||||||
.bgd-status-state[data-state="handled_by_scheduler"] {
|
.bgd-status-state[data-state="handled_by_scheduler"],
|
||||||
|
.bgd-status-state[data-state="handled_by_virtual_channel"] {
|
||||||
color: var(--accent-yellow);
|
color: var(--accent-yellow);
|
||||||
}
|
}
|
||||||
.bgd-status-state[data-state="missing_bookmark"],
|
.bgd-status-state[data-state="missing_bookmark"],
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::server::bookmarks::{Bookmark, BookmarkStore};
|
use crate::server::bookmarks::{Bookmark, BookmarkStore};
|
||||||
use crate::server::scheduler::SchedulerStatusMap;
|
use crate::server::scheduler::SchedulerStatusMap;
|
||||||
|
use crate::server::vchan::{ClientChannel, ClientChannelManager};
|
||||||
|
|
||||||
const SUPPORTED_DECODER_KINDS: &[&str] = &["ft8", "wspr", "hf-aprs"];
|
const SUPPORTED_DECODER_KINDS: &[&str] = &["aprs", "ais", "ft8", "wspr", "hf-aprs"];
|
||||||
const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel";
|
const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel";
|
||||||
|
const VISIBLE_CHANNEL_KIND_NAME: &str = "VirtualChannel";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct BackgroundDecodeConfig {
|
pub struct BackgroundDecodeConfig {
|
||||||
@@ -122,6 +124,7 @@ pub struct BackgroundDecodeManager {
|
|||||||
bookmarks: Arc<BookmarkStore>,
|
bookmarks: Arc<BookmarkStore>,
|
||||||
context: Arc<FrontendRuntimeContext>,
|
context: Arc<FrontendRuntimeContext>,
|
||||||
scheduler_status: SchedulerStatusMap,
|
scheduler_status: SchedulerStatusMap,
|
||||||
|
vchan_mgr: Arc<ClientChannelManager>,
|
||||||
status: Arc<RwLock<HashMap<String, BackgroundDecodeStatus>>>,
|
status: Arc<RwLock<HashMap<String, BackgroundDecodeStatus>>>,
|
||||||
notify_tx: broadcast::Sender<()>,
|
notify_tx: broadcast::Sender<()>,
|
||||||
}
|
}
|
||||||
@@ -132,6 +135,7 @@ impl BackgroundDecodeManager {
|
|||||||
bookmarks: Arc<BookmarkStore>,
|
bookmarks: Arc<BookmarkStore>,
|
||||||
context: Arc<FrontendRuntimeContext>,
|
context: Arc<FrontendRuntimeContext>,
|
||||||
scheduler_status: SchedulerStatusMap,
|
scheduler_status: SchedulerStatusMap,
|
||||||
|
vchan_mgr: Arc<ClientChannelManager>,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let (notify_tx, _) = broadcast::channel(16);
|
let (notify_tx, _) = broadcast::channel(16);
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
@@ -139,6 +143,7 @@ impl BackgroundDecodeManager {
|
|||||||
bookmarks,
|
bookmarks,
|
||||||
context,
|
context,
|
||||||
scheduler_status,
|
scheduler_status,
|
||||||
|
vchan_mgr,
|
||||||
status: Arc::new(RwLock::new(HashMap::new())),
|
status: Arc::new(RwLock::new(HashMap::new())),
|
||||||
notify_tx,
|
notify_tx,
|
||||||
})
|
})
|
||||||
@@ -280,6 +285,13 @@ impl BackgroundDecodeManager {
|
|||||||
&& channel.decoder_kinds == desired.decoder_kinds
|
&& channel.decoder_kinds == desired.decoder_kinds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn virtual_channels_cover_bookmark(&self, rig_id: &str, bookmark: &Bookmark) -> bool {
|
||||||
|
self.vchan_mgr
|
||||||
|
.channels(rig_id)
|
||||||
|
.into_iter()
|
||||||
|
.any(|channel| channel_matches_bookmark(&channel, bookmark))
|
||||||
|
}
|
||||||
|
|
||||||
fn reconcile(&self, runtime: &mut BackgroundRuntimeState, spectrum: &SharedSpectrum) {
|
fn reconcile(&self, runtime: &mut BackgroundRuntimeState, spectrum: &SharedSpectrum) {
|
||||||
let active_rig_id = self.active_rig_id();
|
let active_rig_id = self.active_rig_id();
|
||||||
|
|
||||||
@@ -367,6 +379,13 @@ impl BackgroundDecodeManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.virtual_channels_cover_bookmark(&rig_id, bookmark) {
|
||||||
|
status.state = "handled_by_virtual_channel".to_string();
|
||||||
|
status.channel_kind = Some(VISIBLE_CHANNEL_KIND_NAME.to_string());
|
||||||
|
statuses.push(status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let (Some(center_hz), Some(half_span_hz)) = (center_hz, half_span_hz) else {
|
let (Some(center_hz), Some(half_span_hz)) = (center_hz, half_span_hz) else {
|
||||||
status.state = "waiting_for_spectrum".to_string();
|
status.state = "waiting_for_spectrum".to_string();
|
||||||
statuses.push(status);
|
statuses.push(status);
|
||||||
@@ -506,6 +525,14 @@ fn supported_decoder_kinds(decoders: &[String]) -> Vec<String> {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn channel_matches_bookmark(channel: &ClientChannel, bookmark: &Bookmark) -> bool {
|
||||||
|
channel.freq_hz == bookmark.freq_hz && normalized_mode(&channel.mode) == normalized_mode(&bookmark.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalized_mode(mode: &str) -> String {
|
||||||
|
mode.trim().to_ascii_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/background-decode/{rig_id}")]
|
#[get("/background-decode/{rig_id}")]
|
||||||
pub async fn get_background_decode(
|
pub async fn get_background_decode(
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
|||||||
@@ -88,16 +88,16 @@ async fn serve(
|
|||||||
let background_decode_path = BackgroundDecodeStore::default_path();
|
let background_decode_path = BackgroundDecodeStore::default_path();
|
||||||
let background_decode_store =
|
let background_decode_store =
|
||||||
Arc::new(BackgroundDecodeStore::open(&background_decode_path));
|
Arc::new(BackgroundDecodeStore::open(&background_decode_path));
|
||||||
|
let vchan_mgr = Arc::new(ClientChannelManager::new(4));
|
||||||
let background_decode_mgr = BackgroundDecodeManager::new(
|
let background_decode_mgr = BackgroundDecodeManager::new(
|
||||||
background_decode_store,
|
background_decode_store,
|
||||||
bookmark_store.clone(),
|
bookmark_store.clone(),
|
||||||
context.clone(),
|
context.clone(),
|
||||||
scheduler_status.clone(),
|
scheduler_status.clone(),
|
||||||
|
vchan_mgr.clone(),
|
||||||
);
|
);
|
||||||
background_decode_mgr.spawn();
|
background_decode_mgr.spawn();
|
||||||
|
|
||||||
let vchan_mgr = Arc::new(ClientChannelManager::new(4));
|
|
||||||
|
|
||||||
// Wire the audio-command sender so allocate/delete/freq/mode operations on
|
// Wire the audio-command sender so allocate/delete/freq/mode operations on
|
||||||
// virtual channels are forwarded to the audio-client task.
|
// virtual channels are forwarded to the audio-client task.
|
||||||
if let Ok(guard) = context.vchan_audio_cmd.lock() {
|
if let Ok(guard) = context.vchan_audio_cmd.lock() {
|
||||||
|
|||||||
Reference in New Issue
Block a user