Files
STM32F4-Base/App/algo/freq.h
T
wandering1 9f68b7bc03
CI / 静态门禁 (pre-commit) (push) Failing after 3m46s
CI / 主机侧单测 (MSVC) (push) Successful in 30s
feat(dsh): spawn_teammate 角色 persona 自动注入 + 测频带外饱和边界登记
一、子 Agent 角色自动注入(本批主线)

dsh-plugin-subagent-roles fork(仓库 .dsh/plugins/)新增事件路径注入:
spawn_teammate 的 prompt 首行写 @role:<角色id>,插件在子会话首轮模型
请求组装之前把 .dsh/roles/<id>.md 的 persona 注入其系统提示词,并应用
工具白名单。此前只有 subagent_role 路径自动注入,team 路径需 Lead 手工
拼接 persona,本批把两条路径拉齐。

实现要点(细节见 docs/环境与部署/子Agent角色自动注入实现说明.md):
- 在 lead 会话的 tool/call(spawn_teammate) 截获 @role 标记,在
  subagent/catalog 拿 childId 后经 agents.get(childId) 注入——子会话侧
  user/message 必然晚于首轮 system/message,只能在 lead 侧接力。
- tools.restrict() 只校验全局工具名,ops_* 等作用域工具混入会整批抛错;
  降级为解析报错尾部 known global tools 求交集后重试。
- 保留子会话 @role 标记兜底路径(第 2 步起生效)+ 双去重集合。
- 诊断日志 ~/.dsh/role-inject-diag.log,一次健康注入 4 行。

配套文档与口径:
- 新增 docs/环境与部署/子Agent角色自动注入实现说明.md(实现参考 +
  排障速查,9 章),docs/README.md 索引挂载。
- AGENTS.md 委派策略改为自动注入口径(无需手工拼接)。
- .dsh/roles/README.md 删去"调用预算"列:budget.js 的 arm() 只由
  subagent_role 委派路径调用,teammate 路径不挂账,原列是假上限承诺;
  改加脚注指向实现说明 §1.1。

二、测频幅值门放宽至 5 V + 带外饱和边界登记(freq.h)

- FREQ_AMP_MIN 50 V -> 5 V(用户裁定 2026-11-13):台架低端注入(标定
  用 5~25 V 档)与低负荷工况也能测频;浮空二次域噪声几十 mV,仍差两个
  数量级,噪声不测频的本意不变。
- 注释同步登记:IA 回退候选同拿 5.0 比较,二次电流 0.5~2 A 够不到
  "5 A",只注电流实际不回退测频(电流域门限另裁);窗环钳位区间
  [72,96] 只对 41.7~55.6 Hz 满足整倍条件,带外读数饱和钉沿。
- docs/算法/采集与算法层设计说明.md 新增 §7.3 准确测频带与带外饱和
  (60 Hz 实证钉 ~55;用户裁定不改参数,只登记边界)。

验证:/keil-build 0 错误 0 警告(Code=256592 RO=32036 RW=2088
ZI=152584);pre-commit 52 钩子全绿(含 3 根 manual 阶段:cppcheck、
test-cppcheck、test-coverage)。
2026-11-14 10:07:27 +08:00

142 lines
8.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.
/*
* 模块名称:频率测量层(全周傅氏相量的派生量,过频 / 低频的测量输入)
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
* 模块功能:由 全周傅氏相量使用区的基波相量导出系统频率——相邻两拍基波相量取
* **共轭乘积** 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;工程量域下 IA 候选
* 需二次电流达到门限数值,见 FREQ_AMP_MIN 注释)。
*
* ⚠ 本层**纯算法**:不碰寄存器、不碰 RTOS、不自己取数(输入 = phasor 使用区)
* → 主机侧可免桩纯逻辑单测。
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
* 作者:DTU 项目组
* 创建日期:2026-10-12(B8)
* 修改记录:
* v1.0 2026-10-12 DTU 项目组 创建:相位解缠绕测频 + 滑动平均 + 幅值门
* v1.1 2026-11-13 DTU 项目组 幅值门 50 V → 5 V(用户裁定,台架低端注入
* 也能测频);其余判据不变
* v1.2 2026-11-13 DTU 项目组 纯注释:登记窗环钳位区间的带外饱和边界
* (60 Hz 实证钉 ~55;算法与参数不改,
* 设计说明 §7.3)
*/
#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 口径**,依据
* `docs/算法/采集与算法层设计说明.md` §13.7 表第 1 行):
* 低于该值判"无有效波形"。取值 5 V = 0.05×Un(相电压标称 100 V,与
* App/algo/prot_set.c 头部量纲基准同源):2026-10-25 曾按工程量重定为
* 50 V = 0.5×Un,2026-11-13 用户裁定放宽到 5 V,让台架低端注入(标定用
* 5~25 V 档)与低负荷工况也能测频;浮空噪声实测二次域几十 mV,仍差两个
* 数量级 → "噪声不测频"的本意不变,带载 UA(0.9~1.1×Un)远在门上。
* ⚠ 候选共用数值:IA 回退候选同拿 5.0 比较——二次电流域 0.5~2 A 够不到
* "5 A",台架只注电流实际不回退测频;电流域自己的门限另裁,勿在此顺改。
* ⚠ eng=0 期间(未标定,帧值还是引脚电压,满幅 < 3.3 V)仍低于本门 →
* freq_valid()=0 → OF/UF 按"必不启动"填充复位——与未标定即闭锁同向;
* 标定(eng=1)后按本门限正常放行。 */
#define FREQ_AMP_MIN 5.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 窗不影响动作时序判据。
* ⚠ 钳位区间 [FREQ_AVG_MIN, FREQ_AVG_TAPS] 只对 41.7~55.6 Hz 满足整倍条件:
* 带外窗非纹波周期整数倍 -> 读数饱和钉沿(高侧 ~55、低侧 ~43.5,且带外
* freq_valid() 仍为 1,数值不可消费)。2026-11-13 台架 60 Hz 实证与
* 「参数不改」裁定见 docs/算法/采集与算法层设计说明.md §7.3。 */
#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 */