mirror of
https://github.com/smittix/intercept.git
synced 2026-05-31 18:23:37 -07:00
Add multi-SDR support to WeFax decoder (HackRF, LimeSDR, Airspy, SDRPlay)
Replace hardcoded rtl_fm with SDRFactory abstraction layer so WeFax works with any supported SDR hardware, matching the pattern used by APRS and other modes. RTL-SDR direct sampling flag preserved for HF reception. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+76
-75
@@ -148,20 +148,20 @@ var WeFax = (function () {
|
||||
opt.textContent = f.khz + ' kHz — ' + f.description;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Start / Stop ----
|
||||
|
||||
function selectedFrequencyReference() {
|
||||
var alignCheckbox = document.getElementById('wefaxAutoUsbAlign');
|
||||
if (alignCheckbox && !alignCheckbox.checked) {
|
||||
return 'dial';
|
||||
}
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (state.running) return;
|
||||
}
|
||||
|
||||
// ---- Start / Stop ----
|
||||
|
||||
function selectedFrequencyReference() {
|
||||
var alignCheckbox = document.getElementById('wefaxAutoUsbAlign');
|
||||
if (alignCheckbox && !alignCheckbox.checked) {
|
||||
return 'dial';
|
||||
}
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (state.running) return;
|
||||
|
||||
var freqSel = document.getElementById('wefaxFrequency');
|
||||
var freqKhz = freqSel ? parseFloat(freqSel.value) : 0;
|
||||
@@ -177,41 +177,42 @@ var WeFax = (function () {
|
||||
var gainInput = document.getElementById('wefaxGain');
|
||||
var dsCheckbox = document.getElementById('wefaxDirectSampling');
|
||||
|
||||
var deviceSel = document.getElementById('rtlDevice');
|
||||
var device = deviceSel ? parseInt(deviceSel.value, 10) || 0 : 0;
|
||||
var device = (typeof getSelectedDevice === 'function')
|
||||
? parseInt(getSelectedDevice(), 10) || 0 : 0;
|
||||
|
||||
var body = {
|
||||
frequency_khz: freqKhz,
|
||||
station: station,
|
||||
device: device,
|
||||
gain: gainInput ? parseFloat(gainInput.value) || 40 : 40,
|
||||
ioc: iocSel ? parseInt(iocSel.value, 10) : 576,
|
||||
lpm: lpmSel ? parseInt(lpmSel.value, 10) : 120,
|
||||
direct_sampling: dsCheckbox ? dsCheckbox.checked : true,
|
||||
frequency_reference: selectedFrequencyReference(),
|
||||
};
|
||||
sdr_type: (typeof getSelectedSDRType === 'function') ? getSelectedSDRType() : 'rtlsdr',
|
||||
gain: gainInput ? parseFloat(gainInput.value) || 40 : 40,
|
||||
ioc: iocSel ? parseInt(iocSel.value, 10) : 576,
|
||||
lpm: lpmSel ? parseInt(lpmSel.value, 10) : 120,
|
||||
direct_sampling: dsCheckbox ? dsCheckbox.checked : true,
|
||||
frequency_reference: selectedFrequencyReference(),
|
||||
};
|
||||
|
||||
fetch('/wefax/start', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (data.status === 'started' || data.status === 'already_running') {
|
||||
var tunedKhz = Number(data.tuned_frequency_khz);
|
||||
if (isNaN(tunedKhz) || tunedKhz <= 0) tunedKhz = freqKhz;
|
||||
state.running = true;
|
||||
updateButtons(true);
|
||||
if (data.usb_offset_applied) {
|
||||
setStatus('Scanning ' + tunedKhz + ' kHz (USB aligned from ' + freqKhz + ' kHz)...');
|
||||
} else {
|
||||
setStatus('Scanning ' + tunedKhz + ' kHz...');
|
||||
}
|
||||
setStripFreq(tunedKhz);
|
||||
connectSSE();
|
||||
} else {
|
||||
var errMsg = data.message || 'unknown error';
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (data.status === 'started' || data.status === 'already_running') {
|
||||
var tunedKhz = Number(data.tuned_frequency_khz);
|
||||
if (isNaN(tunedKhz) || tunedKhz <= 0) tunedKhz = freqKhz;
|
||||
state.running = true;
|
||||
updateButtons(true);
|
||||
if (data.usb_offset_applied) {
|
||||
setStatus('Scanning ' + tunedKhz + ' kHz (USB aligned from ' + freqKhz + ' kHz)...');
|
||||
} else {
|
||||
setStatus('Scanning ' + tunedKhz + ' kHz...');
|
||||
}
|
||||
setStripFreq(tunedKhz);
|
||||
connectSSE();
|
||||
} else {
|
||||
var errMsg = data.message || 'unknown error';
|
||||
setStatus('Error: ' + errMsg);
|
||||
showStripError(errMsg);
|
||||
}
|
||||
@@ -342,25 +343,25 @@ var WeFax = (function () {
|
||||
if (idleEl) idleEl.style.display = 'none';
|
||||
}
|
||||
|
||||
// Image complete
|
||||
if (data.status === 'complete' && data.image) {
|
||||
scopeImageBurst = 1.0;
|
||||
loadImages();
|
||||
setStatus('Image decoded: ' + (data.line_count || '?') + ' lines');
|
||||
}
|
||||
|
||||
if (data.status === 'complete') {
|
||||
state.running = false;
|
||||
updateButtons(false);
|
||||
if (!state.schedulerEnabled) {
|
||||
disconnectSSE();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.status === 'error') {
|
||||
state.running = false;
|
||||
updateButtons(false);
|
||||
showStripError(data.message || 'Decode error');
|
||||
// Image complete
|
||||
if (data.status === 'complete' && data.image) {
|
||||
scopeImageBurst = 1.0;
|
||||
loadImages();
|
||||
setStatus('Image decoded: ' + (data.line_count || '?') + ' lines');
|
||||
}
|
||||
|
||||
if (data.status === 'complete') {
|
||||
state.running = false;
|
||||
updateButtons(false);
|
||||
if (!state.schedulerEnabled) {
|
||||
disconnectSSE();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.status === 'error') {
|
||||
state.running = false;
|
||||
updateButtons(false);
|
||||
showStripError(data.message || 'Decode error');
|
||||
}
|
||||
|
||||
if (data.status === 'stopped') {
|
||||
@@ -1062,24 +1063,24 @@ var WeFax = (function () {
|
||||
station: station,
|
||||
frequency_khz: freqKhz,
|
||||
device: device,
|
||||
gain: gainInput ? parseFloat(gainInput.value) || 40 : 40,
|
||||
ioc: iocSel ? parseInt(iocSel.value, 10) : 576,
|
||||
lpm: lpmSel ? parseInt(lpmSel.value, 10) : 120,
|
||||
direct_sampling: dsCheckbox ? dsCheckbox.checked : true,
|
||||
frequency_reference: selectedFrequencyReference(),
|
||||
}),
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (data.status === 'ok') {
|
||||
var status = 'Auto-capture enabled — ' + (data.scheduled_count || 0) + ' broadcasts scheduled';
|
||||
if (data.usb_offset_applied && !isNaN(Number(data.tuned_frequency_khz))) {
|
||||
status += ' (tuning ' + Number(data.tuned_frequency_khz) + ' kHz)';
|
||||
}
|
||||
setStatus(status);
|
||||
syncSchedulerCheckboxes(true);
|
||||
state.schedulerEnabled = true;
|
||||
connectSSE();
|
||||
gain: gainInput ? parseFloat(gainInput.value) || 40 : 40,
|
||||
ioc: iocSel ? parseInt(iocSel.value, 10) : 576,
|
||||
lpm: lpmSel ? parseInt(lpmSel.value, 10) : 120,
|
||||
direct_sampling: dsCheckbox ? dsCheckbox.checked : true,
|
||||
frequency_reference: selectedFrequencyReference(),
|
||||
}),
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (data.status === 'ok') {
|
||||
var status = 'Auto-capture enabled — ' + (data.scheduled_count || 0) + ' broadcasts scheduled';
|
||||
if (data.usb_offset_applied && !isNaN(Number(data.tuned_frequency_khz))) {
|
||||
status += ' (tuning ' + Number(data.tuned_frequency_khz) + ' kHz)';
|
||||
}
|
||||
setStatus(status);
|
||||
syncSchedulerCheckboxes(true);
|
||||
state.schedulerEnabled = true;
|
||||
connectSSE();
|
||||
startSchedulerPoll();
|
||||
} else {
|
||||
setStatus('Scheduler error: ' + (data.message || 'unknown'));
|
||||
|
||||
Reference in New Issue
Block a user