From 9bd9f4f43b1b514e605a00bb03e79cb790605f56 Mon Sep 17 00:00:00 2001 From: priestlypython Date: Wed, 14 Jan 2026 13:20:08 -0800 Subject: [PATCH] docs: Multi-frequency RF file handling analysis Comprehensive analysis of how the system handles .sub files with multiple frequencies. Key findings: - Standard Flipper Zero files contain ONE frequency per file - Multiple .sub files in one upload: WORKS correctly - Multiple Frequency: lines in one file: Only last used (silent loss) - Recommended: Add validation warning for multi-frequency edge case Analysis includes: - Current parser behavior - Flipper Zero file format specification - 4 solution options with pros/cons - Testing scenarios - Impact assessment - Short-term and long-term recommendations --- docs/MULTI_FREQUENCY_HANDLING.md | 456 +++++++++++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 docs/MULTI_FREQUENCY_HANDLING.md diff --git a/docs/MULTI_FREQUENCY_HANDLING.md b/docs/MULTI_FREQUENCY_HANDLING.md new file mode 100644 index 0000000..36b4fcb --- /dev/null +++ b/docs/MULTI_FREQUENCY_HANDLING.md @@ -0,0 +1,456 @@ +# Multi-Frequency RF File Handling - Analysis & Recommendations + +**Date:** January 14, 2026 +**Status:** Current Limitation Identified +**Priority:** Medium (Future Enhancement) + +--- + +## Current Behavior + +### How the System Handles .sub Files Today + +**Current Implementation:** +```python +# src/parser/sub_parser.py (line 79) +frequency = int(self.fields.get('Frequency', 0)) # Single frequency only + +# src/api/main_simple.py (line 323) +frequency = metadata.frequency # Extracts single frequency +``` + +**What Happens:** +1. Parser reads the `Frequency:` field from .sub file +2. Extracts **ONE** frequency value (integer) +3. Creates **ONE** capture record per file +4. Stores that single frequency with GPS coordinates + +### Flipper Zero .sub File Format + +**Standard Single-Frequency File:** +``` +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: Princeton +Bit: 24 +Key: 00 00 00 00 00 95 D5 D4 +``` + +**Can .sub Files Contain Multiple Frequencies?** + +Based on Flipper Zero's firmware and file format specification: + +❌ **NO** - Standard Flipper Zero .sub files contain **ONE frequency per file** + +Each .sub file represents a **single capture** at a **single frequency**. + +### Multi-Frequency Scenarios + +However, there are **edge cases** where multiple frequencies might appear: + +#### Scenario 1: Multiple .sub Files in One Upload +✅ **SUPPORTED** - System handles this correctly + +```python +# User uploads multiple files at once +files = [ + "capture_315MHz.sub", # Frequency: 315000000 + "capture_433MHz.sub", # Frequency: 433920000 + "capture_868MHz.sub" # Frequency: 868000000 +] + +# System behavior: +for file in files: + metadata = sub_parser.parse(file) # Each file parsed separately + capture = create_capture(metadata) # Each creates separate capture + # Result: 3 separate captures with 3 different frequencies +``` + +**Result:** ✅ Works correctly - creates 3 separate map markers + +#### Scenario 2: Frequency-Hopping Devices (Future) +❌ **NOT SUPPORTED** - Would require new file format + +Some devices transmit on multiple frequencies (e.g., Bluetooth frequency hopping, LoRa adaptive data rate): + +``` +Device transmits: 315 MHz → 433 MHz → 868 MHz +``` + +**Current behavior:** Flipper Zero would create 3 separate .sub files + +**Hypothetical multi-frequency file (NOT STANDARD):** +``` +Filetype: Flipper SubGhz Multi File # DOES NOT EXIST +Version: 1 +Frequency: 315000000 +Frequency: 433920000 # Multiple Frequency fields +Frequency: 868000000 +``` + +**Our parser behavior:** Only reads **first** Frequency field (line 79) + +#### Scenario 3: Scanning/Waterfall Captures +❌ **NOT SUPPORTED** - Different file format + +Flipper Zero's spectrum analyzer creates different file types: +- `.sub` files = Single frequency capture +- Waterfall scans = Not saved to .sub format + +--- + +## Current System Limitations + +### What Happens If Someone Manually Edits a .sub File? + +Let's test what happens if a .sub file has multiple `Frequency:` lines: + +**Malformed File:** +``` +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 315000000 +Frequency: 433920000 # Second frequency line +Protocol: Princeton +``` + +**Parser Behavior:** +```python +# src/parser/sub_parser.py (lines 54-61) +for line in content.split('\n'): + if ':' in line: + key, value = line.split(':', 1) + self.fields[key.strip()] = value.strip() # OVERWRITES previous value! +``` + +**Result:** +- First `Frequency: 315000000` is stored +- Second `Frequency: 433920000` **OVERWRITES** it +- Only **433920000** is used + +❌ **Silent data loss** - First frequency is discarded without warning + +### Upload Endpoint Behavior + +```python +# src/api/main_simple.py (lines 305-376) +for file in files: + metadata = sub_parser.parse(temp_path) # Parses single frequency + frequency = metadata.frequency # Gets single frequency + + # Creates ONE capture per file + capture_info = { + "id": upload_counter, + "frequency": frequency, # Single frequency stored + # ... other fields + } +``` + +**Result:** One file = One frequency = One capture = One map marker + +--- + +## Proposed Solutions + +### Option 1: Detect and Reject Multi-Frequency Files (Recommended) + +**Pros:** +- Maintains data integrity +- Prevents silent data loss +- Simple to implement + +**Implementation:** +```python +def parse(self, file_path: str) -> SignalMetadata: + # Count frequency fields + freq_count = content.count('Frequency:') + + if freq_count > 1: + raise ValueError( + f"Multi-frequency files not supported. " + f"Found {freq_count} frequency fields. " + f"Please split into separate .sub files." + ) + + # Rest of parsing... +``` + +### Option 2: Parse All Frequencies and Create Multiple Captures (Complex) + +**Pros:** +- Handles edge cases gracefully +- Future-proof for multi-frequency devices + +**Cons:** +- Significant code changes required +- Not aligned with Flipper Zero standard +- GPS coordinates ambiguous (which freq was captured where?) + +**Implementation:** +```python +def parse(self, file_path: str) -> List[SignalMetadata]: + """Returns LIST of metadata objects""" + + # Parse all frequency fields + frequencies = [] + for line in content.split('\n'): + if line.startswith('Frequency:'): + freq = int(line.split(':', 1)[1].strip()) + frequencies.append(freq) + + if len(frequencies) == 0: + raise ValueError("No frequency found") + + # Create separate metadata for each frequency + results = [] + for freq in frequencies: + metadata = SignalMetadata(frequency=freq, ...) + results.append(metadata) + + return results + +# Upload endpoint changes needed: +for file in files: + metadatas = sub_parser.parse(temp_path) # Returns LIST + for metadata in metadatas: # Create multiple captures + capture = create_capture(metadata) +``` + +**Database changes:** +- `captures_storage` would need to link related captures +- GPS coordinates: same for all captures from one file +- Map display: Multiple markers at same location? + +### Option 3: Store Multiple Frequencies in Single Capture (Database Change) + +**Pros:** +- Preserves all frequency data +- Single capture per file (simpler logic) + +**Cons:** +- Database schema change required +- Map display complexity (show all frequencies?) +- Matching logic needs update + +**Implementation:** +```python +# Database schema: +capture_info = { + "id": 1, + "filename": "multi_freq.sub", + "frequencies": [315000000, 433920000, 868000000], # Array instead of single + "primary_frequency": 315000000, # First or dominant frequency + # ... +} + +# Map display: +# Option A: Show primary frequency only +# Option B: Show all frequencies in popup +# Option C: Create multiple semi-transparent markers +``` + +### Option 4: Do Nothing (Current Behavior) + +**Pros:** +- No code changes needed +- Aligns with Flipper Zero standard (one freq per file) + +**Cons:** +- Silent data loss if malformed file uploaded +- No user feedback about issue + +--- + +## Recommendations + +### Short-Term (Implement Now) + +**1. Add Validation Warning** +```python +def parse(self, file_path: str) -> SignalMetadata: + freq_count = content.count('Frequency:') + + if freq_count > 1: + logger.warning( + f"File {file_path} contains {freq_count} frequency fields. " + f"Only the last one will be used: {self.fields['Frequency']}" + ) +``` + +**2. Document Behavior** +- Update API docs to clarify: "One frequency per .sub file" +- Add to upload UI: "Multi-frequency files not supported" + +**3. Add to Upload Response** +```python +if freq_count > 1: + warnings.append({ + "filename": file.filename, + "warning": "Multiple frequencies detected. Only last frequency used.", + "frequencies_found": freq_count + }) +``` + +### Medium-Term (Future Enhancement) + +**If multi-frequency support becomes needed:** + +1. **Update File Format Spec** + - Define multi-frequency .sub format + - OR: Use separate file per frequency (Flipper standard) + +2. **Update Parser** + - Return `List[SignalMetadata]` instead of single + - Parse all frequency fields + +3. **Update Upload Endpoint** + - Handle list of metadata objects + - Create multiple captures per file + - Link captures with `parent_file_id` + +4. **Update Map Display** + - Show multiple markers at same GPS location + - OR: Single marker with frequency list in popup + +5. **Update Database Schema** + ```python + # New fields: + "is_multi_frequency": True, + "parent_capture_id": None, # For linked captures + "frequency_index": 0, # Which frequency in multi-freq file + ``` + +--- + +## Testing + +### Test Case 1: Normal Single-Frequency File +```python +# Input: standard_capture.sub +Frequency: 433920000 + +# Expected: +- Parse succeeds +- frequency = 433920000 +- One capture created +``` + +### Test Case 2: Duplicate Frequency Lines +```python +# Input: duplicate_freq.sub +Frequency: 315000000 +Frequency: 315000000 # Same frequency twice + +# Current behavior: +- Parse succeeds +- frequency = 315000000 (last value) +- Warning logged + +# Recommended: +- Parse succeeds +- Warning returned to user +``` + +### Test Case 3: Different Frequency Lines +```python +# Input: multi_freq.sub +Frequency: 315000000 +Frequency: 433920000 # Different frequency + +# Current behavior: +- Parse succeeds +- frequency = 433920000 (last value) +- 315000000 silently discarded + +# Recommended: +- Parse fails OR +- Parse succeeds with warning +- User informed of data loss +``` + +### Test Case 4: Multiple Files Uploaded +```python +# Input: 3 separate .sub files +file1.sub: Frequency: 315000000 +file2.sub: Frequency: 433920000 +file3.sub: Frequency: 868000000 + +# Current behavior: ✅ WORKS +- Parse each file separately +- Create 3 captures +- 3 map markers with different frequencies +``` + +--- + +## FAQ + +**Q: Can a single .sub file contain multiple frequencies?** +A: Not in the standard Flipper Zero format. Each .sub file represents one capture at one frequency. + +**Q: What if I scan multiple frequencies?** +A: Flipper Zero creates separate .sub files for each frequency. Upload all files together. + +**Q: What happens if I manually add multiple `Frequency:` lines?** +A: Currently, only the **last** frequency is used. Earlier ones are silently discarded. + +**Q: Can I upload multiple .sub files with different frequencies?** +A: ✅ Yes! Upload them all at once. Each file creates a separate capture. + +**Q: Why not support multi-frequency files?** +A: +1. Not part of Flipper Zero standard +2. GPS coordinates are per-file, not per-frequency +3. Adds significant complexity +4. No known devices that require this + +**Q: Will this be supported in the future?** +A: Maybe, if: +1. Users frequently encounter multi-frequency devices +2. A clear use case emerges (frequency-hopping, channel scanning) +3. Standard file format is defined + +--- + +## Impact Assessment + +### Current Impact +- **Low** - Flipper Zero doesn't create multi-frequency files +- **Edge case only** - Manually edited/malformed files + +### User Impact if Implemented +- **Positive:** Handle more file types +- **Negative:** Increased complexity, potential confusion (multiple markers at same GPS) + +### Development Effort +- **Option 1 (Validation):** Low - 1-2 hours +- **Option 2 (Multi-capture):** High - 1-2 days +- **Option 3 (Database change):** Very High - 3-5 days + migration + +--- + +## Conclusion + +**Current Status:** System handles **one frequency per .sub file** correctly. + +**Recommendation:** Implement **Option 1** (validation warning) in short-term. + +**Reasoning:** +1. Aligns with Flipper Zero standard (one freq per file) +2. No known use cases for multi-frequency files +3. Minimal development effort +4. Prevents silent data loss +5. Future-proof: can enhance later if needed + +**Action Items:** +- [ ] Add frequency count validation +- [ ] Log warning if multiple frequencies detected +- [ ] Return warning in upload API response +- [ ] Document behavior in API docs +- [ ] Add user-facing note in upload UI + +--- + +**Status:** Analysis complete, awaiting decision on implementation priority.