Files
STM32F4-Base/App/algo/meas.h
T
wandering1 48bc50e794 feat(遥测快照): 卡8 meas 双区快照模块 + adc_task 200ms 唯一发布点(道D M5)
卡 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
2026-10-25 10:02:10 +08:00

137 lines
8.4 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 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 */