- main_simple.py upload: map manifest entries by filename so each file gets its own GPS/timestamp (previously every file was assigned captures[0], breaking multi-file uploads) - device_category now uses the category router's real category (e.g. "Remote Control") instead of the mislabeled "manufacturer - device_name" string; also surface category per matched device - add PLAN_TO_PROD.md / FABLE.md development briefs - add CONTEXT.md (leaked PAT redacted from remote URL) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
19 KiB
GigLez — Plan to Production
Analyzed: 2026-07-17
Branch: p1-p2-validation
Status: Backend 65% complete · Frontend 5% · Real-world accuracy 0%
Critical Findings (Read This First)
The 0% Real-World Problem
The most important finding from benchmark analysis:
| Benchmark | Top-1 Accuracy | Source |
|---|---|---|
| Synthetic signals | 33.3% | scripts/benchmark.py |
| Real Flipper Zero captures | 0.0% | REAL_TEST_RESULTS.md |
The matcher produces high-confidence wrong answers on real signals (e.g., LaCrosse TX141 → "Schou 72543 Rain Gauge" at 69% confidence). This is not a threshold issue — it is a fundamental database gap. The RTL_433 protocol set (260+ entries) is dominated by weather sensor timing signatures, so everything pattern-matches to a weather sensor regardless of what it actually is.
Root cause: The rtl433_protocols_imported.py (4540 LOC, the matching backbone) contains only open/known protocols broadcast continuously. Real-world targets like garage door openers, fan controllers, and LED remotes use rolling codes (KeeLoq), proprietary encodings, or undocumented bit structures that have no signatures to match against. These account for 60%+ of Sub-GHz traffic.
Fixing this is the first gating requirement for everything else.
Project Snapshot
What's Built (Do Not Rebuild)
| Component | File(s) | Status | Quality |
|---|---|---|---|
.sub / RAW file parser |
src/parser/sub_parser.py |
✅ Complete | Production-ready |
| GPS validator | src/gps/validator.py |
✅ Complete | 22/22 tests pass |
| ORM models (11 tables) | src/database/models.py |
✅ Complete | PostGIS ready |
| PostgreSQL schema | scripts/create_schema.sql |
✅ Complete | Full triggers/indexes |
| Storage abstraction | src/core/storage/ |
✅ Complete | FS + S3 backends |
| Matching engine scaffolding | src/matcher/engine.py |
✅ Structure | Needs accuracy fix |
| 5-method scorer | src/matcher/device_identifier.py |
✅ Built | Wrong outputs |
| RTL_433 protocol DB | src/matcher/rtl433_protocols_imported.py |
✅ Imported | Incomplete coverage |
| T-Embed hardware controller | src/capture/tembed.py |
✅ Built | Untested live |
| FastAPI app skeleton | src/api/main_simple.py |
🚧 Partial | Routes incomplete |
| Configuration system | config/settings.py |
✅ Complete | Dev/prod aware |
| ML design doc | docs/ML_DESIGN.md |
✅ Designed | Not implemented |
What's Missing
| Component | Priority | Effort |
|---|---|---|
| Accuracy fix (device category DB + protocol routing) | P0 | 2 days |
| ML hybrid ensemble (CNN + heuristic) | P1 | 1 week |
| Complete API endpoints | P1 | 3 days |
| Web UI (map + upload form) | P1 | 1 week |
| User auth (JWT + API keys) | P2 | 2 days |
| Docker + deployment | P2 | 1 day |
| Community features (voting, leaderboard) | P3 | 1 week |
| CI/CD pipeline | P3 | 1 day |
Device Identification: What Works, What Doesn't
Identifiable Today (with fixes applied)
These device classes have open, documented protocols with stable timing signatures that the current architecture can match:
| Device Class | Protocols | Accuracy Potential | Frequency |
|---|---|---|---|
| Weather stations | Oregon Scientific v1/v2/v3, Acurite, LaCrosse | 70-85% | 433.92 MHz |
| TPMS (tire pressure) | Schrader, Continental, Pacific, Citroen | 60-75% | 315/433 MHz |
| Simple door/window sensors | EV1527, PT2262/2272, Princeton | 65-80% | 315/433 MHz |
| Temperature/humidity sensors | Nexus, Ambient Weather, Bresser | 70-80% | 433 MHz |
| Rain gauges | Multiple RTL_433-supported | 65-75% | 433 MHz |
| Thermostats (simple) | Honeywell, Braeburn | 55-70% | 345/433 MHz |
| Smart meters (EU) | Kamstrup, Landis+Gyr | 60-70% | 868 MHz |
| LoRa devices (type only) | LoRaWAN chirp signature | 50-60% | 868/915 MHz |
Total identifiable universe: ~350-400 known device types via RTL_433 + Flipper DB
Cannot Be Identified (Protocol-Level Limitation)
| Device Class | Why | Workaround |
|---|---|---|
| Rolling code garage doors | KeeLoq / Security+ 2.0 — every transmission unique | Can identify device class/family, not specific code |
| Modern car key fobs | Hopping codes | Frequency + timing only → brand inference |
| Z-Wave devices | Proprietary 9-byte network ID | Preamble detection → "Z-Wave" category |
| Zigbee (2.4 GHz) | Out of Sub-GHz range | See 2.4 GHz section below |
| Fan/LED remotes | Undocumented proprietary | ML category classification helps |
| Somfy RTS | Proprietary encrypted | Frequency match only (433.42 MHz = Somfy) |
| DECT cordless phones | 1.88-1.9 GHz | Out of range |
Identification Approach by Protocol Type
Signal received
│
├─ Has Protocol field (decoded .sub) → Heuristic match → 80-95% accuracy
│
├─ RAW data, known timing signature → RTL_433 protocol match → 60-80%
│
├─ RAW data, unknown timing → ML CNN classifier → 50-70% (device category)
│
├─ Frequency-only match → ISM band category → 30-50% (class only)
│
└─ No match → "Unknown Sub-GHz @ {freq}" → stored for community ID
Machine Learning Integration
Why ML Is Needed
The heuristic matcher covers open protocols well. ML fills the gap for:
- RAW signals with no protocol match (fan controllers, LED remotes, proprietary)
- Device category classification when exact ID is impossible (rolling codes)
- Manufacturer inference from RF hardware fingerprints
- Novel protocol discovery — clustering unknown signals
Recommended Architecture (from docs/ML_DESIGN.md)
Implement the designed Hybrid Ensemble in phases:
Phase A: Statistical Features (Lowest effort, highest ROI)
Train a Random Forest / Gradient Boosting classifier on 47 extracted features:
- Pulse width statistics (mean, std, min, max SHORT/LONG)
- LONG/SHORT ratio
- Pulse count, repetition count
- Preamble pattern type
- ISM band classification
- Duty cycle, inter-burst gap
# Feature vector already partially built in:
# src/matcher/statistical_classifier.py (356 LOC)
# src/matcher/timing_analyzer.py (550 LOC)
# Target: device category (not model)
# Classes: weather_sensor, garage_door, key_fob, doorbell,
# security_sensor, fan_controller, tpms, smart_meter, unknown
Training data: UberGuidoZ Flipper DB (10,000+ labeled .sub files) — directory structure provides labels.
Expected accuracy: 60-75% category-level
Effort: 3-4 days (feature extraction exists, need training pipeline)
Phase B: 1D CNN on Raw Pulse Arrays
Input: normalized pulse array → padded to 512 samples
Architecture:
Conv1D(64, 3) → BatchNorm → ReLU
Conv1D(128, 3) → BatchNorm → ReLU
Conv1D(256, 3) → BatchNorm → ReLU
GlobalAvgPool
Dense(256) → Dropout(0.3)
Dense(N_classes) → Softmax
Output: P(device_category) for 9 categories
Training: PyTorch or TensorFlow (Python training, ONNX export for serving)
Inference: Python via ONNX Runtime (server-side, NOT browser — model too complex for TF.js)
Expected accuracy: 70-80% category-level
Effort: 5-7 days
Phase C: Ensemble Combiner
final_score = (
0.40 * heuristic_score + # existing matcher (exact protocols)
0.35 * cnn_score + # CNN category probability
0.25 * stat_score # statistical features
)
The heuristic matcher dominates for known protocols; ML fills gaps for unknowns.
What ML Cannot Do
- Decode rolling code content (cryptographically protected)
- Identify specific device model from RAW alone (insufficient features)
- Work on BinRAW without normalization
- Achieve >90% model-level accuracy with Sub-GHz RAW (physics limitation)
Realistic production targets:
- Known protocol devices: 80-90% top-1 accuracy (heuristic)
- Unknown/proprietary: 60-75% device category (ML)
- Rolling code: 40-55% manufacturer/family inference
2.4 GHz / 5 GHz / 6 GHz Feasibility
Technical Reality
| Band | Hardware Needed | Protocol | File Format | Flipper Support |
|---|---|---|---|---|
| Sub-GHz (300-928 MHz) | Flipper Zero, RTL-SDR | OOK/FSK/ASK | .sub |
✅ Native |
| 2.4 GHz | WiFi card (monitor mode) | 802.11 b/g/n, Zigbee, BT | .pcap / .pcapng |
❌ Hardware limit |
| 5 GHz | WiFi card (monitor mode) | 802.11 a/n/ac | .pcap |
❌ Hardware limit |
| 6 GHz | WiFi 6E card | 802.11ax | .pcap |
❌ Hardware limit |
Can GigLez Support 2.4/5/6 GHz?
Yes, but as a separate ingestion path. The platform architecture (GPS + file + database) generalizes cleanly. What changes:
- Input format: Accept
.pcap/.pcapnginstead of.sub - Parser:
pysharkorscapyto extract beacon/probe frames (already in~/coding/_archive/pyshark) - Identification: Network fingerprinting by:
- SSID patterns → device type (e.g.,
DECO-XXXX→ TP-Link mesh) - OUI lookup from MAC → manufacturer
- IE (Information Elements) → chipset inference
- SSID patterns → device type (e.g.,
- Database: Separate
wifi_capturestable alongsidecaptures
Wigle does exactly this for WiFi — their schema can be adapted directly.
Recommendation: Phase 4 feature. Sub-GHz is the differentiator; WiFi is Wigle's territory. Add 2.4/5/6 GHz support after MVP to expand data collection surface, not before.
Effort: 1 week to add WiFi pcap ingestion + OUI database + map layer.
Production Roadmap
Phase 0: Fix Accuracy (Gating — 1 week)
This blocks everything. No point shipping a product that gives wrong answers with high confidence.
0.1 — Build device category routing layer (2 days)
Create src/matcher/category_router.py that classifies before trying to match:
# Frequency + timing → category → route to specialized sub-matcher
# 315 MHz + short TE < 500 → key_fob_matcher
# 433 MHz + long burst preamble → weather_matcher
# 433 MHz + EV1527 timing → remote_matcher
0.2 — Expand signature database (2 days)
The current DB has RTL_433 protocols. Add:
- Princeton, EV1527, PT2262 parameter ranges (simple remotes — largest category)
- KeeLoq family detection (rolling code → don't match model, classify family)
- Flipper Zero firmware
.subcollection bulk import (UberGuidoZ: 10K+ files)- Parse filenames as labels:
Garage_Doors/LiftMaster/*.sub→ category + brand
- Parse filenames as labels:
- Z-Wave preamble signature (868/908 MHz chirp pattern)
0.3 — Add confidence calibration (1 day)
Current system produces 69-76% confidence for wrong answers. Add isotonic regression calibration or simply apply hard rules:
- If top-1 and top-2 confidence differ by < 5% → reduce both to "low"
- If frequency doesn't match device category → penalize 30%
- If timing LONG/SHORT ratio is outside protocol's known range → cap at 50%
0.4 — Re-run real-world benchmark (0.5 days)
Target: Top-3 accuracy ≥ 30% on REAL_TEST_RESULTS.md test set. This is achievable with routing alone.
Phase 1: Complete the Backend API (1 week)
All routes exist as skeletons. Wire them up.
1.1 — POST /api/v1/captures/upload (2 days)
- Accept multipart: JSON manifest +
.subfiles - Validate GPS (already done —
src/gps/validator.py) - Parse files (already done —
src/parser/sub_parser.py) - Run matcher (fixed in Phase 0)
- Store in PostgreSQL
- Return capture IDs + match results
1.2 — GET /api/v1/search (1 day)
- Query by bounding box (PostGIS
ST_Within) - Filter by: frequency range, protocol, device category, date range
- Pagination (cursor-based, Wigle pattern)
- Return GeoJSON for map rendering
1.3 — GET /api/v1/heatmap (0.5 days)
- Aggregate by grid cell (H3 hexagons or lat/lon bins)
- Use materialized view
capture_heatmap(already designed in schema) - Return density + dominant device category per cell
1.4 — GET /api/v1/stats (0.5 days)
- Total captures, unique devices, geographic coverage
- Top frequencies, protocols, device categories
- Uses materialized view
device_statistics
1.5 — GET /api/v1/devices (1 day)
- List/search known device catalog
- Filter by manufacturer, category, frequency
- Device detail page (signatures, captures seen)
1.6 — Auth middleware (1 day)
- Anonymous uploads allowed (rate-limited by IP)
- Optional JWT for account-linked uploads
- API key header for programmatic access
- FastAPI dependency injection (clean pattern)
Phase 2: Web Interface MVP (1 week)
Minimum: something users can open in a browser and actually use.
2.1 — Upload form (2 days)
- Drag-and-drop
.subfiles - GPS entry: manual lat/lon OR "use browser location"
- Timestamp auto-fill
- Upload progress + results display
- Simple HTML/CSS + vanilla JS (no framework needed for MVP)
2.2 — Interactive map (3 days)
- Leaflet.js (already in plan, free, no API key)
- Marker clustering (
Leaflet.markercluster) - Click → device details panel
- Heatmap toggle (
Leaflet.heat) - Filter sidebar: frequency, category, date
2.3 — Basic pages (2 days)
/— Landing + recent captures/map— Interactive map/upload— Upload form/devices— Device catalog/stats— Platform statistics
Use Jinja2 templates (FastAPI native) + Tailwind CSS via CDN. No build step needed.
Phase 3: ML Integration (1 week, parallel-able with Phase 2)
Implement Phase A + B from the ML strategy above.
3.1 — Training data pipeline (2 days)
# Download UberGuidoZ Flipper database (public, ~2GB)
# Parse directory structure as labels
# Extract 47 statistical features per file
# Split 80/10/10 train/val/test
3.2 — Train statistical classifier (1 day)
# sklearn GradientBoostingClassifier or XGBoost
# 9 device categories
# Save as joblib model
# Expected: 60-75% category accuracy
3.3 — Train 1D CNN (2 days)
# PyTorch, pad RAW arrays to 512 samples
# Train 20 epochs on GPU (if available) or CPU (~2h)
# Export to ONNX
# Expected: 70-80% category accuracy
3.4 — Wire ensemble into matcher (1 day)
- Load ONNX model at startup
- Run in parallel with heuristic matcher (async)
- Combine scores with 40/35/25 weighting
- Store ML category alongside heuristic matches in
capture_matchestable
3.5 — Active learning hook (0.5 days)
- Community verifications → retrain queue
- Simple: flag captures with low confidence + community correction
- Retrain monthly
Phase 4: Dockerize + Deploy (3 days)
4.1 — Docker Compose (1 day)
services:
api: # FastAPI + uvicorn
db: # PostgreSQL 16 + PostGIS
redis: # Cache + rate limiting
worker: # Celery for async file processing
nginx: # Reverse proxy + static files
4.2 — Deployment target (1 day)
- VPS: 4 vCPU, 8GB RAM, 100GB SSD (enough for 10M captures)
- PostgreSQL tuned for geospatial workloads
- Nginx + Let's Encrypt SSL
- systemd service for auto-restart
4.3 — Operational tooling (1 day)
- Health check endpoint
- Prometheus metrics (
/metrics) - Log rotation (loguru already wired)
- DB backup cron (daily dump to S3)
Phase 5: Community Features (2 weeks, post-launch)
5.1 — User accounts (3 days)
- Registration/login (email or anonymous)
- Upload history + statistics
- API key management
- Reputation score
5.2 — Device verification (3 days)
- "Confirm" / "Dispute" buttons on matches
- Voting system (votes table already in schema)
- High-vote matches → promoted to "verified"
- Community corrections feed ML retraining
5.3 — Leaderboards + badges (2 days)
- Top uploaders (by count, by new devices discovered)
- Geographic coverage leader
- Milestone badges (first in city, 100 captures, etc.)
5.4 — 2.4/5 GHz expansion (5 days)
- Accept
.pcap/.pcapnguploads pyshark-based parser for WiFi beacons- OUI database lookup (manufacturer from MAC)
- New map layer: WiFi devices
- Wigle-compatible CSV export
Tech Stack Gap Analysis
Has / Doesn't Need Replacing
| Component | Current | Status |
|---|---|---|
| Parser | Custom Python | ✅ Keep |
| GPS validation | Custom Python | ✅ Keep |
| Database | PostgreSQL + PostGIS | ✅ Keep (needs init) |
| ORM | SQLAlchemy 2.0 | ✅ Keep |
| API framework | FastAPI 0.109 | ✅ Keep |
| Storage | FS (dev) / S3 (prod) | ✅ Keep |
| Config | python-dotenv | ✅ Keep |
Needs Adding
| Component | Recommendation | Why |
|---|---|---|
| Map library | Leaflet.js 1.9 | Free, no API key, proven |
| ML training | PyTorch + scikit-learn | Already in requirements |
| ML inference | ONNX Runtime | Lightweight, no GPU required |
| Caching | Redis (via redis-py) |
Rate limiting + query cache |
| Task queue | Celery + Redis | Async file processing |
| Frontend CSS | Tailwind CSS (CDN) | No build step for MVP |
| Container | Docker + Compose | Reproducible deployment |
| Reverse proxy | Nginx | SSL + static files |
Remove / Simplify
| Item | Action |
|---|---|
src/api/main_orm_legacy.py |
Delete — superseded |
src/api/routes/captures_enhanced.py |
Merge into captures.py, delete |
src/api/routes/captures_with_logging.py |
Merge into captures.py, delete |
src/matcher/strategies.py (610 LOC legacy) |
Keep as fallback but don't extend |
scripts/archive/ |
Leave archived — don't delete |
Honest Timeline to Production
Assuming 4-6 focused hours/day:
| Phase | What | Duration | Cumulative |
|---|---|---|---|
| 0 | Fix accuracy (critical) | 1 week | Week 1 |
| 1 | Complete backend API | 1 week | Week 2 |
| 2 | Web UI MVP | 1 week | Week 3 |
| 3 | ML integration | 1 week | Week 3-4 (parallel) |
| 4 | Docker + deploy | 3 days | Week 4 |
| MVP Live | Public beta | ~Week 4 | |
| 5 | Community features | 2 weeks | Week 6 |
| WiFi | 2.4/5 GHz expansion | 1 week | Week 7 |
Minimum viable launch (something to show GhostArmyIntel): 3-4 weeks
Key External Resources for Phase 0
| Resource | URL | Use |
|---|---|---|
| UberGuidoZ Flipper DB | github.com/UberGuidoZ/Flipper |
10K+ labeled .sub training files |
| RTL_433 test files | github.com/merbanan/rtl_433/tree/master/tests |
Labeled real captures |
| RadioML dataset | deepsig.ai/datasets |
ML training for modulation recognition |
| Protocol table | github.com/merbanan/rtl_433/blob/master/README.md |
260+ protocol reference |
| Flipper firmware protocol list | github.com/flipperdevices/flipperzero-firmware/tree/dev/lib/subghz/protocols |
Canonical protocol params |
Success Criteria (Per Phase)
| Phase | Gate |
|---|---|
| Phase 0 complete | Real-world top-3 accuracy ≥ 30% (up from 0%) |
| Phase 1 complete | POST /upload → stored in DB → returns matches |
| Phase 2 complete | User can upload .sub file in browser, see it on map |
| Phase 3 complete | ML model reduces "unknown" rate from 45% → < 25% |
| Phase 4 complete | docker compose up → production site live on VPS |
| Phase 5 complete | 100+ community-verified identifications |
Generated from deep analysis of project state + docs/ML_DESIGN.md + REAL_TEST_RESULTS.md + docs/RF_SIGNAL_ANALYSIS_RESEARCH.md + docs/FREQUENCY_DEVICE_CHART.md on 2026-07-17.