Files
giglez/signatures/t-embed-rf/SUBGHZ_WIGLE_WARDRIVING.md
T
Trilltechnician 04bd80b25b GPS Auto-Extraction + First Successful Upload Complete
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>
2026-01-13 18:37:13 -08:00

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)