Files
STM32F4-Base/App/algo/README.md
T
wandering1 94b0d79026 feat(ui)+chore(docs): UI 补齐清单清空;数据可信销项与「卡」号清退
本笔含两组收尾工作(同一工作区内交错在 6 个共享文档/门禁配置里,
拆开会产生"文档声称的页数与树里黄金数不一致"的中间提交,故合为一笔):

一、UI 补齐(清单清空)
- 新建 6 个内容页模块:ui_page_accident / oper / alarm / start / yk
  复用 ui_w_records 共享组件(分页、空态"无记录"、按实例读盘缓存),
  记录层与五个写入点已由道F task-10 / 道G task-12 落地,本批只补页面;
  ui_page_runreport 为 13 行 2 页只读统计,四个数逐个挂范围标签
  (自电池周期 / 本次开机),boot_ready=0 显 "--" 不显 0。
- 数据面:ui_data_info_t 尾部追加 9 字段,ui_data_feed 用
  boot_stats_get() + iec104_serv_get_stats() 纯搬运;模拟器填确定值假数据
  (差异登记 docs/测试说明/模拟器差距清单.md §2.3 两行)。
- 接线:菜单树 6 叶绑 #page=,绑定叶 22→28、UI_PAGE_MAP_COUNT=28;
  字库 185→218 字 / 280→313 字形;双端注册 uvprojx +30、CMakeLists +6。
- 回归:内容页黄金 53→60(含 runreport_p2 真拍第 2 页),文本断言
  425 正向 / 36 反向;存量 53 张逐字节零漂移;记录 5 页各用独立空目录
  (模拟器把操作留痕写进 cwd 的 sim_oper.log,共用 tmp 会让空态拍不到)。

二、数据可信销项 + 「数据可信」任务卡号整族清退
- 删除已闭环的"已知电压注入验证"行与闭环结论块(证据在
  docs/硬件平台/ADC通道映射说明.md §8.1 与
  docs/测试说明/ADC前端注入验证流程.md §5~§7),§5 标识列改语义短语。
- 卡 0/0b/1~12 共 14 个号全仓 288 站点 / 56 文件清退成语义短语,
  代码注释、测试、门禁配置与文档一并改;换算表入 docs/编号规范.md §9.3。
  这一族是中文前缀,不进 check_ids 语料,刻意不入 id_aliases.json。

验证(实测)
- @build 0 错 0 警:Code=236456 RO=28124 RW=1876 ZI=137084;
  RAM +464 B(RW +304 / ZI +160),RW_IRAM1 62.2% / RW_CCM 87.9%,
  体积同码凭证与 RAM 台账已回填。
- check_menu_rows 8 状态 rc=0(池 5368/5448 常量未动);
  check_content_pages 60 页 rc=0;check_font_charset / registration /
  coding-standard / bom-drift / mojibake / doc-links --all / layer-deps /
  nested-comment 全 rc=0;check_ids --strict 无错误;主机单测
  ui_data 295 / boot_stats 21 / sys_log_mcu 141 / soe 32 / soe_store 27。

未做(不是遗漏)
- 六页真机判据(真记录翻页、掉电保持、清除后归零、boot_count 自增、
  拔 RTC 电池落 RESTARTED、IWDG 复位落 rst_cause、主站连一次 conn_up+1)
  已登记 docs/任务规划/登记簿.md §7;模拟器结构上照不到。
- docs/产品需求/产品需求设计文档.md 属另一条在飞改动,本笔不含;
  其中我已同步 6 处现状数字(28 叶 / 60 页 / 218 字),随该笔入库。
2026-10-28 07:23:16 +08:00

176 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# App/algo/ — 采集与算法层(L1 / L2 / L3)
> 本目录的**索引**:文件清单、依赖、逐文件的"改代码必须知道"、构建与测试入口、新增模块动作。
> **详细设计——机制、契约、判据、为什么这样设计 → `docs/算法/采集与算法层设计说明.md`**(本文不复制其内容)。
> 分层归属与功能项状态 → `docs/算法/继电保护功能复现规划.md` §2.1;
> 通道映射唯一权威 → `docs/硬件平台/ADC通道映射说明.md`;采样节拍 → `docs/子系统设计/ADC采样节拍设计说明.md`。
## 1. 文件清单(一眼版)
| 文件 | 层 | 作用(一句话) | 主要对外入口 | 覆盖规划项 |
|---|---|---|---|---|
| `adc_input.h` / `.c` | L1 | 统一语义取数:两片转换器的码值按语义(`UA…IN` / 温度)暴露,并消费 STM32 ADC1 的 DMA 乒乓缓冲 | `adc_input_init()` `adc_input_poll()` `adc_input_raw()` | TPAFE 快照消费 / STM32 ADC1 启动与扫描 / 通道语义映射 |
| `adc_health.h` / `.c` | L1 | 采样有效性判定:吃计数快照,产出静默 / 丢帧 / 单调性 / 错误增量故障位(判定与告警编排分离);「采样通道自检」残余的码值贴轨观察器同层 | `adc_health_check()` `adc_health_flags()` `adc_health_rail_watch()` | 「采样有效性校验」/「采样通道自检」(残余) |
| `adc_cal.h` / `.c` | L1.5 | 通道标定折算(「数据可信」标定折算与落盘整定):工程量 =(code+b)×k,Q16 系数/零点两段落盘 + 三态 flags + 合法带哨兵 + 两点公式 | `adc_cal_apply()` `adc_cal_flags()` `adc_cal_set()` | 「通道标定(一次系数 + 零漂)」(M4 半边) |
| `meas.h` / `.c` | L2 发布 | 遥测快照(「数据可信」遥测快照发布,M5 地基):200 ms 基波有效值 + 品质快照,双区无锁读,全仓唯一发布者 = adcTask | `meas_tick()` `meas_get()` `meas_seq()` | M5(104 改读快照与频率点绑定、UI 是读者) |
| `sample_ring.h` / `.c` | L2 输入 | 按语义通道存最近 2 周波(40 点),供相量层"以当前时刻为终止"取窗 | `sample_ring_push_frame()` `sample_ring_window()` | L2 输入(为「每 1 ms 滑动窗刷新」供数) |
| `phasor.h` / `.c` | L2 | 全周傅氏:一周波 20 点 DFT → 基波 / 谐波相量(有效值口径),双区原子切换供无锁读 | `phasor_run()` `phasor_get()` `phasor_calc()` | 全周傅氏系数表 / 基波相量实例 / 相量有效值输出 / 每 1 ms 滑动窗刷新 / 双区指针原子交换 |
| `freq.h` / `.c` | L2 派生量 | 频率测量:相邻两拍基波相量共轭乘积取角 + 自适应窗滑动平均 + 假值钳制 + 幅值门(UA→IA 源回退) | `freq_run()` `freq_get()` `freq_valid()` | 「过频 / 低频」测量侧(测频范围 → `docs/算法/继电保护功能复现规划.md` §4.2) |
| `prot_set.h` / `.c` | L3 输入 | 保护定值模型:Q10 定点 RAM 表 + 范围校验 + 校验和封存 / 自检 / 闭锁 + 持久化 blob 纯逻辑 | `prot_set_load_raw()` `prot_set_get_f()` `prot_set_selfcheck()` | 「定值校验和」载体 |
| `prot_elm.h` / `.c` | L3 | 保护元件框架:通用"越限 + 返回系数滞回 + 定时限"核 + 数据驱动实例表(过流三段 / 过负荷 / 过压 / 欠压 / 零序过压 / 过频 / 低频) | `prot_elm_run()` `prot_elm_eval()` `prot_elm_active_mask()` | 「三段式相间过流」/「过负荷」/「过压 / 欠压」/「过频 / 低频」/「零序过压」(软件侧) |
依赖(自下而上、无环):`adc_input.h`(纯枚举 + 声明)← `adc_cal`(另依赖 `cfg_file.h` 接口,
纯头文件级)← `sample_ring` ← `phasor` ← `freq`;`meas` ← `sample_ring` + `phasor`
(仅 `phasor_calc` 纯核;频率/品质由调用方注入,**不链** `freq`/`adc_cal`);
`prot_set` **独立**;`prot_elm` ← `prot_set` + `phasor` + `freq`。
`adc_health` 只吃调用方喂进来的计数 / 码值,不反向依赖 L1 实现(「采样通道自检」贴轨门限由调用方
从 `adc_cal_rail_ths()` 取,本层仍零算法依赖)。
**除 `adc_input.c` 外全部零平台依赖**(只用 `stdint.h` / `math.h` / `stddef.h` 与本目录头)
→ 可被主机单测 `#include` 原样纳入、免桩跑。`adc_input.c` 另依赖 `adc.h`(`hadc1`)与
`tpafe5160.h`(快照),主机侧以桩头替身。
## 2. 一拍的数据流(一眼版)
`adc_input_poll()` → `adc_input_raw()` ×9 → `adc_cal_apply()` ×9(L1.5 折算,现口径 =
ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(「采样通道自检」,码值域)→ `sample_ring_push_frame()`
→ `phasor_run()` → `freq_run()` → 消费者(`adcTask` 心跳与 L1 健康判定;`protectTask` 每 20 ms 取相量 / 频率跑
`prot_elm_run()`;**每 200 ms** `meas_tick()` 发布遥测快照供 iec104Task / uiTask 读,即「数据可信」遥测快照发布)。节拍 = TIM3 1 kHz 更新事件,两片转换器同源触发。
完整数据流图与初始化落点 → `docs/算法/采集与算法层设计说明.md` §2。
## 3. 逐文件要点(只留"改代码时必须知道"的;机制与理由见设计文档)
> **改本目录前先记五条红线**:① 取数只经 `adc_input_raw()`,不写死下标;② **禁止跨转换器**做
> 相位 / 功率 / 矢量运算(双转换器无共同采样触发);③ 帧值已是 L1.5 工程量,但 **未标定(`adc_cal_flags()` 无
> VALID)时单位只是 ADC 引脚电压**,不是端子二次值——下游消费精度判据见「数据可信」§4.2 三态
> 表;④ 工程量 → 帧的唯一折算点是 `adc_cal_apply()`,禁止旁路再乘系数;⑤ 本层不建任务、
> 不等事件、不写日志(`adc_cal` 的 `[ERR]` 哨兵留痕是唯一例外,且每通道每开机一次),
> `*_init()` 才可在内核前调。
> 全文 → `docs/算法/采集与算法层设计说明.md` §10。
### 3.1 `adc_input.c` / `adc_input.h` — L1 语义取数
- 语义映射表 `k_adc_map[]` 的**内容**唯一权威是 `docs/硬件平台/ADC通道映射说明.md`;V0.2 改版只改表内容,API 不动。
- `adc_input_poll()` **仅任务上下文**(TPAFE 快照是临界区);STM32 侧固定滞后 1 拍。
- 四类 STM32 计数(`adc1_seq` / `adc1_gap` / `adc1_err` / `adc1_hw`)成因不同,**勿混读**。
- 本目录**唯一**允许出现 HAL 调用的文件。→ `docs/算法/采集与算法层设计说明.md` §3。
### 3.2 `sample_ring.c` / `.h` — L2 输入采样环
- 整帧写入后才推进写指针 → 取到的窗永远落在整帧边界。
- 本拍取数失败 → **整拍跳过入环**(`adc_task.c` 的 `skip`),**绝不补 0 点**。
- 不做跨通道运算、不做丢帧判定(只出 `frames()` 计数)。→ `docs/算法/采集与算法层设计说明.md` §5。
### 3.3 `phasor.c` / `.h` — L2 相量计算
- 系数表预乘 √2/N → `fAmSQ` **直接是有效值**(口径 `docs/算法/傅氏算法详解.md` §2.3)。
- 实例表 `k_inst[]` 数据驱动:**新增实例只在表尾加一行**。
- 任一实例不足一周波 → **本拍整体不产出**(不切使用区);每拍 20 点**全部重算**(不做增量递推)。
- → `docs/算法/采集与算法层设计说明.md` §6。
### 3.4 `adc_health.c` / `.h` — L1 有效性判定
- 三类位**严格分开**:电平(可自愈)/ 锁存(必留痕)/ 边沿事件(仅当拍);**静默 ≠ 丢帧**。
- **gap 不告警**(消费者超拍是已知现象);告警编排在 `adc_task.c`,不在本层。
- 首拍 `check()` 只建基线。→ `docs/算法/采集与算法层设计说明.md` §4。
### 3.5 `prot_set.c` / `.h` — 保护定值模型(「定值校验和」载体)
- 装载 **all-or-nothing**:任一项越界整表不装载,越界项由 `prot_set_bad_mask()` 指认。
- 批量 `prot_set_set()` 后**必须** `prot_set_seal()`;单项原子写用 `prot_set_set_sealed()`。
- 定值项 / 实例**只许尾部追加**(枚举序 = 数组序 = 校验和覆盖序)。
- blob **换代(返回 2)≠ 损坏(返回负)**:前者打 INFO、不记自检。→ `docs/算法/采集与算法层设计说明.md` §8、§12.3。
### 3.6 `prot_elm.c` / `.h` — L3 保护元件框架
- 数据缺失两种处理**刻意不同**:相量类任一未就绪 → 整拍跳过;频率类无有效读数 → 按方向填"必不启动"值,**不整拍跳过**。
- `prot_set_locked()` 或投退 = 0 → 元件整体复位不判据;跨上下文复归走 `prot_elm_request_reset()`。
- 零序过流(IN 走 STM32)**故意不在实例表**;OF / UF 无独立 104 遥信点,只经 YX15 总聚合可见。
- `prot_elm_init()` 须在 `phasor_init()` 之后。→ `docs/算法/采集与算法层设计说明.md` §9。
### 3.7 `freq.c` / `.h` — 频率测量(「过频 / 低频」的测量输入)
- `freq_run()` **仅 adcTask 上下文**,须在 `phasor_run()` 之后同拍调;保护口径 =
`freq_get() == 0 && freq_valid() == 1`。
- 精度靠**自适应窗长**(窗长 = 泄漏纹波周期的整数倍);定长平均压不到 0.02 Hz,
换窗长 = 换精度,二者不是独立旋钮。
- 源切换即废相位基准重建(UA / IA 无共同相位基准,即双转换器无共同采样触发)。
- 判据 **J1~J6** 与实测口径 → `docs/算法/采集与算法层设计说明.md` §7.2(与 `test/host/freq/freq_test.c` 文件头同源)。
### 3.8 `adc_cal.c` / `.h` — L1.5 通道标定折算(「数据可信」标定折算与落盘整定)
- 折算模型:工程量 = (code + b) × k;**k 落盘用 int32 Q16(µV/码 × 65536)**、b 用
int32 码值(负零点靠 `CFG_VAL_INT`)——单位与定点格式的选型依据见
`docs/任务规划/数据可信.md` §4.1,勿改回 V/码 直存(Q16 只剩 20 个刻度)。
- **ROM 默认 = 量程口径冻结定死的引脚侧 LSB**(TPAFE 152.59 / STM32 805.66 µV/码,
`docs/硬件平台/ADC通道映射说明.md` §7.1);表下标 = 语义枚举(UA0…IN7),
V0.2 改版与本目录 `adc_input` 映射表同步改。
- 三态可用性 `adc_cal_flags()`:VALID(已按变比标定,二次值成立——端子变比确认与两点标定前恒 0)
/ CLIPPED(运行期按来源派生,V0.2 后自动清零)。**消费者判「能不能信」只许查它**,
`adc_cal_apply()` 不返回可用性(「数据可信」§8 风险表)。
- 合法带哨兵:k ∉ ROM×[0.5,2.0] 或 |b| > 8192 → 折算回退 ROM + 每通道每开机一条
`[ERR]` + VALID 被压制(**即便文件位图说已标定**);这层兜底同时挡坏文件与坏标定。
- `adc_cal_set()` / `adc_cal_two_point()`:整定与两点公式的算术核已齐备(单测还原合成
k/b);**台架两点标定待实测**,本层不含注入。
- cfg 两段 `[adc_cal]` / `[adc_cal_ofs]` **不挂回调**(RAM 副本即生效值,引擎 load/poll
原地改写,逐点现读);注册在 `app_main_init()` 内核前完成,必须先于 `cfg_file_load`。
### 3.9 `meas.c` / `.h` — 遥测快照发布(L2 发布侧,「数据可信」波次)
- **唯一发布者 = adcTask**:`phasor_run()`/`freq_run()` 同拍、200 ms **tick 经过**分频
调 `meas_tick()`(非拍计数——idle/skip 拍拉长名义节拍)。docs/任务规划/数据可信.md §8
风险表"快照双写"行 = 评审判据:全仓 `meas_tick` 调用点数须为 1;iec104Task /
uiTask 只读。
- 值口径(批 2「遥测快照发布」的 A-lite 裁决):**基波有效值瞬时透传**,与 phasor 使用区
**同拍同核逐位相等**(单测钉死);**不做** 4 点平均 / 谐波方和根——按
docs/任务规划/数据可信.md §4.5"上送节拍非平均窗,再平均引入滞后";谐波 k-bin 轨
(docs/算法/继电保护功能复现规划.md 「遥测谐波轨(可选)」)扩展只许结构尾部追加,
docs/任务规划/数据可信.md §4.5 字段序 = 协议(布局由单测按偏移钉死,72 B × 双区)。
- 并发 = phasor 双区 + 单字节索引翻转(「双区指针原子交换」同型,无锁);seq 只增供消费者判换帧。
- 品质注入:削顶位图唯一源 = `adc_cal_flags()`(口径 docs/任务规划/数据可信.md §4.2
三态表;由 adc_task 编排注入,本层不链 `adc_cal`);TEMP = 最新一拍原始值透传 +
`NT`(「数据可信」决策 5);`MEAS_QDS_*` 低字节与 104 QDS 线上字节同值
(本层**不** include 协议层头——分层方向)。
- 104 死区迁移 / 频率点绑定 = 104 改读快照那一波(道D 后续分支);读数与注入源比对 = **待台架实测**。
## 4. 构建与测试
### 4.1 挂载与模拟器边界
- 九个 `.c` 已登记在 `MDK-ARM/STM32F407-Demo.uvprojx`;`../App/algo` 已在 `<IncludePath>` 内。
- 本层**不进** `simulator/CMakeLists.txt`(UI 模拟器不跑采集链路):九个文件全部登记在
`scripts/check_registration.py` 的 `SIM_EXEMPT`(附理由),`registration-check` 钩子反向校验(登记簿条目「rs485_task 白名单缺口」)。
### 4.2 主机侧单测(L0.5)——生产源文件以 `#include` 原样纳入
| 模块 | 命令 | pre-commit 钩子 |
|---|---|---|
| 相量 / 采样环 | `& '.venv\Scripts\python.exe' test/host/phasor/run_unit_test.py` | `phasor-unit-test` |
| ADC 输入层 | `& '.venv\Scripts\python.exe' test/host/adc_input/run_unit_test.py` | `adc-input-unit-test` |
| 有效性校验 | `& '.venv\Scripts\python.exe' test/host/adc_health/run_unit_test.py` | `adc-health-unit-test` |
| 定值模型 | `& '.venv\Scripts\python.exe' test/host/prot_set/run_unit_test.py` | `prot-set-unit-test` |
| 元件框架 | `& '.venv\Scripts\python.exe' test/host/prot_elm/run_unit_test.py` | `prot-elm-unit-test` |
| 频率测量 | `& '.venv\Scripts\python.exe' test/host/freq/run_unit_test.py` | `freq-unit-test` |
| 通道标定(含真实 cfg_file 引擎集成) | `& '.venv\Scripts\python.exe' test/host/adc_cal/run_unit_test.py` | `adc-cal-unit-test` |
| 遥测快照(布局钉死/双区无撕裂/品质汇总) | `& '.venv\Scripts\python.exe' test/host/meas/run_unit_test.py` | `meas-unit-test` |
改本目录任一 `.c/.h`,提交时对应钩子自动跑。**用例清单与判据数字的属主**是各
`test/host/<模块>/` 与 `docs/测试说明/` 下的单测说明,本文不复制(三处抄数字必然打架)。
### 4.3 板端
`@build` 0 错误 0 警告 → `@test` 看 `[ADC_TASK]` 首拍 / 心跳(`phasor(...)`、`freq(...)`、
`cal: ua_mv=… vl=0x00 cl=0xE6 rail=…`、`ring` / `skip` / `adc1_*` / `tgap`);
健康判据 = **零 `[ERR]`**(健康告警与标定哨兵全以 `[ERR]` 开头,误报会被闭环直接抓住;
`vl` 在未标定默认构建必须为 0x00,未标定即闭锁落地后 `cal:` 行就是台架期望的直接证据)。
→ `docs/算法/采集与算法层设计说明.md` §12。
## 5. 新增一个算法模块的固定动作
1. 在本目录建 `xxx.c/h`,文件头写模块注释块(规范 V1.0),**尽量零平台依赖**以便免桩单测。
2. 用 `new-source-file` skill 注册到 `MDK-ARM/STM32F407-Demo.uvprojx`(新建子目录还要补 `<IncludePath>`)。
模拟器侧二选一:有消费路径 → 加 `simulator/CMakeLists.txt`;纯算法无消费路径 → 加
`scripts/check_registration.py` 的 `SIM_EXEMPT`(照本目录既有七条写理由),否则 `registration-check` 拦。
3. 加 `test/host/<模块>/` 主机单测 + `.pre-commit-config.yaml` 钩子(照 `phasor-unit-test` 抄)。
4. 消费者写在 `App/task/`,本层只出"数";跨转换器禁令见 §3 开头四条红线。
5. 在本 README §1 清单加一行,并在 `docs/算法/采集与算法层设计说明.md` 里写清该模块的契约与理由。
## 6. 相关文档
- **本层设计与判据**:`docs/算法/采集与算法层设计说明.md`
- 通道映射权威口径:`docs/硬件平台/ADC通道映射说明.md`
- 分层与功能项清单:`docs/算法/继电保护功能复现规划.md`
- 傅氏算法与有效值口径:`docs/算法/傅氏算法详解.md`
- 采样节拍(TIM3 1 kHz + TRGO 触发 ADC1 / DMA):`docs/子系统设计/ADC采样节拍设计说明.md`
- 相量层 / ADC 输入层单测细则:`docs/测试说明/相量层单元测试说明.md`、`docs/测试说明/ADC输入层单元测试说明.md`
- RAM 与栈台账:`docs/内存管理/RAM使用分析与优化方案.md`