Files
STM32F4-Base/App/algo/freq.h
T
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00

128 lines
6.9 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 派生量,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 建议段)。
*
* 原理与判据(详见 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 */