diff --git a/README.md b/README.md index 2cea831..709306d 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,10 @@ vd-analysis/ ├── img/ # 图示(PNG 预览 + SVG 矢量) └── tools/ # 分析辅助脚本 ├── window_calc.py # 测量窗量化计算器(窗=⌊BASE/Xn₀⌋·Xn₁;六型号内置参数) - ├── plot_timing_chain.py # 端到端时序图生成(纯 stdlib 手写 SVG,曲线按公式真算) + ├── plot_timing_chain.py # 端到端时序图(三段横轴:一个窗/检测到输出/基线;纯 stdlib SVG) + ├── plot_response_compare.py # PD132T↔PD136HA 响应对比(时域阶梯 + 延迟扫描,标出窗主导/τ 主导) + ├── plot_family_overview.py # 六型号总览(窗长/τ/检测延迟 + 归一化延迟表) + ├── plot_sens_alignment.py # PD132T↔PD136HA 档位对齐图(现场照着贴) └── plot_response_compare.py # PD132T↔PD136HA 响应对比图(含"窗主导/τ 主导"分界) ``` diff --git a/docs/cross-model-comparison.md b/docs/cross-model-comparison.md index 17225d1..debdd59 100644 --- a/docs/cross-model-comparison.md +++ b/docs/cross-model-comparison.md @@ -58,6 +58,14 @@ > ⚠ **同族内档位同样不可照搬**:PD132T ↔ PD136HA 一一映射偏差 **−46%~+69%**(两端档差异最大:PD132T 最钝档 0.458% 无对应档;其最高灵敏档 0.032% 亦比 PD136HA 最高档钝 1/3)。跨代必须按 Δf/f 换算后现场复测,见 [pd132t-vs-pd136ha-comparison.md](pd132t-vs-pd136ha-comparison.md) §5.4。 +## 六型号总览图(窗长 / τ / 检测延迟,2026-09-11 新增) + +![车检器六型号总览](img/family-overview.png) + +(矢量版 `docs/img/family-overview.svg`;生成脚本 `tools/plot_family_overview.py`,型号参数内置、改数即重出。图中延迟为「判据越线」口径,不含输出级 50ms 节拍与进入确认——确认时间见面板脚注) + +**说明**:面板 A 的条形与面板 B 的曲线全部按本文口径计算(窗长 = `⌊BASE/Xn⌋·Xn/f_cap` 标称值;τ = 节拍×256/α;延迟由 `CAPVD_n = Δf/f·[1−(1−α/256)^n]` 反解 `n`)。**归一化(按各自档0 判据的 1.1×/2×/10× 倍数)后,六型号的差异只剩「窗长 × τ」两件事**:@10×(强信号)全部 = 1 窗 ⇒ 10.9 / 21.9 / 27.3 / 8.2 / 8.7 / 10.0 ms;@1.1×(近阈值)= 7~9 窗 ⇒ PD136HA 196.7ms 与 PD132L 191.2ms 最慢、M1H 57.3ms 与 DLD154Pro 61.2ms 最快。 + ## 测频路线(周期累积法为统一范式) 纳入对比表的型号(M1H / PD132L / PD132T / PD136HA / DLD154V4B / DLD154Pro)**全部采用周期累积法**: diff --git a/docs/img/family-overview.png b/docs/img/family-overview.png new file mode 100644 index 0000000..07131da Binary files /dev/null and b/docs/img/family-overview.png differ diff --git a/docs/img/family-overview.svg b/docs/img/family-overview.svg new file mode 100644 index 0000000..0cba772 --- /dev/null +++ b/docs/img/family-overview.svg @@ -0,0 +1,210 @@ + + +车检器六型号总览:测量窗长 · IIR 时间常数 · 检测延迟 +窗长 = ⌊BASE/Xn⌋·Xn/f_cap(标称值);τ = 节拍×256/α(产品线口径);检测延迟 = 判据越线时刻(不含输出级 50ms 节拍与进入确认) + +面板 A:测量窗长与 IIR τ(对数刻度,单位 ms) + +1 + +2 + +5 + +10 + +20 + +50 + +100 + +200 +ms +DLD154Pro + +8.74 + +τ 28.3 +DLD154V4B + +1.09 + +τ 32.4 +节拍 10 +PD136HA + +21.84 + +τ 87.4 +PD132T + +10.92 + +τ 35.4 +PD132L + +27.31 + +τ 88.5 +M1H + +8.19 + +τ 26.5 + +深色 = 测量窗长;浅色 = IIR τ;红字 = 滤波更新节拍(与窗长不同者) +面板 B:检测延迟 vs 车辆信号 Δf/f(各型号按本型号档0 判据,对数-对数) + +0.02 + +0.05 + +0.1 + +0.2 + +0.5 + +1 + +2 + +5 +车辆引起的相对频偏 Δf/f(%,对数) + +4 + +10 + +30 + +100 + +300 + +1000 + +2000 +延迟 ms + +典型车信号带 0.1%~1% + + + + + + + + + + + + + + + + + + +竖虚线 = 各型号档0 判据(左端=永不检出);横虚线 = 该型号 1 窗下限 + +型号 / 窗长 + +DLD154Pro 8.74 ms + +DLD154V4B 1.09 ms + +PD136HA 21.84 ms + +PD132T 10.92 ms + +PD132L 27.31 ms + +M1H 8.19 ms + +精确数值(判据越线延迟;不含输出级与进入确认) +型号 +测量窗 +滤波节拍 +α +τ(口径) +档0 判据 +延迟 @1.1×判据 +延迟 @2×判据 +延迟 @10×判据 +进入确认 + +DLD154Pro +8.74 +8.74 +79 +28.3 +0.1650 % +61.2 ms(7 窗) +17.5 ms(2 窗) +8.7 ms(1 窗) +3 tick 确认(5ms tick) +DLD154V4B +1.09 +10.00 +79 +32.4 +0.5140 % +70.0 ms(7 窗) +20.0 ms(2 窗) +10.0 ms(1 窗) +3~5 tick 确认(10ms tick) +PD136HA +21.84 +21.85 +64 +87.4 +0.2472 % +196.7 ms(9 窗) +65.6 ms(3 窗) +21.9 ms(1 窗) +无(单次越线) +PD132T +10.92 +10.92 +79 +35.4 +0.4578 % +76.4 ms(7 窗) +21.8 ms(2 窗) +10.9 ms(1 窗) +无(单次越线) +PD132L +27.31 +27.31 +79 +88.5 +0.4578 % +191.2 ms(7 窗) +54.6 ms(2 窗) +27.3 ms(1 窗) +无(单次越线) +M1H +8.19 +8.19 +79 +26.5 +0.3296 % +57.3 ms(7 窗) +16.4 ms(2 窗) +8.2 ms(1 窗) +10 tick 确认(≈0.6s) + + +口径与注意 +τ 为产品线口径(节拍×256/α);精确 τ = −节拍/ln(1−α/256),约小 13% +V4B 测量窗 1.09ms 与滤波节拍 10ms 不同(Value 每 1.09ms、CAPVD 每 10ms) +延迟仅含「判据越线」;输出级 50ms 节拍与进入确认未计(见右侧) +「档0」各型号判据不同(0.165%~0.514%),仅作同档位号横向对比 + +进入确认(须另加) +M1H:IN_DELAY=10 tick(约 0.6s);V4B:3~5 tick(30~50ms) +154Pro:3 次确认;PD132L / PD132T / PD136HA:无(单次越线判定) +⇒ 整机响应 = 判据越线 + 进入确认 + ≤50ms 输出节拍 + \ No newline at end of file diff --git a/docs/img/sens-alignment-pd132t-pd136ha.png b/docs/img/sens-alignment-pd132t-pd136ha.png new file mode 100644 index 0000000..1b491e0 Binary files /dev/null and b/docs/img/sens-alignment-pd132t-pd136ha.png differ diff --git a/docs/img/sens-alignment-pd132t-pd136ha.svg b/docs/img/sens-alignment-pd132t-pd136ha.svg new file mode 100644 index 0000000..221ffc1 --- /dev/null +++ b/docs/img/sens-alignment-pd132t-pd136ha.svg @@ -0,0 +1,128 @@ + + +PD132T ↔ PD136HA 灵敏度档位对齐图(判据 = 表值/65536 的 Δf/f) +每档画两段:深色 = 离开阈值(回差下沿),浅色 = 进入阈值(回差上沿);横轴 0~0.5%(线性) + + +0.00 + +0.05 + +0.10 + +0.15 + +0.20 + +0.25 + +0.30 + +0.35 + +0.40 + +0.45 + +0.50 +Δf/f(%)—— 数值越大越钝(越不易触发) + +档 0 + + +PD132T +进入 0.4578% / 离开 0.0916% + + +PD136HA +进入 0.2472% + +同档偏差 -46% +档 1 + + +PD132T +进入 0.0916% / 离开 0.0488% + + +PD136HA +进入 0.1236% + +同档偏差 +35% + +档 2 + + +PD132T +进入 0.0488% / 离开 0.0320% + + +PD136HA +进入 0.0824% + +同档偏差 +69% +档 3 + + +PD132T +进入 0.0320% / 离开 0.0214% + + +PD136HA +进入 0.0427% + +同档偏差 +33% +最佳匹配:档 1 → 档 2(−10.0%) +两端档 0 / 3 无对应档 ⇒ 必须复测 + +档位对照表(现场照着贴) +档位 +PD132T 进入 +PD132T 离开 +PD136HA 进入 +PD136HA 离开 +同档偏差 +等效替代(最近进入阈值) + +档 0 +0.4578 % +0.0916 % +0.2472 % +0.1236 % +-46.0 % +PD136HA 档 0(-46.0%) +档 1 +0.0916 % +0.0488 % +0.1236 % +0.0824 % ++35.0 % +PD136HA 档 2(-10.0%) +档 2 +0.0488 % +0.0320 % +0.0824 % +0.0488 % ++68.8 % +PD136HA 档 3(-12.5%) +档 3 +0.0320 % +0.0214 % +0.0427 % +0.0275 % ++33.3 % +PD136HA 档 3(+33.3%) + + +换型动作(4 步) +① 先算目标:原型号档位 → 百分比 = 表值 / 65536 +② 在新型号表里找最接近的档(偏差 ≤15% 可接受;>30% 必须复测) +③ 用 §5.5 公式预估响应:延迟 = (n+1)×窗长(n 由 信号/判据 比决定) +④ 强信号车 + 弱信号车各复测 3 次(记录 判据越线→继电器 实测耗时) + +两端档位无对应(必须注意) +档0(最钝):PD132T 0.4578% ↔ PD136HA 0.2472%(钝 46%) + ⇒ 依赖「最钝档防误触发」的现场,换型后须重新评估误触发率 +档3(最灵敏):PD132T 0.0320% ↔ PD136HA 0.0427%(钝 33%) + ⇒ 依赖「最高灵敏」的现场,PD136HA 可能检不到弱信号车(§5.5 ⑧) + \ No newline at end of file diff --git a/docs/measurement-window-quantization.md b/docs/measurement-window-quantization.md index a93f032..212a7a8 100644 --- a/docs/measurement-window-quantization.md +++ b/docs/measurement-window-quantization.md @@ -253,6 +253,8 @@ loop1_CAPVD = loop1_CapSum; loop1_Origin = loop1_CAPVD; loop1_INI_LOOP = 0; ## 10. 响应速度的跨型号对照(图) +> 六型号完整总览图(窗长 / τ / 检测延迟 + 归一化延迟表):`docs/cross-model-comparison.md` 的「六型号总览图」节(`docs/img/family-overview.png`);PD132T↔PD136HA 细对比见 `docs/pd132t-vs-pd136ha-comparison.md` §5.5。 + 同一判据口径下 PD132T(窗 10.880 ms / α79)与 PD136HA(窗 21.760 ms / α64)的响应对比,含"窗主导 vs τ 主导"分界: ![PD132T vs PD136HA 响应对比](img/response-compare-pd132t-pd136ha.png) diff --git a/docs/pd132t-vs-pd136ha-comparison.md b/docs/pd132t-vs-pd136ha-comparison.md index ad285fe..c5c0dda 100644 --- a/docs/pd132t-vs-pd136ha-comparison.md +++ b/docs/pd132t-vs-pd136ha-comparison.md @@ -217,6 +217,10 @@ loop1_dlt_ORG = (Origin * SensTable1[SensLevel]) >> 16; // 离开表 ### 5.4 ⭐ 跨代档位换算表(照搬档位号 = 错) +![PD132T ↔ PD136HA 灵敏度档位对齐图](img/sens-alignment-pd132t-pd136ha.png) + +(矢量版 `docs/img/sens-alignment-pd132t-pd136ha.svg`;生成脚本 `tools/plot_sens_alignment.py`。图中深色段 = 离开阈值、浅色段 = 进入阈值,二者之差即回差带;右侧「同档偏差」= 同档位号一对一映射的 `(H−T)/T`) + **PD132T → PD136HA(按 Δf/f 找最近档)** | PD132T 档 | 进入 Δf/f | 最近的 PD136HA 档 | 进入偏差 | 离开偏差 | 可用性 | @@ -340,6 +344,7 @@ IIR → 基线(无车时更新/有车冻结) → 进入/离开判据 → VD_FLAG - [ ] **⑤ PD132L 捕获沿**:§2.3 表中 PD132L 的"单沿"是**推断**(由窗口比 0.8× = 时钟比推得),未读其源码的捕获配置 ⇒ 需补证。 - [ ] **⑥ 端点档复测**:PD132T 档 0(0.4578%)与 PD136HA 档 0(0.2472%)在两代之间**无对应档**;现场若停在这两个档位,换型必须专项复测。 - [x] ~~**⑦ 旧文件勘误**~~ ✅ **已落地**(同日提交):`docs/hardware-loop-frontend-pd132t-vs-v4b.md` §8 与 `docs/pd136ha-to-dld154v4b-delivery-alignment.md` §2.3 中「两型号线圈频段差 2×」已改为「**同一频段 ≈23.4~118kHz**」(见 §2.3)。 +- [ ] **⑧ 弱信号复测(§5.5,2026-09-11 新增)**:用弱信号车(目标 Δf/f ≈ 0.10%)验证预测 —— PD132T 档1 应 **≈76 ms** 检出,**PD136HA 档1 应「检不到」**(其档1 判据 0.1236% 比 PD132T 的 0.0916% 钝 35%)。若现场确证,换型文档须写明:**PD136HA 需上调一档(档2 = 0.0824%)才能覆盖同类弱信号车**;同时用强/弱两种车各测 3 次,记录"判据越线 → 继电器动作"的实测时间,与 §5.5 的 `(n+1)·窗长 + ≤50 ms` 预测对照。 --- diff --git a/tools/plot_family_overview.py b/tools/plot_family_overview.py new file mode 100644 index 0000000..f628a0d --- /dev/null +++ b/tools/plot_family_overview.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""六型号总览图(窗长 / τ / 检测延迟)—— 纯 stdlib 手写 SVG + +面板 A:测量窗长 与 IIR 时间常数 τ(横向条形,对数刻度) +面板 B:检测延迟 vs Δf/f(各型号按【本型号档0 判据】计算,对数-对数) + +转 PNG:chromium --headless=new --disable-gpu --no-sandbox \ + --screenshot=docs/img/family-overview.png --window-size=1520,1000 \ + file://$PWD/docs/img/family-overview.svg +""" +import math + +W, H = 1520, 1100 +FONT = "'Noto Serif CJK SC','Noto Sans CJK SC',sans-serif" +MONO = "'DejaVu Sans Mono',monospace" +out = [] + +def esc(s): + return (s.replace('&', '&').replace('<', '<').replace('>', '>')) + +def txt(x, y, s, size=14, fill='#222', anchor='start', weight='normal', family=None): + out.append('%s' + % (x, y, size, fill, anchor, weight, family or 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=3, sw=1.0, op=1.0, dash=None): + d = ' stroke-dasharray="%s"' % dash if dash else '' + out.append('' + % (x, y, w, h, rx, fill, op, stroke, sw, d)) + +def poly(pts, stroke='#1565c0', w=1.8, fill='none', dash=None): + d = ' stroke-dasharray="%s"' % dash if dash else '' + s = ' '.join('%.1f,%.1f' % p for p in pts) + out.append('' + % (s, fill, stroke, w, d)) + +def dot(x, y, r=4.0, fill='#c1121f'): + out.append('' % (x, y, r, fill)) + +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') + +# ---- 型号数据(全部取证过;数值见 docs/cross-model-comparison.md)---- +# name, 测量窗(ms), 滤波更新节拍(ms), α, 档0 判据(Δf/f %), 进入确认说明, 颜色 +M = [ + ('DLD154Pro', 8.738, 8.74, 79, 0.1650, '3 tick 确认(5ms tick)', '#6a1b9a'), + ('DLD154V4B', 1.092, 10.00, 79, 0.5140, '3~5 tick 确认(10ms tick)', '#2e7d32'), + ('PD136HA', 21.845, 21.85, 64, 0.2472, '无(单次越线)', '#e65100'), + ('PD132T', 10.923, 10.92, 79, 0.4578, '无(单次越线)', '#1565c0'), + ('PD132L', 27.307, 27.31, 79, 0.4578, '无(单次越线)', '#00838f'), + ('M1H', 8.192, 8.19, 79, 0.3296, '10 tick 确认(≈0.6s)', '#5d4037'), +] + +def tau_line(tick, alfa): + return tick * 256.0 / alfa + +def delay_ms(m, d_pct): + """判据越线延迟:d_pct 以 % 传入;返回 (窗数, ms) 或 None(检不到)""" + tick, alfa, thr = m[2], m[3], m[4] + if d_pct < thr: + return None + y = 0.0 + for n in range(1, 4001): + y = d_pct * (1.0 - (1.0 - alfa / 256.0) ** n) + if y >= thr: + return (n, n * tick) + return (4000, 4000 * tick) + +# ================== 画布 ================== +out.append('' % (W, H, W, H)) +out.append('' % (W, H)) +txt(30, 44, '车检器六型号总览:测量窗长 · IIR 时间常数 · 检测延迟', 25, '#111', weight='bold') +txt(30, 72, '窗长 = ⌊BASE/Xn⌋·Xn/f_cap(标称值);τ = 节拍×256/α(产品线口径);检测延迟 = 判据越线时刻(不含输出级 50ms 节拍与进入确认)', 13, '#555') +line(30, 86, W - 30, 86, '#ddd', 1.2) + +# ================== 面板 A:窗长 / τ 条形 ================== +txt(40, 118, '面板 A:测量窗长与 IIR τ(对数刻度,单位 ms)', 16, '#111', weight='bold') +LA0, LA1 = 250.0, 700.0 +ALO, AHI = 1.0, 200.0 +lax = lambda v: LA0 + (math.log10(max(v, ALO)) / math.log10(AHI / ALO)) * (LA1 - LA0) +# 网格与刻度 +for g in (1, 2, 5, 10, 20, 50, 100, 200): + x = lax(g) + line(x, 140, x, 560, '#e8e8e8', 1.0) + txt(x, 578, ('%g' % g), 12.5, '#777', anchor='middle') +txt(LA1 + 42, 578, 'ms', 12.5, '#777', anchor='middle') +ROW0, ROWH = 152.0, 68.0 +for i, m in enumerate(M): + nm, win, tick, alfa, thr, conf, col = m + y0 = ROW0 + i * ROWH + txt(240, y0 + 20, nm, 15, '#111', anchor='end', weight='bold') + # 窗长 + w1 = max(lax(win) - LA0, 2.0) + rect(LA0, y0 + 2, w1, 15, col, rx=2, op=0.95) + txt(lax(win) + 6, y0 + 14, '%.2f' % win, 12.5, col) + # τ + tau = tau_line(tick, alfa) + w2 = max(lax(tau) - LA0, 2.0) + rect(LA0, y0 + 24, w2, 15, col, rx=2, op=0.38) + txt(lax(tau) + 6, y0 + 36, 'τ %.1f' % tau, 12.5, '#555') + # 节拍标注(V4B 窗≠节拍) + if abs(win - tick) > 0.05: + txt(LA0 - 6, y0 + 36, '节拍 %.0f' % tick, 11.5, '#c1121f', anchor='end') +line(LA0, 140, LA0, 560, '#bbb', 1.4) +txt(250, 596, '深色 = 测量窗长;浅色 = IIR τ;红字 = 滤波更新节拍(与窗长不同者)', 12.5, '#666') + +# ================== 面板 B:检测延迟 vs Δf/f ================== +txt(800, 118, '面板 B:检测延迟 vs 车辆信号 Δf/f(各型号按本型号档0 判据,对数-对数)', 16, '#111', weight='bold') +BX0, BX1 = 880.0, 1470.0 +BYB, BYT = 545.0, 160.0 +RLO, RHI = 0.02, 5.0 +DLO, DHI = 4.0, 2000.0 +bx = lambda v: BX0 + (math.log10(v / RLO) / math.log10(RHI / RLO)) * (BX1 - BX0) +by = lambda d: BYB - (math.log10(max(d, DLO) / DLO) / math.log10(DHI / DLO)) * (BYB - BYT) +for g in (0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5): + x = bx(g) + line(x, BYT, x, BYB, '#e8e8e8', 1.0) + txt(x, BYB + 20, ('%g' % g), 12.5, '#777', anchor='middle') +txt((BX0 + BX1) / 2, BYB + 42, '车辆引起的相对频偏 Δf/f(%,对数)', 13, '#555', anchor='middle') +for g in (4, 10, 30, 100, 300, 1000, 2000): + y = by(g) + line(BX0, y, BX1, y, '#e8e8e8', 1.0) + txt(BX0 - 8, y + 4, ('%g' % g), 12.5, '#777', anchor='end') +txt(BX0 - 8, BYT - 14, '延迟 ms', 13, '#555', anchor='end') +# 典型车信号带 +rect(bx(0.1), BYT, bx(1.0) - bx(0.1), BYB - BYT, '#fff8e1', rx=0, op=0.9) +txt((bx(0.1) + bx(1.0)) / 2, BYT + 18, '典型车信号带 0.1%~1%', 12.5, '#b8860b', anchor='middle') +# 各型号曲线 +for m in M: + nm, win, tick, alfa, thr, conf, col = m + pts = [] + v = thr * 1.002 + while v <= RHI: + r = delay_ms(m, v) + if r is None: + v *= 1.12 + continue + d = r[1] + if d > DHI: + pts = [] # 越过上限 ⇒ 断开(避免画出界) + v *= 1.10 + continue + pts.append((bx(v), by(d))) + v = v * 1.06 + 0.0015 + if len(pts) > 1: + poly(pts, col, 2.2) + # 1 窗下限虚线(标签统一放到图例,避免互相压字) + line(bx(thr), by(tick), bx(min(thr * 3.2, RHI)), by(tick), col, 1.0, '3,3') +# 阈值竖线 +for m in M: + x = bx(m[4]) + line(x, BYT, x, BYB, m[6], 1.0, '2,3') +txt(BX0 + 6, BYB - 8, '竖虚线 = 各型号档0 判据(左端=永不检出);横虚线 = 该型号 1 窗下限', 12, '#666') +# 图例(放在面板 B 左上空白区) +rect(890, 176, 236, 166, '#ffffff', '#ccc', rx=5, sw=1.0, op=0.94) +txt(902, 196, '型号 / 窗长', 13, '#555', weight='bold') +for i, m in enumerate(M): + yy = 220 + i * 20 + rect(902, yy - 9, 14, 11, m[6], rx=2, op=0.95) + txt(924, yy, '%s %.2f ms' % (m[0], m[1]), 12, m[6]) + +# ================== 表格:精确数值 ================== +TY = 646.0 +line(40, TY - 10, W - 40, TY - 10, '#ddd', 1.2) +txt(40, TY + 14, '精确数值(判据越线延迟;不含输出级与进入确认)', 16, '#111', weight='bold') +COLS = [(60, '型号'), (190, '测量窗'), (285, '滤波节拍'), (370, 'α'), (412, 'τ(口径)'), (500, '档0 判据'), + (620, '延迟 @1.1×判据'), (775, '延迟 @2×判据'), (925, '延迟 @10×判据'), (1060, '进入确认')] +for x, s in COLS: + txt(x, TY + 46, s, 13.5, '#555', weight='bold') +line(40, TY + 56, W - 40, TY + 56, '#ccc', 1.0) +for i, m in enumerate(M): + nm, win, tick, alfa, thr, conf, col = m + y = TY + 84 + i * 40 + txt(60, y, nm, 14, col, weight='bold') + txt(190, y, '%.2f' % win, 13, '#333') + txt(285, y, '%.2f' % tick, 13, '#333') + txt(370, y, str(alfa), 13, '#333') + txt(412, y, '%.1f' % tau_line(tick, alfa), 13, '#333') + txt(500, y, '%.4f %%' % thr, 13, '#333') + for dx, mul in ((620, 1.1), (775, 2.0), (925, 10.0)): + r = delay_ms(m, thr * mul) + if r is None: + txt(dx, y, '—', 13, '#c1121f') + else: + txt(dx, y, '%.1f ms(%d 窗)' % (r[1], r[0]), 13, ('#1565c0' if mul < 1.5 else '#333')) + txt(1060, y, conf, 12.5, '#666') +line(40, TY + 84 + len(M) * 40 - 22, W - 40, TY + 84 + len(M) * 40 - 22, '#ccc', 1.0) + +# ================== 脚注 ================== +note(40, TY + 84 + len(M) * 40 - 6, 700, 118, '口径与注意', [ + 'τ 为产品线口径(节拍×256/α);精确 τ = −节拍/ln(1−α/256),约小 13%', + 'V4B 测量窗 1.09ms 与滤波节拍 10ms 不同(Value 每 1.09ms、CAPVD 每 10ms)', + '延迟仅含「判据越线」;输出级 50ms 节拍与进入确认未计(见右侧)', + '「档0」各型号判据不同(0.165%~0.514%),仅作同档位号横向对比', +], '#c1121f') +note(760, TY + 84 + len(M) * 40 - 6, 720, 118, '进入确认(须另加)', [ + 'M1H:IN_DELAY=10 tick(约 0.6s);V4B:3~5 tick(30~50ms)', + '154Pro:3 次确认;PD132L / PD132T / PD136HA:无(单次越线判定)', + '⇒ 整机响应 = 判据越线 + 进入确认 + ≤50ms 输出节拍', +], '#0b6') + +OPEN = 'docs/img/family-overview.svg' +out.append('') +open(OPEN, 'w', encoding='utf-8').write('\n'.join(out)) + +print('已写出 %s(%d 元素)' % (OPEN, len(out))) +for m in M: + nm = m[0] + f = lambda r: ('%.1f ms(%d 窗)' % (r[1], r[0])) if r else '—' + print(' %-11s 窗 %6.2f | 节拍 %6.2f | τ %6.1f | 档0 %6.4f%% | @1.1x: %-16s @2x: %-16s @10x: %s' + % (nm, m[1], m[2], tau_line(m[2], m[3]), m[4], + f(delay_ms(m, m[4] * 1.1)), f(delay_ms(m, m[4] * 2.0)), f(delay_ms(m, m[4] * 10.0)))) + + + diff --git a/tools/plot_sens_alignment.py b/tools/plot_sens_alignment.py new file mode 100644 index 0000000..a880eaa --- /dev/null +++ b/tools/plot_sens_alignment.py @@ -0,0 +1,140 @@ +#!/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)) + +