# GigLez Cleanup & Optimization Plan **Date:** 2026-01-14 **Purpose:** Identify bloat, unused code, and plan optimizations + JavaScript implementation --- ## Part 1: Code Bloat Analysis ### A. Duplicate/Redundant Matcher Implementations #### 🔴 HIGH PRIORITY - Consolidate Matchers **Current State:** ``` src/matcher/ ├── engine.py # Base MatchStrategy interface (3.4 KB) ├── strategies.py # NEW unified strategy system (610 lines) ✅ ├── strategies_orm.py # OLD ORM-based strategies (356 lines) ❌ REDUNDANT ├── simple_matcher.py # Standalone matcher (351 lines) ❌ REDUNDANT ├── rtl433_matcher.py # Standalone RTL_433 (352 lines) ❌ REDUNDANT ├── rtl433_decoder.py # RTL_433 decoder (385 lines) ✅ USED ├── pattern_decoder.py # Pattern decoder (461 lines) ✅ USED └── protocol_database.py # Protocol DB (435 lines) ✅ USED ``` **Problem:** - **3 different matcher implementations** doing similar things - `strategies.py` is the NEW unified system (includes all strategies) - `strategies_orm.py`, `simple_matcher.py`, `rtl433_matcher.py` are OLD and redundant **Evidence:** ```python # main_simple.py uses: from src.matcher.simple_matcher import get_matcher # OLD from src.matcher.rtl433_decoder import get_decoder # OK # strategies.py contains: class ExactMatcher(MatchStrategy) # Replaces simple_matcher class FrequencyMatcher(MatchStrategy) # Replaces simple_matcher class BitPatternMatcher(MatchStrategy) # Replaces simple_matcher class TimingMatcher(MatchStrategy) # Replaces simple_matcher class RTL433DecoderStrategy(MatchStrategy) # Wraps rtl433_decoder class PatternBasedStrategy(MatchStrategy) # NEW - pattern_decoder ``` **Action Plan:** 1. ✅ **Keep**: `strategies.py` (unified system) 2. ✅ **Keep**: `rtl433_decoder.py` (used by RTL433DecoderStrategy) 3. ✅ **Keep**: `pattern_decoder.py` (used by PatternBasedStrategy) 4. ✅ **Keep**: `protocol_database.py` (used by pattern_decoder) 5. ✅ **Keep**: `engine.py` (base interfaces) 6. ❌ **DELETE**: `strategies_orm.py` (old, unused) 7. ❌ **DELETE**: `simple_matcher.py` (replaced by strategies.py) 8. ❌ **DELETE**: `rtl433_matcher.py` (replaced by strategies.py) **Impact:** - Remove ~1,059 lines of redundant code - Eliminate maintenance burden of 3 parallel implementations - Simplify API by using only `strategies.py` --- ### B. Dual API Implementations #### 🟡 MEDIUM PRIORITY - Consolidate APIs **Current State:** ``` src/api/ ├── main.py # Full ORM-based API (280 lines) └── main_simple.py # Simplified API (536 lines) ✅ CURRENTLY USED ``` **Problem:** - Two FastAPI implementations - `main_simple.py` is actively developed and used - `main.py` uses old ORM approach (SQLAlchemy) - Confusion about which is "production" **Recommendation:** 1. ✅ **Keep**: `main_simple.py` (rename to `main.py` or `app.py`) 2. ❌ **Archive**: Current `main.py` → `main_orm_legacy.py` or delete **Reason:** - `main_simple.py` has RTL_433 integration - `main_simple.py` uses modern psycopg2 approach - Less abstraction = easier debugging --- ### C. Unused/Redundant Scripts #### 🟢 LOW PRIORITY - Archive Old Scripts **Current State:** 19 Python scripts in `scripts/` **Categories:** **1. Active/Essential (Keep):** - ✅ `test_pattern_decoder.py` - New pattern decoder tests - ✅ `test_real_weather_sensors.py` - RTL_433 validation - ✅ `test_rtl433_with_known_devices.py` - RTL_433 tests - ✅ `parse_rtl433_devices.py` - RTL_433 device catalog builder - ✅ `download_rf_test_datasets.sh` - Test data fetcher **2. Utility/Maintenance (Keep):** - ✅ `cleanup_database.py` - Database maintenance - ✅ `import_wardriving_data.py` - CSV import for wardriving - ✅ `create_test_dataset.py` - Test data generator **3. One-Time/Experimental (Archive):** - 🔄 `analyze_flipper_signatures.py` - One-time analysis - 🔄 `analyze_tembed_files.py` - One-time analysis - 🔄 `identify_tembed_devices.py` - One-time analysis - 🔄 `import_flipper_sqlite.py` - One-time import - 🔄 `import_tembed_signatures.py` - One-time import - 🔄 `match_tembed_with_db.py` - Exploratory - 🔄 `match_with_flipper_db.py` - Exploratory - 🔄 `rematch_captures.py` - One-time rematch - 🔄 `test_gps_extraction.py` - Early prototype - 🔄 `test_tembed_matching.py` - Early prototype - 🔄 `test_wardriving_import.py` - Import test **Action Plan:** Create `scripts/archive/` directory and move 11 one-time scripts there. **Impact:** - Cleaner `scripts/` directory (8 files vs 19) - Keep history but reduce clutter - Easy to restore if needed --- ### D. Unused Parser Modules #### 🟢 LOW PRIORITY - Review Parsers **Current State:** ``` src/parser/ ├── sub_parser.py # Main .sub parser ✅ USED ├── rtl433_converter.py # RAW → RTL_433 converter ✅ USED ├── metadata.py # SignalMetadata class ✅ USED ├── raw_parser.py # RAW signal analyzer (326 lines) ? ├── gps_extractor.py # GPS from filenames (241 lines) ? └── wardriving_importer.py # CSV wardriving import (333 lines) ? ``` **Questions:** 1. Is `raw_parser.py` still used? (Seems superseded by pattern_decoder.py) 2. Is `gps_extractor.py` actively used? 3. Is `wardriving_importer.py` production-ready? **Recommendation:** - Check imports/usage in API - If unused, move to `deprecated/` folder - Document if they're experimental --- ### E. Database Connection Complexity #### 🟡 MEDIUM PRIORITY - Simplify Database Layer **Current State:** ``` Database layer has 3 approaches: 1. config/database.py # Psycopg2 connection pooling 2. src/database/connection.py # SQLAlchemy ORM 3. src/database/models.py # SQLAlchemy models (564 lines) ``` **Problem:** - `main_simple.py` uses psycopg2 approach (simpler) - `main.py` uses SQLAlchemy ORM approach (complex) - `strategies_orm.py` uses SQLAlchemy models - Dual maintenance burden **Recommendation:** Since we're keeping `main_simple.py`: 1. ✅ **Keep**: `config/database.py` (psycopg2 pooling) 2. 🔄 **Archive**: `src/database/connection.py` (if not used elsewhere) 3. 🔄 **Archive**: `src/database/models.py` (if only used by old main.py) --- ## Part 2: Optimization Opportunities ### A. Pattern Decoder Enhancements #### 🔵 ENHANCEMENT - Improve Decode Rate **Current Performance:** - 44.4% decode rate (4/9 files) - Confidence range: 33-76% **Optimization Ideas:** **1. Lower Confidence Thresholds (Easy - 30 min)** ```python # Current proto.min_confidence = 0.6 # Too strict? # Proposed proto.min_confidence = 0.4 # Allow more matches ``` **2. Add More Protocols (Medium - 2 hours)** - Currently: 18 protocols - Target: 50+ protocols - Source: Flipper Zero firmware protocol definitions **3. Improve Timing Tolerance (Easy - 1 hour)** ```python # Current timing_tolerance = 0.2 # ±20% # Proposed: Adaptive tolerance based on pulse count if pulse_count < 150: timing_tolerance = 0.3 # ±30% for short captures else: timing_tolerance = 0.2 # ±20% for longer captures ``` **4. Add Preamble Pattern Matching (Medium - 2 hours)** ```python # Currently not fully utilized if proto.preamble_pattern and proto.preamble_pattern in bit_pattern: pattern_confidence = 1.0 ``` Enhance to use fuzzy pattern matching (Levenshtein distance). --- ### B. API Integration of Pattern Decoder #### 🔴 CRITICAL - Pattern Decoder Not Integrated! **Problem:** Pattern decoder exists in `strategies.py` but is **NOT** used by API! **Current API Flow:** ```python # main_simple.py from src.matcher.simple_matcher import get_matcher # OLD from src.matcher.rtl433_decoder import get_decoder # OK # Upload handler uses: matcher = get_matcher() # Returns OLD simple_matcher matches = matcher.match_device(metadata) # DOESN'T use strategies.py! decoder = get_rtl433_decoder() # Separate call rtl433_devices = decoder.decode(metadata) ``` **What's Missing:** The new unified `strategies.py` system with `PatternBasedStrategy` is **NOT CONNECTED** to the API! **Fix Required:** ```python # NEW approach (use strategies.py): from src.matcher.engine import MatchEngine from src.matcher.strategies import ( ExactMatcher, FrequencyMatcher, BitPatternMatcher, TimingMatcher, RTL433DecoderStrategy, PatternBasedStrategy # NEW! ) # Initialize engine engine = MatchEngine() engine.add_strategy(ExactMatcher()) engine.add_strategy(FrequencyMatcher()) engine.add_strategy(BitPatternMatcher()) engine.add_strategy(TimingMatcher()) engine.add_strategy(RTL433DecoderStrategy()) # Uses rtl433_decoder engine.add_strategy(PatternBasedStrategy()) # Uses pattern_decoder # In upload handler: matches = engine.match(metadata, db) # Returns all matches from all strategies ``` **Impact:** - Pattern decoder will finally be used in production! - Unified matching pipeline - Easier to add new strategies --- ### C. Frontend JavaScript Implementation #### 🔵 NEW FEATURE - Client-Side Pattern Decoder **Why JavaScript?** 1. Decode signals client-side before upload (privacy) 2. Real-time feedback during capture 3. Reduce server load 4. Works offline (PWA capability) **Use Cases:** 1. **Web Interface**: Decode .sub files in browser before upload 2. **Mobile App**: Real-time decoding during T-Embed capture 3. **Offline Mode**: Decode signals without server --- ## Part 3: JavaScript Implementation Plan ### Architecture: Port Pattern Decoder to JavaScript #### Target: Node.js + Browser-Compatible Module **File Structure:** ``` src/matcher/js/ ├── protocol-database.js # Protocol signatures (ES6 module) ├── pattern-decoder.js # Core decoder logic ├── pulse-analyzer.js # K-means clustering for pulse widths ├── fingerprint.js # Statistical fingerprinting └── index.js # Main export ``` --- ### JavaScript Implementation: Core Components #### 1. Protocol Database (protocol-database.js) ```javascript /** * RF Protocol Signature Database * Port of protocol_database.py */ export class ProtocolSignature { constructor({ name, category, manufacturer = null, frequency = 433920000, shortPulseUs = 500, longPulseUs = 1000, timingTolerance = 0.2, preamblePattern = null, syncPattern = null, minBits = 24, maxBits = 64, typicalPulseCount = 100 }) { this.name = name; this.category = category; this.manufacturer = manufacturer; this.frequency = frequency; this.shortPulseUs = shortPulseUs; this.longPulseUs = longPulseUs; this.timingTolerance = timingTolerance; this.preamblePattern = preamblePattern; this.syncPattern = syncPattern; this.minBits = minBits; this.maxBits = maxBits; this.typicalPulseCount = typicalPulseCount; } matchesTiming(shortUs, longUs) { const shortMin = this.shortPulseUs * (1 - this.timingTolerance); const shortMax = this.shortPulseUs * (1 + this.timingTolerance); const longMin = this.longPulseUs * (1 - this.timingTolerance); const longMax = this.longPulseUs * (1 + this.timingTolerance); return (shortMin <= shortUs && shortUs <= shortMax) && (longMin <= longUs && longUs <= longMax); } matchesFrequency(freq) { const tolerance = 100000; // ±100 kHz return Math.abs(freq - this.frequency) <= tolerance; } } // Weather Sensors export const WEATHER_SENSORS = [ new ProtocolSignature({ name: 'Oregon Scientific v2.1', category: 'Weather Sensor', manufacturer: 'Oregon Scientific', shortPulseUs: 488, longPulseUs: 976, preamblePattern: '10101010'.repeat(4), minBits: 64, maxBits: 128, typicalPulseCount: 200 }), new ProtocolSignature({ name: 'Acurite Tower Sensor', category: 'Weather Sensor', manufacturer: 'Acurite', shortPulseUs: 220, longPulseUs: 440, minBits: 56, maxBits: 64, typicalPulseCount: 130 }), new ProtocolSignature({ name: 'LaCrosse TX141TH-Bv2', category: 'Weather Sensor', manufacturer: 'LaCrosse', shortPulseUs: 500, longPulseUs: 1000, minBits: 40, maxBits: 48, typicalPulseCount: 100 }), // ... add all 18 protocols ]; export const ALL_PROTOCOLS = [ ...WEATHER_SENSORS, // ...GARAGE_DOOR_OPENERS, // ...DOORBELLS, // ...TIRE_PRESSURE, // ...SECURITY_SENSORS, // ...REMOTE_CONTROLS ]; export class ProtocolDatabase { constructor() { this.protocols = ALL_PROTOCOLS; this._indexProtocols(); } _indexProtocols() { this.byCategory = {}; this.byFrequency = {}; for (const proto of this.protocols) { // Index by category if (!this.byCategory[proto.category]) { this.byCategory[proto.category] = []; } this.byCategory[proto.category].push(proto); // Index by frequency (MHz) const freqMHz = Math.round(proto.frequency / 1_000_000); if (!this.byFrequency[freqMHz]) { this.byFrequency[freqMHz] = []; } this.byFrequency[freqMHz].push(proto); } } findByTiming(shortUs, longUs, frequency = null) { const matches = []; let candidates = this.protocols; if (frequency) { const freqMHz = Math.round(frequency / 1_000_000); candidates = this.byFrequency[freqMHz] || this.protocols; } for (const proto of candidates) { if (proto.matchesTiming(shortUs, longUs)) { if (!frequency || proto.matchesFrequency(frequency)) { matches.push(proto); } } } return matches; } findByFrequency(frequency) { return this.protocols.filter(p => p.matchesFrequency(frequency)); } } ``` --- #### 2. Pulse Analyzer (pulse-analyzer.js) ```javascript /** * Pulse Width Analysis using K-means clustering * Port of PatternDecoder._identify_pulse_widths() */ /** * Simple K-means clustering for 1D data */ export function kMeans(data, k = 2, maxIterations = 100) { if (data.length < k) { return data.map(v => [v]); } // Initialize centroids (use min and max) const sorted = [...data].sort((a, b) => a - b); let centroids = [sorted[0], sorted[sorted.length - 1]]; for (let iter = 0; iter < maxIterations; iter++) { // Assign points to nearest centroid const clusters = Array.from({ length: k }, () => []); for (const point of data) { const distances = centroids.map(c => Math.abs(point - c)); const nearestIdx = distances.indexOf(Math.min(...distances)); clusters[nearestIdx].push(point); } // Update centroids const newCentroids = clusters.map(cluster => { if (cluster.length === 0) return centroids[0]; return cluster.reduce((a, b) => a + b, 0) / cluster.length; }); // Check convergence const converged = centroids.every((c, i) => Math.abs(c - newCentroids[i]) < 1 ); centroids = newCentroids; if (converged) break; } return centroids.sort((a, b) => a - b); } /** * Identify SHORT and LONG pulse widths from pulse train */ export function identifyPulseWidths(pulses) { // Separate HIGH pulses (positive) and LOW gaps (negative) const highPulses = pulses.filter(p => p > 0).map(Math.abs); const lowPulses = pulses.filter(p => p < 0).map(Math.abs); // Cluster into SHORT/LONG const [shortPulse, longPulse] = highPulses.length >= 2 ? kMeans(highPulses, 2) : [highPulses[0] || 0, highPulses[0] || 0]; const [shortGap, longGap] = lowPulses.length >= 2 ? kMeans(lowPulses, 2) : [lowPulses[0] || 0, lowPulses[0] || 0]; return { shortPulse, longPulse, shortGap, longGap }; } /** * Decode pulse train to binary string (PWM encoding) */ export function decodeToBits(pulses, shortPulse, longPulse) { const threshold = (shortPulse + longPulse) / 2; const bits = []; for (const pulse of pulses) { if (pulse > 0) { // Only decode HIGH pulses bits.push(Math.abs(pulse) < threshold ? '0' : '1'); } } return bits.join(''); } ``` --- #### 3. Statistical Fingerprinting (fingerprint.js) ```javascript /** * Extract statistical fingerprint from pulse data */ export function extractFingerprint(pulses) { const highPulses = pulses.filter(p => p > 0); const lowPulses = pulses.filter(p => p < 0).map(Math.abs); // Calculate statistics const meanPulse = mean(highPulses); const stdPulse = std(highPulses); const meanGap = mean(lowPulses); const stdGap = std(lowPulses); const totalHigh = sum(highPulses); const totalLow = sum(lowPulses); const totalTime = totalHigh + totalLow; const pulseGapRatio = meanGap > 0 ? meanPulse / meanGap : 0; const dutyCycle = totalTime > 0 ? totalHigh / totalTime : 0; return { meanPulseWidth: meanPulse, stdPulseWidth: stdPulse, meanGapWidth: meanGap, stdGapWidth: stdGap, pulseGapRatio, dutyCycle, pulseCount: pulses.length, minPulse: Math.min(...highPulses), maxPulse: Math.max(...highPulses), minGap: Math.min(...lowPulses), maxGap: Math.max(...lowPulses) }; } // Helper functions function mean(arr) { return arr.length > 0 ? sum(arr) / arr.length : 0; } function sum(arr) { return arr.reduce((a, b) => a + b, 0); } function std(arr) { const m = mean(arr); const variance = arr.reduce((acc, val) => acc + Math.pow(val - m, 2), 0) / arr.length; return Math.sqrt(variance); } ``` --- #### 4. Pattern Decoder (pattern-decoder.js) ```javascript /** * Main Pattern Decoder * Port of PatternDecoder class */ import { ProtocolDatabase } from './protocol-database.js'; import { identifyPulseWidths, decodeToBits } from './pulse-analyzer.js'; import { extractFingerprint } from './fingerprint.js'; export class DeviceMatch { constructor(protocol, confidence, matchMethod, details) { this.protocol = protocol; this.confidence = confidence; this.matchMethod = matchMethod; this.details = details; } get name() { return this.protocol.name; } get manufacturer() { return this.protocol.manufacturer; } get category() { return this.protocol.category; } } export class PatternDecoder { constructor() { this.protocolDb = new ProtocolDatabase(); } /** * Decode device from signal metadata * @param {Object} metadata - { frequency, raw_data: [] } * @returns {DeviceMatch[]} */ decode(metadata) { if (!metadata.raw_data || metadata.raw_data.length === 0) { return []; } const pulses = metadata.raw_data; const frequency = metadata.frequency; const matches = []; // Strategy 1: Timing Pattern Analysis matches.push(...this._decodeTimingPatterns(pulses, frequency)); // Strategy 2: Statistical Fingerprinting const fingerprint = extractFingerprint(pulses); matches.push(...this._matchFingerprint(fingerprint, frequency)); // Deduplicate and rank return this._rankMatches(matches); } _decodeTimingPatterns(pulses, frequency) { const matches = []; // Identify pulse widths const { shortPulse, longPulse } = identifyPulseWidths(pulses); if (shortPulse === 0 || longPulse === 0) { return []; } // Decode to binary const bitPattern = decodeToBits(pulses, shortPulse, longPulse); // Find matching protocols const protocolMatches = this.protocolDb.findByTiming( shortPulse, longPulse, frequency ); for (const proto of protocolMatches) { // Calculate confidence const timingError = Math.abs(proto.shortPulseUs - shortPulse) / proto.shortPulseUs; const timingConfidence = Math.max(0, 1.0 - timingError); const bitCount = bitPattern.length; const bitCountMatch = (proto.minBits <= bitCount && bitCount <= proto.maxBits); const bitConfidence = bitCountMatch ? 1.0 : 0.5; let patternConfidence = 0.7; if (proto.preamblePattern && bitPattern.includes(proto.preamblePattern)) { patternConfidence = 1.0; } else if (proto.syncPattern && bitPattern.includes(proto.syncPattern)) { patternConfidence = 0.9; } const overallConfidence = ( timingConfidence * 0.4 + bitConfidence * 0.3 + patternConfidence * 0.3 ); if (overallConfidence >= 0.6) { matches.push(new DeviceMatch( proto, overallConfidence, 'timing_pattern', { shortPulseUs: shortPulse, longPulseUs: longPulse, bitCount, bitPattern: bitPattern.substring(0, 64), timingError: `${(timingError * 100).toFixed(2)}%` } )); } } return matches; } _matchFingerprint(fingerprint, frequency) { const matches = []; const candidates = this.protocolDb.findByFrequency(frequency); for (const proto of candidates) { const expectedMeanPulse = (proto.shortPulseUs + proto.longPulseUs) / 2; const pulseError = Math.abs(expectedMeanPulse - fingerprint.meanPulseWidth) / expectedMeanPulse; const expectedCount = proto.typicalPulseCount; const countError = Math.abs(expectedCount - fingerprint.pulseCount) / expectedCount; const pulseSimilarity = Math.max(0, 1.0 - pulseError); const countSimilarity = Math.max(0, 1.0 - countError); const overallConfidence = (pulseSimilarity * 0.6 + countSimilarity * 0.4) * 0.8; if (overallConfidence >= 0.4) { matches.push(new DeviceMatch( proto, overallConfidence, 'fingerprint', { meanPulseUs: fingerprint.meanPulseWidth.toFixed(1), pulseCount: fingerprint.pulseCount, dutyCycle: `${(fingerprint.dutyCycle * 100).toFixed(2)}%`, pulseError: `${(pulseError * 100).toFixed(2)}%`, countError: `${(countError * 100).toFixed(2)}%` } )); } } return matches; } _rankMatches(matches) { // Group by protocol name const byProtocol = {}; for (const match of matches) { const name = match.protocol.name; if (!byProtocol[name]) { byProtocol[name] = []; } byProtocol[name].push(match); } // Keep best match per protocol const bestMatches = []; for (const protoMatches of Object.values(byProtocol)) { const best = protoMatches.reduce((a, b) => a.confidence > b.confidence ? a : b ); bestMatches.push(best); } // Sort by confidence return bestMatches.sort((a, b) => b.confidence - a.confidence); } } ``` --- #### 5. Main Export (index.js) ```javascript /** * GigLez Pattern Decoder - JavaScript Implementation * Browser and Node.js compatible */ export { PatternDecoder, DeviceMatch } from './pattern-decoder.js'; export { ProtocolDatabase, ProtocolSignature } from './protocol-database.js'; export { identifyPulseWidths, decodeToBits, kMeans } from './pulse-analyzer.js'; export { extractFingerprint } from './fingerprint.js'; // Default export for convenience import { PatternDecoder } from './pattern-decoder.js'; export default PatternDecoder; ``` --- ### JavaScript Integration: Usage Examples #### Example 1: Node.js (Backend) ```javascript import PatternDecoder from './src/matcher/js/index.js'; // Parse .sub file (assume you have this) const metadata = { frequency: 433920000, raw_data: [788, -1004, 1194, -68, 3094, -162, 786, -66, 164, -200, ...] }; // Decode const decoder = new PatternDecoder(); const matches = decoder.decode(metadata); console.log(`Found ${matches.length} matches:`); for (const match of matches) { console.log(` ${match.name} (${match.manufacturer}) - ${(match.confidence * 100).toFixed(1)}%`); } ``` #### Example 2: Browser (Frontend) ```html GigLez Pattern Decoder

Decode .sub File

``` #### Example 3: React Component ```jsx import React, { useState } from 'react'; import PatternDecoder from '../matcher/js/index.js'; export function SubFileDecoder() { const [matches, setMatches] = useState([]); const decoder = new PatternDecoder(); const handleFileUpload = async (e) => { const file = e.target.files[0]; const text = await file.text(); const metadata = parseSubFile(text); const results = decoder.decode(metadata); setMatches(results); }; return (
{matches.length > 0 && (

Decoded Devices ({matches.length})

{matches.map((match, i) => (

{match.name}

Manufacturer: {match.manufacturer || 'Unknown'}

Category: {match.category}

Confidence: {(match.confidence * 100).toFixed(1)}%

Method: {match.matchMethod}

))}
)}
); } ``` --- ## Part 4: Implementation Roadmap ### Phase 1: Cleanup (1 day) **Priority: Remove redundant code** 1. ✅ **Delete redundant matchers** (~2 hours) - Remove `strategies_orm.py` - Remove `simple_matcher.py` - Remove `rtl433_matcher.py` - Update imports across codebase 2. ✅ **Consolidate API** (~2 hours) - Rename `main_simple.py` → `app.py` (or keep as `main_simple.py`) - Archive old `main.py` → `main_orm_legacy.py` - Update `start_web.sh` to use correct file 3. ✅ **Archive old scripts** (~1 hour) - Create `scripts/archive/` directory - Move 11 one-time scripts - Update `scripts/README.md` 4. ✅ **Review parser usage** (~1 hour) - Check if `raw_parser.py`, `gps_extractor.py`, `wardriving_importer.py` are used - Move to `deprecated/` if unused **Outcome:** ~1,500 lines of code removed, cleaner structure --- ### Phase 2: Fix Pattern Decoder Integration (2 hours) **Priority: Make pattern decoder work in production** 1. ✅ **Update API to use strategies.py** (~1 hour) ```python # Replace simple_matcher with MatchEngine from src.matcher.engine import MatchEngine from src.matcher.strategies import * engine = MatchEngine() engine.add_strategy(ExactMatcher()) engine.add_strategy(FrequencyMatcher()) engine.add_strategy(PatternBasedStrategy()) # NEW! engine.add_strategy(RTL433DecoderStrategy()) ``` 2. ✅ **Test pattern decoder in API** (~1 hour) - Upload test .sub files - Verify pattern matches appear in results - Check confidence scores **Outcome:** Pattern decoder fully integrated and working --- ### Phase 3: JavaScript Implementation (3-5 days) **Priority: Port pattern decoder to JavaScript** 1. ✅ **Core implementation** (~2 days) - `protocol-database.js` (port 18 protocols) - `pulse-analyzer.js` (K-means, pulse detection) - `fingerprint.js` (statistics) - `pattern-decoder.js` (main logic) - `index.js` (exports) 2. ✅ **Testing** (~1 day) - Unit tests (Jest or Mocha) - Test with same .sub files as Python - Verify decode rate matches (44.4%) 3. ✅ **Browser integration** (~1-2 days) - Build .sub file parser - Create demo HTML page - Optimize for bundle size - Add to GigLez frontend **Outcome:** Client-side decoding capability --- ### Phase 4: Optimization (1-2 days) **Priority: Improve decode rate** 1. ✅ **Lower confidence thresholds** (~30 min) 2. ✅ **Add more protocols** (~2 hours) 3. ✅ **Adaptive timing tolerance** (~1 hour) 4. ✅ **Enhance pattern matching** (~2 hours) **Target:** 60%+ decode rate (vs current 44.4%) --- ## Summary ### Code to Remove | File | Lines | Reason | |------|-------|--------| | `strategies_orm.py` | 356 | Replaced by strategies.py | | `simple_matcher.py` | 351 | Replaced by strategies.py | | `rtl433_matcher.py` | 352 | Replaced by strategies.py | | 11 archived scripts | ~800 | One-time use, move to archive/ | | **Total** | **~1,859 lines** | **19% reduction** | ### Code to Add | Component | Lines (estimate) | Purpose | |-----------|------------------|---------| | JavaScript protocol DB | ~300 | Client-side decoding | | JavaScript decoder | ~400 | Core logic | | JavaScript utilities | ~200 | K-means, fingerprinting | | **Total** | **~900 lines** | **New capability** | ### Net Result - **Remove:** 1,859 lines of redundant Python - **Add:** 900 lines of new JavaScript - **Net reduction:** 959 lines (-10%) - **New capability:** Client-side decoding - **Improved maintainability:** Single matcher system --- ## Next Steps **Immediate (Today):** 1. Review this plan 2. Get approval for deletions 3. Start Phase 1: Cleanup **Short-term (This Week):** 1. Complete Phase 1 & 2 2. Start Phase 3: JavaScript implementation **Medium-term (Next Week):** 1. Complete JavaScript implementation 2. Test in browser 3. Optimize decoder performance --- **Status:** Plan Complete - Ready for Implementation **Approval Needed:** Yes (for deletions) **Risk Level:** Low (all changes are backwards-compatible or cleanup)