04bd80b25b
Major milestone: GPS coordinates now auto-extract from filenames and uploads appear on map with full end-to-end workflow functional! ✨ GPS Auto-Extraction Features: - JavaScript GPS extractor class matching Python patterns - Supports 3 filename formats: * N/S/E/W: 34.0478N_118.2349W_filename.sub * lat/lon prefix: lat34.0478lon-118.2348_filename.sub * Signed decimal: -34.0478_118.2348_filename.sub - Auto-populates latitude/longitude form fields on file drop - Green notification toast shows detected coordinates - File list shows GPS badge for files with coordinates 🗺️ Web Interface Improvements: - Upload endpoint now stores captures in-memory - Query endpoint returns uploaded captures for map display - Stats endpoint shows real-time upload counts - Map displays uploaded captures as markers - Color-coded by frequency band 📁 Updated Files: - static/js/upload.js: GPS extraction + auto-population - src/api/main_simple.py: In-memory storage + endpoints - src/parser/gps_extractor.py: Backend GPS extraction (Python) - scripts/test_gps_extraction.py: Python test suite - test_gps_extraction.html: Browser test suite 📊 T-Embed Files Updated: - 34.0478N_118.2348W_1637_raw_8.sub: 315 MHz Princeton - 34.0478N_118.2349W_1351_raw_10.sub: 433.92 MHz Princeton - 34.0478N_118.2349W_1650_test_raw.sub: 433.92 MHz RAW - All now have proper Flipper SubGhz headers ✅ Tested Features: - GPS extraction from filename: 34.0478N_118.2349W → 34.0478, -118.2349 - Auto-population of GPS fields in upload form - File upload with GPS validation - Capture appears on map after upload - Statistics update in real-time - Frequency distribution calculated correctly 🎯 End-to-End Flow Working: 1. User drops .sub file with GPS in filename 2. GPS auto-detected and form fields populate 3. User clicks Upload 4. Server parses RF data + GPS coordinates 5. Capture stored in memory 6. Map refreshes and displays new marker 7. Stats update with new counts 🚀 Demo: http://localhost:8000 Upload 34.0478N_118.2349W_1351_raw_10.sub and watch it appear on map! 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
392 lines
13 KiB
Markdown
392 lines
13 KiB
Markdown
# SubGHz Wigle-Like Wardriving System
|
|
**Complete GPS+RF Capture and Export System**
|
|
|
|
## Overview
|
|
|
|
This system creates a Wigle-like wardriving platform for sub-GHz RF signals, pairing GPS coordinates with Bruce T-Embed RF captures in a standardized format similar to Wigle WiFi wardriving.
|
|
|
|
**Architecture:**
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ GPS Sources (termux-location) │
|
|
└─────────────────┬────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Bruce T-Embed RF Captures (.sub files) │
|
|
│ - Frequency, Protocol, Preset │
|
|
│ - RAW data, Key data │
|
|
└─────────────────┬────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ SubGHz Wardriving Database (~/logs/subghz_wardriving.db) │
|
|
│ ├─ wardriving_sessions │
|
|
│ ├─ gps_track (GPS breadcrumbs) │
|
|
│ └─ subghz_signals (RF+GPS pairs) │
|
|
└─────────────────┬────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Export Formats │
|
|
│ ├─ JSON (detailed) │
|
|
│ ├─ CSV (standard wardriving) │
|
|
│ ├─ Wigle CSV (standardized for sharing) │
|
|
│ └─ SQLite (database backup) │
|
|
└──────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### 1. Capture GPS + Associate with RF File
|
|
|
|
```bash
|
|
# Capture current GPS and pair with Bruce .sub file
|
|
python3 ~/python/subghz_wardriving/quick_capture.py \
|
|
~/projects/device-integration/bruce/data/captures/raw10.sub
|
|
|
|
# With custom session name
|
|
python3 ~/python/subghz_wardriving/quick_capture.py \
|
|
raw10.sub downtown_survey
|
|
```
|
|
|
|
**Latest GPS:**
|
|
- **Coordinates:** 34.07362505, -118.35955673
|
|
- **Accuracy:** 24.5m
|
|
- **Timestamp:** 2026-01-13T11:17:51-08:00
|
|
- **Provider:** GPS
|
|
|
|
### 2. Export to Wigle Format
|
|
|
|
```bash
|
|
# Export all data to Wigle CSV
|
|
gps wigle-export
|
|
|
|
# Export specific session
|
|
gps wigle-export --session downtown_survey
|
|
|
|
# Direct export
|
|
python3 -c "
|
|
from subghz_wardriving.exporter import SubGHzExporter, ExportConfig
|
|
exporter = SubGHzExporter()
|
|
config = ExportConfig(format='wigle', compress=False)
|
|
result = exporter.export_session(config)
|
|
print(f'Exported to: {result[\"output_file\"]}')
|
|
"
|
|
```
|
|
|
|
**Output Location:** `~/exports/subghz_wardriving/`
|
|
|
|
## Database Schema
|
|
|
|
### Table: `subghz_signals`
|
|
|
|
Stores RF captures paired with GPS coordinates:
|
|
|
|
| Column | Type | Description |
|
|
|--------|------|-------------|
|
|
| `id` | INTEGER | Primary key |
|
|
| `session_id` | TEXT | Wardriving session ID |
|
|
| `capture_id` | TEXT | Unique capture identifier |
|
|
| `timestamp` | TEXT | ISO 8601 timestamp |
|
|
| `bruce_filename` | TEXT | Original .sub filename |
|
|
| `bruce_file_path` | TEXT | Full path to .sub file |
|
|
| `file_size_bytes` | INTEGER | File size |
|
|
| `frequency_hz` | INTEGER | RF frequency in Hz |
|
|
| `protocol` | TEXT | Protocol (RAW, Princeton, etc.) |
|
|
| `gps_latitude` | REAL | Latitude (decimal degrees) |
|
|
| `gps_longitude` | REAL | Longitude (decimal degrees) |
|
|
| `gps_accuracy_meters` | REAL | GPS accuracy |
|
|
| `interpolated` | BOOLEAN | GPS interpolated? |
|
|
| `interpolation_confidence` | REAL | Confidence score (0-1) |
|
|
| `time_gap_seconds` | REAL | Time since last capture |
|
|
| `distance_traveled_meters` | REAL | Distance from last capture |
|
|
| `estimated_speed_mps` | REAL | Speed in m/s |
|
|
| `file_hash_md5` | TEXT | MD5 hash of file |
|
|
| `notes` | TEXT | Additional notes |
|
|
|
|
### Table: `gps_track`
|
|
|
|
GPS breadcrumb trail:
|
|
|
|
| Column | Type | Description |
|
|
|--------|------|-------------|
|
|
| `id` | INTEGER | Primary key |
|
|
| `session_id` | TEXT | Session identifier |
|
|
| `timestamp` | TEXT | ISO 8601 timestamp |
|
|
| `latitude` | REAL | Latitude |
|
|
| `longitude` | REAL | Longitude |
|
|
| `altitude` | REAL | Altitude (meters) |
|
|
| `accuracy` | REAL | Horizontal accuracy (meters) |
|
|
| `speed` | REAL | Speed (m/s) |
|
|
| `provider` | TEXT | GPS provider (gps/network/passive) |
|
|
|
|
### Table: `wardriving_sessions`
|
|
|
|
Session metadata:
|
|
|
|
| Column | Type | Description |
|
|
|--------|------|-------------|
|
|
| `session_id` | TEXT | Primary key |
|
|
| `start_time` | TEXT | Session start time |
|
|
| `end_time` | TEXT | Session end time |
|
|
| `total_signals` | INTEGER | Number of signals captured |
|
|
| `total_distance_km` | REAL | Total distance traveled |
|
|
| `device_info` | TEXT | Device information |
|
|
| `notes` | TEXT | Session notes |
|
|
|
|
## Wigle CSV Format
|
|
|
|
### Header Format
|
|
|
|
```csv
|
|
WigleWifi-1.4,appRelease=SubGHz-Wardrive-1.0,model=BruceT-Embed,release=Android,device=Phone,display=SubGHzWardrive,board=termux,brand=custom
|
|
SignalHash,Frequency(MHz),Protocol,RSSI(dBm),Latitude,Longitude,Altitude,Accuracy,FirstSeen,LastSeen,CaptureCount,Interpolated,Confidence,BruceFile,SessionID
|
|
```
|
|
|
|
### Columns
|
|
|
|
| Column | Description |
|
|
|--------|-------------|
|
|
| `SignalHash` | SHA256 hash of frequency+protocol (16 chars) |
|
|
| `Frequency(MHz)` | Frequency in MHz (e.g., 433.92) |
|
|
| `Protocol` | Protocol name (RAW, Princeton, etc.) |
|
|
| `RSSI(dBm)` | Signal strength (not available from .sub files) |
|
|
| `Latitude` | Decimal degrees |
|
|
| `Longitude` | Decimal degrees |
|
|
| `Altitude` | Meters above sea level |
|
|
| `Accuracy` | GPS accuracy in meters |
|
|
| `FirstSeen` | ISO 8601 timestamp of first capture |
|
|
| `LastSeen` | ISO 8601 timestamp of last capture |
|
|
| `CaptureCount` | Number of times signal was seen |
|
|
| `Interpolated` | true/false - was GPS interpolated? |
|
|
| `Confidence` | Interpolation confidence (0-1) |
|
|
| `BruceFile` | Original .sub filename |
|
|
| `SessionID` | Wardriving session ID |
|
|
|
|
### Signal Aggregation
|
|
|
|
Signals are grouped by hash (frequency + protocol), similar to how Wigle groups WiFi by BSSID. Multiple captures of the same signal show:
|
|
- First and last seen timestamps
|
|
- Total capture count
|
|
- Location from first capture
|
|
|
|
## Usage Examples
|
|
|
|
### Example 1: Quick Capture
|
|
|
|
```bash
|
|
# Capture GPS now with Bruce file
|
|
python3 ~/python/subghz_wardriving/quick_capture.py raw10.sub
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
📡 SubGHz GPS Capture
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Bruce File: raw10.sub
|
|
Session: quick_20260113_112045
|
|
|
|
🛰️ Getting GPS location...
|
|
✓ GPS Location: 34.073625, -118.359557
|
|
Accuracy: 24.5m
|
|
|
|
📻 Parsing RF file...
|
|
✓ Frequency: 433920000 Hz (433.920 MHz)
|
|
Protocol: RAW
|
|
Preset: FCC_433
|
|
|
|
💾 Storing to database...
|
|
✓ Stored signal: quick_20260113_112045_raw10_1736789645
|
|
|
|
✅ Capture paired successfully!
|
|
Database: ~/logs/subghz_wardriving.db
|
|
```
|
|
|
|
### Example 2: Export Session
|
|
|
|
```bash
|
|
# Export to Wigle CSV
|
|
gps wigle-export --session downtown_survey
|
|
```
|
|
|
|
**Output File:** `~/exports/subghz_wardriving/downtown_survey_20260113_112130_wigle.csv`
|
|
|
|
**Content:**
|
|
```csv
|
|
WigleWifi-1.4,appRelease=SubGHz-Wardrive-1.0,model=BruceT-Embed,release=Android,device=Phone,display=SubGHzWardrive,board=termux,brand=custom
|
|
SignalHash,Frequency(MHz),Protocol,RSSI(dBm),Latitude,Longitude,Altitude,Accuracy,FirstSeen,LastSeen,CaptureCount,Interpolated,Confidence,BruceFile,SessionID
|
|
8a3f2d1c4e5b6a7f,433.920,RAW,,34.073625,-118.359557,,24.5,2026-01-13T19:17:51Z,2026-01-13T19:17:51Z,1,false,1.0,raw10.sub,downtown_survey
|
|
```
|
|
|
|
### Example 3: Wardriving Session
|
|
|
|
```bash
|
|
# Start GPS logging
|
|
gps on --session wardrive_session1 --interval 158.4
|
|
|
|
# Bruce captures RF signals to /BruceRF/*.sub files
|
|
# (Manually on Bruce device)
|
|
|
|
# After session, run batch import
|
|
for subfile in ~/projects/device-integration/bruce/data/captures/*.sub; do
|
|
python3 ~/python/subghz_wardriving/quick_capture.py "$subfile" wardrive_session1
|
|
done
|
|
|
|
# Stop GPS logging
|
|
gps off
|
|
|
|
# Export to Wigle format
|
|
gps wigle-export --session wardrive_session1
|
|
```
|
|
|
|
## File Locations
|
|
|
|
| Component | Path |
|
|
|-----------|------|
|
|
| **Database** | `~/logs/subghz_wardriving.db` |
|
|
| **GPS Quick Log** | `~/logs/gps_quick.jsonl` |
|
|
| **GPS Correlations** | `~/logs/gps_correlations.db` (legacy) |
|
|
| **Exports** | `~/exports/subghz_wardriving/` |
|
|
| **Bruce Captures** | `~/projects/device-integration/bruce/data/captures/` |
|
|
| **Quick Capture Script** | `~/python/subghz_wardriving/quick_capture.py` |
|
|
| **Exporter Module** | `~/python/subghz_wardriving/exporter.py` |
|
|
| **Database Module** | `~/python/subghz_wardriving/database.py` |
|
|
|
|
## Integration with Existing GPS Infrastructure
|
|
|
|
This system integrates with your existing GPS wardriving infrastructure:
|
|
|
|
### Existing Systems
|
|
1. **`gps` command** - CLI tool for GPS operations
|
|
2. **`gps_service.py`** - Background GPS logging (158.4s interval)
|
|
3. **`smart_gps_detector.py`** - Auto-detection and file renaming
|
|
4. **`realtime_gps_attribution.py`** - Real-time GPS attribution
|
|
|
|
### New Additions
|
|
1. **`quick_capture.py`** - Quick GPS+RF pairing
|
|
2. **Wigle export format** - Added to `exporter.py`
|
|
3. **`gps wigle-export`** - New command in `gps` script
|
|
4. **Standardized database** - Uses existing `subghz_wardriving.db`
|
|
|
|
## Advanced Features
|
|
|
|
### GPS Interpolation
|
|
|
|
When RF captures occur between GPS readings, the system interpolates GPS coordinates based on:
|
|
- Time gap between captures
|
|
- Distance traveled
|
|
- Estimated speed
|
|
- Interpolation confidence score
|
|
|
|
### Session Management
|
|
|
|
```python
|
|
from subghz_wardriving.database import SubGHzWardrivingDB
|
|
|
|
db = SubGHzWardrivingDB()
|
|
|
|
# Create session
|
|
db.create_session("my_session", device_info="Bruce T-Embed", notes="Downtown survey")
|
|
|
|
# End session (calculates statistics)
|
|
db.end_session("my_session")
|
|
|
|
# Get summary
|
|
summary = db.get_session_summary("my_session")
|
|
print(f"Signals: {summary['session']['total_signals']}")
|
|
print(f"Distance: {summary['session']['total_distance_km']} km")
|
|
```
|
|
|
|
### Filtering Exports
|
|
|
|
```python
|
|
from subghz_wardriving.exporter import SubGHzExporter, ExportConfig
|
|
|
|
exporter = SubGHzExporter()
|
|
|
|
# Export only 433 MHz signals
|
|
config = ExportConfig(
|
|
format='wigle',
|
|
frequency_filter=(433_000_000, 434_000_000), # 433-434 MHz
|
|
confidence_threshold=0.8, # High confidence only
|
|
protocol_filter='RAW'
|
|
)
|
|
|
|
result = exporter.export_session(config)
|
|
```
|
|
|
|
## Comparison to WiFi Wigle
|
|
|
|
| Feature | WiFi Wigle | SubGHz Wigle |
|
|
|---------|------------|--------------|
|
|
| **Signal ID** | BSSID (MAC address) | SignalHash (freq+protocol) |
|
|
| **Signal Name** | SSID | Protocol name |
|
|
| **Frequency** | WiFi channels (2.4/5 GHz) | SubGHz (300-928 MHz) |
|
|
| **Strength** | RSSI (dBm) | Not available from .sub |
|
|
| **Device** | WiFi adapter | Bruce T-Embed CC1101 |
|
|
| **Format** | CSV | CSV (Wigle-compatible) |
|
|
| **Location** | GPS | GPS |
|
|
| **Aggregation** | By BSSID | By SignalHash |
|
|
|
|
## Future Enhancements
|
|
|
|
1. **RSSI Integration** - If Bruce firmware provides signal strength
|
|
2. **Real-time Export** - Live streaming to Wigle format
|
|
3. **Map Visualization** - Plot signals on map
|
|
4. **Protocol Decoding** - Decode known protocols (garage doors, car keys, etc.)
|
|
5. **Community Database** - Share SubGHz signal maps
|
|
6. **Cross-device Correlation** - Combine with Pineapple WiFi wardriving
|
|
|
|
## Troubleshooting
|
|
|
|
### No GPS Fix
|
|
|
|
```bash
|
|
# Check GPS status
|
|
gps status
|
|
|
|
# Try network provider
|
|
termux-location -p network
|
|
|
|
# Use test coordinates
|
|
# Edit gps script to add test coordinate option
|
|
```
|
|
|
|
### Database Locked
|
|
|
|
```bash
|
|
# Check for running processes
|
|
ps aux | grep python | grep subghz
|
|
|
|
# Close database connections
|
|
pkill -f subghz_wardriving
|
|
```
|
|
|
|
### Export Fails
|
|
|
|
```bash
|
|
# Check database exists
|
|
ls -lh ~/logs/subghz_wardriving.db
|
|
|
|
# Check export directory
|
|
mkdir -p ~/exports/subghz_wardriving
|
|
|
|
# Test export directly
|
|
python3 -m subghz_wardriving.exporter
|
|
```
|
|
|
|
## Resources
|
|
|
|
- **Bruce Firmware:** https://github.com/pr3y/Bruce
|
|
- **Flipper SubGHz Format:** https://docs.flipperzero.one/sub-ghz
|
|
- **Wigle.net:** https://wigle.net/
|
|
- **CC1101 Datasheet:** https://www.ti.com/product/CC1101
|
|
|
|
---
|
|
|
|
**Created:** 2026-01-13
|
|
**System Version:** 1.0
|
|
**Database:** ~/logs/subghz_wardriving.db
|
|
**Latest GPS:** 34.07362505, -118.35955673 (24.5m accuracy)
|