docs(measurement): 测量节拍是区间 —— 周期累积法的窗口量化(用户提出)+ 全仓回灌
- 新文件 docs/measurement-window-quantization.md:窗长 = ⌊BASE/Xn₀⌋·Xn₁ tick(LPCNT 整数除法 + 学习后冻结)
· 标称 BASE/f_cap(10.92/21.85ms)是【上限】,实际 = 标称 ×(BASE−r₀)/BASE × f₀/f_now
· 站点偏差:100kHz −0.39%(PD132T 10.88ms / PD136HA 21.76ms)、60kHz −2.34%、带内最差 −6.20%(Xn₀=2049)
· 深占压(频偏 −5%)窗再 +5.3%;"检测阈值"量级(≤0.46%)可忽略
· ✅ Δf/f 口径不受影响:Value/Origin = Xn₁/Xn₀ 精确(余数完全约掉)—— 灵敏度/交付结论全部保持
· ⚠ 新发现:loop1_LPCNT 为 INT8U(≤255),同族靠 406<Xn<2050 保证 ≤80;
PD136HA 该窗口已注释 ⇒ Xn<129 时 8 位截断 ⇒ 异常线圈可能报"堵车"而非"故障"(建议恢复或加钳位)
- 新增 tools/window_calc.py(内置六型号参数,实跑验证文档全部数字)
- 回灌:cross-model 总表(新增脚注 + 测频路线说明 + 待补证1条)、PD132T↔PD136HA 对比报告(§0/§3.1/§3.2/§4.3)、
两份交付对齐报告("测量窗列 = 标称上限" + 区间脚注)、filter-coefficient 专题(M1H 8.19ms 勘误 + τ 复算 + 脚注)、
README(新增"专题文档"段 + 目录树)
- 表格校验 0 异常
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""测量窗量化计算器(周期累积法)
|
||||
|
||||
背景:检测器不是"累加固定 tick 数",而是"累加固定个数的周期":
|
||||
LPCNT = floor(BASE / Xn0) # 整数除法,学习时算一次后【冻结】
|
||||
窗长 = LPCNT * Xn1 # 单位:捕获时钟 tick ← 不是恒定 BASE!
|
||||
窗时长 = 窗长 / f_cap
|
||||
其中 Xn0 = 学习时的周期计数、Xn1 = 当前周期计数(Xn 与线圈频率成反比)。
|
||||
|
||||
结论(详见 docs/measurement-window-quantization.md):
|
||||
* 表中 BASE/f_cap(如 10.92ms / 21.85ms)是【标称上限】,实际窗 = BASE - (BASE mod Xn0) tick
|
||||
* 站点偏差 = r0/BASE(r0 = BASE mod Xn0),带内 0 ~ -6.2%
|
||||
* 深占压(线圈频偏 -d%)时窗时长再放大 1/(1-d)
|
||||
* 【比值口径不受影响】:Value/Origin = Xn1/Xn0 精确(余数完全约掉)
|
||||
|
||||
用法:
|
||||
python3 window_calc.py # 打印内置型号对照表
|
||||
python3 window_calc.py --model PD132T --f-coil 100k
|
||||
python3 window_calc.py --f-cap 3M --base 32768 --edge 2 --divider 32 --f-coil 60k
|
||||
参数:--f-coil 线圈频率(Hz 或 100k/60k)、--f-cap 捕获时钟、--base 归一化基数、
|
||||
--edge 捕获沿系数(双沿=2 / 单沿=1)、--divider 前端分频比(CD4060 ÷32 或直连=1)
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
# 内置型号参数: (BASE, f_cap, edge_factor, front_divider, note)
|
||||
MODELS = {
|
||||
"M1H": (32768, 4.0e6, 2, 32, "STC12C5202, PCA 双沿, ÷32(推断), 4MHz"),
|
||||
"PD132L": (32768, 1.2e6, 1, 32, "STM8S003, TIM1_CC3 下降沿, ÷32, 1.2MHz"),
|
||||
"PD132T": (32768, 3.0e6, 2, 32, "MA801, PCA 双沿, ÷32(Q5), 3MHz"),
|
||||
"PD136HA": (32768, 1.5e6, 1, 32, "HC32F003C4, ADTIM4 单沿, ÷32, 1.5MHz"),
|
||||
"DLD154V4B": (131072, 120e6, 1, 1, "AT32F421, TIM3 直连振荡(1 Xn = 1 原始周期), 120MHz"),
|
||||
"DLD154Pro": (524288, 60e6, 1, 1, "CH591R, TMR0 下降沿直连, 60MHz 实测"),
|
||||
}
|
||||
|
||||
|
||||
def parse_freq(text):
|
||||
"""'100k' / '3M' / '1.5e6' / '480' -> float Hz"""
|
||||
t = str(text).strip()
|
||||
mult = 1.0
|
||||
if t[-1] in "kK":
|
||||
mult, t = 1e3, t[:-1]
|
||||
elif t[-1] in "mM":
|
||||
mult, t = 1e6, t[:-1]
|
||||
return float(t) * mult
|
||||
|
||||
|
||||
def window(base, f_cap, edge, divider, f_coil, f_now=None):
|
||||
"""返回 dict:Xn、LPCNT、窗长(tick)、窗时长(s)、偏差、口径验证"""
|
||||
f_now = f_coil if f_now is None else f_now
|
||||
C = divider * f_cap / float(edge) # 判据常数: Xn = C / f_coil
|
||||
xn0 = C / f_coil
|
||||
lpcnt = int(base // xn0)
|
||||
if lpcnt == 0: # 固件兜底: Xn > BASE 时 LPCNT=1
|
||||
lpcnt = 1
|
||||
xn_float0 = xn0
|
||||
xn0 = round(xn0) # 固件里 Xn 是整数计数
|
||||
xn1 = round(C / f_now)
|
||||
window_ticks = lpcnt * xn0 # 学习时/稳态
|
||||
window_ticks_now = lpcnt * xn1 # 当前频率下
|
||||
r0 = base % xn0 if xn0 <= base else base
|
||||
return {
|
||||
"C": C, "Xn0": xn0, "LPCNT": lpcnt, "r0": r0,
|
||||
"window_ticks": window_ticks,
|
||||
"nominal_ticks": base,
|
||||
"window_s": window_ticks / f_cap,
|
||||
"nominal_s": base / f_cap,
|
||||
"dev_pct": (window_ticks / base - 1.0) * 100.0,
|
||||
"window_s_now": window_ticks_now / f_cap,
|
||||
"dev_now_pct": (window_ticks_now / base - 1.0) * 100.0, # vs 标称上限
|
||||
"dev_now_vs_learned_pct": (window_ticks_now / float(window_ticks) - 1.0) * 100.0, # vs 学习时窗
|
||||
"ratio_exact": xn1 / float(xn0), # Value/Origin 应为该值
|
||||
"ratio_formula": f_now and (f_coil / f_now),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="周期累积法测量窗量化计算器")
|
||||
ap.add_argument("--model", choices=sorted(MODELS), help="内置型号")
|
||||
ap.add_argument("--base", type=int, default=32768)
|
||||
ap.add_argument("--f-cap", default="3M")
|
||||
ap.add_argument("--edge", type=int, choices=(1, 2), default=2, help="捕获沿系数: 双沿=2 单沿=1")
|
||||
ap.add_argument("--divider", type=int, default=32, help="前端分频比 (CD4060 ÷32=32, 直连=1)")
|
||||
ap.add_argument("--f-coil", default="100k", help="线圈频率 (100k / 3M / 480)")
|
||||
ap.add_argument("--f-now", default=None, help="当前频率(默认=f-coil),用于看频率跟踪分量")
|
||||
a = ap.parse_args()
|
||||
|
||||
if a.model:
|
||||
base, fcap, edge, div, note = MODELS[a.model]
|
||||
print("型号 %s: %s" % (a.model, note))
|
||||
else:
|
||||
base, fcap, edge, div = a.base, parse_freq(a.f_cap), a.edge, a.divider
|
||||
|
||||
f_coil = parse_freq(a.f_coil)
|
||||
f_now = parse_freq(a.f_now) if a.f_now else None
|
||||
w = window(base, fcap, edge, div, f_coil, f_now)
|
||||
|
||||
print("参数: BASE=%d f_cap=%.6g Hz 边沿系数=%d 分频=%d 线圈=%.6g Hz"
|
||||
% (base, fcap, edge, div, f_coil))
|
||||
print("判据常数 C = 分频·f_cap/边沿 = %.4g (Xn = C/f_coil)" % w["C"])
|
||||
print("Xn0 = %.2f -> 取整 %d ; LPCNT = floor(%d/%d) = %d ; r0 = BASE mod Xn0 = %d"
|
||||
% (w["C"] / f_coil, w["Xn0"], base, w["Xn0"], w["LPCNT"], w["r0"]))
|
||||
print("窗长 = LPCNT·Xn0 = %d tick (标称上限 %d tick)" % (w["window_ticks"], w["nominal_ticks"]))
|
||||
print("窗时长 = %.4f ms (标称上限 %.4f ms) 偏差 %+.2f%%"
|
||||
% (w["window_s"] * 1e3, w["nominal_s"] * 1e3, w["dev_pct"]))
|
||||
if f_now and abs(f_now - f_coil) > 1e-9:
|
||||
print("频率跟踪: f_now=%.6g Hz -> 窗 %.4f ms(vs 标称 %+.2f%% / vs 学习时窗 %+.2f%%)"
|
||||
% (f_now, w["window_s_now"] * 1e3, w["dev_now_pct"], w["dev_now_vs_learned_pct"]))
|
||||
print(" 比值口径验证: Value/Origin = %.6f ← 应等于 f0/f_now = %.6f"
|
||||
% (w["ratio_exact"], f_coil / f_now))
|
||||
print("口径提示: Δf/f = 表值/65536 不受窗长/ BASE 影响(余数约掉);"
|
||||
"量化误差上限 = Xn0/BASE = %.2f%%" % (w["Xn0"] / base * 100.0))
|
||||
|
||||
# 无参时打印族内对照表
|
||||
if not any([a.model, a.f_now]) and a.f_coil == "100k":
|
||||
print("\n附:内置型号对照(同为 100kHz 线圈,前端均按各自 divider)")
|
||||
print("%-11s %8s %6s %5s %8s %11s %8s" % ("型号", "f_cap", "Xn0", "LPCNT", "窗tick", "窗时长", "偏差"))
|
||||
for name, (b, fc, e, dv, _n) in MODELS.items():
|
||||
C = dv * fc / float(e)
|
||||
if not (1 <= C / 1e5 <= b): # 该频率不在其频段内则跳过
|
||||
continue
|
||||
ww = window(b, fc, e, dv, 1e5)
|
||||
print("%-11s %7.3gM %6d %5d %8d %9.4fms %+7.2f%%"
|
||||
% (name, fc / 1e6, ww["Xn0"], ww["LPCNT"], ww["window_ticks"],
|
||||
ww["window_s"] * 1e3, ww["dev_pct"]))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user