diff --git a/__pycache__/csv_preprocessor.cpython-312.pyc b/__pycache__/csv_preprocessor.cpython-312.pyc index f8c4c47..9237a21 100644 Binary files a/__pycache__/csv_preprocessor.cpython-312.pyc and b/__pycache__/csv_preprocessor.cpython-312.pyc differ diff --git a/csv_preprocessor.py b/csv_preprocessor.py index a3bc3db..bf3d254 100644 --- a/csv_preprocessor.py +++ b/csv_preprocessor.py @@ -65,6 +65,15 @@ CLK_LP_LOW_MIN_NS = 300.0 HS_BURST_AMPLITUDE_MIN_MV = 40.0 # mV — below this, no real HS burst is present # Lowered from 50 mV: 48 mV capture (0001) was a false alarm; true flicker (0008) at 34 mV. +# HS oscillation fraction thresholds. +# Measures fraction of post-LP-low window (100 ns margin, 3 µs look-ahead) where rolling_std +# exceeds HS_OSC_STD_V. With dynamic video at 432 Mbps DDR each bit spans ~4.6 ns; transitions +# (~1 ns) fire the 1 ns rolling window ~20% of the time → healthy HS → osc_frac ≈ 0.14–0.22. +# Blanking/control packets carry uniform data → osc_frac ≈ 0.00–0.02 (confirmed NOT flicker). +# Partial or transient HS dropout sits between these bands → suspicious → send to Claude. +HS_OSC_FRACTION_SUSPICIOUS_LO = 0.04 # below this: dead HS — blanking / control (normal) +HS_OSC_FRACTION_SUSPICIOUS_HI = 0.13 # above this: healthy HS; between bands → flag + # Mode A minimum amplitude: LP-11-return edge artifacts produce near-zero amplitude in the # burst window (burst is pure LP-low DC between two LP-11 regions). Require ≥ this to @@ -754,6 +763,7 @@ class LPMetrics: # A capture is flagged when the LP-low plateau is absent or shorter than # FLICKER_LP_LOW_MAX_NS. Normal captures show ~340 ns; flicker shows 0–50 ns. hs_rolling_std_found: bool = False # rolling-std fired in HS window after LP-low ended + hs_osc_fraction: Optional[float] = None # fraction of post-LP-low window (100 ns margin, 3 µs) where rolling_std ≥ HS_OSC_STD_V flicker_suspect: bool = False warnings: list = field(default_factory=list) @@ -894,6 +904,7 @@ def analyze_lp_file(path: Path) -> "LPMetrics": hs_burst_dur_ns = None hs_amplitude_mv = None hs_rolling_std_found = False + hs_osc_fraction = None s_end = None rstd = None @@ -949,8 +960,6 @@ def analyze_lp_file(path: Path) -> "LPMetrics": ) # Did rolling-std fire in the actual HS window (after LP-low ended)? - # With dynamic display content (video), genuine HS keeps rolling-std above - # threshold; absent HS does not. Used to gate Mode B/D false positives. if lp_low_duration_ns is not None: lp_low_end_idx = s_end + int((lp_low_duration_ns + 50.0) * 1e-9 / dt) hs_check_end = min(lp_low_end_idx + int(1000e-9 / dt), len(rstd)) @@ -958,6 +967,14 @@ def analyze_lp_file(path: Path) -> "LPMetrics": hs_rolling_std_found = bool( np.any(rstd[lp_low_end_idx:hs_check_end] >= HS_OSC_STD_V) ) + # hs_osc_fraction: 100 ns margin past LP-low end, look ahead 3 µs + # (lp_low_end_idx already includes 50 ns; add 50 ns more = 100 ns total) + hs_osc_start = lp_low_end_idx + int(50e-9 / dt) + hs_osc_end = min(hs_osc_start + int(3000e-9 / dt), len(rstd)) + if hs_osc_end - hs_osc_start >= int(500e-9 / dt): + hs_osc_fraction = round( + float(np.mean(rstd[hs_osc_start:hs_osc_end] >= HS_OSC_STD_V)), 4 + ) # ── Warnings ───────────────────────────────────────────────────────── warnings = [] @@ -994,7 +1011,7 @@ def analyze_lp_file(path: Path) -> "LPMetrics": f"(TCLK_PREPARE+TCLK_ZERO minimum) — SN65DSI83 may fail to lock CLK lane" ) - # Flicker suspect: three confirmed failure modes on this hardware: + # Flicker suspect: confirmed failure modes on this hardware: # # A) Normal LP-low (~342–380 ns) → bridge misses SoT → returns to LP-11 # Signature: lp11_to_hs fires at real LP-low end (~347 ns), hs_amplitude ≈ 15–30 mV. @@ -1005,18 +1022,18 @@ def analyze_lp_file(path: Path) -> "LPMetrics": # Signature: lp11_to_hs is None (rolling-std < HS_OSC_STD_V throughout 500 ns # lookahead), hs_amplitude < 50 mV, LP-11 returns ~500 ns later. # - # B) Short LP-low (< 200 ns, vs nominal ~342–380 ns) → anomalous SoT timing. - # Flag on LP-low duration alone: any lp_low < 200 ns is outside the normal range - # and warrants investigation regardless of amplitude or rolling-std state. - # Confirmed: capture 0124 (lp_low=108 ns). + # B) (removed — short-LP-low flicker is now caught by Mode F if osc_frac is in the + # suspicious zone; Mode B was causing false positives on blanking packets which + # legitimately have short LP-low and low amplitude due to uniform DC HS data) # # C) No LP-11 detected at all → MIPI link silent or stuck. # + # F) Normal LP-low but partial/transient HS dropout: hs_osc_fraction in suspicious zone + # (HS_OSC_FRACTION_SUSPICIOUS_LO < osc_frac < HS_OSC_FRACTION_SUSPICIOUS_HI). + # Healthy HS: ~0.14–0.22. Blanking/control (normal): ~0.00–0.02. Dropout: 0.04–0.13. + # Confirmed: capture 0105 Apr-23 run (osc_frac=0.079, lp_low=380 ns). + # # Only flag DAT lane (CLK is continuous HS — LP states not expected). - _lp_low_short = ( - lp_low_duration_ns is not None - and lp_low_duration_ns < 200.0 # below this, LP-low is anomalously brief - ) hs_burst_absent = ( hs_amplitude_mv is not None and hs_amplitude_mv < HS_BURST_AMPLITUDE_MIN_MV @@ -1035,8 +1052,6 @@ def analyze_lp_file(path: Path) -> "LPMetrics": # Mode A2: rolling-std never fired — HS absent or amplitude below HS_OSC_STD_V; # weak oscillations are misclassified as LP-low, masking the true HS failure or lp11_to_hs_ns is None - # Mode B: LP-low anomalously short + low amplitude = marginal HS launch. - or _lp_low_short ) ) # Mode C: no LP-11 at all → link silent (but exclude CLK which is always HS) @@ -1045,6 +1060,12 @@ def analyze_lp_file(path: Path) -> "LPMetrics": and not continuous_hs_clk and not lp11_regions ) + mode_f_partial_hs = ( + lp_transition_valid + and lp_low_duration_ns is not None + and hs_osc_fraction is not None + and HS_OSC_FRACTION_SUSPICIOUS_LO < hs_osc_fraction < HS_OSC_FRACTION_SUSPICIOUS_HI + ) flicker_suspect = ( channel == "dat" and ( @@ -1054,7 +1075,7 @@ def analyze_lp_file(path: Path) -> "LPMetrics": and ( lp_low_duration_ns is None or hs_burst_absent - or _lp_low_short + or mode_f_partial_hs ) ) ) @@ -1077,6 +1098,7 @@ def analyze_lp_file(path: Path) -> "LPMetrics": lp_transition_valid = lp_transition_valid, clk_lp_startup_ok = clk_lp_startup_ok, hs_rolling_std_found = hs_rolling_std_found, + hs_osc_fraction = hs_osc_fraction, flicker_suspect = flicker_suspect, warnings = warnings, )