refactor: replace feature globals with typed services
This commit is contained in:
@@ -384,7 +384,7 @@
|
|||||||
sdrSquelchSupported = false;
|
sdrSquelchSupported = false;
|
||||||
}
|
}
|
||||||
updateSdrSquelchControlVisibility();
|
updateSdrSquelchControlVisibility();
|
||||||
if (typeof vchanApplyCapabilities === "function") vchanApplyCapabilities(caps);
|
window.trx?.modules?.vchan?.applyCapabilities(caps);
|
||||||
}
|
}
|
||||||
var freqEl = document.getElementById("freq");
|
var freqEl = document.getElementById("freq");
|
||||||
var centerFreqEl = document.getElementById("center-freq");
|
var centerFreqEl = document.getElementById("center-freq");
|
||||||
@@ -1217,9 +1217,9 @@
|
|||||||
if (themeToggleBtn) {
|
if (themeToggleBtn) {
|
||||||
themeToggleBtn.addEventListener("click", () => {
|
themeToggleBtn.addEventListener("click", () => {
|
||||||
setTheme(currentTheme() === "dark" ? "light" : "dark");
|
setTheme(currentTheme() === "dark" ? "light" : "dark");
|
||||||
updateMapBaseLayerForTheme(currentTheme());
|
window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme());
|
||||||
syncLocatorMarkerStyles();
|
window.trx.modules.map?.syncLocatorMarkerStyles();
|
||||||
refreshAisMarkerColors();
|
window.trx.modules.map?.refreshAisMarkerColors();
|
||||||
scheduleOverviewDraw();
|
scheduleOverviewDraw();
|
||||||
if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw();
|
if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw();
|
||||||
});
|
});
|
||||||
@@ -1227,9 +1227,9 @@
|
|||||||
if (headerStylePickSelect) {
|
if (headerStylePickSelect) {
|
||||||
headerStylePickSelect.addEventListener("change", () => {
|
headerStylePickSelect.addEventListener("change", () => {
|
||||||
setStyle(headerStylePickSelect.value);
|
setStyle(headerStylePickSelect.value);
|
||||||
updateMapBaseLayerForTheme(currentTheme());
|
window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme());
|
||||||
syncLocatorMarkerStyles();
|
window.trx.modules.map?.syncLocatorMarkerStyles();
|
||||||
refreshAisMarkerColors();
|
window.trx.modules.map?.refreshAisMarkerColors();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function readyText() {
|
function readyText() {
|
||||||
@@ -1315,9 +1315,9 @@
|
|||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (rigListChanged) {
|
if (rigListChanged) {
|
||||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
window.trx.modules.backgroundDecode?.setRig(lastActiveRigId);
|
||||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
window.trx.modules.bookmarks?.populateScopePicker();
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
}
|
}
|
||||||
window.trx.modules.map?.updateMapRigFilter();
|
window.trx.modules.map?.updateMapRigFilter();
|
||||||
}
|
}
|
||||||
@@ -1562,12 +1562,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels)) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
vchanChannels.forEach((ch) => {
|
if (virtualChannels.length > 0) {
|
||||||
|
virtualChannels.forEach((ch) => {
|
||||||
if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return;
|
if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return;
|
||||||
const xc = hzToX(ch.freq_hz);
|
const xc = hzToX(ch.freq_hz);
|
||||||
if (xc < 0 || xc > W) return;
|
if (xc < 0 || xc > W) return;
|
||||||
const isActive = ch.id === vchanActiveId;
|
const isActive = ch.id === window.trx.modules.vchan?.activeId;
|
||||||
const color = cssColorToRgba("#38bdf8");
|
const color = cssColorToRgba("#38bdf8");
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5));
|
signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5));
|
||||||
@@ -1852,7 +1853,8 @@
|
|||||||
refreshWavelengthDisplay(lastFreqHz);
|
refreshWavelengthDisplay(lastFreqHz);
|
||||||
}
|
}
|
||||||
function activeRdsChannelId() {
|
function activeRdsChannelId() {
|
||||||
if (typeof vchanActiveId !== "undefined" && vchanActiveId) return vchanActiveId;
|
const virtualChannelId = window.trx.modules.vchan?.activeId;
|
||||||
|
if (virtualChannelId) return virtualChannelId;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function activeChannelRds() {
|
function activeChannelRds() {
|
||||||
@@ -1861,23 +1863,25 @@
|
|||||||
if (activeId) {
|
if (activeId) {
|
||||||
const rds = vchanRdsById.get(activeId);
|
const rds = vchanRdsById.get(activeId);
|
||||||
if (rds) return rds;
|
if (rds) return rds;
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
if (vchanChannels[0].id === activeId) return primaryRds;
|
if (virtualChannels.length > 0) {
|
||||||
|
if (virtualChannels[0].id === activeId) return primaryRds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return primaryRds;
|
return primaryRds;
|
||||||
}
|
}
|
||||||
function activeChannelIsWfm() {
|
function activeChannelIsWfm() {
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
|
if (virtualChannels.length > 0) {
|
||||||
const activeId = activeRdsChannelId();
|
const activeId = activeRdsChannelId();
|
||||||
const active = vchanChannels.find((ch) => ch.id === activeId) || vchanChannels[0];
|
const active = virtualChannels.find((ch) => ch.id === activeId) || virtualChannels[0];
|
||||||
return String(active?.mode || "").toUpperCase() === "WFM";
|
return String(active?.mode || "").toUpperCase() === "WFM";
|
||||||
}
|
}
|
||||||
return lastModeName === "WFM";
|
return lastModeName === "WFM";
|
||||||
}
|
}
|
||||||
function activeChannelFreqHz() {
|
function activeChannelFreqHz() {
|
||||||
if (typeof vchanActiveChannel === "function") {
|
if (window.trx.modules.vchan) {
|
||||||
const ch = vchanActiveChannel();
|
const ch = window.trx.modules.vchan.activeChannel();
|
||||||
if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz;
|
if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz;
|
||||||
}
|
}
|
||||||
return lastFreqHz;
|
return lastFreqHz;
|
||||||
@@ -1899,11 +1903,12 @@
|
|||||||
}
|
}
|
||||||
function collectRdsOverlayEntries() {
|
function collectRdsOverlayEntries() {
|
||||||
const entries = [];
|
const entries = [];
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
for (const ch of vchanChannels) {
|
if (virtualChannels.length > 0) {
|
||||||
|
for (const ch of virtualChannels) {
|
||||||
if (String(ch?.mode || "").toUpperCase() !== "WFM") continue;
|
if (String(ch?.mode || "").toUpperCase() !== "WFM") continue;
|
||||||
if (!Number.isFinite(ch?.freq_hz)) continue;
|
if (!Number.isFinite(ch?.freq_hz)) continue;
|
||||||
const rds = vchanRdsById.get(ch.id) || (vchanChannels[0].id === ch.id ? primaryRds : null);
|
const rds = vchanRdsById.get(ch.id) || (virtualChannels[0].id === ch.id ? primaryRds : null);
|
||||||
if (!rds) continue;
|
if (!rds) continue;
|
||||||
entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds });
|
entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds });
|
||||||
}
|
}
|
||||||
@@ -3145,7 +3150,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
prevRenderData.mode = update.status.mode;
|
prevRenderData.mode = update.status.mode;
|
||||||
const mode = normalizeMode(update.status.mode);
|
const mode = normalizeMode(update.status.mode);
|
||||||
const modeUpper2 = mode ? mode.toUpperCase() : "";
|
const modeUpper2 = mode ? mode.toUpperCase() : "";
|
||||||
const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual();
|
const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true;
|
||||||
if (!onVirtual) {
|
if (!onVirtual) {
|
||||||
modeEl.value = modeUpper2;
|
modeEl.value = modeUpper2;
|
||||||
if (modeUpper2 === "WFM" && lastModeName !== "WFM") {
|
if (modeUpper2 === "WFM" && lastModeName !== "WFM") {
|
||||||
@@ -3500,10 +3505,10 @@ ${unsupportedBandSummary()}`;
|
|||||||
sseSessionId = d.session_id || null;
|
sseSessionId = d.session_id || null;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
}
|
}
|
||||||
if (typeof vchanHandleSession === "function") vchanHandleSession(evt.data);
|
window.trx.modules.vchan?.handleSession(evt.data);
|
||||||
});
|
});
|
||||||
es.addEventListener("channels", (evt) => {
|
es.addEventListener("channels", (evt) => {
|
||||||
if (typeof vchanHandleChannels === "function") vchanHandleChannels(evt.data);
|
window.trx.modules.vchan?.handleChannels(evt.data);
|
||||||
});
|
});
|
||||||
es.onerror = () => {
|
es.onerror = () => {
|
||||||
if (es.readyState === EventSource.CLOSED) {
|
if (es.readyState === EventSource.CLOSED) {
|
||||||
@@ -3600,9 +3605,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
async function takeSchedulerControlForDecoderDisable(buttonEl) {
|
async function takeSchedulerControlForDecoderDisable(buttonEl) {
|
||||||
const enabled = buttonEl?.dataset?.enabled === "true" || /^\s*Disable\b/i.test(buttonEl?.textContent || "");
|
const enabled = buttonEl?.dataset?.enabled === "true" || /^\s*Disable\b/i.test(buttonEl?.textContent || "");
|
||||||
if (!enabled) return;
|
if (!enabled) return;
|
||||||
if (typeof window.vchanTakeSchedulerControl === "function") {
|
await window.trx.modules.vchan?.takeSchedulerControl();
|
||||||
await window.vchanTakeSchedulerControl();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable;
|
window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable;
|
||||||
async function switchRigFromSelect(selectEl) {
|
async function switchRigFromSelect(selectEl) {
|
||||||
@@ -3635,8 +3638,8 @@ ${unsupportedBandSummary()}`;
|
|||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
window.trx.modules.backgroundDecode?.setRig(lastActiveRigId);
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||||
connect();
|
connect();
|
||||||
stopSpectrumStreaming();
|
stopSpectrumStreaming();
|
||||||
@@ -3914,7 +3917,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
setControlPending(modeEl, true);
|
setControlPending(modeEl, true);
|
||||||
showHint("Setting mode…");
|
showHint("Setting mode…");
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptMode === "function" && await vchanInterceptMode(mode)) {
|
if (await window.trx.modules.vchan?.interceptMode(mode)) {
|
||||||
showHint("Channel mode set", 1500);
|
showHint("Channel mode set", 1500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4047,7 +4050,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
scheduleSpectrumDraw();
|
scheduleSpectrumDraw();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(clamped)) return;
|
if (await window.trx.modules.vchan?.interceptBandwidth(clamped)) return;
|
||||||
await postPath(`/set_bandwidth?hz=${clamped}`);
|
await postPath(`/set_bandwidth?hz=${clamped}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz);
|
||||||
@@ -4131,7 +4134,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
}
|
}
|
||||||
async function applyAutoBandwidth() {
|
async function applyAutoBandwidth() {
|
||||||
if (!lastSpectrumData || lastFreqHz == null) return;
|
if (!lastSpectrumData || lastFreqHz == null) return;
|
||||||
const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual();
|
const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true;
|
||||||
const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci };
|
const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci };
|
||||||
const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference);
|
const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference);
|
||||||
if (!Number.isFinite(estimated) || estimated <= 0) {
|
if (!Number.isFinite(estimated) || estimated <= 0) {
|
||||||
@@ -4155,7 +4158,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
}
|
}
|
||||||
window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5e3 });
|
window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5e3 });
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(estimated)) return;
|
if (await window.trx.modules.vchan?.interceptBandwidth(estimated)) return;
|
||||||
await postPath(`/set_bandwidth?hz=${estimated}`);
|
await postPath(`/set_bandwidth?hz=${estimated}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz);
|
||||||
@@ -4373,9 +4376,9 @@ ${unsupportedBandSummary()}`;
|
|||||||
function initSettingsUI() {
|
function initSettingsUI() {
|
||||||
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||||
window.trx.modules.scheduler?.wireEvents();
|
window.trx.modules.scheduler?.wireEvents();
|
||||||
if (typeof initBackgroundDecode === "function") {
|
if (window.trx.modules.backgroundDecode) {
|
||||||
initBackgroundDecode(lastActiveRigId, authRole);
|
window.trx.modules.backgroundDecode.initialize(lastActiveRigId, authRole);
|
||||||
wireBackgroundDecodeEvents();
|
window.trx.modules.backgroundDecode.wireEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.getElementById("auth-form").addEventListener("submit", async (e) => {
|
document.getElementById("auth-form").addEventListener("submit", async (e) => {
|
||||||
@@ -4619,7 +4622,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
if (!bm) return null;
|
if (!bm) return null;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (bm.name) parts.push(String(bm.name));
|
if (bm.name) parts.push(String(bm.name));
|
||||||
if (typeof bmFmtFreq === "function") parts.push(bmFmtFreq(bm.freq_hz));
|
if (window.trx.modules.bookmarks) parts.push(window.trx.modules.bookmarks.formatFrequency(bm.freq_hz));
|
||||||
if (bm.mode) parts.push(String(bm.mode));
|
if (bm.mode) parts.push(String(bm.mode));
|
||||||
if (bm.locator) parts.push(String(bm.locator));
|
if (bm.locator) parts.push(String(bm.locator));
|
||||||
const distance = bookmarkDistanceText(bm);
|
const distance = bookmarkDistanceText(bm);
|
||||||
@@ -6517,8 +6520,9 @@ ${unsupportedBandSummary()}`;
|
|||||||
}
|
}
|
||||||
vchanRdsById = next;
|
vchanRdsById = next;
|
||||||
vchanSignalDbById = nextSig;
|
vchanSignalDbById = nextSig;
|
||||||
if (typeof vchanActiveId !== "undefined" && vchanActiveId && nextSig.has(vchanActiveId)) {
|
const virtualChannelId = window.trx.modules.vchan?.activeId;
|
||||||
sigLastDbm = nextSig.get(vchanActiveId);
|
if (virtualChannelId && nextSig.has(virtualChannelId)) {
|
||||||
|
sigLastDbm = nextSig.get(virtualChannelId);
|
||||||
refreshSigStrengthDisplay();
|
refreshSigStrengthDisplay();
|
||||||
}
|
}
|
||||||
updateRdsPsOverlay(primaryRds);
|
updateRdsPsOverlay(primaryRds);
|
||||||
@@ -7159,7 +7163,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
}
|
}
|
||||||
function createBookmarkChip(bm, colorMap, options = {}) {
|
function createBookmarkChip(bm, colorMap, options = {}) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
const freqStr = typeof bmFmtFreq === "function" ? bmFmtFreq(bm.freq_hz) : bm.freq_hz + " Hz";
|
const freqStr = window.trx.modules.bookmarks ? window.trx.modules.bookmarks.formatFrequency(bm.freq_hz) : bm.freq_hz + " Hz";
|
||||||
const esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
const esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
span.className = "spectrum-bookmark-chip";
|
span.className = "spectrum-bookmark-chip";
|
||||||
if (options.sideStack) {
|
if (options.sideStack) {
|
||||||
@@ -7745,7 +7749,7 @@ ${unsupportedBandSummary()}`;
|
|||||||
if (_bwDragEdge) {
|
if (_bwDragEdge) {
|
||||||
try {
|
try {
|
||||||
const bwHz = Math.round(currentBandwidthHz);
|
const bwHz = Math.round(currentBandwidthHz);
|
||||||
if (!(typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(bwHz))) {
|
if (!await window.trx.modules.vchan?.interceptBandwidth(bwHz)) {
|
||||||
await postPath(`/set_bandwidth?hz=${bwHz}`);
|
await postPath(`/set_bandwidth?hz=${bwHz}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz);
|
||||||
|
|||||||
+7
-3
@@ -356,7 +356,11 @@ var bgdWindow = window;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bgdWindow.initBackgroundDecode = initBackgroundDecode;
|
bgdWindow.trx ??= {};
|
||||||
bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
|
bgdWindow.trx.modules ??= {};
|
||||||
bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig;
|
bgdWindow.trx.modules.backgroundDecode = {
|
||||||
|
initialize: initBackgroundDecode,
|
||||||
|
wireEvents: wireBackgroundDecodeEvents,
|
||||||
|
setRig: setBackgroundDecodeRig
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -343,15 +343,13 @@ function bmApply(bm) {
|
|||||||
bridge.scheduleSpectrumDraw();
|
bridge.scheduleSpectrumDraw();
|
||||||
}
|
}
|
||||||
const tunePromise = (async () => {
|
const tunePromise = (async () => {
|
||||||
if (typeof bridge.vchanTakeSchedulerControl === "function") {
|
await bridge.trx?.modules?.vchan?.takeSchedulerControl();
|
||||||
await bridge.vchanTakeSchedulerControl();
|
const onVirtual = await bridge.trx?.modules?.vchan?.interceptMode(bm.mode) ?? false;
|
||||||
}
|
|
||||||
const onVirtual = typeof bridge.vchanInterceptMode === "function" && await bridge.vchanInterceptMode(bm.mode);
|
|
||||||
if (!onVirtual) {
|
if (!onVirtual) {
|
||||||
await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode));
|
await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode));
|
||||||
}
|
}
|
||||||
if (bm.bandwidth_hz) {
|
if (bm.bandwidth_hz) {
|
||||||
const bwHandledByVchan = typeof bridge.vchanInterceptBandwidth === "function" && await bridge.vchanInterceptBandwidth(bm.bandwidth_hz);
|
const bwHandledByVchan = await bridge.trx?.modules?.vchan?.interceptBandwidth(bm.bandwidth_hz) ?? false;
|
||||||
if (!bwHandledByVchan) {
|
if (!bwHandledByVchan) {
|
||||||
await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`);
|
await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`);
|
||||||
}
|
}
|
||||||
@@ -414,7 +412,10 @@ bridge.trx.modules.bookmarks = {
|
|||||||
invalidateColors() {
|
invalidateColors() {
|
||||||
bmOverlayRevision += 1;
|
bmOverlayRevision += 1;
|
||||||
},
|
},
|
||||||
apply: bmApply
|
apply: bmApply,
|
||||||
|
formatFrequency: bmFmtFreq,
|
||||||
|
fetch: bmFetch,
|
||||||
|
populateScopePicker: bmPopulateScopePicker
|
||||||
};
|
};
|
||||||
function bmUpdateSelectionUi() {
|
function bmUpdateSelectionUi() {
|
||||||
const count = bmSelected.size;
|
const count = bmSelected.size;
|
||||||
|
|||||||
@@ -3090,6 +3090,8 @@ var mapWindow = window;
|
|||||||
createAisMarker,
|
createAisMarker,
|
||||||
getAisAccentColor,
|
getAisAccentColor,
|
||||||
refreshAisMarkerColors,
|
refreshAisMarkerColors,
|
||||||
|
updateMapBaseLayerForTheme,
|
||||||
|
syncLocatorMarkerStyles,
|
||||||
setMapRadioPathTo,
|
setMapRadioPathTo,
|
||||||
buildReceiverPopupHtml,
|
buildReceiverPopupHtml,
|
||||||
rebuildDecodeContactPaths,
|
rebuildDecodeContactPaths,
|
||||||
|
|||||||
@@ -865,12 +865,12 @@ function schedulerEl(id) {
|
|||||||
const targetId = target.id;
|
const targetId = target.id;
|
||||||
schedulerStepPending = true;
|
schedulerStepPending = true;
|
||||||
renderSchedulerStepControls();
|
renderSchedulerStepControls();
|
||||||
Promise.resolve(schedulerWindow.vchanTakeSchedulerControl?.() ?? null).then(function() {
|
Promise.resolve(schedulerWindow.trx.modules.vchan?.takeSchedulerControl() ?? null).then(function() {
|
||||||
return apiActivateSchedulerEntry(rigId, targetId);
|
return apiActivateSchedulerEntry(rigId, targetId);
|
||||||
}).then(function(status) {
|
}).then(function(status) {
|
||||||
currentSchedulerStatus = status || null;
|
currentSchedulerStatus = status || null;
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
schedulerWindow.vchanToggleSchedulerRelease?.() ?? null
|
schedulerWindow.trx.modules.vchan?.releaseToScheduler() ?? null
|
||||||
).then(function() {
|
).then(function() {
|
||||||
renderStatus(status);
|
renderStatus(status);
|
||||||
renderSchedulerInterleaveStatus();
|
renderSchedulerInterleaveStatus();
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ async function vchanTakeSchedulerControl() {
|
|||||||
console.error("scheduler control takeover failed", e);
|
console.error("scheduler control takeover failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vchanWindow.vchanTakeSchedulerControl = vchanTakeSchedulerControl;
|
|
||||||
function vchanHandleSession(data) {
|
function vchanHandleSession(data) {
|
||||||
try {
|
try {
|
||||||
const d = JSON.parse(data);
|
const d = JSON.parse(data);
|
||||||
@@ -123,8 +122,6 @@ function vchanHandleChannels(data) {
|
|||||||
console.warn("vchan: bad channels event", e);
|
console.warn("vchan: bad channels event", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vchanWindow.vchanHandleSession = vchanHandleSession;
|
|
||||||
vchanWindow.vchanHandleChannels = vchanHandleChannels;
|
|
||||||
function vchanRender() {
|
function vchanRender() {
|
||||||
const picker = document.getElementById("vchan-picker");
|
const picker = document.getElementById("vchan-picker");
|
||||||
if (!picker) return;
|
if (!picker) return;
|
||||||
@@ -267,12 +264,10 @@ function vchanApplyCapabilities(caps) {
|
|||||||
picker.style.display = caps && caps.filter_controls ? "" : "none";
|
picker.style.display = caps && caps.filter_controls ? "" : "none";
|
||||||
vchanRenderSchedulerRelease();
|
vchanRenderSchedulerRelease();
|
||||||
}
|
}
|
||||||
vchanWindow.vchanApplyCapabilities = vchanApplyCapabilities;
|
|
||||||
function vchanIsOnVirtual() {
|
function vchanIsOnVirtual() {
|
||||||
if (!vchanActiveId || vchanChannels.length === 0) return false;
|
if (!vchanActiveId || vchanChannels.length === 0) return false;
|
||||||
return vchanActiveId !== vchanChannels[0]?.id;
|
return vchanActiveId !== vchanChannels[0]?.id;
|
||||||
}
|
}
|
||||||
vchanWindow.vchanIsOnVirtual = vchanIsOnVirtual;
|
|
||||||
function vchanActiveChannel() {
|
function vchanActiveChannel() {
|
||||||
return vchanChannels.find((c) => c.id === vchanActiveId) || null;
|
return vchanChannels.find((c) => c.id === vchanActiveId) || null;
|
||||||
}
|
}
|
||||||
@@ -406,15 +401,34 @@ async function vchanSetChannelMode(mode) {
|
|||||||
console.error("vchan: set mode error", e);
|
console.error("vchan: set mode error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vchanWindow.vchanInterceptMode = async function(mode) {
|
async function vchanInterceptMode(mode) {
|
||||||
if (!vchanIsOnVirtual()) return false;
|
if (!vchanIsOnVirtual()) return false;
|
||||||
await vchanSetChannelMode(mode);
|
await vchanSetChannelMode(mode);
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
vchanWindow.vchanInterceptBandwidth = async function(bwHz) {
|
async function vchanInterceptBandwidth(bwHz) {
|
||||||
if (!vchanIsOnVirtual()) return false;
|
if (!vchanIsOnVirtual()) return false;
|
||||||
await vchanSetChannelBandwidth(bwHz);
|
await vchanSetChannelBandwidth(bwHz);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
vchanWindow.trx ??= {};
|
||||||
|
vchanWindow.trx.modules ??= {};
|
||||||
|
vchanWindow.trx.modules.vchan = {
|
||||||
|
get channels() {
|
||||||
|
return vchanChannels;
|
||||||
|
},
|
||||||
|
get activeId() {
|
||||||
|
return vchanActiveId;
|
||||||
|
},
|
||||||
|
activeChannel: vchanActiveChannel,
|
||||||
|
applyCapabilities: vchanApplyCapabilities,
|
||||||
|
handleSession: vchanHandleSession,
|
||||||
|
handleChannels: vchanHandleChannels,
|
||||||
|
isOnVirtual: vchanIsOnVirtual,
|
||||||
|
interceptMode: vchanInterceptMode,
|
||||||
|
interceptBandwidth: vchanInterceptBandwidth,
|
||||||
|
takeSchedulerControl: vchanTakeSchedulerControl,
|
||||||
|
releaseToScheduler: vchanToggleSchedulerRelease
|
||||||
};
|
};
|
||||||
(function() {
|
(function() {
|
||||||
const original = vchanWindow.setRigFrequency;
|
const original = vchanWindow.setRigFrequency;
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ function applyCapabilities(caps) {
|
|||||||
sdrSquelchSupported = false;
|
sdrSquelchSupported = false;
|
||||||
}
|
}
|
||||||
updateSdrSquelchControlVisibility();
|
updateSdrSquelchControlVisibility();
|
||||||
if (typeof vchanApplyCapabilities === "function") vchanApplyCapabilities(caps);
|
window.trx?.modules?.vchan?.applyCapabilities(caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
const freqEl = document.getElementById("freq");
|
const freqEl = document.getElementById("freq");
|
||||||
@@ -1148,9 +1148,9 @@ setStyle(savedStyle);
|
|||||||
if (themeToggleBtn) {
|
if (themeToggleBtn) {
|
||||||
themeToggleBtn.addEventListener("click", () => {
|
themeToggleBtn.addEventListener("click", () => {
|
||||||
setTheme(currentTheme() === "dark" ? "light" : "dark");
|
setTheme(currentTheme() === "dark" ? "light" : "dark");
|
||||||
updateMapBaseLayerForTheme(currentTheme());
|
window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme());
|
||||||
syncLocatorMarkerStyles();
|
window.trx.modules.map?.syncLocatorMarkerStyles();
|
||||||
refreshAisMarkerColors();
|
window.trx.modules.map?.refreshAisMarkerColors();
|
||||||
scheduleOverviewDraw();
|
scheduleOverviewDraw();
|
||||||
if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw();
|
if (typeof scheduleSpectrumDraw === "function" && lastSpectrumData) scheduleSpectrumDraw();
|
||||||
});
|
});
|
||||||
@@ -1159,9 +1159,9 @@ if (themeToggleBtn) {
|
|||||||
if (headerStylePickSelect) {
|
if (headerStylePickSelect) {
|
||||||
headerStylePickSelect.addEventListener("change", () => {
|
headerStylePickSelect.addEventListener("change", () => {
|
||||||
setStyle(headerStylePickSelect.value);
|
setStyle(headerStylePickSelect.value);
|
||||||
updateMapBaseLayerForTheme(currentTheme());
|
window.trx.modules.map?.updateMapBaseLayerForTheme(currentTheme());
|
||||||
syncLocatorMarkerStyles();
|
window.trx.modules.map?.syncLocatorMarkerStyles();
|
||||||
refreshAisMarkerColors();
|
window.trx.modules.map?.refreshAisMarkerColors();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1261,9 +1261,9 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
|||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (rigListChanged) {
|
if (rigListChanged) {
|
||||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
window.trx.modules.backgroundDecode?.setRig(lastActiveRigId);
|
||||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
window.trx.modules.bookmarks?.populateScopePicker();
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
}
|
}
|
||||||
window.trx.modules.map?.updateMapRigFilter();
|
window.trx.modules.map?.updateMapRigFilter();
|
||||||
}
|
}
|
||||||
@@ -1534,12 +1534,13 @@ function drawSignalOverlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Virtual channel markers (sky-blue dashed lines, active one is solid).
|
// Virtual channel markers (sky-blue dashed lines, active one is solid).
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels)) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
vchanChannels.forEach(ch => {
|
if (virtualChannels.length > 0) {
|
||||||
|
virtualChannels.forEach(ch => {
|
||||||
if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return;
|
if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return;
|
||||||
const xc = hzToX(ch.freq_hz);
|
const xc = hzToX(ch.freq_hz);
|
||||||
if (xc < 0 || xc > W) return;
|
if (xc < 0 || xc > W) return;
|
||||||
const isActive = ch.id === vchanActiveId;
|
const isActive = ch.id === window.trx.modules.vchan?.activeId;
|
||||||
const color = cssColorToRgba("#38bdf8");
|
const color = cssColorToRgba("#38bdf8");
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5));
|
signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5));
|
||||||
@@ -1858,7 +1859,8 @@ function refreshFreqDisplay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function activeRdsChannelId() {
|
function activeRdsChannelId() {
|
||||||
if (typeof vchanActiveId !== "undefined" && vchanActiveId) return vchanActiveId;
|
const virtualChannelId = window.trx.modules.vchan?.activeId;
|
||||||
|
if (virtualChannelId) return virtualChannelId;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1868,25 +1870,27 @@ function activeChannelRds() {
|
|||||||
if (activeId) {
|
if (activeId) {
|
||||||
const rds = vchanRdsById.get(activeId);
|
const rds = vchanRdsById.get(activeId);
|
||||||
if (rds) return rds;
|
if (rds) return rds;
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
if (vchanChannels[0].id === activeId) return primaryRds;
|
if (virtualChannels.length > 0) {
|
||||||
|
if (virtualChannels[0].id === activeId) return primaryRds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return primaryRds;
|
return primaryRds;
|
||||||
}
|
}
|
||||||
|
|
||||||
function activeChannelIsWfm() {
|
function activeChannelIsWfm() {
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
|
if (virtualChannels.length > 0) {
|
||||||
const activeId = activeRdsChannelId();
|
const activeId = activeRdsChannelId();
|
||||||
const active = vchanChannels.find((ch) => ch.id === activeId) || vchanChannels[0];
|
const active = virtualChannels.find((ch) => ch.id === activeId) || virtualChannels[0];
|
||||||
return String(active?.mode || "").toUpperCase() === "WFM";
|
return String(active?.mode || "").toUpperCase() === "WFM";
|
||||||
}
|
}
|
||||||
return lastModeName === "WFM";
|
return lastModeName === "WFM";
|
||||||
}
|
}
|
||||||
|
|
||||||
function activeChannelFreqHz() {
|
function activeChannelFreqHz() {
|
||||||
if (typeof vchanActiveChannel === "function") {
|
if (window.trx.modules.vchan) {
|
||||||
const ch = vchanActiveChannel();
|
const ch = window.trx.modules.vchan.activeChannel();
|
||||||
if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz;
|
if (Number.isFinite(ch?.freq_hz)) return ch.freq_hz;
|
||||||
}
|
}
|
||||||
return lastFreqHz;
|
return lastFreqHz;
|
||||||
@@ -1923,11 +1927,12 @@ function buildRdsOverlayHtml(rds) {
|
|||||||
|
|
||||||
function collectRdsOverlayEntries() {
|
function collectRdsOverlayEntries() {
|
||||||
const entries = [];
|
const entries = [];
|
||||||
if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels) && vchanChannels.length > 0) {
|
const virtualChannels = window.trx.modules.vchan?.channels || [];
|
||||||
for (const ch of vchanChannels) {
|
if (virtualChannels.length > 0) {
|
||||||
|
for (const ch of virtualChannels) {
|
||||||
if (String(ch?.mode || "").toUpperCase() !== "WFM") continue;
|
if (String(ch?.mode || "").toUpperCase() !== "WFM") continue;
|
||||||
if (!Number.isFinite(ch?.freq_hz)) continue;
|
if (!Number.isFinite(ch?.freq_hz)) continue;
|
||||||
const rds = vchanRdsById.get(ch.id) || (vchanChannels[0].id === ch.id ? primaryRds : null);
|
const rds = vchanRdsById.get(ch.id) || (virtualChannels[0].id === ch.id ? primaryRds : null);
|
||||||
if (!rds) continue;
|
if (!rds) continue;
|
||||||
entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds });
|
entries.push({ id: ch.id, freq_hz: ch.freq_hz, rds });
|
||||||
}
|
}
|
||||||
@@ -3378,7 +3383,7 @@ function render(update) {
|
|||||||
prevRenderData.mode = update.status.mode;
|
prevRenderData.mode = update.status.mode;
|
||||||
const mode = normalizeMode(update.status.mode);
|
const mode = normalizeMode(update.status.mode);
|
||||||
const modeUpper = mode ? mode.toUpperCase() : "";
|
const modeUpper = mode ? mode.toUpperCase() : "";
|
||||||
const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual();
|
const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true;
|
||||||
// When subscribed to a virtual channel the mode picker must reflect
|
// When subscribed to a virtual channel the mode picker must reflect
|
||||||
// that channel's mode, not the primary rig mode. Skip the update here;
|
// that channel's mode, not the primary rig mode. Skip the update here;
|
||||||
// vchan.js will apply the correct mode via vchanSyncModeDisplay().
|
// vchan.js will apply the correct mode via vchanSyncModeDisplay().
|
||||||
@@ -3766,10 +3771,10 @@ function connect() {
|
|||||||
const d = JSON.parse(evt.data);
|
const d = JSON.parse(evt.data);
|
||||||
sseSessionId = d.session_id || null;
|
sseSessionId = d.session_id || null;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
if (typeof vchanHandleSession === "function") vchanHandleSession(evt.data);
|
window.trx.modules.vchan?.handleSession(evt.data);
|
||||||
});
|
});
|
||||||
es.addEventListener("channels", evt => {
|
es.addEventListener("channels", evt => {
|
||||||
if (typeof vchanHandleChannels === "function") vchanHandleChannels(evt.data);
|
window.trx.modules.vchan?.handleChannels(evt.data);
|
||||||
});
|
});
|
||||||
es.onerror = () => {
|
es.onerror = () => {
|
||||||
// Check if this is an auth error by looking at readyState
|
// Check if this is an auth error by looking at readyState
|
||||||
@@ -3891,9 +3896,7 @@ async function takeSchedulerControlForDecoderDisable(buttonEl) {
|
|||||||
const enabled = buttonEl?.dataset?.enabled === "true"
|
const enabled = buttonEl?.dataset?.enabled === "true"
|
||||||
|| /^\s*Disable\b/i.test(buttonEl?.textContent || "");
|
|| /^\s*Disable\b/i.test(buttonEl?.textContent || "");
|
||||||
if (!enabled) return;
|
if (!enabled) return;
|
||||||
if (typeof window.vchanTakeSchedulerControl === "function") {
|
await window.trx.modules.vchan?.takeSchedulerControl();
|
||||||
await window.vchanTakeSchedulerControl();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable;
|
window.takeSchedulerControlForDecoderDisable = takeSchedulerControlForDecoderDisable;
|
||||||
|
|
||||||
@@ -3927,8 +3930,8 @@ async function switchRigFromSelect(selectEl) {
|
|||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
window.trx.modules.backgroundDecode?.setRig(lastActiveRigId);
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
window.trx.modules.bookmarks?.fetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||||
connect();
|
connect();
|
||||||
stopSpectrumStreaming();
|
stopSpectrumStreaming();
|
||||||
@@ -4234,7 +4237,7 @@ async function applyModeFromPicker() {
|
|||||||
setControlPending(modeEl, true);
|
setControlPending(modeEl, true);
|
||||||
showHint("Setting mode…");
|
showHint("Setting mode…");
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptMode === "function" && await vchanInterceptMode(mode)) {
|
if (await window.trx.modules.vchan?.interceptMode(mode)) {
|
||||||
showHint("Channel mode set", 1500);
|
showHint("Channel mode set", 1500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4382,7 +4385,7 @@ async function applyBandwidthFromInput() {
|
|||||||
scheduleSpectrumDraw();
|
scheduleSpectrumDraw();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(clamped)) return;
|
if (await window.trx.modules.vchan?.interceptBandwidth(clamped)) return;
|
||||||
await postPath(`/set_bandwidth?hz=${clamped}`);
|
await postPath(`/set_bandwidth?hz=${clamped}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz);
|
||||||
@@ -4493,7 +4496,7 @@ async function applyAutoBandwidth() {
|
|||||||
if (!lastSpectrumData || lastFreqHz == null) return;
|
if (!lastSpectrumData || lastFreqHz == null) return;
|
||||||
// WFM interference telemetry belongs to the primary DSP channel. Do not
|
// WFM interference telemetry belongs to the primary DSP channel. Do not
|
||||||
// apply it to a virtual channel, where it would describe the wrong signal.
|
// apply it to a virtual channel, where it would describe the wrong signal.
|
||||||
const onVirtual = typeof vchanIsOnVirtual === "function" && vchanIsOnVirtual();
|
const onVirtual = window.trx.modules.vchan?.isOnVirtual() === true;
|
||||||
const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci };
|
const interference = onVirtual ? {} : { cci: lastWfmCci, aci: lastWfmAci };
|
||||||
const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference);
|
const estimated = estimateOccupiedBandwidth(lastSpectrumData, lastFreqHz, interference);
|
||||||
if (!Number.isFinite(estimated) || estimated <= 0) {
|
if (!Number.isFinite(estimated) || estimated <= 0) {
|
||||||
@@ -4517,7 +4520,7 @@ async function applyAutoBandwidth() {
|
|||||||
}
|
}
|
||||||
window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5000 });
|
window.trxUi?.notify(`Auto BW: ${formatBwLabel(estimated)} — ${reason}`, { kind: "success", duration: 5000 });
|
||||||
try {
|
try {
|
||||||
if (typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(estimated)) return;
|
if (await window.trx.modules.vchan?.interceptBandwidth(estimated)) return;
|
||||||
await postPath(`/set_bandwidth?hz=${estimated}`);
|
await postPath(`/set_bandwidth?hz=${estimated}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz);
|
||||||
@@ -4752,9 +4755,9 @@ async function initializeApp() {
|
|||||||
function initSettingsUI() {
|
function initSettingsUI() {
|
||||||
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||||
window.trx.modules.scheduler?.wireEvents();
|
window.trx.modules.scheduler?.wireEvents();
|
||||||
if (typeof initBackgroundDecode === "function") {
|
if (window.trx.modules.backgroundDecode) {
|
||||||
initBackgroundDecode(lastActiveRigId, authRole);
|
window.trx.modules.backgroundDecode.initialize(lastActiveRigId, authRole);
|
||||||
wireBackgroundDecodeEvents();
|
window.trx.modules.backgroundDecode.wireEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4915,7 +4918,7 @@ function buildBookmarkTooltipText(bm) {
|
|||||||
if (!bm) return null;
|
if (!bm) return null;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (bm.name) parts.push(String(bm.name));
|
if (bm.name) parts.push(String(bm.name));
|
||||||
if (typeof bmFmtFreq === "function") parts.push(bmFmtFreq(bm.freq_hz));
|
if (window.trx.modules.bookmarks) parts.push(window.trx.modules.bookmarks.formatFrequency(bm.freq_hz));
|
||||||
if (bm.mode) parts.push(String(bm.mode));
|
if (bm.mode) parts.push(String(bm.mode));
|
||||||
if (bm.locator) parts.push(String(bm.locator));
|
if (bm.locator) parts.push(String(bm.locator));
|
||||||
const distance = bookmarkDistanceText(bm);
|
const distance = bookmarkDistanceText(bm);
|
||||||
@@ -6941,8 +6944,9 @@ function startSpectrumStreaming() {
|
|||||||
}
|
}
|
||||||
vchanRdsById = next;
|
vchanRdsById = next;
|
||||||
vchanSignalDbById = nextSig;
|
vchanSignalDbById = nextSig;
|
||||||
if (typeof vchanActiveId !== "undefined" && vchanActiveId && nextSig.has(vchanActiveId)) {
|
const virtualChannelId = window.trx.modules.vchan?.activeId;
|
||||||
sigLastDbm = nextSig.get(vchanActiveId);
|
if (virtualChannelId && nextSig.has(virtualChannelId)) {
|
||||||
|
sigLastDbm = nextSig.get(virtualChannelId);
|
||||||
refreshSigStrengthDisplay();
|
refreshSigStrengthDisplay();
|
||||||
}
|
}
|
||||||
updateRdsPsOverlay(primaryRds);
|
updateRdsPsOverlay(primaryRds);
|
||||||
@@ -7691,8 +7695,8 @@ function bmCategoryColorMap() {
|
|||||||
|
|
||||||
function createBookmarkChip(bm, colorMap, options = {}) {
|
function createBookmarkChip(bm, colorMap, options = {}) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
const freqStr = typeof bmFmtFreq === "function"
|
const freqStr = window.trx.modules.bookmarks
|
||||||
? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz";
|
? window.trx.modules.bookmarks.formatFrequency(bm.freq_hz) : bm.freq_hz + "\u202fHz";
|
||||||
const esc = (s) => String(s)
|
const esc = (s) => String(s)
|
||||||
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
span.className = "spectrum-bookmark-chip";
|
span.className = "spectrum-bookmark-chip";
|
||||||
@@ -8355,7 +8359,7 @@ if (spectrumCanvas || overviewCanvas) {
|
|||||||
if (_bwDragEdge) {
|
if (_bwDragEdge) {
|
||||||
try {
|
try {
|
||||||
const bwHz = Math.round(currentBandwidthHz);
|
const bwHz = Math.round(currentBandwidthHz);
|
||||||
if (!(typeof vchanInterceptBandwidth === "function" && await vchanInterceptBandwidth(bwHz))) {
|
if (!(await window.trx.modules.vchan?.interceptBandwidth(bwHz))) {
|
||||||
await postPath(`/set_bandwidth?hz=${bwHz}`);
|
await postPath(`/set_bandwidth?hz=${bwHz}`);
|
||||||
if (Number.isFinite(lastFreqHz)) {
|
if (Number.isFinite(lastFreqHz)) {
|
||||||
await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz);
|
await ensureTunedBandwidthCoverage(lastFreqHz, currentBandwidthHz);
|
||||||
|
|||||||
@@ -3653,6 +3653,8 @@ const mapWindow = window as unknown as MapWindow;
|
|||||||
createAisMarker,
|
createAisMarker,
|
||||||
getAisAccentColor,
|
getAisAccentColor,
|
||||||
refreshAisMarkerColors,
|
refreshAisMarkerColors,
|
||||||
|
updateMapBaseLayerForTheme,
|
||||||
|
syncLocatorMarkerStyles,
|
||||||
setMapRadioPathTo,
|
setMapRadioPathTo,
|
||||||
buildReceiverPopupHtml,
|
buildReceiverPopupHtml,
|
||||||
rebuildDecodeContactPaths,
|
rebuildDecodeContactPaths,
|
||||||
|
|||||||
+13
-6
@@ -40,9 +40,12 @@ interface BackgroundBridge {
|
|||||||
decoderRegistry?: DecoderDescriptor[];
|
decoderRegistry?: DecoderDescriptor[];
|
||||||
authEnabled?: boolean;
|
authEnabled?: boolean;
|
||||||
trxUi: { confirm(options: { title: string; message: string; confirmLabel: string }): Promise<boolean> };
|
trxUi: { confirm(options: { title: string; message: string; confirmLabel: string }): Promise<boolean> };
|
||||||
initBackgroundDecode?: (rigId: string | null, role: string | null) => void;
|
trx?: { modules?: { backgroundDecode?: BackgroundDecodeService } };
|
||||||
wireBackgroundDecodeEvents?: () => void;
|
}
|
||||||
setBackgroundDecodeRig?: (rigId: string | null) => void;
|
interface BackgroundDecodeService {
|
||||||
|
initialize(rigId: string | null, role: string | null): void;
|
||||||
|
wireEvents(): void;
|
||||||
|
setRig(rigId: string | null): void;
|
||||||
}
|
}
|
||||||
interface WiredElement extends HTMLElement { _wired?: boolean }
|
interface WiredElement extends HTMLElement { _wired?: boolean }
|
||||||
const bgdWindow = window as unknown as BackgroundBridge;
|
const bgdWindow = window as unknown as BackgroundBridge;
|
||||||
@@ -456,7 +459,11 @@ const bgdWindow = window as unknown as BackgroundBridge;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bgdWindow.initBackgroundDecode = initBackgroundDecode;
|
bgdWindow.trx ??= {};
|
||||||
bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
|
bgdWindow.trx.modules ??= {};
|
||||||
bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig;
|
bgdWindow.trx.modules.backgroundDecode = {
|
||||||
|
initialize: initBackgroundDecode,
|
||||||
|
wireEvents: wireBackgroundDecodeEvents,
|
||||||
|
setRig: setBackgroundDecodeRig,
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ interface BookmarkService {
|
|||||||
refreshOverlay(): Promise<void>;
|
refreshOverlay(): Promise<void>;
|
||||||
invalidateColors(): void;
|
invalidateColors(): void;
|
||||||
apply(bookmark: Bookmark): void;
|
apply(bookmark: Bookmark): void;
|
||||||
|
formatFrequency(frequencyHz: number): string;
|
||||||
|
fetch(categoryFilter: string): Promise<void>;
|
||||||
|
populateScopePicker(): void;
|
||||||
|
}
|
||||||
|
interface VirtualChannelService {
|
||||||
|
interceptMode(mode: string): Promise<boolean>;
|
||||||
|
interceptBandwidth(bandwidthHz: number): Promise<boolean>;
|
||||||
|
takeSchedulerControl(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BookmarkBridge extends Window {
|
interface BookmarkBridge extends Window {
|
||||||
@@ -49,7 +57,7 @@ interface BookmarkBridge extends Window {
|
|||||||
currentBandwidthHz?: number;
|
currentBandwidthHz?: number;
|
||||||
modeEl?: HTMLSelectElement | null;
|
modeEl?: HTMLSelectElement | null;
|
||||||
decoderRegistry?: DecoderDescriptor[];
|
decoderRegistry?: DecoderDescriptor[];
|
||||||
trx?: { modules?: { bookmarks?: BookmarkService } };
|
trx?: { modules?: { bookmarks?: BookmarkService; vchan?: VirtualChannelService } };
|
||||||
trxUi: {
|
trxUi: {
|
||||||
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
||||||
notify?(message: string, options: { kind: "error" }): void;
|
notify?(message: string, options: { kind: "error" }): void;
|
||||||
@@ -58,9 +66,6 @@ interface BookmarkBridge extends Window {
|
|||||||
scheduleSpectrumDraw?(): void;
|
scheduleSpectrumDraw?(): void;
|
||||||
syncBandwidthInput?(bandwidthHz: number): void;
|
syncBandwidthInput?(bandwidthHz: number): void;
|
||||||
applyLocalTunedFrequency?(frequencyHz: number, force?: boolean): void;
|
applyLocalTunedFrequency?(frequencyHz: number, force?: boolean): void;
|
||||||
vchanTakeSchedulerControl?(): Promise<void>;
|
|
||||||
vchanInterceptMode?(mode: string): Promise<boolean>;
|
|
||||||
vchanInterceptBandwidth?(bandwidthHz: number): Promise<boolean>;
|
|
||||||
setRigFrequency?(frequencyHz: number): Promise<unknown>;
|
setRigFrequency?(frequencyHz: number): Promise<unknown>;
|
||||||
postPath(path: string): Promise<unknown>;
|
postPath(path: string): Promise<unknown>;
|
||||||
onDecoderRegistryReady?(callback: () => void): void;
|
onDecoderRegistryReady?(callback: () => void): void;
|
||||||
@@ -503,19 +508,16 @@ function bmApply(bm: Bookmark): void {
|
|||||||
// Take scheduler control up front, then apply mode before bandwidth so a
|
// Take scheduler control up front, then apply mode before bandwidth so a
|
||||||
// late SetMode cannot revert a saved WFM bookmark bandwidth to 180 kHz.
|
// late SetMode cannot revert a saved WFM bookmark bandwidth to 180 kHz.
|
||||||
const tunePromise = (async () => {
|
const tunePromise = (async () => {
|
||||||
if (typeof bridge.vchanTakeSchedulerControl === "function") {
|
await bridge.trx?.modules?.vchan?.takeSchedulerControl();
|
||||||
await bridge.vchanTakeSchedulerControl();
|
|
||||||
}
|
|
||||||
|
|
||||||
const onVirtual = typeof bridge.vchanInterceptMode === "function"
|
const onVirtual = await bridge.trx?.modules?.vchan?.interceptMode(bm.mode) ?? false;
|
||||||
&& await bridge.vchanInterceptMode(bm.mode);
|
|
||||||
if (!onVirtual) {
|
if (!onVirtual) {
|
||||||
await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode));
|
await bridge.postPath("/set_mode?mode=" + encodeURIComponent(bm.mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bm.bandwidth_hz) {
|
if (bm.bandwidth_hz) {
|
||||||
const bwHandledByVchan = typeof bridge.vchanInterceptBandwidth === "function"
|
const bwHandledByVchan =
|
||||||
&& await bridge.vchanInterceptBandwidth(bm.bandwidth_hz);
|
await bridge.trx?.modules?.vchan?.interceptBandwidth(bm.bandwidth_hz) ?? false;
|
||||||
if (!bwHandledByVchan) {
|
if (!bwHandledByVchan) {
|
||||||
await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`);
|
await bridge.postPath(`/set_bandwidth?hz=${bm.bandwidth_hz}`);
|
||||||
}
|
}
|
||||||
@@ -590,6 +592,9 @@ bridge.trx.modules.bookmarks = {
|
|||||||
refreshOverlay: bmFetchOverlay,
|
refreshOverlay: bmFetchOverlay,
|
||||||
invalidateColors() { bmOverlayRevision += 1; },
|
invalidateColors() { bmOverlayRevision += 1; },
|
||||||
apply: bmApply,
|
apply: bmApply,
|
||||||
|
formatFrequency: bmFmtFreq,
|
||||||
|
fetch: bmFetch,
|
||||||
|
populateScopePicker: bmPopulateScopePicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
function bmUpdateSelectionUi() {
|
function bmUpdateSelectionUi() {
|
||||||
|
|||||||
+5
-3
@@ -69,9 +69,13 @@ export interface SchedulerService {
|
|||||||
getBookmarks(): SchedulerBookmark[];
|
getBookmarks(): SchedulerBookmark[];
|
||||||
markDirty(): void;
|
markDirty(): void;
|
||||||
}
|
}
|
||||||
|
interface VirtualChannelService {
|
||||||
|
takeSchedulerControl(): Promise<void>;
|
||||||
|
releaseToScheduler(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SchedulerWindow extends Window {
|
export interface SchedulerWindow extends Window {
|
||||||
trx: { modules: { scheduler?: SchedulerService } };
|
trx: { modules: { scheduler?: SchedulerService; vchan?: VirtualChannelService } };
|
||||||
trxUi: {
|
trxUi: {
|
||||||
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
||||||
notify?(message: string, options: { kind: "error" }): void;
|
notify?(message: string, options: { kind: "error" }): void;
|
||||||
@@ -80,7 +84,5 @@ export interface SchedulerWindow extends Window {
|
|||||||
lastActiveRigId?: string | null;
|
lastActiveRigId?: string | null;
|
||||||
serverLat?: number | null;
|
serverLat?: number | null;
|
||||||
serverLon?: number | null;
|
serverLon?: number | null;
|
||||||
vchanTakeSchedulerControl?(): Promise<void>;
|
|
||||||
vchanToggleSchedulerRelease?(): Promise<unknown>;
|
|
||||||
satScheduler?: SatelliteSchedulerApi;
|
satScheduler?: SatelliteSchedulerApi;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1137,14 +1137,14 @@ function schedulerEl(id: string): SchedulerElement {
|
|||||||
schedulerStepPending = true;
|
schedulerStepPending = true;
|
||||||
renderSchedulerStepControls();
|
renderSchedulerStepControls();
|
||||||
|
|
||||||
Promise.resolve(schedulerWindow.vchanTakeSchedulerControl?.() ?? null)
|
Promise.resolve(schedulerWindow.trx.modules.vchan?.takeSchedulerControl() ?? null)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return apiActivateSchedulerEntry(rigId, targetId);
|
return apiActivateSchedulerEntry(rigId, targetId);
|
||||||
})
|
})
|
||||||
.then(function (status) {
|
.then(function (status) {
|
||||||
currentSchedulerStatus = status || null;
|
currentSchedulerStatus = status || null;
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
schedulerWindow.vchanToggleSchedulerRelease?.() ?? null
|
schedulerWindow.trx.modules.vchan?.releaseToScheduler() ?? null
|
||||||
).then(function () {
|
).then(function () {
|
||||||
renderStatus(status);
|
renderStatus(status);
|
||||||
renderSchedulerInterleaveStatus();
|
renderSchedulerInterleaveStatus();
|
||||||
|
|||||||
@@ -50,13 +50,21 @@ interface VirtualChannelBridge {
|
|||||||
applyLocalTunedFrequency?: (frequencyHz: number) => void;
|
applyLocalTunedFrequency?: (frequencyHz: number) => void;
|
||||||
setRigFrequency?: (frequencyHz: number) => void;
|
setRigFrequency?: (frequencyHz: number) => void;
|
||||||
refreshFreqDisplay?: () => void;
|
refreshFreqDisplay?: () => void;
|
||||||
vchanTakeSchedulerControl?: () => Promise<void>;
|
trx?: { modules?: { vchan?: VirtualChannelService } };
|
||||||
vchanInterceptMode?: (mode: string) => Promise<boolean>;
|
}
|
||||||
vchanInterceptBandwidth?: (bandwidthHz: number) => Promise<boolean>;
|
|
||||||
vchanHandleSession?: (data: string) => void;
|
interface VirtualChannelService {
|
||||||
vchanHandleChannels?: (data: string) => void;
|
readonly channels: readonly VirtualChannel[];
|
||||||
vchanApplyCapabilities?: (capabilities: { filter_controls?: boolean } | null) => void;
|
readonly activeId: string | null;
|
||||||
vchanIsOnVirtual?: () => boolean;
|
activeChannel(): VirtualChannel | null;
|
||||||
|
applyCapabilities(capabilities: { filter_controls?: boolean } | null): void;
|
||||||
|
handleSession(data: string): void;
|
||||||
|
handleChannels(data: string): void;
|
||||||
|
isOnVirtual(): boolean;
|
||||||
|
interceptMode(mode: string): Promise<boolean>;
|
||||||
|
interceptBandwidth(bandwidthHz: number): Promise<boolean>;
|
||||||
|
takeSchedulerControl(): Promise<void>;
|
||||||
|
releaseToScheduler(): Promise<void>;
|
||||||
}
|
}
|
||||||
const vchanWindow = window as unknown as VirtualChannelBridge;
|
const vchanWindow = window as unknown as VirtualChannelBridge;
|
||||||
|
|
||||||
@@ -169,7 +177,6 @@ async function vchanTakeSchedulerControl() {
|
|||||||
console.error("scheduler control takeover failed", e);
|
console.error("scheduler control takeover failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vchanWindow.vchanTakeSchedulerControl = vchanTakeSchedulerControl;
|
|
||||||
|
|
||||||
// Called by app.js when the SSE `session` event arrives.
|
// Called by app.js when the SSE `session` event arrives.
|
||||||
function vchanHandleSession(data: string): void {
|
function vchanHandleSession(data: string): void {
|
||||||
@@ -208,8 +215,6 @@ function vchanHandleChannels(data: string): void {
|
|||||||
console.warn("vchan: bad channels event", e);
|
console.warn("vchan: bad channels event", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vchanWindow.vchanHandleSession = vchanHandleSession;
|
|
||||||
vchanWindow.vchanHandleChannels = vchanHandleChannels;
|
|
||||||
|
|
||||||
function vchanRender() {
|
function vchanRender() {
|
||||||
const picker = document.getElementById("vchan-picker");
|
const picker = document.getElementById("vchan-picker");
|
||||||
@@ -388,7 +393,6 @@ function vchanApplyCapabilities(caps: { filter_controls?: boolean } | null): voi
|
|||||||
picker.style.display = (caps && caps.filter_controls) ? "" : "none";
|
picker.style.display = (caps && caps.filter_controls) ? "" : "none";
|
||||||
vchanRenderSchedulerRelease();
|
vchanRenderSchedulerRelease();
|
||||||
}
|
}
|
||||||
vchanWindow.vchanApplyCapabilities = vchanApplyCapabilities;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Freq / mode interception + UI accent
|
// Freq / mode interception + UI accent
|
||||||
@@ -399,7 +403,6 @@ function vchanIsOnVirtual(): boolean {
|
|||||||
if (!vchanActiveId || vchanChannels.length === 0) return false;
|
if (!vchanActiveId || vchanChannels.length === 0) return false;
|
||||||
return vchanActiveId !== vchanChannels[0]?.id;
|
return vchanActiveId !== vchanChannels[0]?.id;
|
||||||
}
|
}
|
||||||
vchanWindow.vchanIsOnVirtual = vchanIsOnVirtual;
|
|
||||||
|
|
||||||
function vchanActiveChannel(): VirtualChannel | null {
|
function vchanActiveChannel(): VirtualChannel | null {
|
||||||
return vchanChannels.find(c => c.id === vchanActiveId) || null;
|
return vchanChannels.find(c => c.id === vchanActiveId) || null;
|
||||||
@@ -554,18 +557,34 @@ async function vchanSetChannelMode(mode: string): Promise<void> {
|
|||||||
// Called by app.js (applyModeFromPicker) and bookmarks.js (bmApply) before
|
// Called by app.js (applyModeFromPicker) and bookmarks.js (bmApply) before
|
||||||
// sending /set_mode to the server. Returns true if the change was handled
|
// sending /set_mode to the server. Returns true if the change was handled
|
||||||
// by the virtual channel (caller should skip the server request).
|
// by the virtual channel (caller should skip the server request).
|
||||||
vchanWindow.vchanInterceptMode = async function(mode: string) {
|
async function vchanInterceptMode(mode: string): Promise<boolean> {
|
||||||
if (!vchanIsOnVirtual()) return false;
|
if (!vchanIsOnVirtual()) return false;
|
||||||
await vchanSetChannelMode(mode);
|
await vchanSetChannelMode(mode);
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Called by app.js bandwidth setters before sending /set_bandwidth to the
|
// Called by app.js bandwidth setters before sending /set_bandwidth to the
|
||||||
// server. Returns true if the change was handled by the virtual channel.
|
// server. Returns true if the change was handled by the virtual channel.
|
||||||
vchanWindow.vchanInterceptBandwidth = async function(bwHz: number) {
|
async function vchanInterceptBandwidth(bwHz: number): Promise<boolean> {
|
||||||
if (!vchanIsOnVirtual()) return false;
|
if (!vchanIsOnVirtual()) return false;
|
||||||
await vchanSetChannelBandwidth(bwHz);
|
await vchanSetChannelBandwidth(bwHz);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vchanWindow.trx ??= {};
|
||||||
|
vchanWindow.trx.modules ??= {};
|
||||||
|
vchanWindow.trx.modules.vchan = {
|
||||||
|
get channels() { return vchanChannels; },
|
||||||
|
get activeId() { return vchanActiveId; },
|
||||||
|
activeChannel: vchanActiveChannel,
|
||||||
|
applyCapabilities: vchanApplyCapabilities,
|
||||||
|
handleSession: vchanHandleSession,
|
||||||
|
handleChannels: vchanHandleChannels,
|
||||||
|
isOnVirtual: vchanIsOnVirtual,
|
||||||
|
interceptMode: vchanInterceptMode,
|
||||||
|
interceptBandwidth: vchanInterceptBandwidth,
|
||||||
|
takeSchedulerControl: vchanTakeSchedulerControl,
|
||||||
|
releaseToScheduler: vchanToggleSchedulerRelease,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrap setRigFrequency (defined in app.js, loaded before this file) so that
|
// Wrap setRigFrequency (defined in app.js, loaded before this file) so that
|
||||||
|
|||||||
+2
-1
@@ -38,9 +38,10 @@ test("background decode loads configuration for the explicitly selected rig", as
|
|||||||
const source = await readFile(new URL("../../assets/web/generated/background-decode.js", import.meta.url), "utf8");
|
const source = await readFile(new URL("../../assets/web/generated/background-decode.js", import.meta.url), "utf8");
|
||||||
new vm.Script(source).runInContext(context);
|
new vm.Script(source).runInContext(context);
|
||||||
|
|
||||||
window.initBackgroundDecode("rig/a", "control");
|
window.trx.modules.backgroundDecode.initialize("rig/a", "control");
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
assert.ok(requested.includes("/background-decode/rig%2Fa"));
|
assert.ok(requested.includes("/background-decode/rig%2Fa"));
|
||||||
assert.ok(requested.includes("/bookmarks"));
|
assert.ok(requested.includes("/bookmarks"));
|
||||||
assert.ok(requested.includes("/background-decode/rig%2Fa/status"));
|
assert.ok(requested.includes("/background-decode/rig%2Fa/status"));
|
||||||
|
assert.equal(window.initBackgroundDecode, undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,5 +69,7 @@ test("bookmarks register an explicit typed service for application consumers", a
|
|||||||
assert.equal(window.trx.modules.bookmarks.overlayList.length, 1);
|
assert.equal(window.trx.modules.bookmarks.overlayList.length, 1);
|
||||||
assert.equal(window.trx.modules.bookmarks.overlayList[0].id, "one");
|
assert.equal(window.trx.modules.bookmarks.overlayList[0].id, "one");
|
||||||
assert.equal(typeof window.trx.modules.bookmarks.apply, "function");
|
assert.equal(typeof window.trx.modules.bookmarks.apply, "function");
|
||||||
|
assert.equal(typeof window.trx.modules.bookmarks.formatFrequency, "function");
|
||||||
|
assert.equal(typeof window.trx.modules.bookmarks.populateScopePicker, "function");
|
||||||
assert.equal(globalThis.bmOverlayList, undefined);
|
assert.equal(globalThis.bmOverlayList, undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,9 +28,12 @@ test("virtual channels expose typed SSE and interception boundaries", async () =
|
|||||||
const source = await readFile(new URL("../../assets/web/generated/vchan.js", import.meta.url), "utf8");
|
const source = await readFile(new URL("../../assets/web/generated/vchan.js", import.meta.url), "utf8");
|
||||||
new vm.Script(source).runInContext(context);
|
new vm.Script(source).runInContext(context);
|
||||||
|
|
||||||
assert.equal(typeof window.vchanHandleSession, "function");
|
const service = window.trx.modules.vchan;
|
||||||
assert.equal(typeof window.vchanHandleChannels, "function");
|
assert.equal(typeof service.handleSession, "function");
|
||||||
assert.equal(typeof window.vchanApplyCapabilities, "function");
|
assert.equal(typeof service.handleChannels, "function");
|
||||||
assert.equal(await window.vchanInterceptMode("USB"), false);
|
assert.equal(typeof service.applyCapabilities, "function");
|
||||||
assert.equal(await window.vchanInterceptBandwidth(2400), false);
|
assert.equal(await service.interceptMode("USB"), false);
|
||||||
|
assert.equal(await service.interceptBandwidth(2400), false);
|
||||||
|
assert.equal(window.vchanHandleSession, undefined);
|
||||||
|
assert.equal(window.vchanInterceptBandwidth, undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user