用户指令(2026-10-30):确认录波任务完成度,把除实际操作之外的所有 步骤做完,然后删除规划文件,避免已完成的工作被误读为未做。 审计结论:代码面确已全部收口——wave_read.py --selftest 92/92、 三套主机单测 113/170/110 复跑全 rc=0、bench 自抓与 PC 读段脚本在仓、 数据可信/复刻明细/短板评测/UI 两处回写均已做过。**唯一未跑的是 板级台架那一轮(实际操作)**,判据不得丢,故先迁后删: - 设计说明 §9 承接落盘道失败留痕名全清单(13+1 条,打印点现值 default_task.c:816/:831 实测核对) - 设计说明 §10 承接原卡 2/卡 3 的板级段(触发来路一行、回放页 一行)与本波执行顺序图(mermaid,原规划 §3 逐字迁) - §4/§11 头部定位、§11 尾注、§14 关系行改属主口径 - 全库 32 处引用改挂设计说明 §10/§9(18 文件):任务规划 README 独立行(顺带订正单测数 100→113 为实测)、登记簿、数据可信 ×2、 复刻明细、产品需求 ×2、ADC 注入 ×3、短板评测 ×2、页面补齐规划、 App/algo README、配图生成器注释、录波三套单测注释 ×6 - git rm docs 该规划文件;人类索引任务规划 6→5 个文件(沿革注照 屏板链路先例走裸句,不留反引号死引用) 验证:残留两形态(任务规划/故障录波、裸文件名)全树扫描 0; mermaid 预检新发 0;doc-links 11 个被改文件逐文件 PASS;生成器 注释改动重跑产物逐字节不变(svg 60a0dd39e4);三套单测与 wave_read selftest 复跑全绿;18 文件 BOM/CR/U+FFFD 全零。 本笔不含固件编译单元(test/host 仅注释行与文档),构建留待合并 轮统一跑;板级轮启动判据与顺序见设计说明 §10。
48 KiB
故障录波设计说明(1 kHz · 8 路原始码值 · 现场单通道回放)
模块名称:故障录波——采样源沿用 TIM3 1 kHz 节拍,专用录波环(
App/algo/wave_ring.c,已落地)、触发、落盘(defaultTask 是文件系统唯一写者)、取回(FTP 21 端口)、现场回放(UI 单通道页)。 文档定位:本波的设计属主——口径、通道集、数据形态、触发模型、文件格式、预算与所有权边界、显示契约、二期不返工的结构约束在此定稿。板级判据与本波执行顺序的属主即本文 §9 与 §10——原任务规划下的《故障录波》卡片登记簿已于 2026-10-30 随销项并入本文后删除(沿革见 §14);排期顺位在docs/任务规划/README.md§2.0「独立」行。 状态:设计已定稿,数据面 + 触发与落盘接线 + UI 两个页面(手动录波、录波回放)的代码面已交付(2026-10-29)——主机单测 113 / 170 / 110 全 rc=0、双端编译 0 错误 0 警告、板端编码器与 PC 侧解码器两份独立实现字节级互验通过。没做完的只有板级判据那一轮(注量对表、FTP 拉回、与meas:和 104 浮点 YC 对表、CRC 不符必须拒收的实测、回放页放一段真波、两个待台架核数值),逐条列在本文 §10 板级各行。三条已裁定口径见 §1.2。 相关文档:docs/子系统设计/ADC采样节拍设计说明.md(节拍与时序账的属主;本文 §3 的数据源就是它)docs/硬件平台/ADC通道映射说明.md(网名AINn不等于板级ADCn,也不等于转换器通道号;链路分段与削顶归属)docs/任务规划/数据可信.md§4.6(动态波形验证判据;录波落地后它的读数出口升级,见 §11)docs/架构设计/STM32F4-Base存储架构说明.md§10.1(FatFs 两条实证陷阱与 8.3 短名限制)docs/内存管理/内存内容分配策略.md§6 与 R6 规则(新增静态缓冲大于 4 KB 的前置动作)docs/UI设计/内容页架构.md、docs/UI设计/RemoDispBus协议与接收设计说明.md(回放页与远端看图的能力边界)
目录
§1三条已裁定口径:1 kHz、8 路全录、一屏一路§2与产品需求 80 点/周波口径的偏离,与二期不返工的四条结构约束§3录什么:通道归属、削顶类标记、为什么存原始码值§4触发模型与段长冻结语义§5四个所有者与三条并发陷阱§6段文件字节布局§7预算(R6 是硬前置)§8现场回放显示契约§9观测:只加一条周期行§10测试与门禁,含 bench 自抓红线§11与数据可信任务卡的三处口径变更§12风险§13范围边界§14与其它文档的关系
1. 这一波要解决什么
1.1 两个动机,一个数据面
| 动机 | 谁提的 | 要求 |
|---|---|---|
| 现场能大概看一眼波形 | 产品(用户裁定 2026-10-29) | 装置上按几下就能看「这一路现在是不是正弦、削没削顶」,不必插串口、不必带笔记本 |
| 台架能把样本拿出来对表 | docs/任务规划/数据可信.md §4.6 |
把板上采到的原始样本导出,在 PC 上独立重算全周傅氏,与板上派生读数逐个对——这才是「验证我们后面的各种计算」唯一硬的做法 |
两条共用同一个数据面:录波环 + 段文件 + 取回通路。区别只在「谁来触发、给谁看」。分开做会变成两套导出机制,所以并成一件。
1.2 三条已裁定口径(本文其余章节都以此为前提)
| # | 口径 | 裁定 | 代价与换来什么 |
|---|---|---|---|
| 1 | 采样率 = 1 kHz(20 点/周波) | 复用现有 TIM3 节拍,不改采集 | 与产品需求的「80 点/周波(4 kHz)」口径存在已登记偏离(§2);换来的是采集主线零改动——不动 Src/tim.c、不动 CubeMX、不动相量与测频 |
| 2 | 8 路电气量全录 | 五路削顶也照录 | 数据不筛选,现场看见的是真实形状;代价是文件必须自带「这一路可信吗」的标记(§3.1 与 §6.2),否则削顶会被读成「系统里真有零序」 |
| 3 | 一屏只画一路,支持 8 路切换 | 160×160 单色屏画不出多路 | 不需要图例(本来也没有颜色);8 路切换用方向键在同一页内换通道,不新增菜单叶子(§8.2) |
2. 与产品需求口径的偏离(显式登记,不许静默降级)
docs/产品需求/参考产品复刻明细.md §7 的「录波采样率」行写着:录波 = 20 × 4 = 80 点/周波(50 Hz 下 4 kHz),理由也写在那一行——「保护计算用 20 点/周波,录波要 4 倍过采样才能看清波形」。本波只做 20 点/周波,是明知故犯的偏离,把理由与代价写在明处:
| 项 | 内容 |
|---|---|
| 为什么先做 1 kHz | 抬到 4 kHz 的第一块骨头不在录波,而在采集主线:一拍时序账要重算(现在 TPAFE 转换约 23 µs、EXTI 读 8 通道约 0.5 µs、ADC1 第 9 点约 67 µs、读窗与下次 CONVST 相隔约 977 µs,见 docs/子系统设计/ADC采样节拍设计说明.md 的时序账),半区语义与「恒定小偏移」都要重推;算法层还有两处按 1 kHz 写死的假设(App/algo/phasor.h 的 PHASOR_N 20U、App/algo/freq.h 的 FREQ_FS_HZ 1000.0f)会被一起牵动 |
| 芯片支持吗 | 支持:TPAFE5160 是 16 bit / 350 kSPS 全通道同时采样(docs/芯片手册/TPAFE5160.md 首行)→ 4 kHz 是纯固件与算法的工程账,不是硬件上限 |
| 1 kHz 能看到什么 | 基波形状、削顶、直流偏置、明显畸变、频率(数过零)。看不到:2/5 次谐波的形状细节(每周期只剩 10 / 4 个点)、暂态前沿 |
| 偏离登记 | 落码时在产品需求那一行补一句指向本文;「看清波形」这一目标在 1 kHz 下只算部分达成,不许拿 1 kHz 的图当满足 80 点口径的证据 |
2.1 二期不返工的结构约束(本波就写进代码注释)
将来抬到 4 kHz 时只允许加、不允许改的四条规则,本波先立着:
- 录波环按「全速原始序列」建模:「每周波点数」(现 20)与「采样率」(现 1000)是写进文件头的数据,不是编译期假设——回放端与 PC 脚本一律从头里读;
- 算法层永远看 1 kHz 视图:抬频时在 ADC 侧加 4:1 抽取喂
sample_ring与freq,PHASOR_N与FREQ_FS_HZ不许被改; - 录波环与
sample_ring解耦(各有独立缓冲与指针,见 §5.1),二期全速只扩录波环,不牵动相量输入; - 文件格式的版本号字段本波置 1,二期换格式时递增,PC 侧按版本分派。
3. 数据源:录什么、从哪一帧录
3.1 语义通道顺序与转换器归属(不可混为一谈的两类)
语义枚举与归属见 App/algo/adc_input.h(ADC_SEM_UA 到 ADC_SEM_IN,再加 ADC_SEM_TEMP 与 ADC_SEM_COUNT):
| 通道 | 转换器 | 位宽与量程 | 在录波里的含义 |
|---|---|---|---|
| UA / UN / IA | TPAFE5160 | 16 bit,±5 V 双极性 | 可信类:能录出真实的双向工频波形 |
| UB / UC / IB / IC / IN | STM32 ADC1 | 12 bit,0 到 3.3 V 单极性、无中点偏置 | 形状受限类:交流必然把负半周钉在下轨。录它是为了如实呈现 V0.1 现状,不是为了当工频量用 |
| TEMP | STM32 ADC1 | 12 bit | 可选附带单点(非线性未折算,docs/任务规划/数据可信.md §7 已定案) |
两条硬件事实决定回放能做到什么,必须进文件头,也必须进页面:
- 跨转换器无共同采样触发(V0.1 硬件缺陷):8 路分属两片转换器,没有同一时刻性 → 禁止把 U 与 I 叠在同一张图上比相位。文件头按通道写「来源转换器 + 采样时刻偏移」;回放只画单通道,这不是妥协,是这条硬件事实逼出来的形态;
- STM32 侧另有 1 拍乒乓滞后(
App/algo/adc_input.h的取数口径注释)→ 同一转换器内也不是严格同刻。
3.2 存原始码值,不存折算工程量(本波最重要的一条数据形态决策)
| 理由 | 说明 |
|---|---|
| 现在根本没有工程量 | 两点标定未做,adc_cal_flags() 的 ADC_CAL_F_VALID 未置位 → adc_cal_prot_blocked() 返 1,帧值仍是引脚域电压(docs/任务规划/数据可信.md §4.3)。「录波必须先有工程量」这条旧前提被本条推翻 |
| 绕开测频幅值门 | App/algo/freq.h 的 FREQ_AMP_MIN 50.0f 以二次值 V 为口径,eng=0 期间帧值远小于它恒不过门 → freq_valid()=0。录波取的是这道门之前的原始帧,所以不受影响,动态验证才有样本可对 |
| 最省且可追溯 | int16 码值 2 B/点;标定完成后不必重录,PC 端拿头里的 k / b 就能把老文件重算成工程量 |
| 诚实 | 文件里装的是「板子看见的东西」,不是「我们希望它看见的东西」 |
文件头因此必须带:每路的来源转换器、位宽、LSV 用 µV 计的定点值(TPAFE 152.59 µV、STM32 805.66 µV)、封存瞬间的 eng 档位、折算 k / b 快照、有效性位图与削顶位图。缺任何一项,老文件在新固件上就会被解释错。
4. 触发模型
4.1 四种触发,本波做前三种
| 触发 | 来源 | 本波 |
|---|---|---|
| 手动 | UI「手动录波」操作页(叶子在菜单装置维护组,页面模块 Drivers/BSP/UI/ui_page_wave_manual.c,2026-10-29 已绑页;确认键在 App/task/ui_task.c:582 行调 wave_ring_request(),走手动来路、元件号填 0) |
做(已落地) |
| 保护启动 | 落点在 App/task/protect_task.c 的 s_log_record_edge()(函数体自 158 行起,picked 上升沿分支 170 到 177 行,实际调用在 176 行:wave_ring_request(WAVE_TRIG_PROTECT, 最低位上升元件)),与 SYSLOG_START 同一条边沿、同一事件点,避免两处各自判定。刻意不落在 App/algo/prot_elm.c:判据核被三套主机单测源码级纳入编译,往它加录波依赖会一次全破——这条就是「判据核零改动」的前提。多元件同拍启动只录一段(环不重入,后续请求由环计 ovf,不算失败) |
做(复用事件点,不新增判据) |
| 定周期自抓 | DTU_BENCH 构建下每 N 秒强制一段,供信号发生器对表 |
做,只在 bench 构建,产品构建无此路径 |
| 突变量启动 | 需要增量化 Δi 类算法与定值 |
不做,归二期(docs/算法/继电保护功能复现规划.md 的「故障启动(波形启动)」行) |
4.2 段长与冻结语义(RAM 就靠这一条省下来)
- 一段 = 预触发 8 周波 + 后触发 8 周波 = 320 点/路(1 kHz 下 320 ms);
- 段内点序(两份实现共同遵守,PC 与回放页都按这条):触发那一拍的样本落在段内点 159(预触发 160 点的最后一点),后触发第一拍是点 160,两半不重叠,故障时刻就是这条分界。因此段内点 0 相对工频周期边界有任意偏移(触发瞬间在周期内任意落点),段字节里没有绝对时间参考——能核对的是相对相位与极性,绝对相位只能以段内点 0 为参考报出(§10 板级判据据此不比绝对相位);
- 触发即冻结环:停止推进录波写指针,defaultTask 直接从已冻结的环落盘,落完解冻;
- 冻结期间丢的是录波样本,不是采集——
sample_ring、相量、保护判据照常跑(这就是 §2.1 第 3 条要求解耦的原因),冻结不拖慢保护动作,也不让 104 断数; - 解冻的唯一入口是落盘方回执(
wave_ring_write_done(result),只在 defaultTask 上下文调):写成功才推进轮转段号,写失败只计 err 且下一段重写同一个号(不留半截文件);冷却期从回执之后才开始计时,回执前新触发一律只加 ovf。「落完解冻」这条原本按时间窗口的假设,实现里改由回执保证,冷却数值只是二次抑制。 - 兜底(绝不允许录波把环焊死):封存态滞留超过
WAVE_SEAL_TIMEOUT_MS(当前 2000 拍 = 2 s,待台架核)就自弃本段、计 err 并解冻——卡在一次落盘上,代价是下一次真故障无波可录。 - 代价:一次触发的落盘窗口(约几十 ms)内再来触发只记第一次(冷却期),不做多段排队——那是二期「6 段 / RAM 16 个」的形态。
flowchart TD
IDLE[空闲: 录波环照常滚动写入] --> ARM{触发源有效?}
ARM -- 无 --> IDLE
ARM -- 手动 / 保护启动 / bench 定时 --> FRZ[冻结环: 停止推进写指针]
FRZ --> POST[后触发计满 8 周波]
POST --> PACK[封段: 写文件头与通道表]
PACK --> W[defaultTask 落盘到 /wave 8.3 短名]
W --> OK{写成功且字节数相符?}
OK -- 否 --> ERR[记 wave: 失败日志, 丢弃本段]
OK -- 是 --> MARK[段计数与最新段号入快照]
ERR --> REL
MARK --> REL[解冻: 恢复滚动写入]
REL --> COOL[冷却期: 期间新触发只计数不重录]
COOL -. 计满 .-> IDLE
COOL -. 期间又来触发 .-> SKIP[丢弃: 溢出计数加一]
SKIP --> COOL
图内不写步骤号;冻结 → 后触发 → 封段 → 落盘 的四段动作在 §4.2 与 §5 正文里。
5. 所有权与并发边界(这块最容易做错)
5.1 谁写谁读
| 角色 | 职责 | 不许做 |
|---|---|---|
| adcTask(生产者) | 每拍把 8 路原始码值(可带 TEMP)压进录波环;查触发标志;触发时冻结 | 不碰 FatFs、不碰网络 |
| protectTask | 在 picked 上升沿置一个请求标志(原子置位,与 prot_elm_request_reset() 同一模式) |
不写环、不落盘 |
| defaultTask(文件系统唯一写者) | 排空已封存的段 → f_write → 校验字节数 → 更新段账 |
不读活缓冲,只读已封存区 |
| uiTask(回放页) | 只读窗口取段数据 → 自绘(与 App/log/soe_store.c 读侧同构) |
不写文件 |
| netTask / lftpd | 把 /wave 暴露给 FTP 取回 | 不在 netTask 里读录波环 |
5.2 四条已知陷阱,本波必须绕开
| 陷阱 | 事实出处 | 本波处置 |
|---|---|---|
| 取窗竞态 | App/algo/sample_ring.c 的 sample_ring_window() 没有临界区(读 s_head 后逐点拷),现在只有 adcTask 同任务调用才安全 |
录波不复用它:adcTask 在自己的节拍里取「本拍帧」压专用环;跨任务只传递「已封存」标志,杜绝跨任务读活缓冲 |
| FatFs 追加写 | 该移植 FA_OPEN_APPEND 不含 FA_WRITE,且 f_write 簇分配失败时返回 FR_OK 加短写、无错误码(docs/架构设计/STM32F4-Base存储架构说明.md §10.1) |
打开一律 FA_WRITE|FA_OPEN_APPEND;判成功必须比对写回字节数,短写按失败处理(丢段 + 日志),绝不当成功记账 |
| 只能 8.3 短名 | 出处同一张存储架构说明的短名限制:FF_USE_LFN=0,不得以点开头、不得长名 |
文件名 = WAVE 加两位十进制再加 .BIN,共 100 个名字轮转覆盖;目录 /wave(与 /log 平级,FTP 根下可见) |
| 落盘途中环被解冻、甚至已换段(实现期复核新增第四条,2026-10-29) | adcTask 优先级高于 defaultTask:搬段窗口里 adcTask 可能正好走到 SEALED 滞留 2 s 自弃,甚至已经封出下一段。数据区 CRC 是对「本道实际读到的字节」算的,半新半旧的段照样自洽——长度等式照过、两个 CRC 照对,文件骗得过任何读者,也骗过我们自己 | 写完关档后复检环侧身份:wave_ring_sealed() 仍为 1,且 wave_ring_get_stat() 的 last_t0_ms 与 last_seq 跟本道开始搬时那份段头一致;不符即走既有失败路——unlink 本道开过的文件 + write_done(-1)(此时多半回 E_STATE,环侧 err 已由自弃计过,不重复补账)+ 段号不推进(下一段同号重写覆盖)+ 单条 [ERR] … seg drop: ring abandoned 留痕。落点 App/task/default_task.c:786 到 796 行。当前只有代码与单测面,板级未测;可观察项见 §10 板级行 |
图由 tools/arch_diagram/gen_wave_record_flow_svg.py 生成(改图改脚本重跑,勿手改 SVG)。
6. 段文件格式
6.1 本波为什么不用 COMTRADE
| 判据 | 结论 |
|---|---|
| 谁要用 | 本波消费者只有两个:装置自己的回放页、仓内一个 PC 脚本——没有任何第三方要求 COMTRADE |
| 代价 | COMTRADE 要「配置 + 数据」两份文件(扩展名 CFG 与 DAT)、ASCII 通道表、时标与采样率段;FatFs 只有 8.3 短名,两套文件名要各占一个号,段数一多就不够分;多段与时标在 Flash 余量里换不来可读性收益 |
| 归属 | 送检或第三方交接时才需要 → 二期(docs/问题记录/采集与数据处理短板评测.md 的记录链缺口行已登记 COMTRADE) |
6.2 段文件布局(小端;文件名 WAVE 加两位十进制)
| 偏移 | 字段 | 宽度 | 说明 |
|---|---|---|---|
| 0 | magic | 3 | 前两字节固定 WR,第三字节是版本号(本波写 1);版本号并进 magic,换格式即变(§2.1 第 4 条) |
| 3 | ver | 1 | 恒 1 |
| 4 | fs_hz | 2 | 采样率,本波恒 1000 |
| 6 | pts_per_cycle | 1 | 每周波点数,本波 20 |
| 7 | ch_count | 1 | 8(带 TEMP 则 9) |
| 8 | pts_per_ch | 2 | 320 |
| 10 | pre_pts | 2 | 160(预触发点数,回放画触发光标用) |
| 12 | trig_src | 1 | 1 是手动,2 是保护启动,3 是 bench 定时 |
| 13 | trig_elm | 1 | 触发元件序号(prot_elm 实例下标;手动与定时为 0) |
| 14 | t0_ms | 4 | 段首相对上电时刻;配合墙钟还原绝对时刻 |
| 18 | wall | 4 | 对时后的 epoch 秒(未对时为 0) |
| 22 | eng | 1 | 封存瞬间的折算档位:0 引脚域、1 二次值、2 一次值 |
| 23 | flags_seq | 1 | 每路 VALID 压成的位图:bit s = 语义通道 s 的 ADC_CAL_F_VALID(电气 8 路恰好占满 1 字节;TEMP 不参与标定,若要带 TEMP 连本行与下一行一起升版本) |
| 24 | clip_mask | 2 | 削顶归属位图:bit s = 语义通道 s,与 App/algo/meas.h 同名参数同口径(封存瞬间 ADC_CAL_F_CLIPPED 快照,V0.1 恒 0xE6 = UB/UC/IB/IC/IN,见 docs/任务规划/数据可信.md §2.7)。⚠ 这个位图的本体是「链路可信性标记(按来源派生)」,不是「本段实测饱和标记」:置位条件一次样本都不看(App/algo/adc_cal.c 291 到 296 行,该路 ROM 系数属片内 ADC 侧即置位);「这一段有没有真钉轨」走另一条链——App/algo/adc_health.c 的 adc_health_rail_watch()(280 行起)与 adc_health_rail_sticky(),门限按满量程百分比由 adc_cal_rail_ths() 给、须连续 ADC_RAIL_N_RUN 拍。V0.2(8 路全接 TPAFE)后本位自动恒 0,届时「与 clip_mask 对表」这条判据退化为只查钉轨实测(§10) |
| 26 | frame_seq | 4 | 段首帧序号,可与串口周期行的 frames 对账 |
| 30 | 通道表 | 每路 12 | 语义号 1 + 来源转换器 1 + 位宽 1 + 可用性 1(= adc_cal_flags(sem) 低 8 位原值,不自造位义)+ k_q16 4(int32,µV/码 × 65536)+ b_codes 4(int32,码值)。没有独立 LSB 字段,LSB 就是 k_q16,见 §6.3 |
| — | CRC16(头) | 2 | CRC-16/MODBUS,低字节在前(与全文件小端统一);覆盖 [0, 30+12×ch_count),本字段自身紧随其后 |
| — | 数据区 | pts_per_ch × ch_count × 2 | 按通道分列:一路连续 320 点,单通道回放只读那一路 |
| — | CRC16(数据区) | 2 | 同上字节序;覆盖整个数据区、自身在文件尾(8 路 320 点整段 5250 B);长度为 0、任一 CRC 不符、或 ver 与魔数第三字节不等 → 整段作废,页面必须显示「该段不可读」 |
段名轮转的呈现语义(回放页与 PC 侧取「哪一段最新」都按这条):段表按数值升序呈现,开屏落点取数值最大的段号。回绕(99 之后回到 00)之后「数值最大」不再等于「时间最新」——要真时间序必须逐段读段头 frame_seq / t0_ms,代价是 100 次开文件,本波不做。所以页顶把段号本身显示出来(§8.1 顶部行),让读者自己看见这是第几号,不去伪造一个「最新」的假象。
6.3 字段口径裁定(两份独立实现的共同依据)
本波段格式有两份独立实现:板端编码器 App/algo/wave_store.c 与 PC 解码器 test/host/wave_read.py,两份必须逐字节同解,所以动工前把 §6.2 的含糊处一次钉死(PC 道九问的逐条答复):
| 问题 | 裁定 |
|---|---|
| CRC 字节序 | 低字节在前,与全文件小端一致。原稿「与屏板链路同一张 CRC-16/MODBUS 表」不成立:全仓没有可复用的 CRC-16 表——App/util/README.md 记 crc.c/h 已于 2026-10-02 删除,屏板链路 Drivers/BSP/UI/ui_net_sink.c 的 remo_crc() 是单字节异或、不是 CRC-16。本波在 wave_store.c 内自带无表位运算 CRC-16/MODBUS(0x8005 反射、初值 0xFFFF、异或输出 0),单测锚点:串 123456789 的 CRC = 0x4B37 |
| CRC 覆盖范围 | 头 CRC 覆盖 [0, 30+12×ch_count)、自身在 30+12×ch_count;数据区自 32+12×ch_count 起;数据 CRC 覆盖整个数据区、自身在文件尾。8 路 320 点整段 5250 B |
| LSB 从哪来 | 不设独立 LSB 字段:通道表的 k_q16 就是 LSB(µV/码 × 65536),与 App/algo/adc_cal.h 的 ADC_CAL_K_Q16_TPAFE 10000000 / ADC_CAL_K_Q16_STM32 52800000 同口径,Q16 下无舍入(选型依据 docs/任务规划/数据可信.md §4.1)。PC 侧 µV = k_q16 / 65536,不许再乘一遍 |
| k / b 宽度与角色 | k_q16 int32、b_codes int32 = 封存瞬间该路生效的两个标定量。adc_cal_apply() 的算式是 eng = (code + b) × k × 1e-6,文件里存的正是等号右边两个数加原始 code,PC 照此重算。必须取 int32 原值,不得经 adc_cal_k_uv() 的 float 通道回算(float 在 5.28e7 量级掉尾数)→ 接线道在 App/algo/adc_cal.[ch] 补一个 int32 直读入口 adc_cal_k_q16()(与 adc_cal_k_uv() 孪生);eng=0 时两者是 ROM 默认值,PC 折出来的单位是引脚域 µV 而非二次值——单位一律以头里的 eng 为准,PC 不许按当前固件口径猜 |
flags_seq 位义 |
bit s = 语义通道 s 的 ADC_CAL_F_VALID;本波 ch_count 固定 8 |
| 可用性字段真值表 | 两行,实现期按 App/algo/adc_cal.c 280 到 298 行核出(VALID 需文件位图与 k 合法带同时成立;CLIPPED 纯由来源派生——该路 ROM 系数属片内 ADC 侧即置位,与样本内容无关):① 未标定(产品构建,两点标定未做)逐路 availability = UA / UN / IA 取 0x00,UB / UC / IB / IC / IN 取 0x02,flags_seq = 0x00,clip_mask = 0xE6;② 已标定(电气 8 路系数全部写入且落合法带)逐路 = 0x01(TPAFE 三路)/ 0x03(片内 ADC 五路,VALID 与 CLIPPED 同时为真),flags_seq = 0xFF,clip_mask 仍是 0xE6。VALID 与 CLIPPED 是两件独立事实:写侧如实透传 adc_cal_flags() 逐路原值(App/task/adc_task.c 548 行),读侧不得据任何品质组合判废,PC 与回放页只给软告警(见下面「判废面与软告警面」一行) |
| eng 派生式 | eng 不是独立事实,是逐路有效位图的派生量:电气 8 路全 VALID → 二次值(1),缺任意一路 → 引脚域(0)。真机两处必须同口径,行号一并登记:判据本体 App/app/ui_data_feed.c 226 到 235 行的 feed_eng()(屏显侧),段头字段填写 App/task/adc_task.c 532 到 553 行(其中 552 到 553 行是段头侧的同一派生)。两处分布在不同任务里现算,理论上会撞上配置重载的毫秒级沉降窗而不一致——这是允许态,见下一行 |
| 判废面与软告警面 | 完整性判废面自始至终只有这些:精确尺寸等式、magic 与 ver(含 ver 须等于魔数第三字节)、头 CRC 与数据 CRC、表序 sem 等于下标、ch_count 上限、pts_per_cycle / pts_per_ch / pre_pts 下限、长度为零、文件短读。品质组合与单位标签永远不进这道门。本轮新增的只是软告警面:avail 同时带 VALID 与 CLIPPED 时 PC 在 NOTE 列打「同时为真,两点标定完成后属正常,非故障」;eng 与 flags_seq 违反上一条派生式时打 WARN。两者都不改退出码、不影响出图、不拒渲染。理由:把「单位标签不一致」当数据损坏,等于让一次改标定文件的沉降窗销毁故障证据 |
| 通道表序与列序 | 第 i 个通道表项 ↔ 数据区第 i 列,按语义号升序:UA=0 UB=1 UC=2 UN=3 IA=4 IB=5 IC=6 IN=7(App/algo/adc_input.h 的 adc_semantic_t)。PC 必须校验表内语义号等于下标,不等即整段作废。交叉验证:clip_mask 恒 0xE6 = 2+4+32+64+128,恰好是 UB/UC/IB/IC/IN 五个语义号位,这份算术就是这个顺序的证据 |
| 样本编码 | int16 小端;TPAFE 侧 16 位补码双极性;STM32 侧 0..4095 原样存正数,不加中点偏置——V0.1 单极性无偏置是硬件现状,加偏置等于伪造双极性(docs/任务规划/数据可信.md §2.7) |
| 三个时间字段 | t0_ms(上电毫秒)、wall(epoch 秒,未对时 0)、frame_seq(段首帧序号)均 u32 无符号小端 |
本波 TEMP 不录(ch_count = 8):温度非线性未折算,进录波只多占一路字节、不增加可解释信息(docs/任务规划/数据可信.md §7 的「温度不参与线性标定」已定案)。
为什么按通道分列而不是按帧交错:回放页只画一路 → 只需连续读那一路;PC 重算全周傅氏也是逐路独立。交错布局两种用法都要跨步长取数,白付代价。
实现期纠正记录(三条,只作追溯,不构成规格)
flags_seq的 V0.1 真值一度被写成0x27:那是把「按来源派生的 CLIPPED」当成了 VALID。按App/algo/adc_cal.c280 到 298 行,未标定态就是0x00(见上面真值表第一行)。凡引用这个数字的地方一律以真值表为准。- 一版互验夹具写成
eng= 0 而flags_seq=0xFF——违反上面的 eng 派生式,是一串真机写不出来的字节。已改为按派生式自洽,并在test/host/wave_store那套单测里加了夹具自洽断言防复发。教训一句话:互验夹具的资格来自「真机能写出这串字节」,一份不可能状态的字节只能验证两个读者对幻觉的一致反应,正好掩盖真分歧。 - 顺带钉一条因果,免得后文写反:PC 侧早期一次
shape子命令在--strict下 rc=1,与品质位无关,那是「实测触轨路数 vs 段头clip_mask」对表不符(合成夹具的波形本来就没真触轨)。那条对表是docs/任务规划/数据可信.md§4.6 与本文 §10 板级判据的本职,不是解码器的判废扩展。
7. 预算(先算后写,R6 是硬前置)
| 项 | 算式 | 结果 |
|---|---|---|
| 录波环 | int16 × 320 点 × 8 路 | 5,120 B |
| 环索引与状态 | 头尾指针、封存标志、段计数、冷却计数 | 约 32 B |
| 落盘搬运缓冲 | 256 B × 2(分次写,不做整段驻留) | 512 B |
| 回放页读窗 | 256 B(与 App/log/soe_store.c 读窗同量级) |
256 B |
| 合计静态增量 | — | 约 5.9 KB |
- 大于 4 KB ⇒ 触发 R6:
docs/内存管理/内存内容分配策略.md的 R6 要求「任何新增静态缓冲 >4 KB 必须先核 §6 台账余量,改后同步 §6 数字、.sct 头注释与 AGENTS 栈表」;加大缓冲本身属 Ask-first; - 落位 = SRAM1,不进 CCM:CCM 受管区已 87.9 %、只剩 7,904 B,本波开工时 SRAM1 为 62.2 %(数字出自
docs/内存管理/RAM预算台账.md§1 受管区表;录波落地后的终值见本节末「实测回填」);录波环不是 DMA 可见对象,没有「CCM 不可达」的约束,但 CCM 也没有余量给它; - UI 子系统配额早已超(同一张表的子系统行)→ 回放页的描述符与名表仍要在台账记账,别拿「反正已超」当免检;
- 前后各跑一次
scripts/ram_report.py与scripts/stack_report.py(defaultTask 多了落盘链、uiTask 多了读窗与自绘);mem-budget钩子是总量红线,ram_baseline.py --update回填趋势; - Flash:段格式编解码 + 状态机 + 回放页 + 一条日志行,粗估 3 到 5 KB(Flash 基线在 25 % 量级,不是瓶颈);
- NAND:一段约 5.2 KB 数据加头。轮转 100 段约 0.5 MB,对 256 MB 单分区是零头——瓶颈不是容量,是写次数(§10 红线)。
实测回填(2026-10-29 权威构建,0 错误 0 警告;逐对象台账见 docs/内存管理/内存内容分配策略.md §6)
| 口径 | 上面的预估 | 实测 |
|---|---|---|
| SRAM1 静态增量 | 约 5.9 KB | 数据面 +6,760 B(81,536 → 88,296);UI 道另 +1,640 B;累计 +8,400 B → 终值 89,936 B / 68.6 % |
| CCM | 0(不得进 CCM) | 0:RW_CCM 三档恒 57,376 B / 87.9 %,既没进 CCM 也没挤动它 |
| Flash | 3 到 5 KB | ER_IROM1 264,580 → 270,248(数据面 +5,668)→ 272,300(UI 道 +2,052)→ 272,360(落盘判废复检 +60)→ 272,336 B(cppcheck 返工纯搬声明,−24),累计 +7,756 B、占 26.0 %;Total ROM 274,308 B、Code 243,552 B |
| 任务栈 | 未估 | 九任务全 PASS、未抬栈:adcTask 静态最坏链 1,424 B(余 1,136 B / 44 %)、defaultTask 3,040 B(余 2,080 B / 40 %)、uiTask 1,336 B(余 15,048 B / 91 %) |
- 差额构成(比预估多 860 B,可对账):原估漏记
FIL528 B(FF_FS_TINY=0,落盘道刻意不上栈)、DIR/FILINFO、已封存段头副本 136 B、打包上下文(内部又含一份 136 B 段头);反向少记一项——落盘实际是单缓冲 256 B,不是预估的 512 B 双缓冲。预估与实测都留在本节:只写实测,下一次估算就看不见原估漏在哪。 - 回放页读窗不在数据面那 6,760 B 内:它计入 UI 道的 +1,640 B;两张表不能相加当总量,逐对象拆分见
docs/内存管理/内存内容分配策略.md§6。
8. 现场回放显示契约
8.1 一屏画什么
单通道,8 路切换(上下键切通道,左右键切段),页面元素:
| 元素 | 内容 | 为什么这么定 |
|---|---|---|
| 顶部 | 通道名 + 段号 + 触发源缩写(M 手动 / P 保护 / T 定时) | 一眼知道「哪一路、哪一段、怎么来的」 |
| 波形区 | 约 120×90 px,画 2 个周波(40 点),阶梯连线(每点占 3 px 宽) | 1 bpp 下平滑折线的平缓段会被阈值吃掉(Drivers/BSP/UI/ui_deco.h 的实测口径:斜线边缘易被吃、须加粗);正弦的过零段正是最平缓的部分。阶梯线每段非水平即垂直,恰好绕开这条,而且更贴近「这是离散采样」的事实 |
| 参考线 | 0 线(虚线)+ 峰值标注(定点 mV) | 没有 0 线就看不出直流偏置与削顶 |
| 底行 | 单位徽标:引脚域 / 二次值(取头里 eng) |
与 UI 交流量页第 7 行同一诚实性口径(docs/UI设计/页面扩充详细方案.md §3.1.1 的落地注) |
| 徽标 | 该路属「形状受限」类时右上打「削」 | 让钉下轨不被读成系统故障 |
| 空态 | 「无录波段」/「该段不可读」 | CRC 不符或零长度必须显式说;画成一条直线等于伪造证据 |
8.2 控件与菜单:两条省事且省 RAM 的裁定
- 不开
LV_USE_CHART:它在Lib/lv_conf.h与simulator/lv_conf.h两份里都是 0,开它要过lv-conf-sync同步门禁、吃 Flash 与 RAM,而 UI 子系统配额已超;内容页框架本身是「一次性自绘、无子对象」,绘制链尾就是页面的draw_cb(docs/UI设计/内容页架构.md的「什么是内容页」一节),自绘线条有先例(ui_deco); - 显示页要新增一条菜单叶子(「录波回放」,放记录查询组),触发入口仍用装置维护组那条既有的「手动录波」叶子。新增叶子会改菜单树叶数与页面绑定口径:落地时必须同步
docs/UI设计/页面补齐规划.md与docs/任务规划/UI补齐.md的叶数闭合,重跑tools/menu_gen/gen_menu.py(generated-fresh逐字节),新增汉字走中文字库链(font-charset)。菜单树与字库都是整文件生成物 → UI 道单写者,同刻只允许一条道写(docs/任务规划/README.md§1 的执行纪律)。
8.3 触发入口与回放页的分工
| 页 | 干什么 | 归哪张卡 |
|---|---|---|
| 「手动录波」操作页 | 确认即触发一段,形态沿用已规划好的操作页确认模式(docs/UI设计/页面补齐规划.md §二 第 26 叶) |
录波 P2 |
| 「录波回放」页 | 列段 → 选通道 → 画单通道 | 录波 P3 |
8.4 现场之外,PC 侧必须同时具备
- 取回:FTP 拉 /wave/WAVE 段文件(lftpd 与 config.ini 的 ftp 段鉴权现成,无需新代码);
- PC 脚本:仓内新增一个纯标准库脚本(与
test/host/iec104_master.py同族风格)——读段、画图、重算全周傅氏,输出「板上报数 vs PC 重算」对照表。这才是docs/任务规划/数据可信.md§4.6 那套判据想要的东西; - 远端看图不是波形通路:RemoDispBus 一帧 3204 B、最坏约 4 s(
Drivers/BSP/UI/ui_net_sink.h的阻塞口径),只适合拍一张回放页的屏做留证。
9. 观测(日志不是免费的)
新增一条周期行,不铺开:
wave: seg=NN armed=1 src=P pts=320 err=N ovf=N drop=N
err是落盘失败或 CRC 自弃次数(字节数不符就算失败,见 §5.2);ovf是冷却期丢弃的触发数;drop是冻结窗口丢掉的录波点数;- 实际落地形态(逐字):打印点在
App/task/adc_task.c:782 行,格式串"wave: seg=%lu armed=%u src=%s pts=%u err=%lu ovf=%lu drop=%lu"——字段名与顺序就是上面的模板(台架与 PC 脚本按行名 grep,动它就断脚本);src取 M(手动)/ P(保护)/ T(定时自抓)/-(本次上电还没触发过);armed只在 FREEZE 与 SEALED 为 1(COOL 期只挡新触发不录新段,画进 armed 会被台架误读);seg是累计成功回执的段数、不是轮转号;err= 落盘失败 + SEALED 滞留超时自弃(含 §5.2 第四条的 ring abandoned 判废),由环内计数收口,落盘道不数第二份账。err与ovf的已知容差(lead 裁定 2026-10-29,不修只入档):这两个计数是跨上下文的读-改-自增(ovf由任意上下文的拒绝路径加、err由 adcTask 的滞留自弃与 defaultTask 的落盘失败分别加),本层双端编译、不许引 FreeRTOS / CMSIS 的临界区内在函数,所以并发拒绝或并发失败时理论上可能少计一次。定性为观测计数的已知容差:只用于趋势与判读,不作为任何判据的唯一依据——要坐实「冷却期内确实来了第二次触发」,同时看armed与那一刻/log/start.log的时刻。契约原文见App/algo/wave_ring.h文件头「观测计数的已知容差」段; - 落盘道失败留痕名清单(2026-10-30 并入原卡片登记簿;台架判读按名索引,不新增第二种形态):单板上一律一行
[WAVE] seg drop: <原因> (idx=… written=…),统一打印点App/task/default_task.c:816——crc selftest FAIL/mkdir /wave/opendir /wave/readdir /wave/seg_path/pack_start/f_open/pack_next/f_write short/pack stall/f_close/len mismatch/ring abandoned(最后一条即 §5.2 第四条陷阱的处置留痕,代码与主机单测面已有、板级未测,观察项见 §10);另有一条非失败的慢段留痕[WAVE][WARN] seg held <毫秒> ms (idx=…)(App/task/default_task.c:831)——搬得慢不是错,真失败才走seg drop。 - 只在原先静默的黑洞处补锚点,别重复已有步骤——每条 DBG 日志约 90 B Flash 加 216 B 栈(
AGENTS.md的日志与工具链节); - 该行已同步进
docs/测试说明/专项验证/ADC前端注入验证流程.md的读数出口表(2026-10-29;同一张表还登记了段文件出口与 PC 侧读段工具)。
10. 测试与门禁
| 层 | 内容 |
|---|---|
主机侧纯逻辑单测(新增,进 test/host/) |
① 环:预触发与后触发点数、冻结与解冻、冷却丢弃计数;② 段格式编解码往返,含 CRC 不符、零长度、字节数不符三条失败路径;③ 触发状态机三条来路(手动 / 保护 / bench)。环与格式必须是纯逻辑(不碰寄存器、不碰 RTOS)才能这样测——这是 App/algo/ 既有分层纪律 |
| PC 侧工具的定性 | test/host/wave_read.py 是 bench 工具(test/host/ 下的 loose .py,先例 test/host/ftp_capture.py),不是主机单测套件 → 不建 run_unit_test.py、不加 pre-commit 专属钩子(专属钩子默认不补,按 Lead 裁定 2026-10-27);它自带 --selftest 就是它的自带回归,跑法与四个子命令见 docs/测试说明/专项验证/ADC前端注入验证流程.md §4 |
| 跨工具链分叉(实证,写进来是为了下一个模块别重踩) | 两份录波 .c 只 include 自身头(链上只有 stdint.h)时,ARMCC 报 22 条 identifier "NULL" is undefined;而主机 MSVC / gcc 会传递性把 stddef.h 拉进来,主机单测完全照不出这个分叉 → 新增模块需要用 NULL 就自己 include stddef.h(与 App/algo/sample_ring.c 同口径,别塞公共头) |
| 板级 | 信号发生器注 1 V / 50 Hz → 手动一段 → FTP 拉回 → PC 脚本量出周波数、幅值、极性与注入一致;再把 PC 重算的基波有效值与板上 meas: 与 104 浮点 YC 对表。判据只量四样:周波数、幅值、极性、钉轨路数——不比绝对相位(理由见 §4.2「段内点序」:触发瞬间在工频周期内任意落点,段字节里没有绝对时间参考;真要比相位必须让发生器给同步触发,本波不做)。钉轨对表的合格判据 = 头位图 ⊇ 实测钉轨路数,且差异必须能由注入条件解释——因为 clip_mask 是按来源派生的链路可信性标记(§6.2 偏移 24 行),不是本段实测值:只加直流或小幅注入时那五路本就不该钉轨,硬取等式会假红。同段另起一条强观察项:V0.1 下这五路若在做交流注入时反而没钉轨,先怀疑接线或该路根本没被驱动——这是告警面,不是失败面(等式仍作为默认尺子留在 PC 侧 shape --strict 里,因为它有这个信号价值;要放行差异用那条判据的解释权,不靠改尺子)。§5.2 第四条的板级观察项(代码与单测面已有、板级未测):bench 连续自抓期间人为把卷占住 2 s 以上(例如同一段跑 FTP 大文件读),看串口 wave: 行的 err 是否加一、是否只出现一条 [ERR] … seg drop: ring abandoned 留痕(不是每拍刷屏)、且段号不推进(下一段仍写同号文件) |
| 板级·触发来路(2026-10-30 并入原卡「手动触发与保护启动接线」板级段) | 真机面板按一次「手动录波」出一段;造一次过流,段号与该时刻 /log/start.log 事件对齐;冷却期内二次触发只加 ovf 不重录(同时看 armed 与那一刻 start.log 的时刻,不要只盯 ovf 数字——容差见 §9);冻结窗口内 104 上送与保护判据不受影响(这条是「丢录波样本不丢采集」的验收) |
| 板级·回放页(2026-10-30 并入原卡「现场单通道回放页」板级段) | 真机上放一段真波(不是模拟器合成节拍);远端看图能拍到该页做留证(只作留证,不是波形通路,§8.4);空态与 CRC 失败态绝不允许画成一条直线——用坏段在真机复现一次 |
| UI | ui-verify 五步;回放页入 ui-golden 黄金参考(参考图一旦入库只能改代码对齐,禁止刷参考掩盖);新叶走 gen_menu.py 重生成 + 字库链 |
| 构建 | /keil-build 要求 0 错误 0 警告;新 .c 双端注册走 new-source-file(MDK-ARM/STM32F407-Demo.uvprojx 与 simulator/CMakeLists.txt 两处显式列表) |
| 台账 | R6 三处同步 + scripts/ram_baseline.py --update + mem-numbers 与 mem-budget 钩子 |
| 板级(读路径耗时,2026-10-29 补) | 进回放页后连续切段 + 连续切道,读 task_prof 里 uiTask 的单轮工作耗时峰值与 800 ms 窗的余量;不要为此新加埋点(现成读数够用)。判读:峰值贴着窗走就是读路径该再加节流(先想缓存与分次,别去动心跳窗) |
| 图与生成物 | 本设计文档的配图生成器受 generated-fresh 逐字节幂等约束;脚本与产物同一笔提交 |
bench 定时自抓的红线:定周期自抓在 1 kHz / 8 路下每 2 s 一段,每 5 分钟就是一轮 NAND 写。长时间挂着它白烧写次数,而 dhara 的坏块记账是持久代价、普通恢复清不掉(AGENTS.md 测试构建节,docs/测试说明/专项验证/存储维护与容量恢复.md)→ 定时模式必须默认关闭 + 有时长上限 + 启动时打一次告警,用完立刻回产品构建。
本波执行顺序(2026-10-30 并入原卡片登记簿 §3;BENCH 与 COOL 两个判定就是「板级那一轮」未跑的回路,DONE = 台账回填 + 文档现状化):
flowchart TD
DSG[设计定稿: 本文 + §10 判据] --> RING[专用录波环与段格式编解码]
RING --> PC[PC 侧读段脚本与对表]
PC --> BENCH{bench 台架对表通过?}
BENCH -- 否: 回头改环或格式 --> RING
BENCH -- 是 --> TRIG[两路真实触发来路: 手动 + 保护启动]
TRIG --> COOL{冻结窗口内采集与上送无恙?}
COOL -- 否 --> TRIG
COOL -- 是 --> PAGE[单通道回放页与叶子上线]
PAGE --> DONE[录波本波收口: 台账回填 + 文档现状化]
PAGE -. 二期另立项 .-> NEXT[4 kHz 全速 / 突变量启动 / COMTRADE / 104 文件传输];
图内不写步骤号,节点名与 §10 各行语义逐字可对。并行度规则见 docs/环境与部署/并行任务工作树方案.md §7.3;UI 道是单写者——菜单树与中文字库都是整文件生成物,动叶子的那条道不许与别的改 UI 的道并行。
11. 与 docs/任务规划/数据可信.md 的口径变更(三处)
| 旧口径 | 新口径 | 原因 |
|---|---|---|
| 「录波通道必须先有工程量,排在本波之后」 | 录波录原始码值,不等工程量;解释靠头里的系数快照 | §3.2:现在根本没有工程量,等下去等于永远不动 |
动态波形验证的读数出口 = 派生标量(meas: / 104 / phasor(hb)) |
派生标量仍是判据,但样本改从录波段导出 → 判据升级为「PC 独立重算 vs 板上读数」对表 | 录波段就是现成的样本出口,不必再造一条串口通路 |
| 动态波形验证「手边可做、排在最前」 | 排在录波 P1 之后(用户裁定 2026-10-29:录波先行) | 先有样本通路,验证才不留遗憾 |
(2026-10-30 更新:原卡片登记簿已销项并入本文,§10 尾有本波执行顺序图;排期顺位在 docs/任务规划/README.md §2.0「独立」行。)
12. 风险
| 风险 | 后果 | 处置 |
|---|---|---|
| 削顶五路被读成「系统有零序 / 有谐波」 | 现场误判 | 头里 clip_mask + 页面「削」徽标 + 空态不许画直线(§8.1) |
| 有人把 U 与 I 两段叠图比相位 | 得出没有物理意义的相位差 | 回放不做多路叠加;头里写明来源转换器;与 docs/任务规划/数据可信.md §9 的 V0.1 禁止行同源 |
| 落盘把 defaultTask 拖住 | 日志与配置引擎变慢(defaultTask 是两者唯一写者) | 分次写(256 B 双缓冲)、一次心跳最多一段、f_write 前后有耗时观测;不做整段驻留再写 |
| 回放读路径把 uiTask 拖住(2026-10-29 lead 评审补,此前风险表只有写侧没有读侧) | 一次渲染的实码 I/O(App/task/ui_task.c 376 到 398 行):段表刷新 1 次 f_opendir + 至多 100 次 f_readdir;未缓存段的段头装载 = 1 次 f_open + 1 次 128 B 头读 + 20 次 256 B 数据区读并边读边算数据 CRC + 尾 2 B 一次 = 最坏 22 次小读;已验过则只剩窗装载 = 1 次 f_open + 1 次 f_lseek + 1 次 80 B 窗读 + 40 点解码与峰值扫描(一屏只碰一路通道一个窗,不是 8 路 320 点)。uiTask 在 IWDG required_mask 内、窗 800 ms(口径同 docs/子系统设计/看门狗设计说明.md §11.3 的解耦心跳)→ 现象先是 LVGL 停摆与远程推流缺席,不是复位(心跳与画屏解耦) |
卷锁 try 非阻塞(拿不到就显示「存储忙」,不等)+ 256 B 分次读 + 已验段结果缓存(同段同道不重复验)+ 只在首次进页 / 切段 / 切道时读。单轮耗时上界未实测:不新加埋点,用现成的 task_prof 读数(板级动作见 §10) |
| 触发风暴 + 轮转覆盖 | 现场唯一一段有效波形被后到的抖动冲掉 | 冷却期 + ovf 计数;「最新段号」进快照;分段优先级属二期 |
| 定时自抓忘关 | 白烧 NAND 写次数(持久代价) | 默认关 + 上限 + 告警(§10 红线) |
| 标定落地后老文件被解释错 | 同一个系数字段值变了含义漂移 | 头里存封存瞬间的 eng 与系数快照;PC 脚本按头解释,绝不按当前固件口径解释 |
| 环写成 float 或按帧交错 | RAM 翻倍 / 单通道回放跨步取数 | §6.2 与 §7 把形态钉死为 int16 分列 |
| 拿 1 kHz 的图宣称「看清波形」 | 掩盖与产品 80 点口径的偏离 | §2 偏离登记 + §2.1 结构约束写进代码注释 |
13. 范围边界(本波不做)
| 不做 | 归属 |
|---|---|
| 4 kHz 全速、每周波 80 点 | 二期;触发条件 = 采集节拍改造与算法层抽取结构一并评审(§2.1) |
| 突变量启动 | docs/算法/继电保护功能复现规划.md 的「故障启动(波形启动)」行 |
| COMTRADE 导出、104 文件传输 | 送检与第三方交接时才需要(产品需求「文件传输(录波)」行至今标未开始) |
| 参考机形态(录波 65 通道;3 类型 / 6 段 / RAM 16 个) | 二期对标;本波只有 8 路单段 |
| 电度与暂态量录波 | 不属本波 |
| 60 Hz 工况自适应 | pts_per_cycle 已留字段,但不做实现 |
14. 与其它文档的关系
| 文档 / 工作 | 关系 |
|---|---|
| 原任务规划《故障录波》卡片登记簿(2026-10-30 销项删除) | 卡片判据与顺序图已并入本文 §9 与 §10;排期顺位归 docs/任务规划/README.md §2.0「独立」行——判据不再另立文件 |
docs/任务规划/数据可信.md |
上游:静态与动态信度、削顶归属由它定义;本文 §11 记录因录波而变的三处口径 |
docs/子系统设计/ADC采样节拍设计说明.md |
数据源与节拍属主;本波不改它,二期改节拍时必须回头改它 |
docs/架构设计/STM32F4-Base存储架构说明.md §10.1 |
FatFs 陷阱与 8.3 短名的属主;本文 §5.2 只是消费者 |
docs/UI设计/页面补齐规划.md、docs/任务规划/UI补齐.md |
「手动录波」叶子旧状态是「不做(无录波模块)」;P2 与 P3 落地时两处的叶数闭合与重开条件要同步 |
docs/问题记录/采集与数据处理短板评测.md |
记录链缺口(波形数据面 / COMTRADE / 104 文件服务)的登记处;P1 落地后该行改指本文 |
docs/产品需求/参考产品复刻明细.md |
§7「录波采样率」行是本文 §2 偏离的对照基准 |
docs/硬件平台/ADC通道映射说明.md |
通道归属与注入点清单的属主;回放页「削」徽标的判据出处 |
