Files
STM32F4-Base/App/algo/meas.h
T
wandering1 b7c0889cc7 refactor(编号·批次二): 代码与测试侧 215 处保护功能号改成功能名,零行为改动
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与
自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与
L-TEST 经实测各 0 处(不是本族),TC 族一字未动。

固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha
sor_test.c
57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。

**两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**:
· 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类
  跨行句 ⇒ 指针一律**单独成行**;
· 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用
  映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。

自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量
恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**,
Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐
字节同数**,
这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。

文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比
留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串;
id-check 对变更记录行本就豁免,两种选择都不影响门禁。
2026-10-27 08:59:26 +08:00

138 lines
8.6 KiB
C
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.
/*
* 模块名称:遥测快照发布层(L2 派生·发布侧,「数据可信」卡 8)
* 模块功能:每 **200 ms** 从 L2 层取一次基波有效值 + 品质,产出一份全仓唯一的
* 遥测快照(meas_snap_t,布局权威 = docs/任务规划/数据可信.md §4.5),
* 让 104 与 UI **共用同一份上游**——"什么时候算新值、品质怎么判"只此
* 一处决定(M5 遥测链的地基;104 改读快照属卡 9,UI 侧 ui_data_feed 属
* 「UI 扩展」波次,两者都只是本快照的读者)。
* 值口径(批 2 卡 8 的 A-lite 裁决,2026-10-25):**基波有效值瞬时透传**
* (同拍 phasor 使用区口径,系数已在 L1.5 折算进帧值——现单位 = ADC
* 引脚电压 V,二次值语义等 adc_cal_flags() 出 VALID,见
* docs/算法/采集与算法层设计说明.md §10 红线③);**不做** 4 点平均 /
* 谐波方和根——docs/任务规划/数据可信.md §4.5 明示"200 ms 是上送节拍
* 不是平均窗,再平均引入滞后";谐波 k-bin 轨(docs/算法/继电保护功能
* 复现规划.md 遥测谐波轨(可选))将来扩展时只许在本结构**尾部追加**或留在发布侧
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
* 内部状态,既有字段序不得变更。
* TEMP 通道(「数据可信」决策 5:不参与线性标定):yc = 最新一拍原始
* 帧值透传,qds 恒置 NT——值在场但非交流量语义,UI 按「数据可信」§4.2
* 三态表显示占位符,104 无对应点。
* 并发(docs/任务规划/数据可信.md §4.5 原文口径):沿用 phasor 的
* **双区 + 单字节活跃索引翻转**(双区指针原子交换 同型,不是新机制)——写侧填满
* 计算区后一次 store 翻转,读者先取索引再整体拷贝,全程无锁;
* seq 只增,消费者据此判"是否换帧"。
* 本层**纯算法**:不碰寄存器、不碰 RTOS、不自己取数(窗自 sample_ring、
* 频率/品质由调用方注入)→ 主机侧免桩单测 test/host/meas/(L0.5)。
* 发布纪律:唯一发布者 = adcTask(在 phasor_run()/freq_run() 之后 200 ms 分频
* 调 meas_tick())。评审判据 = 全仓 meas_tick 调用点数 == 1(「数据可信」
* §8 风险表"快照双写"行);其余任务/模块**只读**。
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
* 作者:DTU 项目组
* 创建日期:2026-10-25(道D 卡 8)
* 修改记录:
* v1.0 2026-10-25 DTU 项目组 创建:200 ms 遥测快照 + 双区无锁读
*/
#ifndef MEAS_H
#define MEAS_H
#include <stdint.h>
#include "adc_input.h" /* adc_semantic_t / ADC_SEM_COUNT:yc/qds 下标 = 语义通道 */
#ifdef __cplusplus
extern "C" {
#endif
/* 上送节拍(ms):adc_task 按本宏对 1 kHz 拍做**经过时间**分频(非拍计数——
* idle/skip 拍会把拍计数口径拉得比名义值长),落点见 App/task/adc_task.c */
#define MEAS_PERIOD_MS 200u
/* 每点品质位(uint16)。**低字节与线上 104 QDS 字节同值**(OV/NT/IV,数值
* 口径同 App/protocol/iec104/iec104_asdu.h 的 IEC104_QDS_*)——但本层**不得**
* include 协议层头(分层方向:算法层在协议层之下,反向依赖禁止),故在此自
* 定义同值常量;卡 9 的 104 侧消费直接截低字节上送,一致性由本目录单测与
* test/host/iec104/ 既有断言双向钉住。高字节为 meas 域扩展位(线上不直接
* 出现,由消费者映射): */
#define MEAS_QDS_OV (0x0001u) /* 溢出(留位,卡 8 不产出——判据待运行参数化) */
#define MEAS_QDS_NT (0x0020u) /* 非当前值:未就绪 / 语义未处理(TEMP) */
#define MEAS_QDS_IV (0x0080u) /* 无效:该路取窗失败,无有效读数 */
#define MEAS_QDS_CLIPPED (0x0100u) /* 硬件削顶(adc_cal_flags 位图直通,
* 「数据可信」§4.2 三态表;AC 量不可信,
* 消费者应映射为品质无效,如卡 9 的 104 IV) */
/* 遥测快照(docs/任务规划/数据可信.md §4.5 权威布局;实测 72 B,2×72 ≈ 144 B
* 双区 = 卡 8 DoD 预算口径,由 test/host/meas/ 的布局钉死用例锁字段偏移——
* 改字段 = 破协议,须同步卡 9 / UI) */
typedef struct {
uint32_t seq; /* 只增;消费者据此判断是否换帧 */
uint32_t tick_ms; /* 发布拍时基(osKernelGetTickCount 原值) */
uint8_t valid; /* 整帧有效:本拍 8 电气路取窗全部成功 */
uint8_t rsv[3]; /* 对齐填充,恒 0 */
float yc[ADC_SEM_COUNT]; /* 工程量(引脚电压 V 域;二次值等 VALID 标定) */
uint16_t qds[ADC_SEM_COUNT]; /* 每点品质 MEAS_QDS_*(值本身不因品质清零) */
uint8_t freq_valid; /* 频率读数新鲜度(freq_valid() 注入) */
float freq_hz; /* 频率读数(freq_last() 注入,未收敛为 0) */
} meas_snap_t;
/*
* 函数功能:遥测快照层初始化(清双区 / 索引 / 序号 / 就绪标志)
* 入口参数:无
* 返 回 值:0 - 成功 int
* 限定条件:内核启动前或任务上下文皆可(纯 .bss 状态,无 RTOS 依赖);
* 本仓落点 = adc_task_init()(每开机一次,幂等可重复调)
* 函数说明:init 后 meas_get() 返 -2、meas_seq() 返 0,直到首次 meas_tick()
* 发布——消费者据 -2 走"未就绪"分支,不读半初始化数据。
*/
int meas_init(void);
/*
* 函数功能:一拍遥测发布(200 ms 分频由调用方把关,本函数被调即发布一帧)
* 入口参数:tick_ms - 本拍时基(毫秒) uint32_t
* freq_valid_in - 频率读数新鲜度(freq_valid() 值) uint8_t
* freq_hz_in - 频率读数(freq_last() 值;从未收敛为 0.0) float
* clip_mask - 削顶通道位图(bit s = 语义通道 s;来源唯一 =
* adc_cal_flags() 的 ADC_CAL_F_CLIPPED,编排方注入,
* 本层不链 adc_cal——可用性属主在折算侧) uint32_t
* 返 回 值:无
* 限定条件:**仅 adcTask 上下文、全仓唯一调用点**(发布纪律见文件头);
* 须在 sample_ring_push_frame()/phasor_run() 之后调用,本拍窗才含最新点
* 函数说明:8 电气路逐路取"以本拍为终止的一周波"做基波 DFT(复用 phasor_calc
* 纯函数核,与 phasor 使用区同窗同核 → 同拍同值,见单测钉死),
* 任一路窗不足 → 该路 yc=0 + qds=IV|NT、整帧 valid=0(不产出半新半旧
* 混合帧;与 phasor_run 的差异:phasor 宁可不切区,meas **照常发布**
* 让品质位可见——消费者需要节拍性的品质刷新,seq 亦随之只增)。
* TEMP = 最新一点透传 + NT(见文件头)。末尾单字节翻转使用区,
* 读者无锁。
*/
void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
uint32_t clip_mask);
/*
* 函数功能:读当前使用区快照(无锁整体拷贝)
* 入口参数:out - 输出快照;失败(含 -2 未就绪)时**清零填充**,调用方可安全读
* 返 回 值:0 - 成功;-1 - 参数错;-2 - 从未发布过(out 已清零,seq=0) int
* 限定条件:任意上下文(任务级读者:iec104Task / uiTask / protectTask)
* 函数说明:先取一次使用区索引再索引拷贝——拷贝期间写者最多改"**非使用区**",
* 本帧无撕裂(双区不变量,同 phasor_get 口径;写者两帧间隔 200 ms
* 远大于 72 B 拷贝耗时,读者被压超 200 ms 的竞态窗口按 §4.5 设计接受)。
* 判"新不新"用 seq:与上次不同则换帧。
*/
int meas_get(meas_snap_t *out);
/*
* 函数功能:读已发布帧数(只增)
* 入口参数:无
* 返 回 值:seq(0 = 从未发布;单字 32 位读,Cortex-M 原子) uint32_t
* 限定条件:任意上下文
* 函数说明:轻量观测入口(104 周期上送可先比 seq 省 72 B 拷贝);
* 与 meas_get 之间无原子性配对要求(读者以 get 返回的 seq 为准)。
*/
uint32_t meas_seq(void);
#ifdef __cplusplus
}
#endif
#endif /* MEAS_H */