mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 06:01:56 -07:00
feat(tscm): add custom frequency range option to RF sweep
Adds a "Custom Range" sweep type that lets users specify start/end MHz instead of using a fixed preset. Useful in dense RF environments where a full or standard sweep returns too many signals and causes slowdown. UI shows start/end MHz inputs when "Custom Range" is selected. Range is validated (0 < start < end ≤ 6000 MHz) before the sweep starts. Backend threads the ranges through to _scan_rf_signals(), which already supports arbitrary frequency bands. Closes #172
This commit is contained in:
@@ -490,6 +490,7 @@ def _start_sweep_internal(
|
|||||||
bt_interface: str = '',
|
bt_interface: str = '',
|
||||||
sdr_device: int | None = None,
|
sdr_device: int | None = None,
|
||||||
verbose_results: bool = False,
|
verbose_results: bool = False,
|
||||||
|
custom_ranges: list[dict] | None = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Start a TSCM sweep without request context."""
|
"""Start a TSCM sweep without request context."""
|
||||||
global _sweep_running, _sweep_thread, _current_sweep_id
|
global _sweep_running, _sweep_thread, _current_sweep_id
|
||||||
@@ -532,7 +533,7 @@ def _start_sweep_internal(
|
|||||||
_sweep_thread = threading.Thread(
|
_sweep_thread = threading.Thread(
|
||||||
target=_run_sweep,
|
target=_run_sweep,
|
||||||
args=(sweep_type, baseline_id, wifi_enabled, bt_enabled, rf_enabled,
|
args=(sweep_type, baseline_id, wifi_enabled, bt_enabled, rf_enabled,
|
||||||
wifi_interface, bt_interface, sdr_device, verbose_results),
|
wifi_interface, bt_interface, sdr_device, verbose_results, custom_ranges),
|
||||||
daemon=True
|
daemon=True
|
||||||
)
|
)
|
||||||
_sweep_thread.start()
|
_sweep_thread.start()
|
||||||
@@ -1127,7 +1128,8 @@ def _run_sweep(
|
|||||||
wifi_interface: str = '',
|
wifi_interface: str = '',
|
||||||
bt_interface: str = '',
|
bt_interface: str = '',
|
||||||
sdr_device: int | None = None,
|
sdr_device: int | None = None,
|
||||||
verbose_results: bool = False
|
verbose_results: bool = False,
|
||||||
|
custom_ranges: list[dict] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Run the TSCM sweep in a background thread.
|
Run the TSCM sweep in a background thread.
|
||||||
@@ -1504,7 +1506,7 @@ def _run_sweep(
|
|||||||
'rf_count': len(all_rf),
|
'rf_count': len(all_rf),
|
||||||
})
|
})
|
||||||
# Try RF scan even if sdr_device is None (will use device 0)
|
# Try RF scan even if sdr_device is None (will use device 0)
|
||||||
rf_signals = _scan_rf_signals(sdr_device, sweep_ranges=preset.get('ranges'))
|
rf_signals = _scan_rf_signals(sdr_device, sweep_ranges=custom_ranges or preset.get('ranges'))
|
||||||
|
|
||||||
# If no signals and this is first RF scan, send info event
|
# If no signals and this is first RF scan, send info event
|
||||||
if not rf_signals and last_rf_scan == 0:
|
if not rf_signals and last_rf_scan == 0:
|
||||||
|
|||||||
@@ -58,6 +58,25 @@ def start_sweep():
|
|||||||
bt_interface = data.get('bt_interface', '')
|
bt_interface = data.get('bt_interface', '')
|
||||||
sdr_device = data.get('sdr_device')
|
sdr_device = data.get('sdr_device')
|
||||||
|
|
||||||
|
# Validate custom frequency ranges if provided
|
||||||
|
custom_ranges = None
|
||||||
|
if sweep_type == 'custom':
|
||||||
|
raw_ranges = data.get('custom_ranges') or []
|
||||||
|
validated = []
|
||||||
|
for rng in raw_ranges:
|
||||||
|
try:
|
||||||
|
start = float(rng.get('start', 0))
|
||||||
|
end = float(rng.get('end', 0))
|
||||||
|
step = float(rng.get('step', 0.1))
|
||||||
|
if 0 < start < end <= 6000:
|
||||||
|
validated.append({'start': start, 'end': end, 'step': step,
|
||||||
|
'name': rng.get('name') or f'{start:.0f}–{end:.0f} MHz'})
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
if not validated:
|
||||||
|
return jsonify({'status': 'error', 'message': 'custom sweep requires valid start/end MHz'}), 400
|
||||||
|
custom_ranges = validated
|
||||||
|
|
||||||
result = _start_sweep_internal(
|
result = _start_sweep_internal(
|
||||||
sweep_type=sweep_type,
|
sweep_type=sweep_type,
|
||||||
baseline_id=baseline_id,
|
baseline_id=baseline_id,
|
||||||
@@ -68,6 +87,7 @@ def start_sweep():
|
|||||||
bt_interface=bt_interface,
|
bt_interface=bt_interface,
|
||||||
sdr_device=sdr_device,
|
sdr_device=sdr_device,
|
||||||
verbose_results=verbose_results,
|
verbose_results=verbose_results,
|
||||||
|
custom_ranges=custom_ranges,
|
||||||
)
|
)
|
||||||
http_status = result.pop('http_status', 200)
|
http_status = result.pop('http_status', 200)
|
||||||
return jsonify(result), http_status
|
return jsonify(result), http_status
|
||||||
|
|||||||
@@ -12082,6 +12082,12 @@
|
|||||||
async function startTscmSweep() {
|
async function startTscmSweep() {
|
||||||
const sweepType = document.getElementById('tscmSweepType').value;
|
const sweepType = document.getElementById('tscmSweepType').value;
|
||||||
const baselineId = document.getElementById('tscmBaselineSelect').value || null;
|
const baselineId = document.getElementById('tscmBaselineSelect').value || null;
|
||||||
|
const customRanges = sweepType === 'custom' ? [{
|
||||||
|
start: parseFloat(document.getElementById('tscmCustomStartMhz').value),
|
||||||
|
end: parseFloat(document.getElementById('tscmCustomEndMhz').value),
|
||||||
|
step: 0.1,
|
||||||
|
name: `Custom ${document.getElementById('tscmCustomStartMhz').value}–${document.getElementById('tscmCustomEndMhz').value} MHz`
|
||||||
|
}] : null;
|
||||||
const wifiEnabled = document.getElementById('tscmWifiEnabled').checked;
|
const wifiEnabled = document.getElementById('tscmWifiEnabled').checked;
|
||||||
const btEnabled = document.getElementById('tscmBtEnabled').checked;
|
const btEnabled = document.getElementById('tscmBtEnabled').checked;
|
||||||
const rfEnabled = document.getElementById('tscmRfEnabled').checked;
|
const rfEnabled = document.getElementById('tscmRfEnabled').checked;
|
||||||
@@ -12122,7 +12128,8 @@
|
|||||||
wifi_interface: wifiInterface,
|
wifi_interface: wifiInterface,
|
||||||
bt_interface: btInterface,
|
bt_interface: btInterface,
|
||||||
sdr_device: sdrDevice ? parseInt(sdrDevice) : null,
|
sdr_device: sdrDevice ? parseInt(sdrDevice) : null,
|
||||||
verbose_results: verboseResults
|
verbose_results: verboseResults,
|
||||||
|
custom_ranges: customRanges
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -12891,7 +12898,7 @@
|
|||||||
if (tscmSweepStartTime) {
|
if (tscmSweepStartTime) {
|
||||||
const elapsed = (Date.now() - tscmSweepStartTime) / 1000;
|
const elapsed = (Date.now() - tscmSweepStartTime) / 1000;
|
||||||
const sweepType = document.getElementById('tscmSweepType')?.value || 'standard';
|
const sweepType = document.getElementById('tscmSweepType')?.value || 'standard';
|
||||||
const durations = { quick: 120, standard: 300, full: 900 };
|
const durations = { quick: 120, standard: 300, full: 900, custom: 300 };
|
||||||
const maxDuration = durations[sweepType] || 300;
|
const maxDuration = durations[sweepType] || 300;
|
||||||
const progress = Math.min(95, (elapsed / maxDuration) * 100);
|
const progress = Math.min(95, (elapsed / maxDuration) * 100);
|
||||||
updateTscmProgress({ progress: Math.round(progress), phase: 'Scanning' });
|
updateTscmProgress({ progress: Math.round(progress), phase: 'Scanning' });
|
||||||
|
|||||||
@@ -6,14 +6,28 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Sweep Type</label>
|
<label>Sweep Type</label>
|
||||||
<select id="tscmSweepType">
|
<select id="tscmSweepType" onchange="document.getElementById('tscmCustomRangeControls').style.display = this.value === 'custom' ? 'block' : 'none'">
|
||||||
<option value="quick">Quick Scan (2 min)</option>
|
<option value="quick">Quick Scan (2 min)</option>
|
||||||
<option value="standard" selected>Standard (5 min)</option>
|
<option value="standard" selected>Standard (5 min)</option>
|
||||||
<option value="full">Full Sweep (15 min)</option>
|
<option value="full">Full Sweep (15 min)</option>
|
||||||
<option value="wireless_cameras">Wireless Cameras</option>
|
<option value="wireless_cameras">Wireless Cameras</option>
|
||||||
<option value="body_worn">Body-Worn Devices</option>
|
<option value="body_worn">Body-Worn Devices</option>
|
||||||
<option value="gps_trackers">GPS Trackers</option>
|
<option value="gps_trackers">GPS Trackers</option>
|
||||||
|
<option value="custom">Custom Range</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div id="tscmCustomRangeControls" style="display: none; margin-top: 8px;">
|
||||||
|
<div style="display: flex; gap: 8px;">
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<label style="font-size: 10px; color: var(--text-dim);">Start (MHz)</label>
|
||||||
|
<input type="number" id="tscmCustomStartMhz" value="400" min="1" max="6000" step="1">
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<label style="font-size: 10px; color: var(--text-dim);">End (MHz)</label>
|
||||||
|
<input type="number" id="tscmCustomEndMhz" value="500" min="1" max="6000" step="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="info-text" style="font-size: 10px; color: var(--text-dim); margin-top: 3px;">Step: 100 kHz. Duration: ~5 min.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
Reference in New Issue
Block a user