92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Rebuild the folded CLK+ eye diagram for the v2 report.
|
||
|
||
The original plot_eye() in make_flicker_report.py looks for an LP-11 → HS
|
||
transition (CLK+ > 0.5 V then falling). In session 20260515_135656 the
|
||
captures landed entirely in HS state (CLK+ stays in ~0.07–0.36 V), so the
|
||
edge detector returned None for every segment and the plot rendered with
|
||
zero overlays.
|
||
|
||
This script auto-detects the common mode per segment and folds around
|
||
every crossing of common mode — which is what the eye really wants.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
import numpy as np
|
||
import matplotlib
|
||
matplotlib.use("Agg")
|
||
import matplotlib.pyplot as plt
|
||
|
||
ARRIVE_PURPLE = "#5f016f"
|
||
ARRIVE_PURPLE_DARK = "#3e0049"
|
||
|
||
SESSION = Path("data/flicker_bursts/20260515_135656")
|
||
BURST = 15
|
||
N_SEGS = 20
|
||
UI_NS = 2.315
|
||
OUT = Path("flicker_investigation_report_v2_plots/mipi_typical_eye.png")
|
||
|
||
|
||
def fold_segment(t_ns: np.ndarray, v_mv: np.ndarray, ui_ns: float,
|
||
ax: plt.Axes) -> int:
|
||
"""Overlay every common-mode crossing in this segment as a ±1 UI slice."""
|
||
cm = float(np.median(v_mv))
|
||
above = (v_mv > cm).astype(int)
|
||
edges = np.where(np.diff(above) != 0)[0]
|
||
n = 0
|
||
for e in edges:
|
||
t_cross = t_ns[e]
|
||
mask = (t_ns >= t_cross - ui_ns) & (t_ns <= t_ns[e] + ui_ns)
|
||
if mask.sum() < 3:
|
||
continue
|
||
ax.plot(t_ns[mask] - t_cross, v_mv[mask] - cm,
|
||
color=ARRIVE_PURPLE, linewidth=0.4, alpha=0.18)
|
||
n += 1
|
||
return n
|
||
|
||
|
||
def main() -> None:
|
||
clk_files = sorted(SESSION.glob(f"burst_{BURST:04d}_*_mipi_seg*_clk.csv"))
|
||
if not clk_files:
|
||
raise SystemExit(f"no CLK files for burst {BURST} in {SESSION}")
|
||
|
||
fig, ax = plt.subplots(figsize=(8.5, 3.0))
|
||
total_segs = 0
|
||
total_xings = 0
|
||
for f in clk_files[:N_SEGS]:
|
||
arr = np.genfromtxt(f, delimiter=",")
|
||
t_ns = arr[:, 0] * 1e9
|
||
v_mv = arr[:, 1] * 1000
|
||
n = fold_segment(t_ns, v_mv, UI_NS, ax)
|
||
if n:
|
||
total_segs += 1
|
||
total_xings += n
|
||
|
||
ax.axhline(0, color="grey", linewidth=0.4, alpha=0.5)
|
||
ax.axvline(-UI_NS / 2, color="grey", linestyle=":", linewidth=0.4, alpha=0.5)
|
||
ax.axvline(+UI_NS / 2, color="grey", linestyle=":", linewidth=0.4, alpha=0.5)
|
||
ax.set_xlabel(f"time (ns, folded on UI = {UI_NS} ns)")
|
||
ax.set_ylabel("CLK+ − common-mode (mV)")
|
||
ax.set_xlim(-UI_NS, UI_NS)
|
||
ax.set_title(
|
||
f"CLK+ folded eye ({total_segs} segments × ~{total_xings // max(total_segs,1)} "
|
||
f"crossings overlaid on a 2-UI window, burst {BURST})",
|
||
color=ARRIVE_PURPLE, fontsize=11)
|
||
ax.grid(True, alpha=0.25)
|
||
ax.text(0.01, 0.95,
|
||
f"{total_segs} segments × ~{total_xings // max(total_segs,1)} cycles overlaid",
|
||
transform=ax.transAxes, fontsize=9, color=ARRIVE_PURPLE_DARK,
|
||
bbox=dict(facecolor="white", edgecolor="none", alpha=0.85), va="top")
|
||
|
||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||
fig.tight_layout()
|
||
fig.savefig(OUT, dpi=140)
|
||
print(f"wrote {OUT} ({total_segs} segments, {total_xings} crossings)")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|