diff --git a/PROJECT.md b/PROJECT.md index efb1242..16180fa 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -18,15 +18,17 @@ Understanding the RF environment around you is useful for: ## Key Features - **WiFi Scanning** - Discover networks with SSID, BSSID, RSSI, channel, encryption -- **Bluetooth Scanning** - Classic BT and BLE device discovery with device type inference +- **Bluetooth Scanning** - BLE device discovery via bleak library with reliable RSSI - **Distance Estimation** - RSSI-based distance calculation using log-distance path loss model - **OUI Lookup** - Manufacturer identification from MAC addresses - **Web Dashboard** - Real-time visualization with multiple views: - Radar view (polar plot) - 2D World Map (Leaflet/OpenStreetMap) - 3D Building Map (MapLibre GL JS) -- **Floor-based Positioning** - Assign devices to building floors -- **Live Tracking** - Real-time Bluetooth tracking mode +- **Floor-based Positioning** - Assign devices to building floors (persisted in database) +- **Live Tracking** - Real-time Bluetooth tracking (auto-starts, 4-second intervals) +- **Movement Detection** - Statistical analysis to detect moving devices (purple markers) +- **Historical Database** - SQLite storage for device history, RSSI time-series, statistics - **Auto-scan** - Scheduled background scanning - **Data Export** - JSON scan history with timestamps @@ -71,8 +73,8 @@ Understanding the RF environment around you is useful for: - Linux (tested on Raspberry Pi OS / Debian) - Python 3.11+ - `iw` - WiFi scanning -- `hcitool` / `bluetoothctl` - Bluetooth scanning -- `sudo` access for RF scanning +- BlueZ/D-Bus - Bluetooth (via bleak library) +- `sudo` access for WiFi scanning ### Python - Flask - Web framework diff --git a/ROADMAP.md b/ROADMAP.md index 702524a..2ced3a6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -42,7 +42,9 @@ - [x] Moving device detection (purple markers) - [x] Filter-aware scanning (WiFi/BT toggle) - [x] Reliable RSSI acquisition for movement tracking (bleak) -- [ ] Position smoothing/averaging +- [x] Position smoothing/averaging (statistical, 5-sample + stddev) +- [x] Floor persistence in SQLite database +- [x] Popup persistence during live updates - [ ] Device trails/history visualization --- diff --git a/TASKS.md b/TASKS.md index d03cd4a..6866f98 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1,7 +1,7 @@ # RF Mapper - Active Tasks **Sprint:** v0.3.0 - 3D Visualization -**Updated:** 2026-01-31 +**Updated:** 2026-02-01 --- @@ -48,7 +48,7 @@ | Status | Task | Notes | |--------|------|-------| -| [ ] | Position smoothing | Average RSSI over multiple samples | +| [x] | Position smoothing | Statistical averaging (5 samples + stddev) | | [ ] | Device trails | Show movement history on map | | [ ] | Signal strength graph | Per-device RSSI over time | | [ ] | Scan history browser | View past scans in UI | @@ -81,6 +81,9 @@ | SQLite historical database | 2026-02-01 | | Bleak BLE scanning (reliable RSSI) | 2026-02-01 | | Auto-start live BT tracking | 2026-02-01 | +| Statistical movement detection | 2026-02-01 | +| Floor persistence in database | 2026-02-01 | +| Popup persistence during updates | 2026-02-01 | --- @@ -101,5 +104,8 @@ - BLE scanning now uses `bleak` Python library (reliable RSSI via D-Bus) - WiFi scanning works well with `iw` command -- Live BT tracking auto-starts on page load (3-second scan interval) +- Live BT tracking auto-starts on page load (4-second scan interval) - Historical data stored in SQLite database with auto-cleanup +- Movement detection uses statistical analysis (5-sample avg + 2σ threshold) +- Floor assignments persist in database across page reloads +- Popups stay open during live tracking updates diff --git a/TODO.md b/TODO.md index ac019cb..d86f56c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # RF Mapper - TODO / Backlog -**Last Updated:** 2026-01-31 +**Last Updated:** 2026-02-01 --- @@ -87,8 +87,8 @@ ## Data & Storage -- [ ] SQLite database backend -- [ ] Automatic scan rotation/cleanup +- [x] SQLite database backend +- [x] Automatic scan rotation/cleanup - [ ] Compressed JSON storage - [ ] Cloud backup option - [ ] Import from other tools (Kismet, etc.) @@ -215,6 +215,9 @@ - [x] New device alerts - [x] Automatic data retention/cleanup - [x] Reliable BLE scanning with bleak library +- [x] Statistical movement detection (reduces false positives) +- [x] Floor persistence in database +- [x] Popup persistence during live updates --- diff --git a/src/rf_mapper/web/static/js/app.js b/src/rf_mapper/web/static/js/app.js index e62f669..53f4725 100644 --- a/src/rf_mapper/web/static/js/app.js +++ b/src/rf_mapper/web/static/js/app.js @@ -101,6 +101,7 @@ let map3dDeviceData = {}; // Store device info for popup display let currentFloor = 'all'; // Will be set to building.currentFloor on init let map3dInitialized = false; let map3dLoaded = false; +let openPopupDeviceId = null; // Track which device popup is open // Initialize on DOM ready document.addEventListener('DOMContentLoaded', () => { @@ -1127,6 +1128,14 @@ function update3DMarkers() { return; } + // Save currently open popup before removing markers + let savedPopupDeviceId = openPopupDeviceId; + map3dMarkers.forEach(m => { + if (m.getPopup() && m.getPopup().isOpen()) { + savedPopupDeviceId = m._deviceId || savedPopupDeviceId; + } + }); + // Remove existing markers map3dMarkers.forEach(m => m.remove()); map3dMarkers = []; @@ -1215,6 +1224,9 @@ function update3DMarkers() { .setLngLat([lon + lonOffset, lat + latOffset]) .setPopup(popup) .addTo(map3d); + marker._deviceId = wifiDeviceId; + popup.on('open', () => { openPopupDeviceId = wifiDeviceId; }); + popup.on('close', () => { if (openPopupDeviceId === wifiDeviceId) openPopupDeviceId = null; }); map3dMarkers.push(marker); }); @@ -1266,9 +1278,20 @@ function update3DMarkers() { .setLngLat([lon + lonOffset, lat + latOffset]) .setPopup(popup) .addTo(map3d); + marker._deviceId = btDeviceId; + popup.on('open', () => { openPopupDeviceId = btDeviceId; }); + popup.on('close', () => { if (openPopupDeviceId === btDeviceId) openPopupDeviceId = null; }); map3dMarkers.push(marker); }); + // Reopen saved popup if device still exists + if (savedPopupDeviceId) { + const markerToOpen = map3dMarkers.find(m => m._deviceId === savedPopupDeviceId); + if (markerToOpen && markerToOpen.getPopup()) { + markerToOpen.togglePopup(); + } + } + console.log('update3DMarkers:', map3dMarkers.length, 'DOM markers at floor', scannerFloor); // Update floor device count