Phase 1 & 2: Cleanup redundant code and integrate pattern decoder
## Phase 1: Code Cleanup (~1,859 lines removed) **Deleted Redundant Matchers:** - ❌ strategies_orm.py (356 lines) - Old ORM-based strategies - ❌ simple_matcher.py (351 lines) - Replaced by strategies.py - ❌ rtl433_matcher.py (352 lines) - Replaced by strategies.py **Archived Old Scripts:** - Moved 11 one-time analysis/import scripts to scripts/archive/ - Scripts: analyze_flipper_signatures, analyze_tembed_files, identify_tembed_devices, import_flipper_sqlite, import_tembed_signatures, match_tembed_with_db, match_with_flipper_db, rematch_captures, test_gps_extraction, test_tembed_matching, test_wardriving_import **Consolidated API:** - Renamed main.py → main_orm_legacy.py (archived old ORM-based API) - main_simple.py is now the primary production API ## Phase 2: Pattern Decoder Integration ✅ **CRITICAL FIX: Pattern decoder now integrated into production API!** **Changes:** 1. Updated main_simple.py to use unified SignatureMatcher 2. Added 6 strategies to matcher pipeline: - ExactMatcher (protocol + frequency) - FrequencyMatcher (frequency-based) - BitPatternMatcher (data patterns) - TimingMatcher (timing-based) - RTL433DecoderStrategy (RTL_433 decoder) - PatternBasedStrategy (NEW! Pattern decoder for short captures) 3. Created MockDB class for simplified mode (no real database) 4. Replaced old get_matcher() with get_matcher_engine() 5. Updated upload handler to use MatchResult format 6. All matches now include confidence scores and match methods **Result:** - Pattern decoder is NOW ACTIVE in production 🎉 - Unified matching pipeline with 6 strategies - Cleaner codebase (-1,859 lines) - Single source of truth for matching logic 🎉 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Import Flipper Zero signatures into SQLite database
|
||||
|
||||
Uses SQLite for immediate testing without PostgreSQL setup
|
||||
"""
|
||||
|
||||
import sys
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.parser.sub_parser import SubFileParser
|
||||
|
||||
|
||||
def create_sqlite_schema(conn):
|
||||
"""Create SQLite schema"""
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Devices table
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_name TEXT,
|
||||
manufacturer TEXT,
|
||||
model TEXT,
|
||||
device_type TEXT,
|
||||
typical_frequency INTEGER,
|
||||
protocol TEXT,
|
||||
description TEXT,
|
||||
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
is_verified BOOLEAN DEFAULT 0,
|
||||
source TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# Signatures table
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS signatures (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_id INTEGER REFERENCES devices(id),
|
||||
protocol TEXT,
|
||||
frequency INTEGER,
|
||||
modulation TEXT,
|
||||
bit_pattern BLOB,
|
||||
bit_mask BLOB,
|
||||
timing_min INTEGER,
|
||||
timing_max INTEGER,
|
||||
raw_pattern TEXT,
|
||||
confidence_threshold REAL DEFAULT 0.7,
|
||||
source TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# Indexes
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_sig_freq ON signatures(frequency)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_sig_device ON signatures(device_id)')
|
||||
|
||||
conn.commit()
|
||||
|
||||
|
||||
def import_flipper_signature(conn, sub_file: Path, parser: SubFileParser):
|
||||
"""Import a single Flipper Zero .sub file"""
|
||||
|
||||
try:
|
||||
metadata = parser.parse(str(sub_file))
|
||||
|
||||
# Skip if frequency is 0
|
||||
if metadata.frequency == 0:
|
||||
return None
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Determine device type from frequency
|
||||
freq_mhz = metadata.frequency / 1e6
|
||||
|
||||
if 300 <= freq_mhz <= 350:
|
||||
device_type = 'garage_door'
|
||||
elif 400 <= freq_mhz <= 450:
|
||||
device_type = 'remote_control'
|
||||
elif 800 <= freq_mhz <= 900:
|
||||
device_type = 'sensor'
|
||||
else:
|
||||
device_type = 'unknown'
|
||||
|
||||
# Create device record
|
||||
cursor.execute('''
|
||||
INSERT INTO devices (device_name, manufacturer, model, device_type,
|
||||
typical_frequency, protocol, description, source)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
sub_file.stem,
|
||||
'Unknown',
|
||||
sub_file.stem,
|
||||
device_type,
|
||||
metadata.frequency,
|
||||
metadata.protocol or 'RAW',
|
||||
f'Imported from Flipper Zero: {sub_file.name}',
|
||||
'flipper_zero'
|
||||
))
|
||||
|
||||
device_id = cursor.lastrowid
|
||||
|
||||
# Extract timing info
|
||||
timing_min, timing_max = None, None
|
||||
raw_pattern = None
|
||||
|
||||
if metadata.raw_data and len(metadata.raw_data) > 0:
|
||||
abs_timings = [abs(t) for t in metadata.raw_data]
|
||||
timing_min = min(abs_timings)
|
||||
timing_max = max(abs_timings)
|
||||
raw_pattern = ','.join(map(str, metadata.raw_data[:100])) # First 100 samples
|
||||
|
||||
# Create signature record
|
||||
cursor.execute('''
|
||||
INSERT INTO signatures (device_id, protocol, frequency, modulation,
|
||||
timing_min, timing_max, raw_pattern, source)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
device_id,
|
||||
metadata.protocol or 'RAW',
|
||||
metadata.frequency,
|
||||
metadata.modulation,
|
||||
timing_min,
|
||||
timing_max,
|
||||
raw_pattern,
|
||||
'flipper_zero'
|
||||
))
|
||||
|
||||
conn.commit()
|
||||
|
||||
return {
|
||||
'device_id': device_id,
|
||||
'device_name': sub_file.stem,
|
||||
'frequency': metadata.frequency,
|
||||
'protocol': metadata.protocol
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
|
||||
print("="*80)
|
||||
print("FLIPPER ZERO → SQLite IMPORT")
|
||||
print("="*80)
|
||||
print()
|
||||
|
||||
# Create/connect to SQLite database
|
||||
db_path = Path(__file__).parent.parent / 'giglez.db'
|
||||
|
||||
print(f"Database: {db_path}")
|
||||
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
print("✅ Connected to SQLite database\n")
|
||||
|
||||
# Create schema
|
||||
print("Creating schema...")
|
||||
create_sqlite_schema(conn)
|
||||
print("✅ Schema ready\n")
|
||||
|
||||
# Find Flipper signatures
|
||||
flipper_dir = Path(__file__).parent.parent / 'signatures' / 'flipperzero-firmware'
|
||||
|
||||
if not flipper_dir.exists():
|
||||
print(f"❌ Flipper directory not found: {flipper_dir}")
|
||||
return 1
|
||||
|
||||
sub_files = list(flipper_dir.glob('**/*.sub'))
|
||||
print(f"Found {len(sub_files)} Flipper Zero .sub files\n")
|
||||
|
||||
# Import all signatures
|
||||
parser = SubFileParser()
|
||||
imported = []
|
||||
skipped = 0
|
||||
|
||||
print("Importing signatures...")
|
||||
for i, sub_file in enumerate(sub_files, 1):
|
||||
if i % 10 == 0:
|
||||
print(f" Processed {i}/{len(sub_files)}...")
|
||||
|
||||
result = import_flipper_signature(conn, sub_file, parser)
|
||||
|
||||
if result:
|
||||
imported.append(result)
|
||||
else:
|
||||
skipped += 1
|
||||
|
||||
print(f"✅ Import complete\n")
|
||||
|
||||
# Summary
|
||||
print("="*80)
|
||||
print("IMPORT SUMMARY")
|
||||
print("="*80)
|
||||
print(f"Total files: {len(sub_files)}")
|
||||
print(f"Imported: {len(imported)}")
|
||||
print(f"Skipped: {skipped}")
|
||||
|
||||
# Query database
|
||||
cursor = conn.cursor()
|
||||
|
||||
print(f"\nDATABASE CONTENTS")
|
||||
print("-"*80)
|
||||
|
||||
cursor.execute("SELECT COUNT(*) FROM devices")
|
||||
device_count = cursor.fetchone()[0]
|
||||
print(f"Devices: {device_count}")
|
||||
|
||||
cursor.execute("SELECT COUNT(*) FROM signatures")
|
||||
sig_count = cursor.fetchone()[0]
|
||||
print(f"Signatures: {sig_count}")
|
||||
|
||||
# Frequency distribution
|
||||
print(f"\nFREQUENCY DISTRIBUTION")
|
||||
print("-"*80)
|
||||
|
||||
cursor.execute('''
|
||||
SELECT frequency, COUNT(*) as count
|
||||
FROM signatures
|
||||
GROUP BY frequency
|
||||
ORDER BY count DESC
|
||||
''')
|
||||
|
||||
for freq, count in cursor.fetchall():
|
||||
print(f"{freq/1e6:8.2f} MHz: {count:3d} devices")
|
||||
|
||||
conn.close()
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print("✅ Database ready at:", db_path)
|
||||
print("="*80)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user