#!/usr/bin/env python3 """ Analyze T-Embed RF files and extract signatures Shows what RF patterns we can extract from T-Embed captures without needing database connection """ import sys from pathlib import Path from typing import List, Dict, Any # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent)) from src.parser.sub_parser import SubFileParser def analyze_rf_file(file_path: Path, parser: SubFileParser) -> Dict[str, Any]: """Analyze a single RF file and extract features""" try: metadata = parser.parse(str(file_path)) # Extract features features = { 'filename': file_path.name, 'parsed': True, 'file_type': metadata.file_type, 'frequency_hz': metadata.frequency, 'frequency_mhz': metadata.frequency / 1e6 if metadata.frequency else 0, 'protocol': metadata.protocol or 'RAW', 'format': metadata.file_format, 'modulation': metadata.modulation or 'Unknown' } # RAW format specific features if metadata.raw_data and len(metadata.raw_data) > 0: abs_timings = [abs(t) for t in metadata.raw_data] features.update({ 'raw_samples': len(metadata.raw_data), 'timing_min': min(abs_timings), 'timing_max': max(abs_timings), 'timing_avg': sum(abs_timings) / len(abs_timings), 'timing_range': max(abs_timings) - min(abs_timings), 'raw_data_preview': metadata.raw_data[:20] }) # Calculate pattern characteristics features['pulse_count'] = len([t for t in metadata.raw_data if t > 0]) features['gap_count'] = len([t for t in metadata.raw_data if t < 0]) # KEY format specific features if metadata.key_data: features.update({ 'key_data': metadata.key_data.hex(), 'key_length': len(metadata.key_data), 'bit_length': metadata.bit_length, 'timing_element': metadata.timing_element }) # Empty file check if metadata.frequency == 0 or (metadata.file_format == 'RAW' and not metadata.raw_data): features['empty'] = True else: features['empty'] = False return features except Exception as e: return { 'filename': file_path.name, 'parsed': False, 'error': str(e) } def generate_signature_from_features(features: Dict[str, Any]) -> Dict[str, Any]: """Generate a device signature from extracted features""" if features.get('empty') or not features.get('parsed'): return None signature = { 'device_name': f"{features['filename'].replace('.sub', '')}_{features['frequency_mhz']:.0f}MHz", 'frequency': features['frequency_hz'], 'protocol': features['protocol'], 'modulation': features['modulation'] } # Add timing signature for RAW if 'timing_min' in features: signature['timing_signature'] = { 'min': features['timing_min'], 'max': features['timing_max'], 'avg': features['timing_avg'], 'range': features['timing_range'], 'samples': features['raw_samples'] } # Add pattern signature if 'raw_data_preview' in features: signature['pattern_preview'] = features['raw_data_preview'] # Guess device type from frequency freq_mhz = features['frequency_mhz'] if 300 <= freq_mhz <= 350: signature['likely_type'] = 'Garage Door / Gate Opener' elif 400 <= freq_mhz <= 440: signature['likely_type'] = 'Remote Control / Key Fob' elif 860 <= freq_mhz <= 870: signature['likely_type'] = 'Sensor / RFID' elif 900 <= freq_mhz <= 930: signature['likely_type'] = 'ISM Device / Sensor / IoT' else: signature['likely_type'] = 'Unknown' return signature def main(): """Main entry point""" print("="*80) print("T-Embed RF File Analysis") print("="*80) print() # Find T-Embed files tembed_dir = Path(__file__).parent.parent / 'signatures' / 't-embed-rf' if not tembed_dir.exists(): print(f"❌ Directory not found: {tembed_dir}") return 1 sub_files = sorted(tembed_dir.glob('*.sub')) print(f"Found {len(sub_files)} .sub files\n") parser = SubFileParser() all_features = [] signatures = [] # Analyze each file for sub_file in sub_files: print("-"*80) features = analyze_rf_file(sub_file, parser) all_features.append(features) if not features.get('parsed'): print(f"❌ {features['filename']}: {features.get('error', 'Unknown error')}\n") continue if features.get('empty'): print(f"⏭️ {features['filename']}: Empty capture (skipped)\n") continue # Show analysis print(f"✅ {features['filename']}") print(f"\n Basic Info:") print(f" File Type: {features['file_type']}") print(f" Frequency: {features['frequency_mhz']:.2f} MHz ({features['frequency_hz']} Hz)") print(f" Protocol: {features['protocol']}") print(f" Format: {features['format']}") print(f" Modulation: {features['modulation']}") if 'raw_samples' in features: print(f"\n RAW Signal Characteristics:") print(f" Samples: {features['raw_samples']}") print(f" Timing Range: {features['timing_min']}-{features['timing_max']} μs") print(f" Average Timing: {features['timing_avg']:.1f} μs") print(f" Pulse Count: {features['pulse_count']}") print(f" Gap Count: {features['gap_count']}") print(f" Preview: {features['raw_data_preview']}") if 'key_data' in features: print(f"\n KEY Format Data:") print(f" Key: {features['key_data']}") print(f" Bit Length: {features['bit_length']}") print(f" Timing Element: {features['timing_element']}") # Generate signature sig = generate_signature_from_features(features) if sig: signatures.append(sig) print(f"\n Device Signature:") print(f" Device Name: {sig['device_name']}") print(f" Likely Type: {sig['likely_type']}") if 'timing_signature' in sig: ts = sig['timing_signature'] print(f" Timing Signature: {ts['min']}-{ts['max']}μs (avg: {ts['avg']:.1f})") print() # Summary print("="*80) print("ANALYSIS SUMMARY") print("="*80) total = len(all_features) parsed = sum(1 for f in all_features if f.get('parsed')) empty = sum(1 for f in all_features if f.get('empty')) valid = sum(1 for f in all_features if f.get('parsed') and not f.get('empty')) print(f"Total files: {total}") print(f"Successfully parsed: {parsed}") print(f"Empty captures: {empty}") print(f"Valid captures: {valid}") print(f"\n Signatures Generated: {len(signatures)}") if signatures: print("\nSignature Database Preview:") for i, sig in enumerate(signatures, 1): print(f"\n{i}. {sig['device_name']}") print(f" Frequency: {sig['frequency']/1e6:.2f} MHz") print(f" Type: {sig['likely_type']}") if 'timing_signature' in sig: ts = sig['timing_signature'] print(f" Timing: {ts['min']}-{ts['max']} μs ({ts['samples']} samples)") print("\n" + "="*80) print("✅ Analysis complete!") print("\nThese signatures can be imported into the database for device matching.") print("="*80) return 0 if __name__ == '__main__': sys.exit(main())