48bc50e794
卡 8(App/algo/meas.*,新增;布局权威 docs/任务规划/数据可信.md §4.5): - meas_snap_t = §4.5 布局(sizeof 72 B;字段偏移由 TC-MEAS-002 钉死——卡 9/ UI 按整体拷贝消费,破布局=破协议;谐波 k-bin 扩展(docs/算法/继电保护功能 复现规划.md L2-8)只许尾部追加,留口注释已写进 meas.h) - 并发 = phasor 双区 + 单字节索引翻转(L2-7 同型,无锁);seq 只增供判"换帧"; meas_get 失败路径清零填充(-2 未就绪时调用方可安全读) - 值口径 = A-lite 裁决(lead 2026-10-25 快裁,推翻票面全链):基波有效值瞬时 透传,复用 phasor_calc 纯核 → 与 phasor 使用区同拍同值(TC-MEAS-005 逐位 相等钉死);不做 4 点平均/方和根(§4.5"200 ms 是上送节拍不是平均窗") - 品质注入:CLIPPED 位图唯一源 = adc_cal_flags()(「数据可信」§4.2 三态表, 编排方注入、本层不链 adc_cal);取窗不足路 = IV|NT 且整帧 valid=0 但照常 发布(品质节拍性刷新);TEMP = 最新一点透传 + NT(「数据可信」决策 5); MEAS_QDS_* 低字节 = 104 QDS 线上值(本层不 include 协议层头——分层方向, 卡 9 直接截低字节映射);OV 留位不填(判据待运行参数化,卡 10 域) - adc_task.c 编排:新帧拍 freq_run() 后按 **tick 经过**分频(非拍计数,idle/ skip 拍会拉长名义节拍)调 meas_tick()——全仓调用点数 = 1(「数据可信」§8 风险表"快照双写"行评审判据);meas_init 挂 adc_task_init(不动 app_main.c); 心跳新增 meas: 行(seq/v/ua·un·ia_mv/f/fv)作 M5 台架观测面 - 死区 2 点确认状态机本卡不建(卡 9 迁移 104 语义时随建,lead 裁③);104 层 现有 deadband 常量未动;频率点绑定归卡 11 - 实测采样类判据(读数 vs 注入源)= 待台架实测,本卡零台架动作 测试与注册:新套件 test/host/meas(226/226,链真源 meas+phasor+sample_ring 免桩;覆盖 seq/双区无撕裂/布局/品质汇总/回绕/守卫);id_registry 建族 TC-HOST-MEAS(游标 011);meas-unit-test 钩子 + SIM_EXEMPT 一条 + uvprojx 仅增 meas.c 单条挂载(diff 已核零 UV4 抖动);App/algo/README.md §1/§2/ §3.9/§4 同步(设计说明大节与 AGENTS 口径留 lead 收口,同批 1 先例) gate:@build 0 错 0 警(Code=216352 RO=21476 RW=1232 ZI=132128);RAM 增量 RW+ZI = +152 B(快照双区恰 +144 B = 卡面 DoD;余 s_meas_ms 等编排状态), IRAM1 57.8%→约 57.9%;Flash(Code+RO+RW)+696 B;adcTask 静态最坏链 1408 B / margin 1152 B(45%)PASS(stack_report 实测,无抬栈);BOM/CR/U+FFFD/ 字面量 ASCII 逐文件自查通过(未触碰带 BOM 文件)。pre-commit run --all-files 实跑:其余钩子全绿(含 doc-links/id-check/ 覆盖率总闸/双端注册/ui-golden/全部主机单测套),仅 mem-budget + mem-numbers 两条红 = 台账受管区 vs .map 漂移(Flash/RAM 台账回填属 lead 单写者区,批 1 同口径): gate: only mem-budget/mem-numbers pending lead ledger backfill
137 lines
8.4 KiB
C
137 lines
8.4 KiB
C
/*
|
||
* 模块名称:遥测快照发布层(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 L2-8)将来扩展时只许在本结构**尾部追加**或留在发布侧
|
||
* 内部状态,既有字段序不得变更。
|
||
* TEMP 通道(「数据可信」决策 5:不参与线性标定):yc = 最新一拍原始
|
||
* 帧值透传,qds 恒置 NT——值在场但非交流量语义,UI 按「数据可信」§4.2
|
||
* 三态表显示占位符,104 无对应点。
|
||
* 并发(docs/任务规划/数据可信.md §4.5 原文口径):沿用 phasor 的
|
||
* **双区 + 单字节活跃索引翻转**(L2-7 同型,不是新机制)——写侧填满
|
||
* 计算区后一次 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 */
|