Files
STM32F4-Base/App/algo/freq.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

129 lines
7.0 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 派生量,过频 / 低频的测量输入)
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
* 模块功能:由 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 过频 / 低频 的测量侧与 §4.2 测频范围(45~55 Hz 建议段)。
*
* 原理与判据(详见 docs/算法/采集与算法层设计说明.md §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)。
* 适用平台: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
/* 幅值门限(有效值,**二次值 V 口径**,「数据可信」卡 6 重定,依据其 §2.3 表第 1 行):
* 低于该值判"无有效波形"。取值 50 V = 0.5×Un(相电压标称 100 V,与
* App/algo/prot_set.c 头部量纲基准同源):正常带载 UA 在 0.9~1.1×Un,
* 50 V 之下只可能是断保险/PT 空载/浮空噪声(浮空实测 ~1.7 V 引脚域、
* 二次域更小),不会吃掉正常纹波(纹波谷值远大于 0.5×Un)。
* ⚠ 过渡期行为(eng=0,卡 12 标定前):帧值还是引脚电压(≤5 V < 50 V),
* 本门**恒失效** → freq_valid()=0 → OF/UF 按"必不启动"填充复位——与卡 5
* 未标定闭锁同向(判据链本就停走),不构成额外风险;标定后自动恢复本意。
* 数值沿用原 50.0f 属巧合(原为"远小于千级码值"的拟定值),不是漏改。 */
#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 */