bug fixes and feature updates

This commit is contained in:
James Smith
2026-01-14 10:30:24 +00:00
parent aa8a6baac4
commit fe3b3b536c
8 changed files with 116 additions and 40 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
{
"version": "2026-01-04_e27bf619",
"downloaded": "2026-01-07T14:55:20.680977Z"
"version": "2026-01-11_fae1348c",
"downloaded": "2026-01-12T15:55:42.769654Z"
}

View File

@@ -25,6 +25,7 @@ from utils.validation import (
from utils.sse import format_sse
from utils.process import safe_terminate, register_process
from utils.sdr import SDRFactory, SDRType, SDRValidationError
from utils.dependencies import get_tool_path
pager_bp = Blueprint('pager', __name__)
@@ -245,7 +246,10 @@ def start_decoding() -> Response:
bias_t=bias_t
)
multimon_cmd = ['multimon-ng', '-t', 'raw'] + decoders + ['-f', 'alpha', '-']
multimon_path = get_tool_path('multimon-ng')
if not multimon_path:
return jsonify({'status': 'error', 'message': 'multimon-ng not found'}), 400
multimon_cmd = [multimon_path, '-t', 'raw'] + decoders + ['-f', 'alpha', '-']
full_cmd = ' '.join(rtl_cmd) + ' | ' + ' '.join(multimon_cmd)
logger.info(f"Running: {full_cmd}")

View File

@@ -285,7 +285,7 @@ install_multimon_ng_from_source_macos() {
trap 'rm -rf "$tmp_dir"' EXIT
info "Cloning multimon-ng..."
git clone --depth 1 https://github.com/EliasOeworkem/multimon-ng.git "$tmp_dir/multimon-ng" >/dev/null 2>&1 \
git clone --depth 1 https://github.com/EliasOewornal/multimon-ng.git "$tmp_dir/multimon-ng" >/dev/null 2>&1 \
|| { fail "Failed to clone multimon-ng"; exit 1; }
cd "$tmp_dir/multimon-ng"

View File

@@ -1229,8 +1229,8 @@ header h1 .tagline {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
background: var(--bg-primary);
min-height: 100px;
max-height: 250px;
min-height: 400px;
max-height: 600px;
}
.output-content::-webkit-scrollbar {
@@ -1496,20 +1496,18 @@ header h1 .tagline {
background: var(--accent-cyan);
}
.waterfall-container {
padding: 0 15px;
margin-bottom: 10px;
}
#waterfallCanvas {
/* Waterfall canvases (inside collapsible panels) */
#waterfallCanvas,
#sensorWaterfallCanvas {
width: 100%;
height: 60px;
height: 30px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
transition: box-shadow 0.3s ease;
border: none;
display: block;
}
#waterfallCanvas.active {
#waterfallCanvas.active,
#sensorWaterfallCanvas.active {
border-color: var(--accent-cyan);
}
@@ -1637,20 +1635,54 @@ header h1 .tagline {
font-weight: bold;
}
.waterfall-container {
position: relative;
background: #000;
/* Removed - now using sensor-waterfall-panel structure for waterfalls */
/* Waterfall Panel (used for both pager and 433MHz modes) */
.sensor-waterfall-panel {
margin: 0 15px 10px 15px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 4px;
overflow: hidden;
}
#waterfallCanvas {
width: 100%;
height: 200px;
display: block;
.sensor-waterfall-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
font-size: 11px;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
user-select: none;
}
.sensor-waterfall-header:hover {
background: var(--bg-hover);
}
.sensor-waterfall-content {
background: #000;
transition: max-height 0.3s ease, padding 0.3s ease;
max-height: 50px;
overflow: hidden;
}
.sensor-waterfall-panel.collapsed .sensor-waterfall-content {
max-height: 0;
padding: 0;
}
.sensor-waterfall-panel.collapsed .sensor-waterfall-header {
border-bottom: none;
}
/* Removed duplicate - consolidated above */
.waterfall-scale {
display: flex;
justify-content: space-between;

View File

@@ -1530,9 +1530,9 @@
</div>
<!-- Device Intelligence Dashboard (above waterfall for prominence) -->
<div class="recon-panel" id="reconPanel">
<div class="recon-panel collapsed" id="reconPanel">
<div class="recon-header" onclick="toggleReconCollapse()" style="cursor: pointer;">
<h4><span id="reconCollapseIcon"></span> Device Intelligence</h4>
<h4><span id="reconCollapseIcon"></span> Device Intelligence</h4>
<div class="recon-stats">
<div>TRACKED: <span id="trackedCount">0</span></div>
<div>NEW: <span id="newDeviceCount">0</span></div>
@@ -1546,16 +1546,32 @@
</div>
</div>
<div class="waterfall-container">
<canvas id="waterfallCanvas" width="800" height="60"></canvas>
</div>
<div class="output-content" id="output">
<div class="placeholder" style="color: #888; text-align: center; padding: 50px;">
Configure settings and click "Start Decoding" to begin.
</div>
</div>
<!-- Pager Waterfall (pager mode only, at bottom, collapsible) -->
<div class="sensor-waterfall-panel collapsed" id="pagerWaterfallPanel" style="display: none;">
<div class="sensor-waterfall-header" onclick="togglePagerWaterfall()" style="cursor: pointer;">
<span id="pagerWaterfallIcon"></span> Signal Waterfall
</div>
<div class="sensor-waterfall-content" id="pagerWaterfallContent">
<canvas id="waterfallCanvas" width="800" height="30"></canvas>
</div>
</div>
<!-- Sensor Waterfall (433MHz mode only, at bottom, collapsible) -->
<div class="sensor-waterfall-panel collapsed" id="sensorWaterfallPanel" style="display: none;">
<div class="sensor-waterfall-header" onclick="toggleSensorWaterfall()" style="cursor: pointer;">
<span id="sensorWaterfallIcon"></span> Signal Waterfall
</div>
<div class="sensor-waterfall-content" id="sensorWaterfallContent">
<canvas id="sensorWaterfallCanvas" width="800" height="30"></canvas>
</div>
</div>
<div class="status-bar">
<div class="status-indicator">
<div class="status-dot" id="statusDot"></div>
@@ -2231,7 +2247,10 @@
document.getElementById('toolStatusAircraft').style.display = (mode === 'aircraft') ? 'grid' : 'none';
// Hide waterfall and output console for modes with their own visualizations
document.querySelector('.waterfall-container').style.display = (mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth') ? 'none' : 'block';
// Pager waterfall: show only for pager mode
document.getElementById('pagerWaterfallPanel').style.display = (mode === 'pager') ? 'block' : 'none';
// Sensor waterfall: show only for sensor (433MHz) mode
document.getElementById('sensorWaterfallPanel').style.display = (mode === 'sensor') ? 'block' : 'none';
document.getElementById('output').style.display = (mode === 'satellite' || mode === 'aircraft' || mode === 'wifi' || mode === 'bluetooth' || mode === 'listening') ? 'none' : 'block';
document.querySelector('.status-bar').style.display = (mode === 'satellite') ? 'none' : 'flex';
@@ -2518,8 +2537,9 @@
signalActivity = Math.min(1, signalActivity + 0.4);
lastMessageTime = Date.now();
// Flash waterfall canvas
const canvas = document.getElementById('waterfallCanvas');
// Flash waterfall canvas (use appropriate canvas based on mode)
const canvasId = (currentMode === 'sensor') ? 'sensorWaterfallCanvas' : 'waterfallCanvas';
const canvas = document.getElementById(canvasId);
if (canvas) {
canvas.classList.add('active');
setTimeout(() => canvas.classList.remove('active'), 500);
@@ -2539,7 +2559,9 @@
}
function renderWaterfall() {
const canvas = document.getElementById('waterfallCanvas');
// Render to the appropriate canvas based on current mode
const canvasId = (currentMode === 'sensor') ? 'sensorWaterfallCanvas' : 'waterfallCanvas';
const canvas = document.getElementById(canvasId);
if (!canvas) return;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = canvas.width;
@@ -3537,6 +3559,20 @@
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
}
function toggleSensorWaterfall() {
const panel = document.getElementById('sensorWaterfallPanel');
const icon = document.getElementById('sensorWaterfallIcon');
panel.classList.toggle('collapsed');
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
}
function togglePagerWaterfall() {
const panel = document.getElementById('pagerWaterfallPanel');
const icon = document.getElementById('pagerWaterfallIcon');
panel.classList.toggle('collapsed');
icon.textContent = panel.classList.contains('collapsed') ? '▶' : '▼';
}
// ============== WIFI RECONNAISSANCE ==============
let wifiEventSource = null;

View File

@@ -7,8 +7,8 @@ from typing import Any
logger = logging.getLogger('intercept.dependencies')
# Additional paths to search for tools (e.g., /usr/sbin on Debian)
EXTRA_TOOL_PATHS = ['/usr/sbin', '/sbin']
# Additional paths to search for tools (e.g., /usr/sbin on Debian, /opt/local/bin for MacPorts)
EXTRA_TOOL_PATHS = ['/usr/sbin', '/sbin', '/opt/local/bin', '/opt/local/sbin']
def check_tool(name: str) -> bool:
@@ -51,7 +51,7 @@ TOOL_DEPENDENCIES = {
'description': 'Digital transmission decoder',
'install': {
'apt': 'sudo apt install multimon-ng',
'brew': 'brew install multimon-ng',
'brew': 'sudo port install multimon-ng (MacPorts) or build from source',
'manual': 'https://github.com/EliasOewornal/multimon-ng'
}
},

View File

@@ -10,6 +10,7 @@ from __future__ import annotations
from typing import Optional
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
from utils.dependencies import get_tool_path
class RTLSDRCommandBuilder(CommandBuilder):
@@ -53,8 +54,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
Used for pager decoding. Supports local devices and rtl_tcp connections.
"""
rtl_fm_path = get_tool_path('rtl_fm') or 'rtl_fm'
cmd = [
'rtl_fm',
rtl_fm_path,
'-d', self._get_device_arg(device),
'-f', f'{frequency_mhz}M',
'-M', modulation,
@@ -99,8 +101,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
"connect to its SBS output (port 30003)."
)
dump1090_path = get_tool_path('dump1090') or 'dump1090'
cmd = [
'dump1090',
dump1090_path,
'--net',
'--device-index', str(device.index),
'--quiet'
@@ -127,8 +130,9 @@ class RTLSDRCommandBuilder(CommandBuilder):
Outputs JSON for easy parsing. Supports local devices and rtl_tcp connections.
"""
rtl_433_path = get_tool_path('rtl_433') or 'rtl_433'
cmd = [
'rtl_433',
rtl_433_path,
'-d', self._get_device_arg(device),
'-f', f'{frequency_mhz}M',
'-F', 'json'