From 15dc295ae16f4d6d717631b575bc1556e087aedd Mon Sep 17 00:00:00 2001 From: david rice Date: Wed, 8 Apr 2026 14:19:31 +0100 Subject: [PATCH] updated ai --- __pycache__/analyze_captures.cpython-312.pyc | Bin 0 -> 11444 bytes __pycache__/csv_preprocessor.cpython-312.pyc | Bin 27353 -> 28399 bytes __pycache__/mipi_test.cpython-312.pyc | Bin 0 -> 16724 bytes analyze_captures.py | 90 ++++++++++++- csv_preprocessor.py | 132 ++++++++++++------- mipi_test.py | 12 +- 6 files changed, 175 insertions(+), 59 deletions(-) create mode 100644 __pycache__/analyze_captures.cpython-312.pyc create mode 100644 __pycache__/mipi_test.cpython-312.pyc diff --git a/__pycache__/analyze_captures.cpython-312.pyc b/__pycache__/analyze_captures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d915d7f491bba7bf905fbe3a139eb70cb07e13ad GIT binary patch literal 11444 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 V$4usz=DN=)=C4q4Q}r5S{s(pj>vRAB literal 0 HcmV?d00001 diff --git a/__pycache__/csv_preprocessor.cpython-312.pyc b/__pycache__/csv_preprocessor.cpython-312.pyc index 3bb0973dede6636be641df9cc740c3e09ba5997a..fff6d4bfa83ab4f25d91552cacf2e1d90ea860cc 100644 GIT binary patch delta 4155 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 delta 3240 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 R#&#^_*IXYx@z)bv^xx#rR)7Ei diff --git a/__pycache__/mipi_test.cpython-312.pyc b/__pycache__/mipi_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ff00f6abf6c19a32d342e6bde09681beaead632 GIT binary patch literal 16724 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 str: # Main # --------------------------------------------------------------------------- +def run_analysis(last: int = 10) -> None: + """ + Called by mgmt_worker after each file transfer. + Analyses the most recent `last` captures and prints the Claude report. + """ + groups = group_captures(DATA_DIR) + if not groups: + print("[ANALYSIS] No captures found.") + return + + keys = sorted(groups.keys())[-last:] + print(f"\n[ANALYSIS] Processing {len(keys)} most-recent capture(s)...") + + all_summaries: list[str] = [] + for ts, num in keys: + summary_text, _ = process_capture(ts, num, groups[(ts, num)]) + all_summaries.append(summary_text) + + prompt = build_prompt(all_summaries) + print(f"[ANALYSIS] Sending {len(prompt):,} chars to {CLAUDE_MODEL}...") + + client = anthropic.Anthropic() + message = client.messages.create( + model = CLAUDE_MODEL, + max_tokens = 1024, + system = SYSTEM_PROMPT, + messages = [{"role": "user", "content": prompt}], + ) + analysis = message.content[0].text + token_line = f"Tokens: {message.usage.input_tokens} in / {message.usage.output_tokens} out" + + # ── Console ─────────────────────────────────────────────────────────── + separator = "=" * 60 + print(f"\n{separator}") + print("CLAUDE ANALYSIS") + print(separator) + print(analysis) + print(f"({token_line})") + print(separator + "\n") + + # ── Append to log file ──────────────────────────────────────────────── + ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + with open(ANALYSIS_LOG, "a", encoding="utf-8") as f: + f.write(f"\n{'='*60}\n{ts} — captures {keys[0][1]:04d}–{keys[-1][1]:04d}\n{'='*60}\n") + f.write(analysis) + f.write(f"\n({token_line})\n") + print(f"[ANALYSIS] Report appended to {ANALYSIS_LOG}") + + # ── Send to display ─────────────────────────────────────────────────── + try: + requests.post(DISPLAY_URL, json={"text": analysis}, timeout=5) + print("[ANALYSIS] Report sent to display.") + except Exception as e: + print(f"[ANALYSIS] Display send failed: {e}") + + def main() -> None: parser = argparse.ArgumentParser(description="Analyse MIPI CSV captures with Claude") parser.add_argument("--last", type=int, default=None, metavar="N", @@ -147,14 +207,30 @@ def main() -> None: system = SYSTEM_PROMPT, messages = [{"role": "user", "content": prompt}], ) - analysis = message.content[0].text + analysis = message.content[0].text + token_line = f"Tokens: {message.usage.input_tokens} in / {message.usage.output_tokens} out" + separator = "=" * 60 + ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - print("=" * 60) - print("CLAUDE ANALYSIS") - print("=" * 60) + # Console + print(f"\n{separator}\nCLAUDE ANALYSIS\n{separator}") print(analysis) - print() - print(f"(Tokens used: {message.usage.input_tokens} in / {message.usage.output_tokens} out)") + print(f"({token_line})") + print(separator) + + # Log file + with open(ANALYSIS_LOG, "a", encoding="utf-8") as f: + f.write(f"\n{separator}\n{ts}\n{separator}\n") + f.write(analysis) + f.write(f"\n({token_line})\n") + print(f"\nReport appended to {ANALYSIS_LOG}") + + # Display + try: + requests.post(DISPLAY_URL, json={"text": analysis}, timeout=5) + print("Report sent to display.") + except Exception as e: + print(f"Display send failed: {e}") if __name__ == "__main__": diff --git a/csv_preprocessor.py b/csv_preprocessor.py index ee2de8b..3511769 100644 --- a/csv_preprocessor.py +++ b/csv_preprocessor.py @@ -32,7 +32,9 @@ TRANSITION_BAND_MV = 50.0 # |Vdiff| < this is considered a transition, not sett # MIPI D-PHY LP state thresholds (single-ended voltage, after probe compensation) LP11_HIGH_V = 0.8 # V — single-ended voltage above this → LP-11 (both pins high ~1.2 V) -LP_LOW_V = 0.05 # V — single-ended voltage below this → LP-00 or LP-01 pin low +LP_LOW_V = 0.25 # V — single-ended voltage below this → LP-00 or LP-01 pin low +# Note: probe loading can shift LP-low from true 0 V to ~100 mV; 0.25 V clears that offset +# The rolling-std gate (HS_OSC_STD_V) prevents HS minima near 0 V being called LP-low. LP11_SPEC_MIN_V = 1.0 # V — LP-11 minimum voltage spec LP11_SPEC_MAX_V = 1.45 # V — LP-11 maximum voltage spec LP_LOW_DUR_MIN_NS = 50.0 # ns — minimum LP-low duration per D-PHY spec (LP-01 + LP-00 combined) @@ -353,8 +355,9 @@ class LPMetrics: lp11_voltage_v: Optional[float] # mean level in LP-11 region (spec 1.0–1.45 V) lp11_duration_us: Optional[float] # total LP-11 time in capture (pre-trigger) - # LP-low (LP-01 + LP-00 combined — CLK+ = 0 V in both states) - lp_low_duration_ns: Optional[float] # duration between LP-11 end and HS start + # LP exit: gap between LP-11 falling edge and HS oscillation onset + lp11_to_hs_ns: Optional[float] # total LP exit time LP-11→HS (includes LP-01+LP-00) + lp_low_duration_ns: Optional[float] # LP-low plateau duration if a clear plateau was seen # HS bursts detected within the window n_hs_bursts: int @@ -378,12 +381,14 @@ class LPMetrics: ) if self.lp11_duration_us is not None: lines.append(f" LP-11 duration : {self.lp11_duration_us:.2f} µs") - if self.lp_low_duration_ns is not None: - ok_lp = self.lp_low_duration_ns >= LP_LOW_DUR_MIN_NS + if self.lp11_to_hs_ns is not None: + ok_exit = self.lp11_to_hs_ns >= LP_LOW_DUR_MIN_NS lines.append( - f" LP-low duration : {self.lp_low_duration_ns:.0f} ns " - f"(spec ≥{LP_LOW_DUR_MIN_NS:.0f} ns) {ok(ok_lp)}" + f" LP exit → HS : {self.lp11_to_hs_ns:.0f} ns " + f"(spec ≥{LP_LOW_DUR_MIN_NS:.0f} ns) {ok(ok_exit)}" ) + if self.lp_low_duration_ns is not None: + lines.append(f" LP-low plateau : {self.lp_low_duration_ns:.0f} ns") lines.append( f" LP→HS sequence : {'valid ✓' if self.lp_transition_valid else 'NOT DETECTED ✗'}" ) @@ -442,17 +447,11 @@ def analyze_lp_file(path: Path) -> "LPMetrics": sample_rate = 1.0 / dt duration_us = (float(times[-1]) - float(times[0])) * 1e6 - # ── State classification ────────────────────────────────────────────── - # Rolling std over ~1 ns window to detect HS oscillation - window = max(10, int(1e-9 / dt)) - rstd = _rolling_std(volts, window) - - lp11_mask = volts > LP11_HIGH_V - lp_low_mask = (volts < LP_LOW_V) & (rstd < HS_OSC_STD_V) - hs_mask = (~lp11_mask) & (~lp_low_mask) & (rstd >= HS_OSC_STD_V) - - # ── LP-11 region ────────────────────────────────────────────────────── + # ── LP-11 detection ─────────────────────────────────────────────────── + # LP-11 is reliable: voltage is clearly above LP11_HIGH_V (0.8 V). + lp11_mask = volts > LP11_HIGH_V lp11_regions = _find_contiguous_regions(lp11_mask, min_samples=10) + lp11_voltage_v = None lp11_duration_us = None if lp11_regions: @@ -461,42 +460,71 @@ def analyze_lp_file(path: Path) -> "LPMetrics": lp11_duration_us = round( sum((times[e] - times[s]) for s, e in lp11_regions) * 1e6, 3) - # ── LP-low region (between last LP-11 and first HS) ─────────────────── + # ── HS burst detection ──────────────────────────────────────────────── + # On DAT0+ with a uniform-colour display, HS data can look DC (no bit + # transitions), making oscillation-based HS detection unreliable. + # Instead: every non-LP-11 gap between LP-11 regions is treated as an + # HS burst. The first gap starts at the end of the first LP-11 region; + # subsequent gaps are between consecutive LP-11 regions. + lp11_to_hs_ns = None lp_low_duration_ns = None lp_transition_valid = False - - lp_low_regions = _find_contiguous_regions(lp_low_mask, min_samples=5) - hs_regions = _find_contiguous_regions(hs_mask, min_samples=20) - - if lp11_regions and lp_low_regions and hs_regions: - # Find the LP-low gap that sits between the last LP-11 and the first HS burst - last_lp11_end = lp11_regions[-1][1] - first_hs_start = hs_regions[0][0] - bridging = [(s, e) for s, e in lp_low_regions - if s >= last_lp11_end and e <= first_hs_start + int(100e-9 / dt)] - if bridging: - s0, e0 = bridging[0][0], bridging[-1][1] - lp_low_duration_ns = round((times[e0] - times[s0]) * 1e9, 1) - lp_transition_valid = True - - # ── HS burst metrics ────────────────────────────────────────────────── - n_hs_bursts = len(hs_regions) + n_hs_bursts = 0 hs_burst_dur_ns = None hs_amplitude_mv = None - if hs_regions: - durations = [(times[e] - times[s]) * 1e9 for s, e in hs_regions] - hs_burst_dur_ns = round(float(np.mean(durations)), 1) + if len(lp11_regions) >= 1: + # Measure LP-11 → HS exit gap (LP-01 + LP-00 combined) using a rolling + # std: the brief exit transition is the first period of measurable + # oscillation (rolling std > threshold) after LP-11 ends. + window = max(10, int(1e-9 / dt)) + rstd = _rolling_std(volts, window) - # HS single-ended amplitude: peak-to-peak / 2 of the oscillating signal - hs_volts = np.concatenate([volts[s:e] for s, e in hs_regions]) - hs_amplitude_mv = round( - (float(np.percentile(hs_volts, 95)) - float(np.percentile(hs_volts, 5))) / 2 * 1000, 1 - ) + hs_bursts = [] + for i, (lp11_s, lp11_e) in enumerate(lp11_regions): + # Burst ends at start of next LP-11, or at window end + burst_end = lp11_regions[i + 1][0] if i + 1 < len(lp11_regions) else len(times) - 1 + burst_dur_ns = round((times[burst_end] - times[lp11_e]) * 1e9, 1) + hs_bursts.append((lp11_e, burst_end, burst_dur_ns)) + + if hs_bursts: + n_hs_bursts = len(hs_bursts) + hs_burst_dur_ns = round(float(np.mean([d for _, _, d in hs_bursts])), 1) + lp_transition_valid = True + + # LP exit gap: find first rolling-std > threshold after LP-11 ends + s_end = lp11_regions[0][1] + lookahead = min(s_end + int(500e-9 / dt), len(times) - 1) + high_std_idx = np.where(rstd[s_end:lookahead] >= HS_OSC_STD_V)[0] + if len(high_std_idx): + lp11_to_hs_ns = round((times[s_end + high_std_idx[0]] - times[s_end]) * 1e9, 1) + + # LP-low plateau: look for a contiguous region in the exit window + # where voltage < LP_LOW_V and std is low (true LP-01/LP-00 plateau) + lp_low_mask = (volts < LP_LOW_V) & (rstd < HS_OSC_STD_V) + lp_low_regions = _find_contiguous_regions(lp_low_mask, min_samples=5) + exit_window = int(1e-6 / dt) + for lplow_s, lplow_e in lp_low_regions: + if s_end <= lplow_s <= s_end + exit_window: + lp_low_duration_ns = round( + (times[lplow_e] - times[lplow_s]) * 1e9, 1) + break + + # HS single-ended amplitude from the first burst (where data may vary) + if hs_bursts: + s, e, _ = hs_bursts[0] + burst_volts = volts[s:e] + hs_amplitude_mv = round( + (float(np.percentile(burst_volts, 95)) - + float(np.percentile(burst_volts, 5))) / 2 * 1000, 1 + ) # ── Warnings ───────────────────────────────────────────────────────── warnings = [] - if not lp11_regions: + continuous_hs_clk = (not lp11_regions) and (channel == "clk") and (float(volts.max()) < LP11_HIGH_V) + if continuous_hs_clk: + warnings.append("CLK lane is in continuous HS mode — LP states not expected on CLK") + elif not lp11_regions: warnings.append("No LP-11 state detected in capture window") elif lp11_voltage_v is not None: if lp11_voltage_v < LP11_SPEC_MIN_V: @@ -504,16 +532,17 @@ def analyze_lp_file(path: Path) -> "LPMetrics": if lp11_voltage_v > LP11_SPEC_MAX_V: warnings.append(f"LP-11 voltage {lp11_voltage_v:.3f} V above spec max {LP11_SPEC_MAX_V} V") - if lp_low_duration_ns is not None and lp_low_duration_ns < LP_LOW_DUR_MIN_NS: + if lp11_to_hs_ns is not None and lp11_to_hs_ns < LP_LOW_DUR_MIN_NS: warnings.append( - f"LP-low duration {lp_low_duration_ns:.0f} ns below spec min {LP_LOW_DUR_MIN_NS:.0f} ns" + f"LP exit duration {lp11_to_hs_ns:.0f} ns below spec min {LP_LOW_DUR_MIN_NS:.0f} ns " + f"— LP-01/LP-00 states may be absent or too brief" ) - if not lp_transition_valid: - warnings.append("LP-11 → LP-low → HS transition sequence not detected") - - if n_hs_bursts == 0: - warnings.append("No HS bursts detected after LP transition") + if not continuous_hs_clk: + if not lp_transition_valid: + warnings.append("LP-11 → LP-low → HS transition sequence not detected") + if n_hs_bursts == 0: + warnings.append("No HS bursts detected after LP transition") return LPMetrics( timestamp = timestamp, @@ -524,6 +553,7 @@ def analyze_lp_file(path: Path) -> "LPMetrics": n_samples = len(times), lp11_voltage_v = lp11_voltage_v, lp11_duration_us = lp11_duration_us, + lp11_to_hs_ns = lp11_to_hs_ns, lp_low_duration_ns = lp_low_duration_ns, n_hs_bursts = n_hs_bursts, hs_burst_dur_ns = hs_burst_dur_ns, diff --git a/mipi_test.py b/mipi_test.py index c58dd99..4275ffb 100644 --- a/mipi_test.py +++ b/mipi_test.py @@ -14,6 +14,7 @@ import requests import threading from datetime import datetime import ai_mgmt +import analyze_captures # --- Configuration --- URL = "http://192.168.45.8:5000/display" @@ -251,6 +252,9 @@ def _configure_for_lp(): scope.write(f":CHANnel{ch}:SCALe {LP_V_SCALE:.3f}") scope.write(f":CHANnel{ch}:OFFSet {LP_V_OFFSET:.3f}") time.sleep(0.05) + # Trigger on DAT0+ (Ch3) — CLK is continuous HS so it never reaches LP-11 (1.2 V). + # DAT0 has LP-11 between bursts, so Ch3 falling at 0.6 V catches LP-11 → LP-01. + scope.write(":TRIGger:EDGE:SOURce CHANnel3") scope.write(":TRIGger:EDGE:SLOPe NEGative") scope.write(f":TRIGger:EDGE:LEVel {LP_TRIG_LEVEL:.3f}") time.sleep(0.1) @@ -262,6 +266,7 @@ def _restore_hs_config(): scope.write(f":CHANnel{ch}:SCALe 0.1") scope.write(f":CHANnel{ch}:OFFSet 0.0") time.sleep(0.05) + scope.write(":TRIGger:EDGE:SOURce CHANnel1") scope.write(":TRIGger:EDGE:SLOPe POSitive") scope.write(f":TRIGger:EDGE:LEVel 0.05") time.sleep(0.1) @@ -331,8 +336,13 @@ def mgmt_worker(): try: copied, failed = ai_mgmt.transfer_csv_files() print(f"[MGMT] TRANSFERRED {copied} FILE(S) TO DATA FOLDER. {failed} FAILED.") + if copied > 0: + try: + analyze_captures.run_analysis() + except Exception as e: + print(f"[MGMT] ANALYSIS ERROR: {e}") except Exception as e: - print(f"[MGMT] ERROR: {e}") + print(f"[MGMT] TRANSFER ERROR: {e}") finally: resume_event.set() print("[MGMT] RESUMING TEST.\n")