Files
STM32F4-Base/App/algo/prot_elm.h
T
wandering1 0264b49489 feat(algo): B8 频率测量层 + 过频/低频保护(L3-5)
新增 App/algo/freq.c/h:由 L2 基波相量导出系统频率。相邻两拍共轭
乘积 P=Z(m)·conj(Z(m-1)) 取角 = 每拍相位增量(免显式解缠绕),再按
自适应窗长 K_eff=round(8·Fs/(2·f_hat))(45~55 Hz 段 73~89 拍)做滑动
平均。定长窗受 20 点矩形窗负序泄漏纹波限制(实测扫频最坏 0.317 Hz;
60 拍 Kay 复数和 0.197 Hz,交叉项和角不按 1/K^2 衰减),自适应窗使
窗长恰为纹波周期 fs/(2f) 整数倍 -> 最坏 0.0101 Hz。推导与两轮弯路
写在 freq.c 文件头。

假值钳制带取 35~65 Hz,刻意宽于系统合理带 40.5~60.5:单拍相位差本身
带 ±5 Hz 泄漏纹波,贴边钳制会非对称丢弃纹波半周 -> 均值偏置(实测
45 Hz 点 +0.16 Hz)。系统合理带由 OF/UF 定值范围在消费侧把关。
幅值门 FREQ_AMP_MIN=50 码(拟定,待 L1-5 标定复核):低于门判无有效
波形,停更新、valid 清零、读数保持 -> 浮空/PT 断线不发散。源优先级
UA(C02)->IA(C01),按名前缀解析、每拍动态回退,切换即废相位基准。

prot_set:尾追加 8 项(PS_ID_COUNT 32->40,编号不回收)
OF_EN/OF_HZ/OF_DLY/UF_EN/UF_HZ/UF_DLY/RET_OF/RET_UF。OF/UF 逐项通讯
地址不在本仓库 -> 全部 comm_addr=0 本地项(104 C_SE 不可寻址,待对照
参考表补)。频率返回系数独立(0.99/1.01),勿与电气量 0.95/1.05 共用。
bad_mask 随项数扩为 uint64_t。PROT_SET_BLOB_VER 1->2。

prot_set_store:换代与损坏分流。load 返回 2 = 版本/项数换代(本文件
打 INFO "prot set blob version change, defaults in use"),-1 = 真损坏
(defaultTask 按 CORRUPT 报 ERROR + 自检留痕)。B7 旧档(ver=1/32 项)
升级首启属预期路径,不得报成损坏。语义分流收在属主文件内,
default_task.c 不动。

prot_elm:实例表加 src 字段(0=phasor rms,1=freq),PE_OF/PE_UF 尾
追加(PE_COUNT 7->9)。freq 无有效读数按方向填"必不启动"值(过量 0 /
欠量 1e30 -> 滞回比较恒假 -> 元件复位),不整拍跳过——频率源缺失不得
饿死电流/电压类元件(B5 行为不回归)。OF/UF 暂未映射独立 104 遥信点,
动作只经 YX15 保护总聚合可见(送检点表 = B10)。

板端接线:adcTask 在 phasor_run() 之后同拍调 freq_run();心跳/首拍日志
追加 freq(<tag>): f=%ld valid=%u (x0.01)(MicroLIB 无 %f)。app_main
内核前 freq_init()。

主机单测:freq 46 用例(J1 扫频 45~55 Hz <=0.02 Hz 实测最坏 0.0101;
J2 噪声 1% <=0.10 实测 0.0160;J3 启动收敛 <=40 ms 实测 20;J4 阶跃
50->51 <=120 ms 实测 98;J5 浮空 valid=0 不发散;J6 跳变 60 ms 内回
±0.05;另直流 8000 码 / 3-5 次谐波 20%/10% / UA->IA 回退)。
prot_set 90(+TC-PS-011 OF/UF 行 + 64 位 bad_mask bit39 + 旧档拒绝)、
prot_elm 47(+OF/UF 滞回与方向隔离 + run() 测频接线)。回归:phasor
57、iec104 94 全绿。

J4=120 ms 是"精度优先于响应"的显式取舍(Lead 批准):4 周波窗换
0.317->0.0101 Hz,测频响应 98 ms << OF/UF 定时限默认 1000 ms(占
总动作时间 <10%),暂态另有 J6 <=60 ms 兜底。

注册:freq.c 入 uvprojx algo 组 + check_registration.py SIM_EXEMPT
(纯算法、模拟器无消费路径,照 prot_set.c 先例)。

@build 0 错误 0 警告(Code=207468 RO=18304 RW=1068 ZI=153084)。
RAM:RW_IRAM1 96096->96728 B(+632 B,73.3%->73.8%)、RW_CCM 不变
57376 B(87.9%)、Flash 224596->226840 B(+2244 B,21.4%->21.6%);
台账随合并由 Lead 回填。stack_report:adcTask 静态最坏链 1320 B /
余量 1240 B(48%)改前后完全不变(freq_run 帧未进最坏链,无需抬栈);
protectTask 链 872 B / 余 1176 B(57%)同样不变。

gate: only mem-budget/mem-numbers pending lead ledger backfill
--------------------------------------------------------------
门禁实跑(pre-commit run --all-files,Lead 批准 --no-verify 提交):
28 Passed / 2 Failed,失败仅 mem-numbers 与 mem-budget 两条——均为
docs/内存管理 受管快照与本 .map 不一致(台账由 Lead 每次合并后
--write 回填,避免多分支同改受管表冲突)。其余含 doc-links /
coding-standard / no-mojibake / cppcheck / registration-check /
13 条主机单测钩子全 Passed(gitlint 属 commit-msg 阶段,不在该次
--all-files 内)。ci_local L0.5 全绿(freq 46/46 等)。
字节卫生自查(--no-verify 跳过卫生钩子,故手工核):17 个暂存文件
全部 BOM=无 / CR=0(纯 LF)/ U+FFFD=0;自研 .c/.h 去注释后字符串
字面量 100% ASCII(唯一非 ASCII 字面量在 test/host/iec104/iec104_test.c
的既有用例名,非本次改动行;test/ 不在 coding-standard 钩子范围)。
2026-10-12 19:16:37 +08:00

104 lines
5.1 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.
/*
* 模块名称:保护元件框架(L3,P1 子集)
* 模块功能:通用"越限 + 返回系数滞回 + 定时限"元件核 + 实例表(数据驱动,
* 同 phasor 实例表模式)。判据为**纯逻辑**(吃测量值 + 定值),
* 主机侧可离线单测;出口/SOE(L4)与任务编排为后续。
*
* V0.1 有效实例(N-18:仅 TPAFE 三路):
* OC1/OC2/OC3 三段式相间过流(A 相 IA,L3-1)
* OL 过负荷(A 相 IA,L3-3)
* OV/UV 过压/欠压(UA,L3-4)
* 3U0 零序过压(UN 外部零序电压通道,L3-6)
* OF/UF 过频/低频(测量量 = freq 层测得频率,L3-5,B8 落地)
*
* 测量数组口径:相量类实例 = 基波有效值(A/V);OF/UF = 测得频率(Hz)。
* run() 按实例表 src 字段取数(0=phasor rms,1=freq)。freq 无有效读数
* (未启动收敛或幅值门失效)→ 该行按方向填"必不启动"值(过量填 0、
* 欠量填极大值 → 滞回核比较恒假 → 元件复位不误动),**不整拍跳过**
* (否则浮空台架会把 OC/UV 等全部饿死,B5 行为回归)。
*
* 与参考系统的口径差异(有意简化,均已在规划文档登记):
* - 返回门槛 = 定值 × 返回系数(参考经 CN_SET_RET 独立定值,等价);
* - 返回**即时**(参考 GRly_TimeRelay 带 10 ms 返回延时——V0.1 无出口硬件,
* 抖动只影响日志,P4 接出口时再补返回延时);
* - 复压/方向/无压无流闭锁(dwSetFyCtrl 类)随 P2/P3 元件扩展;
* - 工程量↔码值换算系数暂为 1(L1-5 标定未实现,同 phasor 层口径)。
*
* 线程约定:init 内核启动前或保护消费者任务上下文;eval/run 仅保护消费者任务
* 上下文(B5 起 = protectTask);request_reset 任意上下文(跨上下文复归请求,
* 如 104 遥控 YK11 在 iec104Task 落请求、下一拍 eval 头部消费)。板端接线
* 走栈预算 + IWDG 心跳门禁。
*
* 创建日期:2026-10-11
*/
#ifndef PROT_ELM_H
#define PROT_ELM_H
#include <stdint.h>
/* 元件实例编号(枚举序 = 实例表序 = 测量数组序;尾追加,编号不回收) */
typedef enum {
PE_OC1 = 0, /* 过流 Ⅰ 段(IA) */
PE_OC2, /* 过流 Ⅱ 段(IA) */
PE_OC3, /* 过流 Ⅲ 段(IA) */
PE_OL, /* 过负荷(IA) */
PE_OV, /* 过压(UA) */
PE_UV, /* 欠压(UA,欠量方向) */
PE_3U0, /* 零序过压(UN) */
PE_OF, /* 过频(测量量 = freq 层测得频率,B8,L3-5) */
PE_UF, /* 低频(测量量 = freq 层测得频率,欠量方向,B8,L3-5) */
PE_COUNT
} prot_elm_id_t;
/* 单元件状态快照(读侧拷贝语义,见 prot_elm_state) */
typedef struct {
uint8_t enabled; /* 本拍投入(定值投退 && 未闭锁) */
uint8_t picked; /* 越限启动(滞回后电平) */
uint8_t asserted; /* 定时限到点(动作) */
uint32_t pick_ms; /* 本次启动起始时基(0 = 未启动过;asserted 延时观测用) */
} prot_elm_state_t;
/*
* 函数功能:复位全部元件状态(定值不动;幂等,内核启动前可调)
* 限定条件:内核启动前,或保护消费者任务上下文(跨上下文请用 request_reset)
*/
void prot_elm_init(void);
/*
* 函数功能:登记"复归元件状态"请求(任意上下文安全,单字标志)
* 函数说明:下一拍 prot_elm_eval() 头部消费并执行 prot_elm_init()。
* 104 遥控 YK11(iec104Task 上下文)必须走本接口,不得直接调
* prot_elm_init()(会与 protectTask 的 eval 竞态)。
*/
void prot_elm_request_reset(void);
/*
* 函数功能:纯判据步进——按当前定值(prot_set RAM)+ 给定测量值推进所有元件
* 入口参数:now_ms - 单调时基(tick ms,回绕安全:差值法)
* meas - PE_COUNT 个测量值(工程量:A / V),NULL 视为全 0
* 返 回 值:无
* 限定条件:仅保护消费者任务上下文;调用方保证定值已装载
* 函数说明:越限方向由实例表决定;投退=0 或定值闭锁(L5-1)→ 状态复位、
* 不判据。延时判定用 (uint32_t)(int32_t)(now - t0) >= dly,回绕安全。
*/
void prot_elm_eval(uint32_t now_ms, const float meas[PE_COUNT]);
/*
* 函数功能:适配层——从 phasor 使用区取 rms 组装测量数组后调 prot_elm_eval
* 入口参数:now_ms - 同上
* 说明:phasor 未就绪(-2,未满一周波)的实例本拍按"测量=上一拍值"处理会
* 引入陈旧判据 → 直接**整拍跳过**(不 eval),与 adc_task 跳拍同口径。
*/
void prot_elm_run(uint32_t now_ms);
/* 状态快照拷贝(NULL 安全;越界全零) */
void prot_elm_state(prot_elm_id_t id, prot_elm_state_t *out);
/* 动作位图(bit i = 元件 i asserted),供 L4 出口 / 心跳日志 */
uint32_t prot_elm_active_mask(void);
/* 实例名(ROM 常量,日志用) */
const char *prot_elm_name(prot_elm_id_t id);
#endif /* PROT_ELM_H */