# Kingst VIS 压缩边沿流分析: 数字波形按占空比签名自动分段, 输出行结构/占空比/消隐明细 # 用法: python la_wave_analyzer.py # CSV 列: Time[s], 通道A, 通道B (时间分辨率 10ns; 默认抓 B 通道 D0/D1) # 判读基线见 doc/architecture/rgb_bit_swap_test.md §1.1 import csv, statistics, time, sys PATH = sys.argv[1] if len(sys.argv) > 1 else \ r"E:\Hulk-Coding\LT8619C_DLPC3421_HDMI\tools\wave\B-D0-D7.csv" CLK_NS = 25.0 # 40MHz 像素时钟 t0 = time.time() rows = [] with open(PATH, encoding='utf-8-sig', errors='replace') as f: r = csv.reader(f) next(r) # header for row in r: if len(row) < 3: continue rows.append((float(row[0]) * 1e9, int(row[1]), int(row[2]))) # ns n = len(rows) t_start, t_stop = rows[0][0], rows[-1][0] + 1000.0 # 末行状态延续按1us估 print(f"rows={n} span={t_stop - t_start / 1e9:.9f}s") # ---- 按行区间累计高低时间, 50ms 窗口计算占空比 ---- W = 50_000_000.0 # ns win = {'t0': t_start, 'hi0': 0.0, 'hi1': 0.0} wins = [] for i in range(n - 1): t, d0, d1 = rows[i] dt = min(rows[i + 1][0], t_stop) - t if dt <= 0: continue win['hi0'] += dt if d0 else 0.0 win['hi1'] += dt if d1 else 0.0 if rows[i + 1][0] - win['t0'] >= W: span = rows[i + 1][0] - win['t0'] wins.append((win['t0'], win['hi0'] / span, win['hi1'] / span)) win = {'t0': rows[i + 1][0], 'hi0': 0.0, 'hi1': 0.0} # ---- 窗口分类: 依 (d0duty, d1duty) 签名 ---- def classify(a, b): hi = lambda x: x > 0.5 if hi(a) and hi(b): return 'FF' if hi(a) and not hi(b): return '55' if not hi(a) and hi(b): return 'AA' return '00' segs = [] for t, a, b in wins: c = classify(a, b) if segs and segs[-1][0] == c: segs[-1][1].append((t, a, b)) else: segs.append((c, [(t, a, b)])) print(f"\n{'段':<4}{'签名':<6}{'时长(s)':<10}{'D0占空':<10}{'D1占空':<10}") for i, (c, ws) in enumerate(segs): dur = (ws[-1][0] - ws[0][0]) / 1e9 a = statistics.mean(x[1] for x in ws) b = statistics.mean(x[2] for x in ws) print(f"{i:<6}{c:<8}{dur:<12.2f}{a:<12.1%}{b:<12.1%}") # ---- 每段: 行周期 + 有效区宽度(长高电平段) ---- print(f"\n按段结构分析 (时钟数 @40MHz):") for i, (c, ws) in enumerate(segs): seg_t0 = ws[0][0] - W / 2 seg_t1 = ws[-1][0] + W / 2 ch = 0 if c in ('FF', '55') else 1 # 选有效区为高的通道 # 找长高电平段(>=15us)及其起点 starts, widths = [], [] for j in range(n - 1): t, d0, d1 = rows[j] if t < seg_t0 or t > seg_t1: continue lvl = d0 if ch == 0 else d1 if lvl and rows[j + 1][0] - t >= 15_000.0: starts.append(t) widths.append(rows[j + 1][0] - t) if len(starts) < 3: print(f" 段{i} [{c}]: 线段不足({len(starts)}), 跳过") continue periods = [b - a for a, b in zip(starts, starts[1:]) if 20_000 < b - a < 35_000] mp = statistics.median(periods) mw = statistics.median(widths) print(f" 段{i} [{c}]: 行数~{len(starts)} 行周期={mp:.0f}ns={mp/CLK_NS:.1f}clk " f"有效高={mw:.0f}ns={mw/CLK_NS:.1f}clk 消隐={(mp-mw)/CLK_NS:.1f}clk") # ---- 每段抽一行: 消隐区的边沿明细 ---- print(f"\n消隐区边沿明细(每段第一行, ns 相对行起点, clk=时钟数):") for i, (c, ws) in enumerate(segs): seg_t0 = ws[0][0] - W / 2 ch = 0 if c in ('FF', '55') else 1 line_start = None for j in range(n - 1): t, d0, d1 = rows[j] if t < seg_t0: continue lvl = d0 if ch == 0 else d1 if lvl and rows[j + 1][0] - t >= 15_000.0: line_start = t break if line_start is None: continue print(f" 段{i} [{c}] 行起点+有效区之后:") j0 = next(k for k in range(n - 1) if rows[k][0] >= line_start) out = [] for k in range(j0, min(j0 + 30, n - 1)): t, d0, d1 = rows[k] dt = rows[k + 1][0] - t rel = t - line_start out.append(f" +{rel/1000:8.2f}us D0={d0} D1={d1} 持续{dt/CLK_NS:6.1f}clk") if rel > 30_000: break print('\n'.join(out[:18])) print(f"\n耗时 {time.time()-t0:.1f}s")