This commit is contained in:
david rice
2026-04-24 15:24:27 +01:00
parent f8d7727ff7
commit bc1d5bdc30
5 changed files with 119 additions and 25 deletions

View File

@@ -104,23 +104,51 @@ def find_clock_edges(t_clk, v_clk, threshold=0.0):
def find_hs_start(t_dat, v_dat, t_clk=None, window_ns=500.0):
"""
Find the start of the main HS burst in the DAT trace.
Find the start of the post-LP HS burst in the DAT trace.
The proto capture often starts mid-HS (previous packet), so we:
1. Find the LP quiet region (both LP-11 and LP-low have low differential std)
2. Find the first sustained oscillation AFTER that quiet region
For LP-triggered captures (trigger = DAT D+ falling at LP-11→LP-01 transition):
- CLK is in continuous HS mode throughout (215 MHz running)
- DAT shows LP-01 (diff ≈ -1 V) near t=0, preceded by HS data from the
previous line and possibly an earlier LP-01 at the start of the capture
- LP-00 follows LP-01 briefly (~50-200 ns), then the new HS burst begins
- To avoid the LP-01 from the previous line (at capture start), search
from N//4 onwards — the trigger LP-01 is at the capture midpoint (t=0)
Returns: index into t_dat of approximate HS burst start, or None.
Returns index into t_dat just past LP-00, ready for CLK-edge sampling.
Falls back to original std-based method for HS-triggered captures.
"""
dt_ns = float(np.median(np.diff(t_dat))) * 1e9
win = max(1, int(1.0 / dt_ns)) # 1 ns rolling window
min_run = max(5, int(5.0 / dt_ns)) # at least 5 ns continuous
dt_ns = float(np.median(np.diff(t_dat))) * 1e9
N = len(v_dat)
rstd = np.array([v_dat[max(0, i - win):i + 1].std() for i in range(len(v_dat))])
OSC_THRESH = 0.04 # 40 mV — HS oscillation
QUIET_THRESH = 0.02 # 20 mV — LP quiet (LP-11 differential ≈ 0, low std)
# --- LP-triggered path ---
# LP-01: D+ = 0 V, D- = high → diff strongly negative (< -0.5 V for ≥ 20 ns)
LP01_THRESH = -0.5
min_lp01 = max(2, int(20.0 / dt_ns))
search_from = N // 4 # skip any LP-01 fragment at capture start
run = 0
lp01_end = None
for i in range(search_from, N):
if v_dat[i] < LP01_THRESH:
run += 1
else:
if run >= min_lp01:
lp01_end = i
break
run = 0
if lp01_end is not None:
# Skip 200 ns past LP-01 end to clear LP-00, then hand off to bit decoder
skip = max(1, int(200.0 / dt_ns))
return min(lp01_end + skip, N - 1)
# --- Fallback: HS-triggered captures (original rolling-std method) ---
win = max(1, int(1.0 / dt_ns))
min_run = max(5, int(5.0 / dt_ns))
rstd = np.array([v_dat[max(0, i - win):i + 1].std() for i in range(N)])
OSC_THRESH = 0.04
QUIET_THRESH = 0.02
# Step 1: find a quiet (LP) region of at least 200 ns
quiet_min_run = max(5, int(200.0 / dt_ns))
quiet_end = None
run_len = 0
@@ -133,9 +161,8 @@ def find_hs_start(t_dat, v_dat, t_clk=None, window_ns=500.0):
run_len = 0
if quiet_end is None:
return None # no LP region found
return None
# Step 2: find first sustained oscillation after the LP region
run_start = None
run_len = 0
for i in range(quiet_end, len(rstd)):
@@ -273,7 +300,7 @@ def decode_capture(cap_num: int, data_dir: Path, verbose: bool = True):
dt_ns = float(np.median(np.diff(t_dat))) * 1e9
if verbose:
print(f" Window: {t_dat[0]*1e6:.2f}..{t_dat[-1]*1e6:.2f} µs ({len(t_dat)} samples, {dt_ns:.0f} ps/sample)")
print(f" Window: {t_dat[0]*1e6:.2f}..{t_dat[-1]*1e6:.2f} µs ({len(t_dat)} samples, {dt_ns*1000:.0f} ps/sample)")
# Find HS burst start
hs_start_idx = find_hs_start(t_dat, v_dat)
@@ -299,18 +326,33 @@ def decode_capture(cap_num: int, data_dir: Path, verbose: bool = True):
print(" ERROR: Too few bits decoded")
return None
raw_bytes = bits_to_bytes(bits)
# Try all 8 bit-phase offsets to handle framing uncertainty from LP-00 CLK edges.
# LP-00 CLK edges before HS starts produce garbage bits; the correct phase is
# the one where 0xB8 appears earliest in the byte stream.
raw_bytes = None
sync_idx = None
best_phase = 0
best_sync = len(bits) # sentinel: "not found"
for phase in range(8):
rb = bits_to_bytes(bits[phase:])
si = find_sync_byte(rb)
if si is not None and si < best_sync:
best_sync = si
best_phase = phase
raw_bytes = rb
sync_idx = si
if raw_bytes is None:
raw_bytes = bits_to_bytes(bits)
# Find sync byte alignment
sync_idx = find_sync_byte(raw_bytes)
if sync_idx is None:
if verbose:
print(f" WARNING: HS sync byte (0x{HS_SYNC_BYTE:02X}) not found — using raw byte 0 as start")
print(f" WARNING: HS sync byte (0x{HS_SYNC_BYTE:02X}) not found in any bit phase — using raw byte 0")
sync_idx = 0
else:
if verbose:
t_sync = raw_bytes[sync_idx][0]
print(f" HS sync byte found at byte {sync_idx} (t={t_sync:.0f} ns)")
print(f" HS sync byte found at byte {sync_idx} (t={t_sync:.0f} ns, bit phase={best_phase})")
# Data bytes after sync
data_bytes = raw_bytes[sync_idx + 1:] # skip the sync byte itself