TT Lab
开始
学习 学习路径 课程

电缆也有回声

TDR:利用反射极性和往返延迟定位边界

在 TT Lab 中继续学习

目标

根据反射的符号和往返延迟还原一个阻抗边界,对于没有反射的情形,则保留为无法观测位置。

预计需要 80 分钟。请在默认的 60 分钟会话结束之前,用“+时间”延长(最多 180 分钟)。Pod 中的文件在结束后会消失。请在结束之前另行保存源代码和结果。先修知识是前面的信号完整性实验,以及 Python 函数、列表、JSON、文件处理。

为什么重要

即使数值看起来正确,只要混淆了反射符号、时间原点或往返区间,报告出的就是另一个边界。要同时防止两种错误:把配置里写的位置原样写进结果,以及从没有反射的波形中凭空得出位置。电路是理想的单模无损线路,不能代替实物测量与认证。

步骤

  1. 编写 analyze.py 的 config(value)。对象的键只允许 lead_ns、tail_ohm、rise_ns。值分别是 2/3/4、25/50/100、0.2/0.6 中的一个。对于 bool、字符串、NaN、无穷大、缺失、多余的键,要以 ValueError 拒绝,并返回没有修改原始数据的新 dict。不要仅因 import 就执行或输出任何内容。
  2. read_trace(path) 读取 2MiB 以下、表头为 time v(tx)、以空白分隔的 TSV,返回 [(time_ns,voltage_v),...]。两列都必须是有限数值,时间必须是不小于 0 且严格递增。需要至少 100 行、第一个时刻为 0、最后一个时刻为 16ns(误差在 1e-9ns 以下)。损坏或不足的数据为 ValueError,并且不修改文件。最初文件的 time 单位是 s。
  3. plateaus(rows) 接收 ns/V 两列的列表,返回闭合窗口 [2,3]ns 内的中位数 launch_v,以及 [12,14]ns 内的中位数 settled_v。每个窗口至少需要两个点。行数不少于两行,时刻不小于 0 且严格递增,值必须是非布尔的有限数字,不足或损坏为 ValueError。不要修改原始行。
  4. reflection(launch_v,settled_v) 返回 gamma=(B-A)/A 以及 z_ohm=50*(1+gamma)/(1-gamma)。输入是有限数字,禁止 bool。如果不满足 A>0、-1
  5. crossing(rows,threshold_v,after_ns,direction) 返回 direction=整数 1 或 -1 的第一个穿越时刻 ns。上升是对满足 a.vthreshold>=b.v 的区间做线性插值,并选取结果时刻>=after_ns 的第一个事件。没有则为 None。rows 要满足与第 3 步相同的有效性条件,after_ns 是不小于 0 的有限数,阈值是有限数值。错误的值或 bool 方向为 ValueError。
  6. analyze(folder) 读取 config.json 和 trace.tsv,返回 config、launch_v、settled_v、gamma、z_ohm、launch_ns、return_ns、distance_m、echo、consistent、simulated、physical_certified。出发是 A/2 的上升穿越,如果有反射,返回则是 after=3ns 之后 (A+B)/2 的穿越(符号方向)。echo 是 |gamma|>1e-6,否则返回时刻和距离都为 None。距离是 0.2*(返回-出发)/2 m。如果没有时刻,距离也为 None。请保持 simulated=True、physical_certified=False。一致性标准遵循下面的参考。
  7. campaign(values,folder) 预先验证由 1–12 个互不重复的配置组成的整个 list 之后,创建新文件夹。对每个配置运行提供的 simulator.capture(config,folder/case-NN),并用 analyze 分析。NN 按输入顺序从两位数 00 开始。如果分析结果的 config 与请求不同,则为 ValueError。返回 dict:cases 按顺序为结果列表,passed 表示所有 consistent 是否为 True,measured 为 False。配置错误时也不创建输出文件夹,也不覆盖已有的空文件夹。
  8. 编写 analyze.py --configs <JSON 목록 경로> --out <새 폴더>(占位符依次为 JSON 列表路径与新文件夹)命令行程序(CLI)。运行 campaign,把返回的对象以相同的 JSON 保存到 report.json 并输出到 stdout。passed=True 时以 0 退出,False 时以 2 退出,并保留报告。配置或文件错误时以非 0 退出,并把原因写入 stderr。要保留每个 case-NN 的 config.json、circuit.cir、ngspice.log、trace.tsv,不要重复使用已有的结果或已有的空输出文件夹。

参考

所有实现都保存到 /root/tdr-location/analyze.py 中。不需要额外安装、联网或真实设备。提供的辅助程序是 /opt/lab/fixtures/tdr_location/simulator.py,只负责配置验证、电路生成和真实运行。像下面这样指定 PYTHONPATH,代码中的 from simulator import capture 才能工作。

PYTHONPATH=/opt/lab/fixtures/tdr_location python3 /root/tdr-location/analyze.py   --configs /opt/lab/fixtures/tdr_location/cases.json --out /root/tdr-location/run-01

辅助程序 capture(config,新文件夹) 返回 Path,并留下 config.json、circuit.cir、ngspice.log、trace.tsv。错误的配置会在运行之前被拒绝。因为会拒绝已有的文件夹,所以重新运行时请使用像 run-02 这样的新名称。默认的 cases.json 是 TD 2/3/4ns × Z2 25/50/100Ω 的 9 个条件,rise_ns=0.2。

第 6 步中 consistent 的条件必须同时满足。要有出发时刻,A 与 0.5V 的差在 1e-6V 以下,观测的 gamma 与 (tail_ohm−50)/(tail_ohm+50) 的差在 1e-6 以下,echo 必须等于 tail_ohm!=50。如果配置上有反射,就要有两个穿越,并且观测到的往返时间与 2*lead_ns 的差在 0.02ns 以下。如果配置上没有反射,则 return_ns 必须是 None。即使模型不一致,也不删除观测值,而是以 consistent=False 保留。ngspice 原始文件是时间单位为 s 的 time v(tx) 两列 TSV。分析返回的时刻单位是 ns,距离单位是 m。

波形和配置只能读取。analyze.py 必须是 64KiB 以下的普通文件,并且 import 时不输出任何内容。评分会运行另外的小采样点和 4 个真实电路条件(包括慢边沿),数值容差默认为 1e-8,真实反推阻抗为 1e-6Ω,时刻为 0.001ns,距离为 0.001m。布尔值和 None 会被严格区分。评分进程的限制是 CPU 12 秒、文件 8MiB、总计 35 秒,结束后会清理子进程。这个运行限制与学习时间不同。被强制终止或输出缺失,都不算通过。

先固定实验条件

编写 analyze.py 的 config(value)。对象的键只允许 lead_ns、tail_ohm、rise_ns。值分别是 2/3/4、25/50/100、0.2/0.6 中的一个。对于 bool、字符串、NaN、无穷大、缺失、多余的键,要以 ValueError 拒绝,并返回没有修改原始数据的新 dict。不要仅因 import 就执行或输出任何内容。

在 Python 中,bool 是 int 的子类型。请显式检查类型,并确认所有字段都是有限的允许数值。

把秒换算成 ns,并拒绝被截断的波形

read_trace(path) 读取 2MiB 以下、表头为 time v(tx)、以空白分隔的 TSV,返回 [(time_ns,voltage_v),...]。两列都必须是有限数值,时间必须是不小于 0 且严格递增。需要至少 100 行、第一个时刻为 0、最后一个时刻为 16ns(误差在 1e-9ns 以下)。损坏或不足的数据为 ValueError,并且不修改文件。最初文件的 time 单位是 s。

先确认表头和行的长度。1e9 的换算只做一次,重复的时刻也要拒绝。如果不确认最后一行,部分输出就会看起来像完整的结果。

在不同窗口读取两个平台

plateaus(rows) 接收 ns/V 两列的列表,返回闭合窗口 [2,3]ns 内的中位数 launch_v,以及 [12,14]ns 内的中位数 settled_v。每个窗口至少需要两个点。行数不少于两行,时刻不小于 0 且严格递增,值必须是非布尔的有限数字,不足或损坏为 ValueError。不要修改原始行。

这不是整个波形的平均值,也不是两个端值的平均值。窗口边界上的采样点也要包含,采样点个数为奇数和偶数时,都要遵循中位数的约定。

把负的反射也还原成阻抗

reflection(launch_v,settled_v) 返回 gamma=(B-A)/A 以及 z_ohm=50*(1+gamma)/(1-gamma)。输入是有限数字,禁止 bool。如果不满足 A>0、-1

B/A 是连原来的前进波也包含在内的比值。必须用 B-A 分离出反射分量,不要用绝对值抹掉负的反射。

对上升沿和下降沿做插值

crossing(rows,threshold_v,after_ns,direction) 返回 direction=整数 1 或 -1 的第一个穿越时刻 ns。上升是对满足 a.vthreshold>=b.v 的区间做线性插值,并选取结果时刻>=after_ns 的第一个事件。没有则为 None。rows 要满足与第 3 步相同的有效性条件,after_ns 是不小于 0 的有限数,阈值是有限数值。错误的值或 bool 方向为 ValueError。

即使 after_ns 位于区间之内,只要插值结果满足条件就是有效的。起点已经为高的情形,或者平坦区间本身,不算作新的穿越。

从真实波形中读出边界的位置

analyze(folder) 读取 config.json 和 trace.tsv,返回 config、launch_v、settled_v、gamma、z_ohm、launch_ns、return_ns、distance_m、echo、consistent、simulated、physical_certified。出发是 A/2 的上升穿越,如果有反射,返回则是 after=3ns 之后 (A+B)/2 的穿越(符号方向)。echo 是 |gamma|>1e-6,否则返回时刻和距离都为 None。距离是 0.2*(返回-出发)/2 m。如果没有时刻,距离也为 None。请保持 simulated=True、physical_certified=False。一致性标准遵循下面的参考。

不要把配置中的 lead_ns 直接当作距离,而要使用观测到的两个穿越时刻。对于没有反射的情形,即使配置中有边界,也不要凭空得出观测位置。

验证全部配置之后再运行

campaign(values,folder) 预先验证由 1–12 个互不重复的配置组成的整个 list 之后,创建新文件夹。对每个配置运行提供的 simulator.capture(config,folder/case-NN),并用 analyze 分析。NN 按输入顺序从两位数 00 开始。如果分析结果的 config 与请求不同,则为 ValueError。返回 dict:cases 按顺序为结果列表,passed 表示所有 consistent 是否为 True,measured 为 False。配置错误时也不创建输出文件夹,也不覆盖已有的空文件夹。

如果只验证并运行第一个配置,后面的配置有误时就会产生部分结果。请用 PYTHONPATH 连接提供的模块路径,并把观测文件夹中的配置与请求重新比较。

保留报告和原始证据

编写 analyze.py --configs <JSON 목록 경로> --out <새 폴더>(占位符依次为 JSON 列表路径与新文件夹)命令行程序(CLI)。运行 campaign,把返回的对象以相同的 JSON 保存到 report.json 并输出到 stdout。passed=True 时以 0 退出,False 时以 2 退出,并保留报告。配置或文件错误时以非 0 退出,并把原因写入 stderr。要保留每个 case-NN 的 config.json、circuit.cir、ngspice.log、trace.tsv,不要重复使用已有的结果或已有的空输出文件夹。

把运行代码放在 name 保护之下。如果只输出报告而丢弃原始波形,就无法重新审查。不要把异常变成成功。