#!/usr/bin/env python3 # -*- coding: utf-8 -*- """PD132T ↔ PD136HA 灵敏度档位对齐图(现场调档照着贴)—— 纯 stdlib 手写 SVG 横轴 = Δf/f(进入/离开阈值的相对频偏,%);每档画 进入(实心)+离开(浅色) 两段 偏差 = 同档位号一对一映射的 (H−T)/T;最佳匹配 = 最近的进入阈值 转 PNG:chromium --headless=new --disable-gpu --no-sandbox \ --screenshot=docs/img/sens-alignment-pd132t-pd136ha.png --window-size=1500,880 \ file://$PWD/docs/img/sens-alignment-pd132t-pd136ha.svg """ W, H = 1500, 1060 FONT = "'Noto Serif CJK SC','Noto Sans CJK SC',sans-serif" K = 65536.0 out = [] def esc(s): return s.replace('&', '&').replace('<', '<').replace('>', '>') def txt(x, y, s, size=14, fill='#222', anchor='start', weight='normal'): out.append('%s' % (x, y, size, fill, anchor, weight, FONT, esc(s))) def line(x1, y1, x2, y2, stroke='#999', w=1.0, dash=None): d = ' stroke-dasharray="%s"' % dash if dash else '' out.append('' % (x1, y1, x2, y2, stroke, w, d)) def rect(x, y, w, h, fill='none', stroke='none', rx=2, sw=1.0, op=1.0): out.append('' % (x, y, w, h, rx, fill, op, stroke, sw)) def note(x, y, w, h, title, lines_, color='#0b6'): rect(x, y, w, h, '#fbfdff', color, rx=6, sw=1.2, op=0.95) txt(x + 12, y + 24, title, 14.5, color, weight='bold') for i, s in enumerate(lines_): txt(x + 12, y + 48 + i * 21, s, 13, '#333') T_IN = [300, 60, 32, 21] # PD132T 进入表(档0..3) T_OUT = [60, 32, 21, 14] # PD132T 离开表 H_IN = [162, 81, 54, 28] # PD136HA 进入表 H_OUT = [81, 54, 32, 18] # PD136HA 离开表 ct = lambda v: v / K * 100.0 out.append('' % (W, H, W, H)) out.append('' % (W, H)) txt(30, 44, 'PD132T ↔ PD136HA 灵敏度档位对齐图(判据 = 表值/65536 的 Δf/f)', 24, '#111', weight='bold') txt(30, 72, '每档画两段:深色 = 离开阈值(回差下沿),浅色 = 进入阈值(回差上沿);横轴 0~0.5%(线性)', 13, '#555') line(30, 86, W - 30, 86, '#ddd', 1.2) # ================== 轴 ================== AX0, AX1 = 380.0, 1230.0 VMAX = 0.50 ax = lambda v: AX0 + (v / VMAX) * (AX1 - AX0) GY0, GGH = 118.0, 116.0 for g in [0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5]: x = ax(g) line(x, 112, x, 112 + 4 * GGH, '#ececec', 1.0) txt(x, 112 + 4 * GGH + 22, ('%.2f' % g), 12, '#777', anchor='middle') txt((AX0 + AX1) / 2, 112 + 4 * GGH + 46, 'Δf/f(%)—— 数值越大越钝(越不易触发)', 13, '#555', anchor='middle') # ================== 四档 ================== COLT, COLH = '#1565c0', '#e65100' for i in range(4): gy = GY0 + i * GGH viT, voT = ct(T_IN[i]), ct(T_OUT[i]) viH, voH = ct(H_IN[i]), ct(H_OUT[i]) dev = (viH - viT) / viT * 100.0 # 分组背景 if i % 2 == 0: rect(40, gy - 8, W - 80, GGH - 12, '#fafafa', rx=6, sw=0) txt(60, gy + 52, '档 %d' % i, 17, '#111', weight='bold') # --- PD132T 行 --- yT = gy + 6 rect(AX0, yT, ax(voT) - AX0, 17, COLT, rx=2, op=0.30) rect(AX0, yT, ax(viT) - AX0, 17, COLT, rx=2, op=0.85) txt(AX0 - 10, yT + 14, 'PD132T', 13.5, COLT, anchor='end', weight='bold') txt(ax(viT) + 8, yT + 14, '进入 %.4f%% / 离开 %.4f%%' % (viT, voT), 12.5, COLT) # --- PD136HA 行 --- yH = gy + 52 rect(AX0, yH, ax(voH) - AX0, 17, COLH, rx=2, op=0.30) rect(AX0, yH, ax(viH) - AX0, 17, COLH, rx=2, op=0.85) txt(AX0 - 10, yH + 14, 'PD136HA', 13.5, COLH, anchor='end', weight='bold') txt(ax(viH) + 8, yH + 14, '进入 %.4f%%' % viH, 12.5, COLH) # 同档偏差标注 + 连接线 line(ax(viT), yT + 17, ax(viH), yH, '#999', 1.2, '4,3') badge_col = '#c1121f' if abs(dev) >= 30 else ('#b8860b' if abs(dev) >= 15 else '#0b6') txt(AX1 + 16, gy + 40, '同档偏差 %+.0f%%' % dev, 14, badge_col, weight='bold') # 最佳匹配说明 txt(W - 30, GY0 + 4 * GGH + 10, '最佳匹配:档 1 → 档 2(−10.0%)', 13, '#0b6', anchor='end') txt(W - 30, GY0 + 4 * GGH + 32, '两端档 0 / 3 无对应档 ⇒ 必须复测', 13, '#c1121f', anchor='end') line(AX1 + 4, 112, AX1 + 4, 112 + 4 * GGH, '#ccc', 1.0) # ================== 对照表 ================== TY = 660.0 txt(40, TY, '档位对照表(现场照着贴)', 17, '#111', weight='bold') COLS = [(60, '档位'), (150, 'PD132T 进入'), (290, 'PD132T 离开'), (430, 'PD136HA 进入'), (570, 'PD136HA 离开'), (710, '同档偏差'), (860, '等效替代(最近进入阈值)')] for x, s in COLS: txt(x, TY + 30, s, 13.5, '#555', weight='bold') line(40, TY + 40, W - 40, TY + 40, '#ccc', 1.0) for i in range(4): y = TY + 66 + i * 36 viT, voT, viH, voH = ct(T_IN[i]), ct(T_OUT[i]), ct(H_IN[i]), ct(H_OUT[i]) dev = (viH - viT) / viT * 100.0 j = min(range(4), key=lambda k: abs(H_IN[k] - T_IN[i])) dev2 = (ct(H_IN[j]) - viT) / viT * 100.0 txt(60, y, '档 %d' % i, 14, '#111', weight='bold') txt(150, y, '%.4f %%' % viT, 13, '#1565c0') txt(290, y, '%.4f %%' % voT, 13, '#777') txt(430, y, '%.4f %%' % viH, 13, '#e65100') txt(570, y, '%.4f %%' % voH, 13, '#777') txt(710, y, '%+.1f %%' % dev, 13.5, ('#c1121f' if abs(dev) >= 30 else '#b8860b'), weight='bold') txt(860, y, 'PD136HA 档 %d(%+.1f%%)' % (j, dev2), 13, '#0b6') line(40, TY + 66 + 4 * 36 - 16, W - 40, TY + 66 + 4 * 36 - 16, '#ccc', 1.0) # ================== 脚注 ================== note(40, TY + 66 + 4 * 36 + 4, 700, 128, '换型动作(4 步)', [ '① 先算目标:原型号档位 → 百分比 = 表值 / 65536', '② 在新型号表里找最接近的档(偏差 ≤15% 可接受;>30% 必须复测)', '③ 用 §5.5 公式预估响应:延迟 = (n+1)×窗长(n 由 信号/判据 比决定)', '④ 强信号车 + 弱信号车各复测 3 次(记录 判据越线→继电器 实测耗时)', ], '#c1121f') note(760, TY + 66 + 4 * 36 + 4, 700, 128, '两端档位无对应(必须注意)', [ '档0(最钝):PD132T 0.4578% ↔ PD136HA 0.2472%(钝 46%)', ' ⇒ 依赖「最钝档防误触发」的现场,换型后须重新评估误触发率', '档3(最灵敏):PD132T 0.0320% ↔ PD136HA 0.0427%(钝 33%)', ' ⇒ 依赖「最高灵敏」的现场,PD136HA 可能检不到弱信号车(§5.5 ⑧)', ], '#e65100') OPEN = 'docs/img/sens-alignment-pd132t-pd136ha.svg' out.append('') open(OPEN, 'w', encoding='utf-8').write('\n'.join(out)) print('已写出 %s' % OPEN) for i in range(4): viT, viH = ct(T_IN[i]), ct(H_IN[i]) j = min(range(4), key=lambda k: abs(H_IN[k] - T_IN[i])) print(' 档%d: PD132T %.4f%% ↔ PD136HA %.4f%%(同档 %+.1f%%)| 最近档 %d(%+.1f%%)' % (i, viT, viH, (viH - viT) / viT * 100, j, (ct(H_IN[j]) - viT) / viT * 100))