4237c4bdb8
## 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>
174 lines
5.4 KiB
Python
174 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Match T-Embed captures against Flipper Zero signature database
|
|
|
|
Demonstrates device identification using expanded signature knowledge base
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List, Dict
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from src.parser.sub_parser import SubFileParser
|
|
|
|
|
|
def load_flipper_signatures(flipper_dir: Path) -> List[Dict]:
|
|
"""Load all Flipper Zero signatures"""
|
|
|
|
parser = SubFileParser()
|
|
signatures = []
|
|
|
|
for sub_file in flipper_dir.glob('**/*.sub'):
|
|
try:
|
|
metadata = parser.parse(str(sub_file))
|
|
|
|
signatures.append({
|
|
'device_name': sub_file.stem,
|
|
'filename': sub_file.name,
|
|
'frequency': metadata.frequency,
|
|
'protocol': metadata.protocol or 'RAW',
|
|
'file_format': metadata.file_format,
|
|
'bit_length': metadata.bit_length,
|
|
'has_raw': bool(metadata.raw_data),
|
|
'raw_samples': len(metadata.raw_data) if metadata.raw_data else 0
|
|
})
|
|
except:
|
|
pass
|
|
|
|
return signatures
|
|
|
|
|
|
def match_by_frequency(target_freq: int, signatures: List[Dict], tolerance_hz: int = 10000) -> List[Dict]:
|
|
"""Match by frequency with tolerance"""
|
|
|
|
matches = []
|
|
|
|
for sig in signatures:
|
|
freq_diff = abs(sig['frequency'] - target_freq)
|
|
|
|
if freq_diff <= tolerance_hz:
|
|
confidence = 1.0 - (freq_diff / tolerance_hz)
|
|
confidence = max(0.5, confidence)
|
|
|
|
matches.append({
|
|
'signature': sig,
|
|
'confidence': confidence,
|
|
'freq_diff_hz': freq_diff,
|
|
'match_method': 'frequency'
|
|
})
|
|
|
|
return sorted(matches, key=lambda x: x['confidence'], reverse=True)
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
|
|
print("="*80)
|
|
print("DEVICE MATCHING: T-Embed vs Flipper Zero Database")
|
|
print("="*80)
|
|
print()
|
|
|
|
# Load Flipper signatures
|
|
flipper_dir = Path(__file__).parent.parent / 'signatures' / 'flipperzero-firmware'
|
|
|
|
if not flipper_dir.exists():
|
|
print("❌ Flipper Zero database not found")
|
|
return 1
|
|
|
|
print("Loading Flipper Zero signature database...")
|
|
signatures = load_flipper_signatures(flipper_dir)
|
|
print(f"✅ Loaded {len(signatures)} signatures\n")
|
|
|
|
# Load T-Embed capture
|
|
tembed_dir = Path(__file__).parent.parent / 'signatures' / 't-embed-rf'
|
|
parser = SubFileParser()
|
|
|
|
tembed_file = tembed_dir / 'raw_7.sub'
|
|
|
|
print(f"Analyzing T-Embed capture: {tembed_file.name}")
|
|
print("-"*80)
|
|
|
|
metadata = parser.parse(str(tembed_file))
|
|
|
|
print(f"Frequency: {metadata.frequency/1e6:.2f} MHz")
|
|
print(f"Protocol: {metadata.protocol or 'RAW (undecoded)'}")
|
|
print(f"Format: {metadata.file_format}")
|
|
if metadata.raw_data:
|
|
print(f"RAW Samples: {len(metadata.raw_data)}")
|
|
|
|
print(f"\n{'='*80}")
|
|
print("MATCHING AGAINST FLIPPER ZERO DATABASE")
|
|
print(f"{'='*80}\n")
|
|
|
|
# Match by frequency (±10 kHz tolerance)
|
|
matches = match_by_frequency(metadata.frequency, signatures, tolerance_hz=10000)
|
|
|
|
if matches:
|
|
print(f"Found {len(matches)} potential matches:\n")
|
|
|
|
for i, match in enumerate(matches[:10], 1):
|
|
sig = match['signature']
|
|
print(f"{i}. {sig['device_name']}")
|
|
print(f" Frequency: {sig['frequency']/1e6:.3f} MHz (diff: {match['freq_diff_hz']/1000:.1f} kHz)")
|
|
print(f" Protocol: {sig['protocol']}")
|
|
print(f" Format: {sig['file_format']}")
|
|
print(f" Confidence: {match['confidence']:.1%}")
|
|
print()
|
|
|
|
# Best match
|
|
best = matches[0]
|
|
print(f"{'='*80}")
|
|
print(f"BEST MATCH: {best['signature']['device_name']}")
|
|
print(f"Confidence: {best['confidence']:.1%}")
|
|
print(f"Method: Frequency matching ({best['freq_diff_hz']/1000:.1f} kHz difference)")
|
|
print(f"{'='*80}")
|
|
|
|
else:
|
|
print("❌ No matches found in Flipper Zero database")
|
|
print("\nThis device is at 915 MHz (ISM band)")
|
|
print("Flipper Zero database contains mostly 433 MHz devices")
|
|
print("\nTo improve matching:")
|
|
print("- Import RTL_433 database (has 915 MHz devices)")
|
|
print("- Add more T-Embed captures from wardriving")
|
|
print("- Import community-contributed 915 MHz signatures")
|
|
|
|
print(f"\n{'='*80}")
|
|
print("DATABASE COVERAGE ANALYSIS")
|
|
print(f"{'='*80}\n")
|
|
|
|
# Analyze frequency coverage
|
|
freq_groups = {}
|
|
for sig in signatures:
|
|
freq_mhz = sig['frequency'] / 1e6
|
|
freq_band = f"{int(freq_mhz/100)*100}-{int(freq_mhz/100)*100+100}"
|
|
|
|
if freq_band not in freq_groups:
|
|
freq_groups[freq_band] = 0
|
|
freq_groups[freq_band] += 1
|
|
|
|
print("Frequency Band Coverage:")
|
|
for band in sorted(freq_groups.keys()):
|
|
print(f" {band} MHz: {freq_groups[band]} devices")
|
|
|
|
# Check if 915 MHz covered
|
|
target_freq = metadata.frequency / 1e6
|
|
target_band = f"{int(target_freq/100)*100}-{int(target_freq/100)*100+100}"
|
|
|
|
print(f"\nTarget device: {target_freq:.2f} MHz ({target_band} MHz band)")
|
|
|
|
if target_band in freq_groups:
|
|
print(f"✅ Coverage: {freq_groups[target_band]} devices in target band")
|
|
else:
|
|
print(f"❌ No coverage: Target band not in Flipper database")
|
|
|
|
print("\n" + "="*80)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|