[feat](trx-client): configure spectrum guard tuning
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -170,6 +170,10 @@ pub struct FrontendRuntimeContext {
|
||||
pub http_show_sdr_gain_control: bool,
|
||||
/// Initial APRS map zoom level when receiver coordinates are available.
|
||||
pub http_initial_map_zoom: u8,
|
||||
/// Spectrum center-retune guard margin on each side of the tuned passband.
|
||||
pub http_spectrum_coverage_margin_hz: u32,
|
||||
/// Fraction of the sampled spectrum span treated as usable by the web UI.
|
||||
pub http_spectrum_usable_span_ratio: f32,
|
||||
/// Currently selected remote rig id (used by remote client routing).
|
||||
pub remote_active_rig_id: Arc<Mutex<Option<String>>>,
|
||||
/// Cached remote rig list from GetRigs polling.
|
||||
@@ -209,6 +213,8 @@ impl FrontendRuntimeContext {
|
||||
http_auth_cookie_same_site: "Lax".to_string(),
|
||||
http_show_sdr_gain_control: true,
|
||||
http_initial_map_zoom: 10,
|
||||
http_spectrum_coverage_margin_hz: 50_000,
|
||||
http_spectrum_usable_span_ratio: 1.0,
|
||||
remote_active_rig_id: Arc::new(Mutex::new(None)),
|
||||
remote_rigs: Arc::new(Mutex::new(Vec::new())),
|
||||
owner_callsign: None,
|
||||
|
||||
@@ -1167,7 +1167,8 @@ function effectiveSpectrumCoverageSpanHz(sampleRateHz) {
|
||||
const sampleRate = Number(sampleRateHz);
|
||||
if (!Number.isFinite(sampleRate) || sampleRate <= 0) return 0;
|
||||
// Keep a guard band at the spectrum edges; practical usable span is slightly smaller.
|
||||
return sampleRate * 1.0;
|
||||
const ratio = Number.isFinite(spectrumUsableSpanRatio) ? spectrumUsableSpanRatio : 1.0;
|
||||
return sampleRate * Math.max(0.01, Math.min(1.0, ratio));
|
||||
}
|
||||
|
||||
function requiredCenterFreqForCoverageInFrame(data, freqHz, bandwidthHz = coverageGuardBandwidthHz()) {
|
||||
@@ -1180,7 +1181,7 @@ function requiredCenterFreqForCoverageInFrame(data, freqHz, bandwidthHz = covera
|
||||
|
||||
const safeBw = Math.max(0, Number.isFinite(bandwidthHz) ? bandwidthHz : 0);
|
||||
const halfSpanHz = sampleRate / 2;
|
||||
const requiredHalfSpanHz = safeBw / 2 + SPECTRUM_COVERAGE_MARGIN_HZ;
|
||||
const requiredHalfSpanHz = safeBw / 2 + spectrumCoverageMarginHz;
|
||||
if (requiredHalfSpanHz * 2 >= sampleRate) {
|
||||
return alignFreqToRigStep(Math.round(freqHz));
|
||||
}
|
||||
@@ -1258,7 +1259,7 @@ function sweetSpotCandidateForFrame(data, freqHz, bandwidthHz) {
|
||||
|
||||
const halfUsableSpanHz = usableSpanHz / 2;
|
||||
const fullHalfSpanHz = sampleRate / 2;
|
||||
const guardHalfSpanHz = bandwidthHz / 2 + SPECTRUM_COVERAGE_MARGIN_HZ;
|
||||
const guardHalfSpanHz = bandwidthHz / 2 + spectrumCoverageMarginHz;
|
||||
if (guardHalfSpanHz * 2 >= usableSpanHz) {
|
||||
const fallbackCenterHz = requiredCenterFreqForCoverageInFrame(data, freqHz, bandwidthHz);
|
||||
if (!Number.isFinite(fallbackCenterHz)) return null;
|
||||
@@ -1346,7 +1347,7 @@ function sweetSpotProbeCenters(data, freqHz, bandwidthHz) {
|
||||
if (!Number.isFinite(usableSpanHz) || usableSpanHz <= 0) return [];
|
||||
|
||||
const halfUsableSpanHz = usableSpanHz / 2;
|
||||
const guardHalfSpanHz = bandwidthHz / 2 + SPECTRUM_COVERAGE_MARGIN_HZ;
|
||||
const guardHalfSpanHz = bandwidthHz / 2 + spectrumCoverageMarginHz;
|
||||
if (guardHalfSpanHz * 2 >= usableSpanHz) {
|
||||
return [alignFreqToRigStep(Math.round(freqHz))];
|
||||
}
|
||||
@@ -1446,7 +1447,7 @@ function tunedFrequencyForCenterCoverage(centerHz, freqHz = lastFreqHz, bandwidt
|
||||
|
||||
const safeBw = Math.max(0, Number.isFinite(bandwidthHz) ? bandwidthHz : 0);
|
||||
const halfSpanHz = sampleRate / 2;
|
||||
const requiredHalfSpanHz = safeBw / 2 + SPECTRUM_COVERAGE_MARGIN_HZ;
|
||||
const requiredHalfSpanHz = safeBw / 2 + spectrumCoverageMarginHz;
|
||||
if (requiredHalfSpanHz * 2 >= sampleRate) {
|
||||
return alignFreqToRigStep(Math.round(centerHz));
|
||||
}
|
||||
@@ -1650,7 +1651,8 @@ let serverActiveRigId = null;
|
||||
let serverLat = null;
|
||||
let serverLon = null;
|
||||
let initialMapZoom = 10;
|
||||
const SPECTRUM_COVERAGE_MARGIN_HZ = 50_000;
|
||||
let spectrumCoverageMarginHz = 50_000;
|
||||
let spectrumUsableSpanRatio = 1.0;
|
||||
|
||||
function updateFooterBuildInfo() {
|
||||
const serverEl = document.getElementById("footer-server-build");
|
||||
@@ -1702,6 +1704,18 @@ function render(update) {
|
||||
if (typeof update.initial_map_zoom === "number" && Number.isFinite(update.initial_map_zoom)) {
|
||||
initialMapZoom = Math.max(1, Math.round(update.initial_map_zoom));
|
||||
}
|
||||
if (
|
||||
typeof update.spectrum_coverage_margin_hz === "number" &&
|
||||
Number.isFinite(update.spectrum_coverage_margin_hz)
|
||||
) {
|
||||
spectrumCoverageMarginHz = Math.max(1, Math.round(update.spectrum_coverage_margin_hz));
|
||||
}
|
||||
if (
|
||||
typeof update.spectrum_usable_span_ratio === "number" &&
|
||||
Number.isFinite(update.spectrum_usable_span_ratio)
|
||||
) {
|
||||
spectrumUsableSpanRatio = Math.max(0.01, Math.min(1.0, Number(update.spectrum_usable_span_ratio)));
|
||||
}
|
||||
updateTitle();
|
||||
updateFooterBuildInfo();
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ struct FrontendMeta {
|
||||
owner_website_name: Option<String>,
|
||||
show_sdr_gain_control: bool,
|
||||
initial_map_zoom: u8,
|
||||
spectrum_coverage_margin_hz: u32,
|
||||
spectrum_usable_span_ratio: f32,
|
||||
}
|
||||
|
||||
#[get("/status")]
|
||||
@@ -99,6 +101,14 @@ fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String {
|
||||
"initial_map_zoom".to_string(),
|
||||
serde_json::json!(meta.initial_map_zoom),
|
||||
);
|
||||
map.insert(
|
||||
"spectrum_coverage_margin_hz".to_string(),
|
||||
serde_json::json!(meta.spectrum_coverage_margin_hz),
|
||||
);
|
||||
map.insert(
|
||||
"spectrum_usable_span_ratio".to_string(),
|
||||
serde_json::json!(meta.spectrum_usable_span_ratio),
|
||||
);
|
||||
|
||||
serde_json::to_string(&value).unwrap_or_else(|_| json.to_string())
|
||||
}
|
||||
@@ -118,6 +128,8 @@ fn frontend_meta_from_context(
|
||||
owner_website_name: owner_website_name_from_context(context),
|
||||
show_sdr_gain_control: show_sdr_gain_control_from_context(context),
|
||||
initial_map_zoom: initial_map_zoom_from_context(context),
|
||||
spectrum_coverage_margin_hz: spectrum_coverage_margin_hz_from_context(context),
|
||||
spectrum_usable_span_ratio: spectrum_usable_span_ratio_from_context(context),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +179,14 @@ fn initial_map_zoom_from_context(context: &FrontendRuntimeContext) -> u8 {
|
||||
context.http_initial_map_zoom
|
||||
}
|
||||
|
||||
fn spectrum_coverage_margin_hz_from_context(context: &FrontendRuntimeContext) -> u32 {
|
||||
context.http_spectrum_coverage_margin_hz
|
||||
}
|
||||
|
||||
fn spectrum_usable_span_ratio_from_context(context: &FrontendRuntimeContext) -> f32 {
|
||||
context.http_spectrum_usable_span_ratio
|
||||
}
|
||||
|
||||
#[get("/events")]
|
||||
pub async fn events(
|
||||
state: web::Data<watch::Receiver<RigState>>,
|
||||
|
||||
Reference in New Issue
Block a user