From be7658b54d6b8317eb6db5e97161a3c5bf5ff292 Mon Sep 17 00:00:00 2001 From: david rice Date: Thu, 9 Apr 2026 09:17:42 +0100 Subject: [PATCH] updates --- __pycache__/rigol_scope.cpython-312.pyc | Bin 7301 -> 8604 bytes analyze_captures.py | 4 +- csv_preprocessor.py | 6 ++- mipi_test.py | 21 +++++------ rigol_scope.py | 47 +++++++++++++++++------- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/__pycache__/rigol_scope.cpython-312.pyc b/__pycache__/rigol_scope.cpython-312.pyc index 955fce61129f98f432d6ef4f4316bd531b9f1587..83fd44c0388df10439c428d04487f75bce90661d 100644 GIT binary patch delta 3009 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 None: client = anthropic.Anthropic() message = client.messages.create( model = CLAUDE_MODEL, - max_tokens = 1024, + max_tokens = 3072, system = SYSTEM_PROMPT, messages = [{"role": "user", "content": prompt}], ) @@ -282,7 +282,7 @@ def main() -> None: client = anthropic.Anthropic() message = client.messages.create( model = CLAUDE_MODEL, - max_tokens = 1024, + max_tokens = 3072, system = SYSTEM_PROMPT, messages = [{"role": "user", "content": prompt}], ) diff --git a/csv_preprocessor.py b/csv_preprocessor.py index 60954d3..77f4242 100644 --- a/csv_preprocessor.py +++ b/csv_preprocessor.py @@ -127,7 +127,11 @@ def _read_csv(path: Path) -> tuple[np.ndarray, np.ndarray]: volts.append(float(row[1])) except ValueError: pass # skip any header row - return np.array(times, dtype=np.float64), np.array(volts, dtype=np.float64) + t = np.array(times, dtype=np.float64) + v = np.array(volts, dtype=np.float64) + if len(t) < 2: + raise ValueError(f"Insufficient samples in {path.name} ({len(t)} rows parsed)") + return t, v def _zero_crossings(times: np.ndarray, volts: np.ndarray) -> np.ndarray: diff --git a/mipi_test.py b/mipi_test.py index 892f041..e3eea55 100644 --- a/mipi_test.py +++ b/mipi_test.py @@ -322,19 +322,18 @@ def dual_capture(iteration): else: print(" SKIPPING PASS 3 SAVE.") - # Collect Rigol 1.8 V waveform (Agilent save takes ~5 s, Rigol should be done) + # Collect Rigol 1.8 V waveform. + # The Agilent LP acquire + save takes ~35 s, so the Rigol will have + # long since auto-captured by now. read_waveform_csv() sends :STOP + # before reading to guarantee the acquisition is finalised. if rigol_scope.is_connected(): - print(" PASS 3: WAITING FOR RIGOL 1.8 V CAPTURE...") - if rigol_scope.wait_captured(timeout_s=10.0): - DATA_DIR.mkdir(exist_ok=True) - v18_path = DATA_DIR / f"{ts}_pwr_{iteration:04d}_1v8.csv" - n = rigol_scope.read_waveform_csv(v18_path) - if n: - print(f" SAVED: {v18_path.name} ({n} samples)") - else: - print(" RIGOL: Waveform read returned 0 samples.") + DATA_DIR.mkdir(exist_ok=True) + v18_path = DATA_DIR / f"{ts}_pwr_{iteration:04d}_1v8.csv" + n = rigol_scope.read_waveform_csv(v18_path) + if n: + print(f" SAVED: {v18_path.name} ({n} samples)") else: - print(" RIGOL: Timed out waiting for capture.") + print(" RIGOL: Waveform read failed — check connection and probe.") _restore_hs_config() diff --git a/rigol_scope.py b/rigol_scope.py index 3e02649..313fb44 100644 --- a/rigol_scope.py +++ b/rigol_scope.py @@ -72,7 +72,7 @@ def configure(): rigol.write(":CHANnel1:DISPlay 1") rigol.write(":CHANnel2:DISPlay 0") rigol.write(":CHANnel1:COUPling DC") - rigol.write(":CHANnel1:PROBe 1") + rigol.write(":CHANnel1:PROBe 10") rigol.write(f":CHANnel1:SCALe {V18_SCALE:.3f}") rigol.write(f":CHANnel1:OFFSet {V18_OFFSET:.3f}") rigol.write(f":TIMebase:MAIN:SCALe {V18_TIMEBASE:.2E}") @@ -82,9 +82,11 @@ def configure(): rigol.write(f":TRIGger:EDGe:LEVel {V18_TRIG_LEVEL:.3f}") rigol.write(":TRIGger:SWEep AUTO") # auto: captures even without a droop trigger time.sleep(0.3) + rigol.write(":RUN") # start acquiring immediately after configure + time.sleep(0.2) print(f"[RIGOL] Configured: 1.8 V rail, {int(V18_TIMEBASE*1e6)} µs/div, " - f"trigger <{V18_TRIG_LEVEL} V falling (AUTO sweep)") + f"trigger <{V18_TRIG_LEVEL} V falling (AUTO sweep, running)") # --------------------------------------------------------------------------- @@ -92,8 +94,9 @@ def configure(): # --------------------------------------------------------------------------- def arm(): - """Arm for a single acquisition. Non-blocking — returns immediately.""" - rigol.write(":SINGle") + """Ensure scope is running so it is actively acquiring when the LP event occurs. + The waveform is frozen with :STOP inside read_waveform_csv() at collection time.""" + rigol.write(":RUN") def wait_captured(timeout_s: float = TRIG_TIMEOUT_S) -> bool: @@ -117,32 +120,52 @@ def wait_captured(timeout_s: float = TRIG_TIMEOUT_S) -> bool: def read_waveform_csv(path: Path) -> int: """ Read Ch1 waveform from Rigol over SCPI and write to CSV. - The Rigol returns ASCII voltage values; we reconstruct the time axis - from the waveform preamble. + Sends :STOP first to ensure acquisition is complete before reading — + this is reliable regardless of trigger/status state. Returns the number of samples written, or 0 on error. """ try: - rigol.write(":WAVeform:SOURce CHANnel1") - rigol.write(":WAVeform:FORMat ASCII") - rigol.write(":WAVeform:MODE NORMal") + rigol.write(":STOP") + time.sleep(0.3) + rigol.write(":WAVeform:SOURce CHANnel1") + rigol.write(":WAVeform:FORMat ASC") # Rigol DS1000Z uses ASC not ASCII + time.sleep(0.1) + + except Exception as e: + print(f"[RIGOL] Waveform setup error: {e}") + return 0 + + try: preamble = rigol.ask(":WAVeform:PREamble?").strip().split(",") # [0]=fmt [1]=type [2]=points [3]=count [4]=x_incr [5]=x_orig [6]=x_ref # [7]=y_incr [8]=y_orig [9]=y_ref x_incr = float(preamble[4]) x_orig = float(preamble[5]) x_ref = float(preamble[6]) + except Exception as e: + print(f"[RIGOL] Preamble error: {e}") + return 0 + try: raw = rigol.ask(":WAVeform:DATA?").strip() - # Strip any TMC binary header (#) if present + # Strip TMC binary header (#...) if present if raw.startswith("#"): n_digits = int(raw[1]) raw = raw[2 + n_digits:] vals = [float(v) for v in raw.split(",") if v.strip()] + except Exception as e: + print(f"[RIGOL] Data read error: {e}") + return 0 + if not vals: + print("[RIGOL] No samples parsed — check scope channel and format settings") + return 0 + + try: path.parent.mkdir(exist_ok=True) with open(path, "w", newline="") as f: writer = csv.writer(f) @@ -150,9 +173,7 @@ def read_waveform_csv(path: Path) -> int: for i, v in enumerate(vals): t = x_orig + (i - x_ref) * x_incr writer.writerow([f"{t:.9f}", f"{v:.6f}"]) - return len(vals) - except Exception as e: - print(f"[RIGOL] Waveform read error: {e}") + print(f"[RIGOL] CSV write error: {e}") return 0