feat(parser): route BinRAW captures through RAW identification path

Reconstruct a signed pulse train from BinRAW Data_RAW demodulated bits
(run-length encoded at TE-µs steps) so BinRAW files populate raw_data and
flow through the existing RAW identification pipeline. Previously 0/120
BinRAW files yielded a predicted_category; now 118/120 categorize via the
live SignatureMatcher engine. RAW code path is unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-19 20:19:20 -07:00
parent 3aadc09e13
commit 4396c745a8
+40
View File
@@ -182,6 +182,20 @@ class SubFileParser:
data_hex = self.fields['Data_RAW'].replace(' ', '')
metadata.bin_raw_data = bytes.fromhex(data_hex)
# Reconstruct a signed pulse train (µs) from the demodulated bit
# stream so BinRAW captures flow through the same RAW identification
# path as RAW_Data files. Data_RAW is the signal sampled at TE-µs
# steps: a run of N identical bits is one pulse of N*TE µs (+high on
# 1s, -low on 0s). This populates raw_data, which makes the
# has_raw_data property true.
n_bits = metadata.bin_raw_bit or metadata.bit_length
if metadata.bin_raw_data and metadata.bin_raw_te and n_bits:
pulses = self._binraw_to_pulses(
metadata.bin_raw_data, n_bits, metadata.bin_raw_te)
if pulses:
metadata.raw_data = pulses
metadata.pulse_count = len(pulses)
# Calculate duration
if metadata.bit_length and metadata.bin_raw_te:
metadata.duration_ms = (metadata.bit_length * metadata.bin_raw_te) / 1000.0
@@ -190,6 +204,32 @@ class SubFileParser:
self.errors.append(f"Error parsing BinRAW fields: {e}")
logger.warning(f"Parse error: {e}")
@staticmethod
def _binraw_to_pulses(data: bytes, n_bits: int, te: int) -> List[int]:
"""Expand a BinRAW bit stream into a signed timing array (µs).
Bits are read MSB-first, truncated to ``n_bits``, then run-length
encoded: each run of ``k`` equal bits becomes ``k*te`` µs, signed
positive for 1s (pulse) and negative for 0s (gap).
"""
bits = []
for byte in data:
for i in range(7, -1, -1):
bits.append((byte >> i) & 1)
bits = bits[:n_bits]
if not bits:
return []
pulses: List[int] = []
current, run = bits[0], 1
for b in bits[1:]:
if b == current:
run += 1
else:
pulses.append(run * te * (1 if current else -1))
current, run = b, 1
pulses.append(run * te * (1 if current else -1))
return pulses
def parse_sub_file(file_path: str) -> SignalMetadata:
"""