diff --git a/CHANGELOG.md b/CHANGELOG.md
index 253ee03..0ed30ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,36 +1,77 @@
# Changelog
-All notable changes to iNTERCEPT will be documented in this file.
-
-## [2.21.1] - 2026-02-20
-
-### Fixed
-- BT Locate map first-load rendering race that could cause blank/late map initialization
-- BT Locate mode switch timing so Leaflet invalidation runs after panel visibility settles
-- BT Locate trail restore startup latency by batching historical GPS point rendering
-
----
-
-## [2.21.0] - 2026-02-20
-
-### Added
-- Analytics panels for operational insights and temporal pattern analysis
-
-### Changed
-- Global map theme refresh with improved contrast and cross-dashboard consistency
-- Cross-app UX refinements for accessibility, mode consistency, and render performance
-- BT Locate enhancements including improved continuity, smoothing, and confidence reporting
-
-### Fixed
-- Weather satellite auto-scheduler and Mercator tracking reliability issues
-- Bluetooth/WiFi runtime health issues affecting scanner continuity
-- ADS-B SSE multi-client fanout stability and remote VDL2 streaming reliability
-
----
-
-## [2.15.0] - 2026-02-09
-
-### Added
+All notable changes to iNTERCEPT will be documented in this file.
+
+## [2.22.0] - 2026-02-23
+
+### Added
+- **Waterfall Receiver Overhaul** - WebSocket-based I/Q streaming with server-side FFT, click-to-tune, zoom controls, and auto-scaling
+- **Voice Alerts** - Configurable text-to-speech event notifications across modes
+- **Signal Fingerprinting** - RF device identification and pattern analysis mode
+- **RF Heatmap** - Geographic signal density visualization with Leaflet heatmap overlay
+- **SignalID** - Automatic signal classification via SigIDWiki API integration
+- **PWA Support** - Installable web app with service worker caching and manifest
+- **Real-time Signal Scope** - Live signal visualization for pager, sensor, and SSTV modes
+- **ADS-B MSG2 Surface Parsing** - Ground vehicle movement tracking from MSG2 frames
+- **Cheat Sheets** - Quick reference overlays for keyboard shortcuts and mode controls
+- App icon (SVG) for PWA and browser tab
+
+### Changed
+- **WebSDR overhaul** - Improved receiver management, audio streaming, and UI
+- **Mode stop responsiveness** - Faster timeout handling and improved WiFi/Bluetooth scanner shutdown
+- **Mode transitions** - Smoother navigation with performance instrumentation
+- **BT Locate** - Refactored JS engine with improved trail management and signal smoothing
+- **Listening Post** - Refactored with cross-module frequency routing
+- **SSTV decoder** - State machine improvements and partial image streaming
+- Analytics mode removed; per-mode analytics panels integrated into existing dashboards
+
+### Fixed
+- ADS-B SSE multi-client fanout stability and update flush timing
+- WiFi scanner robustness and monitor mode teardown reliability
+- Agent client reliability improvements for remote sensor nodes
+- SSTV VIS detector state reporting in signal monitor diagnostics
+
+### Documentation
+- Complete documentation audit across README, FEATURES, USAGE, help modal, and GitHub Pages
+- Fixed license badge (MIT → Apache 2.0) to match actual LICENSE file
+- Fixed tool name `rtl_amr` → `rtlamr` throughout all docs
+- Fixed incorrect entry point examples (`python app.py` → `sudo -E venv/bin/python intercept.py`)
+- Removed duplicate AIS Vessel Tracking section from FEATURES.md
+- Updated SSTV requirements: pure Python decoder, no external `slowrx` needed
+- Added ACARS and VDL2 mode descriptions to in-app help modal
+- GitHub Pages site: corrected Docker command, license, and tool name references
+
+---
+
+## [2.21.1] - 2026-02-20
+
+### Fixed
+- BT Locate map first-load rendering race that could cause blank/late map initialization
+- BT Locate mode switch timing so Leaflet invalidation runs after panel visibility settles
+- BT Locate trail restore startup latency by batching historical GPS point rendering
+
+---
+
+## [2.21.0] - 2026-02-20
+
+### Added
+- Analytics panels for operational insights and temporal pattern analysis
+
+### Changed
+- Global map theme refresh with improved contrast and cross-dashboard consistency
+- Cross-app UX refinements for accessibility, mode consistency, and render performance
+- BT Locate enhancements including improved continuity, smoothing, and confidence reporting
+
+### Fixed
+- Weather satellite auto-scheduler and Mercator tracking reliability issues
+- Bluetooth/WiFi runtime health issues affecting scanner continuity
+- ADS-B SSE multi-client fanout stability and remote VDL2 streaming reliability
+
+---
+
+## [2.15.0] - 2026-02-09
+
+### Added
- **Real-time WebSocket Waterfall** - I/Q capture with server-side FFT
- Click-to-tune, zoom controls, and auto-scaling quantization
- Shared waterfall UI across SDR modes with function bar controls
diff --git a/Dockerfile b/Dockerfile
index fb48ca8..c6045db 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -57,7 +57,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
soapysdr-module-airspy \
airspy \
limesuite \
- hackrf \
# Utilities
curl \
procps \
@@ -190,6 +189,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
fi \
&& cd /tmp \
&& rm -rf /tmp/SatDump \
+ # Build hackrf CLI tools from source — avoids libhackrf0 version conflict
+ # between the 'hackrf' apt package and soapysdr-module-hackrf's newer libhackrf0
+ && cd /tmp \
+ && git clone --depth 1 https://github.com/greatscottgadgets/hackrf.git \
+ && cd hackrf/host \
+ && mkdir build && cd build \
+ && cmake .. \
+ && make \
+ && make install \
+ && ldconfig \
+ && rm -rf /tmp/hackrf \
# Build rtlamr (utility meter decoder - requires Go)
&& cd /tmp \
&& curl -fsSL "https://go.dev/dl/go1.22.5.linux-$(dpkg --print-architecture).tar.gz" | tar -C /usr/local -xz \
diff --git a/README.md b/README.md
index ed677d9..b4d27b6 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -40,7 +40,7 @@ Support the developer of this open-source project
- **HF SSTV** - Terrestrial SSTV on shortwave frequencies (80m-10m, VHF, UHF)
- **APRS** - Amateur packet radio position reports and telemetry via direwolf
- **Satellite Tracking** - Pass prediction with polar plot and ground track map
-- **Utility Meters** - Electric, gas, and water meter reading via rtl_amr
+- **Utility Meters** - Electric, gas, and water meter reading via rtlamr
- **ADS-B History** - Persistent aircraft history with reporting dashboard (Postgres optional)
- **WiFi Scanning** - Monitor mode reconnaissance via aircrack-ng
- **Bluetooth Scanning** - Device discovery and tracker detection (with Ubertooth support)
@@ -57,8 +57,6 @@ Support the developer of this open-source project
## Installation / Debian / Ubuntu / MacOS
-```
-
**1. Clone and run:**
```bash
git clone https://github.com/smittix/intercept.git
@@ -150,7 +148,7 @@ Set these as environment variables for either local installs or Docker:
```bash
INTERCEPT_ADSB_AUTO_START=true \
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
-python app.py
+sudo -E venv/bin/python intercept.py
```
**Docker example (.env)**
@@ -172,7 +170,7 @@ Then open **/adsb/history** for the reporting dashboard.
After starting, open **http://localhost:5050** in your browser. The username and password is admin:admin
-The credentials can be change in the ADMIN_USERNAME & ADMIN_PASSWORD variables in config.py
+The credentials can be changed in the ADMIN_USERNAME & ADMIN_PASSWORD variables in config.py
---
@@ -245,7 +243,7 @@ Created by **smittix** - [GitHub](https://github.com/smittix)
[AIS-catcher](https://github.com/jvde-github/AIS-catcher) |
[acarsdec](https://github.com/TLeconte/acarsdec) |
[direwolf](https://github.com/wb2osz/direwolf) |
-[rtl_amr](https://github.com/bemasher/rtlamr) |
+[rtlamr](https://github.com/bemasher/rtlamr) |
[dumpvdl2](https://github.com/szpajder/dumpvdl2) |
[aircrack-ng](https://www.aircrack-ng.org/) |
[Leaflet.js](https://leafletjs.com/) |
diff --git a/config.py b/config.py
index 6b94432..b03efe5 100644
--- a/config.py
+++ b/config.py
@@ -6,35 +6,54 @@ import logging
import os
import sys
-# Application version
-VERSION = "2.21.1"
-
-# Changelog - latest release notes (shown on welcome screen)
-CHANGELOG = [
- {
- "version": "2.21.1",
- "date": "February 2026",
- "highlights": [
- "BT Locate map first-load fix with render stabilization retries during initial mode open",
- "BT Locate trail restore optimization for faster startup when historical GPS points exist",
- "BT Locate mode-switch map invalidation timing fix to prevent delayed/blank map render",
- ]
- },
- {
- "version": "2.21.0",
- "date": "February 2026",
- "highlights": [
- "Global map theme refresh with improved contrast and cross-dashboard consistency",
- "Cross-app UX updates for accessibility, mode consistency, and render performance",
- "Weather satellite reliability fixes for auto-scheduler and Mercator pass tracking",
- "Bluetooth/WiFi runtime health fixes with BT Locate continuity and confidence improvements",
- "ADS-B/VDL2 streaming reliability upgrades for multi-client SSE fanout and remote decoding",
- "Analytics enhancements with operational insights and temporal pattern panels",
- ]
- },
- {
- "version": "2.20.0",
- "date": "February 2026",
+# Application version
+VERSION = "2.22.0"
+
+# Changelog - latest release notes (shown on welcome screen)
+CHANGELOG = [
+ {
+ "version": "2.22.0",
+ "date": "February 2026",
+ "highlights": [
+ "Waterfall receiver overhaul: WebSocket I/Q streaming with server-side FFT, click-to-tune, and zoom controls",
+ "Voice alerts for configurable event notifications across modes",
+ "Signal fingerprinting mode for RF device identification and pattern analysis",
+ "RF Heatmap for geographic signal density visualization",
+ "SignalID integration via SigIDWiki API for automatic signal classification",
+ "PWA support: installable web app with service worker and manifest",
+ "Mode stop responsiveness improvements with faster timeout handling",
+ "Navigation performance instrumentation and smoother mode transitions",
+ "Pager, sensor, and SSTV real-time signal scope visualization",
+ "ADS-B MSG2 surface movement parsing for ground vehicle tracking",
+ "WebSDR major overhaul with improved receiver management and audio streaming",
+ "Documentation audit: fixed license, tool names, entry points, and SSTV decoder references",
+ "Help modal updated with ACARS and VDL2 mode descriptions",
+ ]
+ },
+ {
+ "version": "2.21.1",
+ "date": "February 2026",
+ "highlights": [
+ "BT Locate map first-load fix with render stabilization retries during initial mode open",
+ "BT Locate trail restore optimization for faster startup when historical GPS points exist",
+ "BT Locate mode-switch map invalidation timing fix to prevent delayed/blank map render",
+ ]
+ },
+ {
+ "version": "2.21.0",
+ "date": "February 2026",
+ "highlights": [
+ "Global map theme refresh with improved contrast and cross-dashboard consistency",
+ "Cross-app UX updates for accessibility, mode consistency, and render performance",
+ "Weather satellite reliability fixes for auto-scheduler and Mercator pass tracking",
+ "Bluetooth/WiFi runtime health fixes with BT Locate continuity and confidence improvements",
+ "ADS-B/VDL2 streaming reliability upgrades for multi-client SSE fanout and remote decoding",
+ "Analytics enhancements with operational insights and temporal pattern panels",
+ ]
+ },
+ {
+ "version": "2.20.0",
+ "date": "February 2026",
"highlights": [
"Space Weather mode: real-time solar and geomagnetic monitoring from NOAA SWPC, NASA SDO, and HamQSL",
"Kp index, solar wind, X-ray flux charts with Chart.js visualization",
@@ -99,14 +118,14 @@ CHANGELOG = [
"Pure Python SSTV decoder replacing broken slowrx dependency",
"Real-time signal scope for pager, sensor, and SSTV modes",
"USB-level device probe to prevent cryptic rtl_fm crashes",
- "SDR device lock-up fix from unreleased device registry on crash",
+ "SDR device lock-up fix from unreleased device registry on crash",
]
},
{
"version": "2.14.0",
"date": "February 2026",
"highlights": [
- "HF SSTV general mode with predefined shortwave frequencies",
+ "HF SSTV general mode with predefined shortwave frequencies",
"WebSDR integration for remote HF/shortwave listening",
"Listening Post signal scanner and audio pipeline improvements",
"TSCM sweep resilience, WiFi detection, and correlation fixes",
diff --git a/docs/FEATURES.md b/docs/FEATURES.md
index 8d57ef9..73a2909 100644
--- a/docs/FEATURES.md
+++ b/docs/FEATURES.md
@@ -24,17 +24,6 @@ Complete feature list for all modules.
- **Wideband spectrum analysis** with real-time visualization
- **I/Q capture** - record raw samples for offline analysis
-## AIS Vessel Tracking
-
-- **Real-time vessel tracking** via AIS-catcher on 161.975/162.025 MHz
-- **Full-screen dashboard** - dedicated popout with interactive map
-- **Interactive Leaflet map** with OpenStreetMap tiles (dark-themed)
-- **Vessel details popup** - name, MMSI, callsign, destination, ETA
-- **Navigation data** - speed, course, heading, rate of turn
-- **Ship type classification** - cargo, tanker, passenger, fishing, etc.
-- **Vessel dimensions** - length, width, draught
-- **Multi-SDR support** - RTL-SDR, HackRF, LimeSDR, Airspy, SDRplay
-
## Spy Stations (Number Stations)
- **Comprehensive database** of active number stations and diplomatic networks
diff --git a/docs/USAGE.md b/docs/USAGE.md
index 8f15fd7..37774ad 100644
--- a/docs/USAGE.md
+++ b/docs/USAGE.md
@@ -172,7 +172,7 @@ Set the following environment variables (Docker recommended):
```bash
INTERCEPT_ADSB_AUTO_START=true \
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
-python app.py
+sudo -E venv/bin/python intercept.py
```
**Docker example (.env)**
diff --git a/docs/index.html b/docs/index.html
index 7e0a7ac..a2bbc56 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -110,7 +110,7 @@
Utility Meters
-
Smart meter monitoring via rtl_amr. Receive electric, gas, and water meter broadcasts in real time.
+
Smart meter monitoring via rtlamr. Receive electric, gas, and water meter broadcasts in real time.
@@ -321,7 +321,7 @@ sudo -E venv/bin/python intercept.py
git clone https://github.com/smittix/intercept.git
cd intercept
-docker compose up -d
+docker compose --profile basic up -d --build
Requires privileged mode for USB SDR access
@@ -422,7 +422,7 @@ docker compose up -d
diff --git a/static/css/modes/bt_locate.css b/static/css/modes/bt_locate.css
index d83a15b..236cca6 100644
--- a/static/css/modes/bt_locate.css
+++ b/static/css/modes/bt_locate.css
@@ -560,3 +560,69 @@
font-size: 9px;
}
}
+
+/* ── Crosshair sweep animation ───────────────────────────────────── */
+.btl-crosshair-overlay {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ overflow: hidden;
+ z-index: 1200;
+ --btl-crosshair-x-start: 100%;
+ --btl-crosshair-y-start: 100%;
+ --btl-crosshair-x-end: 50%;
+ --btl-crosshair-y-end: 50%;
+ --btl-crosshair-duration: 1500ms;
+}
+
+.btl-crosshair-line {
+ position: absolute;
+ opacity: 0;
+ background: var(--accent-cyan);
+ will-change: transform, opacity;
+}
+
+.btl-crosshair-vertical {
+ top: 0;
+ bottom: 0;
+ width: 1px;
+ left: 0;
+ transform: translateX(var(--btl-crosshair-x-start));
+}
+
+.btl-crosshair-horizontal {
+ left: 0;
+ right: 0;
+ height: 1px;
+ top: 0;
+ transform: translateY(var(--btl-crosshair-y-start));
+}
+
+.btl-crosshair-overlay.active .btl-crosshair-vertical {
+ animation: btlCrosshairSweepX var(--btl-crosshair-duration) cubic-bezier(0.2, 0.85, 0.28, 1) forwards;
+}
+
+.btl-crosshair-overlay.active .btl-crosshair-horizontal {
+ animation: btlCrosshairSweepY var(--btl-crosshair-duration) cubic-bezier(0.2, 0.85, 0.28, 1) forwards;
+}
+
+@keyframes btlCrosshairSweepX {
+ 0% { transform: translateX(var(--btl-crosshair-x-start)); opacity: 0; }
+ 12% { opacity: 1; }
+ 85% { opacity: 1; }
+ 100% { transform: translateX(var(--btl-crosshair-x-end)); opacity: 0; }
+}
+
+@keyframes btlCrosshairSweepY {
+ 0% { transform: translateY(var(--btl-crosshair-y-start)); opacity: 0; }
+ 12% { opacity: 1; }
+ 85% { opacity: 1; }
+ 100% { transform: translateY(var(--btl-crosshair-y-end)); opacity: 0; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .btl-crosshair-overlay.active .btl-crosshair-vertical,
+ .btl-crosshair-overlay.active .btl-crosshair-horizontal {
+ animation-duration: 220ms;
+ }
+}
diff --git a/static/js/core/keyboard-shortcuts.js b/static/js/core/keyboard-shortcuts.js
index 1028687..6bebdd2 100644
--- a/static/js/core/keyboard-shortcuts.js
+++ b/static/js/core/keyboard-shortcuts.js
@@ -8,17 +8,17 @@ const KeyboardShortcuts = (function () {
function _handle(e) {
if (e.target.matches(GUARD_SELECTOR)) return;
- if (e.altKey) {
- switch (e.key.toLowerCase()) {
- case 'w': e.preventDefault(); window.switchMode && switchMode('waterfall'); break;
- case 'm': e.preventDefault(); window.VoiceAlerts && VoiceAlerts.toggleMute(); break;
- case 's': e.preventDefault(); _toggleSidebar(); break;
- case 'k': e.preventDefault(); showHelp(); break;
- case 'c': e.preventDefault(); window.CheatSheets && CheatSheets.showForCurrentMode(); break;
+ if (e.altKey) {
+ switch (e.code) {
+ case 'KeyW': e.preventDefault(); window.switchMode && switchMode('waterfall'); break;
+ case 'KeyM': e.preventDefault(); window.VoiceAlerts && VoiceAlerts.toggleMute(); break;
+ case 'KeyS': e.preventDefault(); _toggleSidebar(); break;
+ case 'KeyK': e.preventDefault(); showHelp(); break;
+ case 'KeyC': e.preventDefault(); window.CheatSheets && CheatSheets.showForCurrentMode(); break;
default:
- if (e.key >= '1' && e.key <= '9') {
+ if (e.code >= 'Digit1' && e.code <= 'Digit9') {
e.preventDefault();
- _switchToNthMode(parseInt(e.key) - 1);
+ _switchToNthMode(parseInt(e.code.replace('Digit', '')) - 1);
}
}
} else if (!e.ctrlKey && !e.metaKey) {
diff --git a/static/js/modes/bt_locate.js b/static/js/modes/bt_locate.js
index 59246be..7187c45 100644
--- a/static/js/modes/bt_locate.js
+++ b/static/js/modes/bt_locate.js
@@ -44,6 +44,7 @@ const BtLocate = (function() {
let queuedDetectionTimer = null;
let lastDetectionRenderAt = 0;
let startRequestInFlight = false;
+ let crosshairResetTimer = null;
const MAX_HEAT_POINTS = 1200;
const MAX_TRAIL_POINTS = 1200;
@@ -349,19 +350,18 @@ const BtLocate = (function() {
}
function stop() {
+ // Update UI immediately — don't wait for the backend response.
+ if (queuedDetectionTimer) {
+ clearTimeout(queuedDetectionTimer);
+ queuedDetectionTimer = null;
+ }
+ queuedDetection = null;
+ queuedDetectionOptions = null;
+ showIdleUI();
+ disconnectSSE();
+ stopAudio();
+ // Notify backend asynchronously.
fetch('/bt_locate/stop', { method: 'POST' })
- .then(r => r.json())
- .then(() => {
- if (queuedDetectionTimer) {
- clearTimeout(queuedDetectionTimer);
- queuedDetectionTimer = null;
- }
- queuedDetection = null;
- queuedDetectionOptions = null;
- showIdleUI();
- disconnectSSE();
- stopAudio();
- })
.catch(err => console.error('[BtLocate] Stop error:', err));
}
@@ -703,6 +703,32 @@ const BtLocate = (function() {
if (gpsCountEl) gpsCountEl.textContent = gpsPoints || 0;
}
+ function triggerCrosshairAnimation(lat, lon) {
+ if (!map) return;
+ const overlay = document.getElementById('btLocateCrosshairOverlay');
+ if (!overlay) return;
+ const size = map.getSize();
+ const point = map.latLngToContainerPoint([lat, lon]);
+ const targetX = Math.max(0, Math.min(size.x, point.x));
+ const targetY = Math.max(0, Math.min(size.y, point.y));
+ const startX = size.x + 8;
+ const startY = size.y + 8;
+ const duration = 1500;
+ overlay.style.setProperty('--btl-crosshair-x-start', `${startX}px`);
+ overlay.style.setProperty('--btl-crosshair-y-start', `${startY}px`);
+ overlay.style.setProperty('--btl-crosshair-x-end', `${targetX}px`);
+ overlay.style.setProperty('--btl-crosshair-y-end', `${targetY}px`);
+ overlay.style.setProperty('--btl-crosshair-duration', `${duration}ms`);
+ overlay.classList.remove('active');
+ void overlay.offsetWidth;
+ overlay.classList.add('active');
+ if (crosshairResetTimer) clearTimeout(crosshairResetTimer);
+ crosshairResetTimer = setTimeout(() => {
+ overlay.classList.remove('active');
+ crosshairResetTimer = null;
+ }, duration + 100);
+ }
+
function addMapMarker(point, options = {}) {
if (!map || point.lat == null || point.lon == null) return false;
const lat = Number(point.lat);
@@ -737,6 +763,7 @@ const BtLocate = (function() {
'Time: ' + formatPointTimestamp(trailPoint.timestamp) +
''
);
+ marker.on('click', () => triggerCrosshairAnimation(lat, lon));
trailPoints.push(trailPoint);
mapMarkers.push(marker);
diff --git a/templates/index.html b/templates/index.html
index 8b27a9e..f3415b4 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1831,6 +1831,10 @@
+