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>
13 KiB
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
# 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
# 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
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
# 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
# Export to Wigle CSV
gps wigle-export --session downtown_survey
Output File: ~/exports/subghz_wardriving/downtown_survey_20260113_112130_wigle.csv
Content:
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
# 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
gpscommand - CLI tool for GPS operationsgps_service.py- Background GPS logging (158.4s interval)smart_gps_detector.py- Auto-detection and file renamingrealtime_gps_attribution.py- Real-time GPS attribution
New Additions
quick_capture.py- Quick GPS+RF pairing- Wigle export format - Added to
exporter.py gps wigle-export- New command ingpsscript- 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
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
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
- RSSI Integration - If Bruce firmware provides signal strength
- Real-time Export - Live streaming to Wigle format
- Map Visualization - Plot signals on map
- Protocol Decoding - Decode known protocols (garage doors, car keys, etc.)
- Community Database - Share SubGHz signal maps
- Cross-device Correlation - Combine with Pineapple WiFi wardriving
Troubleshooting
No GPS Fix
# 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
# Check for running processes
ps aux | grep python | grep subghz
# Close database connections
pkill -f subghz_wardriving
Export Fails
# 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)