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 钩子范围)。
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 模块名称:频率测量层(L2 派生量,L3-5 过频/低频的测量输入)
|
||||
* 模块功能:由 L2 相量使用区的基波相量导出系统频率——相邻两拍基波相量取
|
||||
* **共轭乘积** P = Z(m)·conj(Z(m-1))(其角 = 每拍相位增量,纯音 =
|
||||
* +2πf/Fs,免显式解缠绕),再对**自适应窗长**的相位增量做滑动平均:
|
||||
* K_eff = round(8·Fs/(2·f_hat)) ≈ 泄漏纹波周期 fs/(2f) 的整数倍
|
||||
* -> 非整周波 DFT 负序泄漏造成的 ~10 ms 周期相位纹波相消
|
||||
* (定长窗平均实测扫频误差 0.2~0.32 Hz,自适应窗进入 0.01 Hz 量级,
|
||||
* 推导与实测见 freq.c 文件头与 test/host/freq 输出)。
|
||||
* 覆盖规划 §3.3 L3-5 的测量侧与 §4.2 测频范围(45~55 Hz 建议段)。
|
||||
*
|
||||
* 原理与判据(详见 App/algo/README.md §3.7 与 test/host/freq/):
|
||||
* - 采样率 Fs = 1000 Hz(TIM3 节拍,与 phasor 同源);45~55 Hz 段每拍
|
||||
* 相位增量 ≈ 0.28~0.35 rad,距 ±π 缠绕边界裕度巨大,无歧义;
|
||||
* - 瞬时值钳制:单拍相位差折算越出 35~65 Hz(**假值带**,宽于系统合理带
|
||||
* 40.5~60.5=参考 CN_HERTZ_SOFTFR_MIN/MAX,规划 §10.1——贴边会非对称
|
||||
* 丢弃泄漏纹波半周造成偏置,见 FREQ_INST_* 注释)判为采样间隙/相位
|
||||
* 跳变假值,本拍不入窗;
|
||||
* - 幅值门:源相量有效值 < FREQ_AMP_MIN 判"无有效波形"(浮空输入/PT 断线),
|
||||
* 停止更新、valid 清零、读数保持 → 台架浮空 rms≈0 不发散;
|
||||
* - 信号源优先级:UA 基波 → IA 基波(UA 无幅值时用 IA,台架只注电流亦可测频)。
|
||||
*
|
||||
* ⚠ 本层**纯算法**:不碰寄存器、不碰 RTOS、不自己取数(输入 = phasor 使用区)
|
||||
* → 主机侧可免桩单测(L0.5,落实登记簿 A-4)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-12(B8)
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-12 DTU 项目组 创建:相位解缠绕测频 + 滑动平均 + 幅值门
|
||||
*/
|
||||
|
||||
#ifndef FREQ_H
|
||||
#define FREQ_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* 测频标称采样率(Hz):TIM3 1 kHz 节拍,与 phasor/sample_ring 同一时基 */
|
||||
#define FREQ_FS_HZ 1000.0f
|
||||
|
||||
/* 单拍(逐拍相位差折算)**假值钳制带**(Hz):越出即判采样间隙/相位跳变/
|
||||
* 深度畸变过渡段产生的粗大假值,本拍不入窗(读数保持)。
|
||||
* ⚠ 与"系统合理频率带"分开定:非整周波 DFT 泄漏让单拍相位差本身带
|
||||
* ±5 Hz 量级纹波(45/55 Hz 端实测),钳制带若贴住 40.5~60.5(参考
|
||||
* CN_HERTZ_SOFTFR_MIN/MAX,规划 §10.1)会**非对称丢弃纹波半周** → 均值
|
||||
* 偏置(实测 45 Hz 点 +0.16 Hz)。放宽到 35~65 Hz 后仍拦得住窗滑 2 拍的
|
||||
* ≈2f(90~110 Hz)与相位跳变假值,又不吃掉正常纹波。
|
||||
* 系统合理带 40.5~60.5 由消费侧把关(OF/UF 定值范围 50~55 / 45~50),
|
||||
* 测量层不做截断——测量层只保证"测得准"。 */
|
||||
#define FREQ_INST_MIN_HZ 35.0f
|
||||
#define FREQ_INST_MAX_HZ 65.0f
|
||||
|
||||
/* 幅值门限(有效值,**原始码值口径**,L1-5 标定未实现):低于该值判
|
||||
* "无有效波形"。⚠ 拟定值(远小于正常波形千级码值、远大于浮空噪声),
|
||||
* 标定落地后须按工程量口径复核(同 prot_set 默认值"拟定"口径)。 */
|
||||
#define FREQ_AMP_MIN 50.0f
|
||||
|
||||
/* 相位增量滑窗环容量(拍):96 拍 = 96 ms 上限。实际平均窗长**自适应**
|
||||
* (freq.c fq_read_freq:K_eff = round(8·Fs/(2·f_hat)),45~55 Hz 段 73~89 拍),
|
||||
* 使窗长恰为 DFT 负序泄漏纹波周期 fs/(2f) 的整数倍 -> 纹波相消。
|
||||
* 保护定值延时 ≥100 ms,~90 ms 窗不影响动作时序判据。 */
|
||||
#define FREQ_AVG_TAPS 96u
|
||||
|
||||
/* 自适应窗长下限(拍):K_eff 钳位区间下界(启动段窗未满时按已有拍数)。 */
|
||||
#define FREQ_AVG_MIN 72u
|
||||
|
||||
/* 判"启动收敛完成"所需最少样本数(拍):20 拍 = 20 ms(首个有效相量后
|
||||
* 21 拍内出首个读数)。窗未满时按已有样本求均值(部分窗),收敛快。 */
|
||||
#define FREQ_START_TAPS 20u
|
||||
|
||||
/*
|
||||
* 函数功能:频率测量层初始化
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功
|
||||
* 限定条件:内核启动前调用(无 RTOS 依赖);须在 phasor_init() 之后无要求
|
||||
* (本层只在使用区就绪后才产出)
|
||||
* 函数说明:清平均缓冲/计数/读数/有效标志;可重复调用(幂等)。
|
||||
*/
|
||||
int freq_init(void);
|
||||
|
||||
/*
|
||||
* 函数功能:每拍测频步进(读 phasor 使用区 → 相位差 → 钳制 → 滑动平均)
|
||||
* 入口参数:无
|
||||
* 返 回 值:无
|
||||
* 限定条件:仅采集消费者任务上下文(adcTask),**须在 phasor_run() 之后**
|
||||
* 同拍调用(本拍相量已切换使用区)
|
||||
* 函数说明:相量未就绪(-2)→ 本拍不更新;幅值门失效 → 停更新、valid 清零、
|
||||
* 读数保持;单拍乘积角折算越出 40.5~60.5 Hz → 假值拍入 0(不进窗)。
|
||||
* 窗未满 FREQ_AVG_TAPS 时按已有乘积求和(部分窗),收敛快。
|
||||
*/
|
||||
void freq_run(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读测得频率(工程量 Hz)
|
||||
* 入口参数:out - 输出(NULL 直接返回 -1)
|
||||
* 返 回 值:0 - 已有读数(out = 现值,可能为保持值);-1 参数错;
|
||||
* -2 - 从未启动收敛(尚无任何有效读数,out 写 0)
|
||||
* 限定条件:无(任意上下文;float 单字读,Cortex-M 原子)
|
||||
* 函数说明:本函数判"**有没有数**";判"数新不新"(幅值门是否仍 OK)
|
||||
* 用 freq_valid()。保护元件消费口径 = get==0 && valid==1。
|
||||
*/
|
||||
int freq_get(float *out);
|
||||
|
||||
/*
|
||||
* 函数功能:读"本拍有效"标志(幅值门 OK 且已启动收敛)
|
||||
* 入口参数:无
|
||||
* 返 回 值:1 - 读数新鲜;0 - 未启动或幅值门失效(读数仍保持)
|
||||
* 限定条件:无
|
||||
* 函数说明:供心跳日志与保护元件判"测量可信"用。
|
||||
*/
|
||||
uint8_t freq_valid(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读最近一次有效读数(不判新鲜度)
|
||||
* 入口参数:无
|
||||
* 返 回 值:Hz;从未收敛返回 0.0
|
||||
* 限定条件:无
|
||||
* 函数说明:单字 float 读,原子。
|
||||
*/
|
||||
float freq_last(void);
|
||||
|
||||
#endif /* FREQ_H */
|
||||
Reference in New Issue
Block a user