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:
+113
-20
@@ -16,11 +16,12 @@
|
||||
| `sample_ring.h` / `sample_ring.c` | L2 输入 | **采样环缓冲**:按语义通道保存最近 2 个周波(40 点)采样值,供相量层"以当前时刻为终止"取窗 | `sample_ring_push_frame()` `sample_ring_window()` | L2 输入(为 L2-6 滑动窗供数) |
|
||||
| `phasor.h` / `phasor.c` | L2 | **全周傅氏相量计算**:一周波 20 点单频 DFT → 基波/谐波相量(有效值口径),双区原子切换供无锁读 | `phasor_run()` `phasor_get()` `phasor_calc()` | L2-1 / L2-2 / L2-3 / L2-6 / L2-7 |
|
||||
| `prot_set.h` / `prot_set.c` | L3 输入 | **保护定值模型**:Q10 定点 RAM 表(通讯地址对照参考 `g_tCommSetParaTab`)+ 范围校验 + 校验和封存自检闭锁 | `prot_set_init()` `prot_set_load_raw()` `prot_set_get_f()` `prot_set_selfcheck()` | §5 定值模型 / L5-1 |
|
||||
| `prot_elm.h` / `prot_elm.c` | L3 | **保护元件框架**:通用"越限 + 返回系数滞回 + 定时限"核 + 数据驱动实例表(过流三段 / 过负荷 / 过压 / 欠压 / 零序过压) | `prot_elm_init()` `prot_elm_eval()` `prot_elm_run()` `prot_elm_active_mask()` | L3-1 / L3-3 / L3-4 / L3-6(软件侧) |
|
||||
| `prot_elm.h` / `prot_elm.c` | L3 | **保护元件框架**:通用"越限 + 返回系数滞回 + 定时限"核 + 数据驱动实例表(过流三段 / 过负荷 / 过压 / 欠压 / 零序过压 / **过频 / 低频**) | `prot_elm_init()` `prot_elm_eval()` `prot_elm_run()` `prot_elm_active_mask()` | L3-1 / L3-3 / L3-4 / L3-5 / L3-6(软件侧) |
|
||||
| `freq.h` / `freq.c` | L2 派生量 | **频率测量**:相邻两拍基波相量共轭乘积取角(每拍相位增量)+ 自适应窗长滑动平均 + 假值钳制 + 幅值门(UA→IA 源回退) | `freq_init()` `freq_run()` `freq_get()` `freq_valid()` | L3-5 测量侧(`docs/算法/继电保护功能复现规划.md` §4.2 测频范围) |
|
||||
|
||||
依赖关系(自下而上,无环):`adc_input.h`(纯枚举 + 声明)← `sample_ring` ← `phasor`;
|
||||
`prot_set` **独立**(仅 `stdint.h`);`prot_elm` ← `prot_set` + `phasor`。
|
||||
`sample_ring.c` / `phasor.c` / `adc_health.c` / `prot_set.c` / `prot_elm.c` **零平台依赖**
|
||||
依赖关系(自下而上,无环):`adc_input.h`(纯枚举 + 声明)← `sample_ring` ← `phasor` ← `freq`;
|
||||
`prot_set` **独立**(仅 `stdint.h`);`prot_elm` ← `prot_set` + `phasor` + `freq`。
|
||||
`sample_ring.c` / `phasor.c` / `adc_health.c` / `prot_set.c` / `prot_elm.c` / `freq.c` **零平台依赖**
|
||||
(只用 `math.h` / `stddef.h` / `stdint.h` 与本目录头);
|
||||
`adc_input.c` 另依赖 `adc.h`(`hadc1`)与 `tpafe5160.h`(快照),主机侧以桩头替身。
|
||||
|
||||
@@ -38,12 +39,15 @@ flowchart TD
|
||||
POLL --> RAW["adc_input_raw(ch) × 9<br/>按语义取原始码值"]
|
||||
RAW --> RING["sample_ring_push_frame()<br/>整帧入环(40 点窗)"]
|
||||
RING --> RUN["phasor_run()<br/>逐实例 20 点重算 + 双区切换"]
|
||||
RUN --> FREQ["freq_run()<br/>共轭乘积相位增量 + 自适应窗均值(B8)"]
|
||||
RUN --> GET["phasor_get(idx)"]
|
||||
FREQ --> FGET["freq_get() / freq_valid()"]
|
||||
GET --> CONS["消费者:adcTask 日志(现状)<br/>UI / 自检 / L3 保护任务(待接)"]
|
||||
FGET --> PCONS["消费者:prot_elm_run() OF/UF 判据(B8)<br/>adcTask 心跳 freq 行"]
|
||||
```
|
||||
|
||||
初始化落点:`adc_input_init()` 与 `phasor_init()` 由 `App/app/app_main.c` 的
|
||||
`app_main_init()` 在**内核启动前**调用(三者 `*_init()` 均无 RTOS 依赖、幂等);
|
||||
初始化落点:`adc_input_init()` 与 `phasor_init()`、`freq_init()` 由 `App/app/app_main.c` 的
|
||||
`app_main_init()` 在**内核启动前**调用(各 `*_init()` 均无 RTOS 依赖、幂等);
|
||||
`phasor_init()` 内部会连带 `sample_ring_init()`,等价于"刚上电"。
|
||||
|
||||
## 3. 逐文件说明
|
||||
@@ -176,6 +180,19 @@ flowchart TD
|
||||
(基址 0x8820);单测 TC-PS-006 保证地址不重复、`min ≤ def ≤ max`、投退项 0..1。
|
||||
- ⚠ **默认值/范围为拟定值**(参考 EEPROM 出厂表不在本仓库),待对照修订——
|
||||
见规划 §5.4 落实进度注。
|
||||
- **B8 扩 8 项(32→40)**:`PS_OF_EN/OF_HZ/OF_DLY/UF_EN/UF_HZ/UF_DLY/RET_OF/RET_UF`
|
||||
**尾追加**(编号不回收,枚举序 = 数组序 = 校验和覆盖序)。OF/UF 的逐项通讯地址
|
||||
不在本仓库(参考保护定值段 0x8820~0x8882 仅示"含低压/低频"类目)→ 全部按
|
||||
**本地项 comm_addr=0** 登记(104 C_SE 不可寻址),待对照参考表补。
|
||||
频率返回系数**独立**(`RET_OF=0.99 / RET_UF=1.01`,返回门槛 = 定值×系数)——
|
||||
频率滞回带与电气量 0.95/1.05 不同量级,勿共用。
|
||||
- **B8 换代语义**:项数变 → blob 144→176 B、`PROT_SET_BLOB_VER` 1→2。B7 旧档
|
||||
(ver=1/32 项)在新固件 parse 即被**版本/长度闸**拒绝 → 出厂默认;
|
||||
`prot_set_store_load()` 返回码分流:**2 = 换代**(`prot_set_store.c` 打 INFO
|
||||
"prot set blob version change, defaults in use",**不是** CORRUPT、不记自检),
|
||||
−1 = 真损坏(defaultTask 打 ERROR + 自检留痕)。升级路径不得报成损坏。
|
||||
- **bad_mask 随项数扩为 `uint64_t`**(bit i = 第 i 项越界;消费方仅主机单测,
|
||||
板端闭锁走 `prot_set_locked()`)。
|
||||
|
||||
### 3.6 `prot_elm.c` / `prot_elm.h` — 保护元件框架(L3,P1 子集)
|
||||
|
||||
@@ -185,24 +202,88 @@ flowchart TD
|
||||
|
||||
| API | 干什么 | 注意 |
|
||||
|---|---|---|
|
||||
| `prot_elm_init()` | 复位状态 + 解析相量实例索引(按名前缀 `C01/C02/C03`) | 须在 `phasor_init()` 之后调 |
|
||||
| `prot_elm_init()` | 复位状态 + 解析相量实例索引(按名前缀 `C01/C02/C03`) | 须在 `phasor_init()` 之后调;`src=FREQ` 行无相量绑定(`pidx=0xFF`,run 不查它) |
|
||||
| `prot_elm_eval(now_ms, meas)` | **纯判据**:定值 RAM + 测量数组 → 全元件状态机 | 主机单测入口;NULL 测量按 0 |
|
||||
| `prot_elm_run(now_ms)` | 适配层:`phasor_get()` 组测量 → eval | 任一相量未就绪 → **整拍跳过**(不引入陈旧判据) |
|
||||
| `prot_elm_run(now_ms)` | 适配层:按实例表 `src` 组测量 → eval(`src=0` 取 `phasor_get()` rms,`src=1` 取 `freq_get()`) | 相量类任一未就绪 → **整拍跳过**(不引入陈旧判据);频率类无有效读数 → **按方向填"必不启动"值**(过量填 0 / 欠量填 `1e30` → 滞回比较恒假 → 元件复位),**不整拍跳过**——频率源缺失不得饿死电流/电压类元件 |
|
||||
| `prot_elm_state()` / `prot_elm_active_mask()` | 状态快照 / 动作位图 | 供 L4 出口与心跳日志 |
|
||||
|
||||
**要点**
|
||||
|
||||
- **滞回**:已启动用返回门槛(`定值 × 返回系数`),未启动用动作门槛——
|
||||
过量/欠量方向由实例表 `over` 位统一处理(`UV` 是唯一欠量实例)。
|
||||
过量/欠量方向由实例表 `over` 位统一处理(`UV`/`UF` 为欠量实例)。
|
||||
- **定时限**:`(uint32_t)(int32_t)(now - t0) >= dly` 差值法,tick 回绕安全;
|
||||
`dly=0` → 启动同拍即动作。返回**即时**(参考带 10 ms 返回延时,P4 接出口硬件时再补)。
|
||||
- **闭锁联动**:`prot_set_locked()`(L5-1)或投退=0 → 该元件状态整体复位、不判据。
|
||||
- **V0.1 实例**(N-18):`OC1/OC2/OC3/OL`(IA)、`OV/UV`(UA)、`3U0`(UN 外部通道)。
|
||||
零序过流(IN 走 STM32)与过频/低频(待频率算法)**故意不在表中**。
|
||||
- **V0.1 实例**(N-18):`OC1/OC2/OC3/OL`(IA)、`OV/UV`(UA)、`3U0`(UN 外部通道)、
|
||||
`OF/UF`(freq 层测频,B8)。零序过流(IN 走 STM32)**故意不在表中**。
|
||||
- **OF/UF 出口可见性(B8)**:两元件**未单独映射 104 遥信点**(送检点表 = B10 口径),
|
||||
其动作目前**只经 YX15 保护总聚合可见**(`iec104_pts` 的 fault-total 掩码含 OC/3U0,
|
||||
OF/UF 动作会置总位但无独立分点)——README 与规划 §3.3 L3-5 行必须同此口径,勿写"独立遥信"。
|
||||
- **板端接线已落地(B5,2026-10-12)**:`App/task/protect_task.c`(protectTask,
|
||||
20 ms 节拍)消费 `prot_elm_run()` + L5-4 采样故障闭锁 + L5-1 校验和巡检;
|
||||
跨上下文复归走 `prot_elm_request_reset()`(104 YK11 落请求、eval 头部消费)。
|
||||
判据结果经 104 遥信上送(`iec104_pts` YX15~23/31)。→ `docs/算法/继电保护功能复现规划.md` §7
|
||||
⚠ 实例数 7→9(B8):protectTask 启动锚点 `elm=` 与 `prot_elm_active_mask()` 位宽随之变,
|
||||
`.serialtest` 锚点表未含 `elm=` 数字(已核),无需改配置。
|
||||
|
||||
### 3.7 `freq.c` / `freq.h` — 频率测量层(L2 派生量,L3-5 测量输入,B8)
|
||||
|
||||
**解决的问题**:过频/低频(L3-5)需要系统频率读数,而 L2 只出相量。本层把
|
||||
"相邻两拍基波相量的相位差"变成"频率",并解决**非整周波窗长的 DFT 泄漏纹波**
|
||||
这一决定精度的关键问题。
|
||||
|
||||
| API | 干什么 | 注意 |
|
||||
|---|---|---|
|
||||
| `freq_init()` | 清增量环/计数/读数/相位基准/有效标志(幂等) | 内核启动前可调,须在 `phasor_init()` 之后无要求 |
|
||||
| `freq_run()` | 每拍步进:选源(UA→IA 过幅值门)→ 共轭乘积 `P=Z(m)·conj(Z(m-1))` 取角 → 假值钳制 → 自适应窗均值 | **仅 adcTask 上下文**,须在 `phasor_run()` 之后同拍调 |
|
||||
| `freq_get(out)` | 读数(Hz) | 0 有读数(可能是保持值)/ −1 参数错 / −2 从未启动收敛(写 0) |
|
||||
| `freq_valid()` | "本拍新鲜"标志 | 1 = 幅值门 OK 且已收敛;保护消费口径 = `get()==0 && valid()==1` |
|
||||
| `freq_last()` | 最近有效读数 | 从未收敛返回 0.0 |
|
||||
|
||||
**算法要点(精度是实测驱动定稿的,两轮弯路记录在 freq.c 文件头)**
|
||||
|
||||
- **相位微分免解缠绕**:`arg(Z_now·conj(Z_prev))` 天然落在 (−π, π],纯音每拍
|
||||
角**增大** 2πf/Fs(本层相量约定 φ=atan2(fIm,fRe)=窗首初相,窗每拍前滑一点);
|
||||
45~55 Hz 段 |Δφ| ≈ 0.28~0.35 rad,距 ±π 裕度巨大。
|
||||
- **自适应窗长**(核心):20 点矩形窗在非整周波时混入**负序泄漏项**
|
||||
(45/55 Hz 处 |S₂/S₁| ≈ 3~5%),相量端轨迹成椭圆 → 逐拍相位差带周期
|
||||
δ(φ) 纹波,周期 = fs/(2f) ≈ 9.1~11.1 拍。定长窗平均不整除纹波周期 →
|
||||
扫频最坏误差 **0.317 Hz**(40 拍算术平均)/ **0.197 Hz**(60 拍复数和 Kay,
|
||||
交叉项和角不随 K² 衰减,勿再尝试)。改 `K_eff = round(8·Fs/(2·f̂))`
|
||||
(45~55 Hz 段 73~89 拍)使窗长恰为纹波周期整数倍 → 纹波相消,最坏 **0.0101 Hz**。
|
||||
- **假值钳制带 35~65 Hz**(宽于系统合理带 40.5~60.5 = 参考 `CN_HERTZ_SOFTFR_MIN/MAX`,
|
||||
规划 §10.1):单拍相位差本身带 ±5 Hz 纹波,钳制带贴边会**非对称丢弃纹波半周**
|
||||
→ 均值偏置(实测 45 Hz 点 +0.16 Hz,已修)。35~65 仍拦得住窗滑 2 拍的 ≈2f
|
||||
(90~110 Hz)与相位跳变假值;系统合理带由 OF/UF 定值范围在消费侧把关。
|
||||
- **幅值门** `FREQ_AMP_MIN=50.0`(原始码值口径,**拟定待 L1-5 标定复核**):
|
||||
低于门 = 无有效波形(浮空/PT 断线 rms≈0,相位无意义)→ 停更新、`valid` 清零、
|
||||
读数保持 → 台架浮空不发散。信号消失过渡段(≤20 ms 残窗)读数漂移有界 ≤0.3 Hz。
|
||||
- **源优先级 UA(C02)→IA(C01)**,按名前缀解析、每拍动态回退(台架只注电流亦可测频);
|
||||
源切换即废相位基准重建(UA/IA 无共同相位基准,N-18)。
|
||||
|
||||
**精度判据(J1~J6,与 `test/host/freq/freq_test.c` 文件头同口径,数字为实测)**
|
||||
|
||||
| 判据 | 条件 | 门槛 | 实测最坏 |
|
||||
|---|---|---|---|
|
||||
| J1 扫频 | 纯音 45.00~55.00 Hz,0.5 Hz 步进(21 点) | **≤ 0.02 Hz** | **0.0101 Hz** @55.0 Hz |
|
||||
| J2 噪声 | 幅值 1000 码、噪声 rms = 1% 幅值 | **≤ 0.10 Hz** | **0.0160 Hz** |
|
||||
| J3 启动收敛 | 首个有效相量 → 首个有效读数 | **≤ 40 ms** | 20 ms(@39 ms 拍,相量 @19 ms) |
|
||||
| J4 频率阶跃 | 50→51 Hz 进入 ±0.05 Hz 并保持 | **≤ 120 ms** | 98 ms(稳态 err 0.0053 Hz) |
|
||||
| J5 零幅/浮空 | rms < 幅值门 | valid=0、不发散、保持漂移 ≤0.3 Hz | 漂移 0.13 Hz,无 NaN |
|
||||
| J6 假值钳制 | 幅值+频率双跳变拍 | 60 ms 内回 ±0.05 Hz | 0.0000 Hz |
|
||||
|
||||
附加鲁棒性(同套件实测):直流偏置 8000 码 → err 0.0018 Hz;3/5 次谐波 20%/10%
|
||||
→ err 0.0065 Hz;UA 无幅值落 IA → err 0.0020 Hz。
|
||||
|
||||
**J4 = 120 ms 是"精度优先于响应"的显式取舍(Lead 批准 2026-10-12)**:自适应窗
|
||||
(M=8 ≈ 4 个泄漏纹波周期 ≈ 4 个信号周波)换来 0.317→0.0101 Hz,代价是频率骤变后
|
||||
的窗冲刷(实测 98 ms)。OF/UF 定时限默认 **1000 ms** → 测频响应占保护总动作时间
|
||||
**<10%**;暂态假值另有 J6(跳变后 ≤60 ms 回 ±0.05 Hz)兜底。故 120 ms 是工程整定,
|
||||
不是判据失守——若将来要压响应,须同时重估精度(缩短窗 = 精度回退到 0.2~0.3 Hz 档)。
|
||||
|
||||
**板端可见性(DoD)**:adcTask 心跳/首拍日志追加一行
|
||||
`freq(hb): f=5000 valid=1 (x0.01)`(MicroLIB 无 %f → ×0.01 定点,分辨率 0.01 Hz)。
|
||||
正常带波形应 `valid=1` 且 `f≈5000`;浮空应 `valid=0`、`f` 为保持值。
|
||||
|
||||
## 4. 关键约定
|
||||
|
||||
@@ -224,17 +305,22 @@ flowchart TD
|
||||
| `phasor.c` `s_zone[2][5]` `phasor_t` | 160 B |
|
||||
| `adc_input.c` `s_stm32_raw[16]` + `s_tpafe_raw[8]` | 48 B |
|
||||
| `adc_health.c` 内部状态(9 × uint32 + uint8) | ≈ 40 B |
|
||||
| `prot_set.c` `s_val[31]` + 封存/位图/闭锁 | 136 B(**未接线前被链接器移除,实测 0**) |
|
||||
| `prot_elm.c` `s_rt[7]`(16 B/实例) | 112 B(同上,实测 0) |
|
||||
| `prot_set.c` `s_val[2][40]` + 封存/位图(64 位)/闭锁 | 336 B(B8:31→40 项) |
|
||||
| `prot_elm.c` `s_rt[9]`(16 B/实例) | 144 B(B8:7→9 实例) |
|
||||
| `freq.c` 相位增量环 `fq_d[96]` + 占位标志 + 状态 | **≈ 520 B**(B8 新增) |
|
||||
| 运行期栈:`phasor_run()` 的 `win[20]` | 80 B/帧 |
|
||||
| 运行期栈:`freq_run()` 局部 | ≈ 30 B/帧(无大局部数组) |
|
||||
|
||||
改容量(`SAMPLE_RING_CYCLES` / 实例数 / `ADC_SEM_COUNT`)前后各跑一次
|
||||
改容量(`SAMPLE_RING_CYCLES` / 实例数 / `ADC_SEM_COUNT` / `FREQ_AVG_TAPS`)前后各跑一次
|
||||
`& '.venv\Scripts\python.exe' scripts/stack_report.py`(口径见 `mem-budget` skill)。
|
||||
**B8 实测(adcTask 静态最坏链)**:见 READY 消息与 `docs/内存管理/` 台账回填。
|
||||
|
||||
## 6. 构建与测试
|
||||
|
||||
- **挂载**:七个 `.c` 已登记在 `MDK-ARM/STM32F407-Demo.uvprojx`,`../App/algo` 已在 `<IncludePath>` 内。
|
||||
本层**不进** `simulator/CMakeLists.txt`(UI 模拟器不跑采集链路)。
|
||||
- **挂载**:八个 `.c` 已登记在 `MDK-ARM/STM32F407-Demo.uvprojx`,`../App/algo` 已在 `<IncludePath>` 内。
|
||||
本层**不进** `simulator/CMakeLists.txt`(UI 模拟器不跑采集链路)——纯算法且模拟器无消费路径的
|
||||
新文件(`prot_set.c` / `freq.c`)改在 `scripts/check_registration.py` 的 `SIM_EXEMPT` 登记豁免
|
||||
(`registration-check` 钩子反向校验,N-23)。
|
||||
- **主机侧单测(L0.5)**——生产源文件以 `#include` 原样纳入,无需改产品代码:
|
||||
|
||||
| 套件 | 命令 | pre-commit 钩子 |
|
||||
@@ -242,19 +328,26 @@ flowchart TD
|
||||
| 相量层(57 用例:系数表逐项复核 / 幅值·相位 / 谐波抑制与提取 / 频偏 / 采样环 / 双区交换) | `& '.venv\Scripts\python.exe' test/host/phasor/run_unit_test.py` | `phasor-unit-test` |
|
||||
| ADC 输入层(逐 Rank 注入互异码值钉死映射 + HAL 启动失败/超时分支) | `& '.venv\Scripts\python.exe' test/host/adc_input/run_unit_test.py` | `adc-input-unit-test` |
|
||||
| 有效性校验(31 用例:100 ms 静默门限两侧 / 丢帧边沿 / 倒退锁存 / 错误增量 / tick 回绕 / NULL 守卫 / init 幂等) | `& '.venv\Scripts\python.exe' test/host/adc_health/run_unit_test.py` | `adc-health-unit-test` |
|
||||
| 定值模型(30 用例:范围校验 / all-or-nothing 装载 / 封存-自检-闭锁 / Q10 往返 / 元数据表自检) | `& '.venv\Scripts\python.exe' test/host/prot_set/run_unit_test.py` | `prot-set-unit-test` |
|
||||
| 元件框架(27 用例:滞回与定时限 / 欠量方向 / 投退 / 闭锁联动 / 时基回绕 / 位图聚合) | `& '.venv\Scripts\python.exe' test/host/prot_elm/run_unit_test.py` | `prot-elm-unit-test` |
|
||||
| 定值模型(90 用例:范围校验 / all-or-nothing 装载 / 封存-自检-闭锁 / Q10 往返 / 元数据表自检 / B7 blob 往返与损坏拒绝 / **B8 OF-UF 行 + 64 位 bad_mask + 换代拒绝**) | `& '.venv\Scripts\python.exe' test/host/prot_set/run_unit_test.py` | `prot-set-unit-test` |
|
||||
| 元件框架(47 用例:滞回与定时限 / 欠量方向 / 投退 / 闭锁联动 / 时基回绕 / 位图聚合 / **B8 OF-UF 滞回与方向隔离 + run() 测频接线**) | `& '.venv\Scripts\python.exe' test/host/prot_elm/run_unit_test.py` | `prot-elm-unit-test` |
|
||||
| **频率测量(46 用例:J1 扫频 45~55 Hz / J2 噪声 / J3 启动收敛 / J4 阶跃 / J5 浮空不发散 / J6 假值钳制 / 直流·谐波抑制 / UA→IA 回退 / 守卫与幂等)** | `& '.venv\Scripts\python.exe' test/host/freq/run_unit_test.py` | `freq-unit-test`(**待登记**,合并时由 Lead 加钩子) |
|
||||
|
||||
改本目录任一 `.c/.h`,提交时对应钩子自动跑;详见 `docs/测试说明/相量层单元测试说明.md`。
|
||||
- **板端**:`@build` 0 错误 0 警告 → `@test` 看 `[ADC_TASK]` 首拍/心跳日志(`phasor(...)` 行、
|
||||
`ring` / `skip` 计数、`adc1_rc` / `adc1_err`、`tgap`);健康判据 = 零 `[ERR]`
|
||||
`freq(...)` 行、`ring` / `skip` 计数、`adc1_rc` / `adc1_err`、`tgap`);健康判据 = 零 `[ERR]`
|
||||
(`adc_health_report` 的告警全部以 `[ERR]` 开头 → 误报会被闭环直接抓住)。
|
||||
- **定值档升级 vs 损坏(B8,台架判读)**:`prot_set_store_load()` **rc=2 = 换代**
|
||||
(旧固件/旧项数的档被版本闸拒绝)→ 只打 INFO `prot set blob version change, defaults in use`,
|
||||
**不是** `[ERR] ... CORRUPT`、不记自检;**rc<0 = 真损坏** → `[ERR] prot set blob CORRUPT` + 自检留痕。
|
||||
升级固件后首启看到"version change"一行属**预期**,勿当故障排查。
|
||||
|
||||
## 7. 新增一个算法模块的固定动作
|
||||
|
||||
1. 在本目录建 `xxx.c/h`,文件头写模块注释块(规范 V1.0),**尽量零平台依赖**以便免桩单测。
|
||||
2. 用 `new-source-file` skill 注册到 `MDK-ARM/STM32F407-Demo.uvprojx`(目录已在 IncludePath,
|
||||
若新建子目录则必须补 IncludePath)。
|
||||
若新建子目录则必须补 IncludePath)。模拟器侧二选一:有消费路径 → 加
|
||||
`simulator/CMakeLists.txt`;**纯算法无消费路径 → 加 `scripts/check_registration.py`
|
||||
的 `SIM_EXEMPT`**(照 `prot_set.c` / `freq.c` 先例写理由),否则 `registration-check` 钩子拦。
|
||||
3. 加 `test/host/<模块>/` 主机单测 + `.pre-commit-config.yaml` 钩子(照 `phasor-unit-test` 抄)。
|
||||
4. 消费者写在 `App/task/`,本层只出"数";跨转换器运算的禁令见 §4。
|
||||
|
||||
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 模块名称:频率测量层(L2 派生量)——实现
|
||||
* 模块功能:见 freq.h。相邻两拍基波相量共轭乘积取角(= 每拍相位增量,免
|
||||
* 显式解缠绕)+ **自适应窗长滑动平均** + 瞬时值钳制 + 幅值门。
|
||||
* 输入 = phasor 使用区基波相量(UA 优先,UA 无幅值用 IA)。
|
||||
*
|
||||
* 精度问题的根与解(2026-10-12 单测实测驱动,数据见 test/host/freq 输出):
|
||||
* 非整周波窗长让 20 点矩形窗 DFT 混入**负序泄漏项** S2(45/55 Hz 处
|
||||
* |S2/S1| ≈ 3~5%),相量端轨迹是椭圆 → 逐拍相位差带周期纹波
|
||||
* δ(φ),周期 = π/ω = fs/(2f) ≈ 9.1~11.1 拍。固定窗长平均时残差
|
||||
* = [δ(m+K)-δ(m)]/K,K 不整除纹波周期 → 扫频最坏 0.2~0.32 Hz(两轮实测:
|
||||
* 固定 40 拍算术平均 0.317 Hz;固定 60 拍复数和 Kay 0.197 Hz——Kay 的
|
||||
* 交叉项和角同样不随 K² 衰减,勿再尝试)。
|
||||
* **自适应窗长**:K_eff = round(M·Fs/(2·f_hat))(M=8,≈4 个泄漏纹波周期
|
||||
* ≈4 个信号周波),使窗长恰为纹波周期的整数倍 → δ(m+K)≈δ(m),残差进入
|
||||
* 0.01 Hz 量级。f_hat 用上一拍读数(收敛后误差 <0.1 Hz → K_eff 偏差 <1 拍,
|
||||
* 自洽)。
|
||||
* **精度优先于响应的显式取舍(Lead 批准 2026-10-12)**:4 周波窗换来
|
||||
* 0.317→0.0101 Hz,代价是频率骤变后的窗冲刷(J4 阶跃整定 ≤120 ms,实测
|
||||
* 98 ms)。OF/UF 定时限默认 1000 ms → 测频响应占保护总动作时间 <10%;
|
||||
* 暂态假值另有 J6(跳变后 ≤60 ms 回 ±0.05 Hz)兜底。故 J4 的 120 ms 是
|
||||
* 工程整定,不是判据失守。
|
||||
* 另:单拍假值钳制带取 35~65 Hz(宽于系统合理带 40.5~60.5)——贴边钳制
|
||||
* 会非对称丢弃纹波半周 → 均值偏置(实测 45 Hz 点 +0.16 Hz,已修)。
|
||||
* 创建日期:2026-10-12(B8)
|
||||
*/
|
||||
#include "freq.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "phasor.h"
|
||||
|
||||
/* 圆周率(本层自带,避免依赖 math.h 的 M_* 扩展宏;ARMCC math.h 不保证定义) */
|
||||
#define FREQ_PI 3.14159265358979f
|
||||
|
||||
/* 相位增量 -> 频率 换算系数:Fs/(2π)(Hz/rad) */
|
||||
#define FREQ_HZ_PER_RAD (FREQ_FS_HZ / (2.0f * FREQ_PI))
|
||||
|
||||
/* 自适应窗长目标纹波周期数 M:K_eff = round(M·Fs/(2·f_hat))。
|
||||
* 45~55 Hz 段 K_eff ∈ [73, 89] ⊂ [FREQ_AVG_MIN, FREQ_AVG_TAPS]。 */
|
||||
#define FREQ_RIPPLE_M 8u
|
||||
|
||||
/* 源相量候选(优先级序):UA 基波 = "C02",IA 基波 = "C01"。
|
||||
* 按**名字前缀**解析索引(同 prot_elm 口径,不写死下标),开机解析一次。 */
|
||||
#define FREQ_SRC_N 2
|
||||
static const char *const k_src_prefix[FREQ_SRC_N] = { "C02", "C01" };
|
||||
|
||||
/* 测频内部状态(BSS ≈ 96*(4+1) + 常数 ≈ 520 B) */
|
||||
static float fq_d[FREQ_AVG_TAPS]; /* 每拍相位增量环(rad) */
|
||||
static uint8_t fq_d_ok[FREQ_AVG_TAPS]; /* 对应拍是否有效(假值拍 = 0) */
|
||||
static uint8_t fq_head; /* 环写指针 */
|
||||
static uint8_t fq_count; /* 已入环样本数(饱和于 TAPS) */
|
||||
static float fq_last; /* 最近有效读数(Hz) */
|
||||
static float fq_prev_re; /* 上一拍源相量(实部) */
|
||||
static float fq_prev_im; /* 上一拍源相量(虚部) */
|
||||
static uint8_t fq_prev_valid; /* 上一拍是否有相量(首拍建基准) */
|
||||
static uint8_t fq_src_idx[FREQ_SRC_N]; /* 候选实例索引;0xFF = 未找到 */
|
||||
static uint8_t fq_src_resolved; /* 源已解析标志 */
|
||||
static uint8_t fq_prev_src; /* 上一拍所用候选(切换即废基准) */
|
||||
static uint8_t fq_started; /* 启动收敛完成(样本数 >= START_TAPS) */
|
||||
static uint8_t fq_gate_ok; /* 本拍幅值门 OK 且已收敛 */
|
||||
|
||||
/*
|
||||
* 函数功能:解析候选源相量实例索引(开机一次)
|
||||
* 入口参数:无
|
||||
* 返 回 值:无(结果写 fq_src_idx[],未找到 = 0xFF)
|
||||
* 限定条件:phasor_init() 之后(开机一次)
|
||||
* 函数说明:按实例名前缀匹配("C02"=UA 基波 / "C01"=IA 基波)且 harm==1,
|
||||
* 与 prot_elm 同款朴素比较;**不写死实例下标**。
|
||||
*/
|
||||
static void fq_resolve_src(void) {
|
||||
uint8_t p;
|
||||
|
||||
for (p = 0u; p < FREQ_SRC_N; p++) {
|
||||
uint8_t i, n = phasor_inst_count();
|
||||
|
||||
fq_src_idx[p] = 0xFFu;
|
||||
for (i = 0u; i < n; i++) {
|
||||
const phasor_inst_t *pi = phasor_inst(i);
|
||||
uint8_t j = 0u, hit = 1u;
|
||||
|
||||
if (pi == 0) {
|
||||
continue;
|
||||
}
|
||||
while ((k_src_prefix[p][j] != '\0') && (pi->name[j] != '\0')) {
|
||||
if (k_src_prefix[p][j] != pi->name[j]) {
|
||||
hit = 0u;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if ((hit != 0u) && (k_src_prefix[p][j] == '\0') && (pi->harm == 1u)) {
|
||||
fq_src_idx[p] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fq_src_resolved = 1u;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把相位增量入环
|
||||
* 入口参数:d - 本拍相位增量(rad);ok - 本拍是否有效(钳制失败 = 0)
|
||||
* 返 回 值:无
|
||||
* 限定条件:无
|
||||
* 函数说明:假值拍也**占位**(ok=0),保持环时序 = 拍时序(自适应窗长
|
||||
* 的"窗 = 最近 K_eff 拍"语义依赖这一点);求和时跳过占位拍。
|
||||
*/
|
||||
static void fq_push(float d, uint8_t ok) {
|
||||
fq_d[fq_head] = d;
|
||||
fq_d_ok[fq_head] = ok;
|
||||
fq_head++;
|
||||
if (fq_head >= FREQ_AVG_TAPS) {
|
||||
fq_head = 0u;
|
||||
}
|
||||
if (fq_count < FREQ_AVG_TAPS) {
|
||||
fq_count++;
|
||||
if ((fq_started == 0u) && (fq_count >= FREQ_START_TAPS)) {
|
||||
fq_started = 1u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:按自适应窗长求最近 K_eff 拍的相位增量均值 -> 频率
|
||||
* 入口参数:无
|
||||
* 返 回 值:频率读数(Hz);窗内无有效样本时返回 fq_last(保持)
|
||||
* 限定条件:fq_started != 0
|
||||
* 函数说明:K_eff = clamp(round(M·Fs/(2·f_hat)), FREQ_AVG_MIN, FREQ_AVG_TAPS)
|
||||
* (f_hat = 上一读数,首轮用标称 50 Hz 起步)。窗 = 最近 K_eff 拍
|
||||
* (含占位拍,占位不计和但计窗推进),恰为泄漏纹波周期整数倍
|
||||
* -> δ(m+K)≈δ(m) 相消。每拍重算和(<=96 次加法,us 级)。
|
||||
*/
|
||||
static float fq_read_freq(void) {
|
||||
float f_hat = (fq_last > 30.0f) ? fq_last : 50.0f;
|
||||
uint16_t k_eff;
|
||||
uint16_t i;
|
||||
uint16_t idx;
|
||||
float sum = 0.0f;
|
||||
uint16_t n_ok = 0u;
|
||||
|
||||
/* K_eff = M*Fs/(2*f_hat):四舍五入 + 钳到环容量区间 */
|
||||
k_eff = (uint16_t)(((float)FREQ_RIPPLE_M * FREQ_FS_HZ) / (2.0f * f_hat) + 0.5f);
|
||||
if (k_eff < FREQ_AVG_MIN) {
|
||||
k_eff = FREQ_AVG_MIN;
|
||||
}
|
||||
if (k_eff > FREQ_AVG_TAPS) {
|
||||
k_eff = FREQ_AVG_TAPS;
|
||||
}
|
||||
if (k_eff > (uint16_t)fq_count) {
|
||||
k_eff = (uint16_t)fq_count;
|
||||
}
|
||||
|
||||
/* 从写指针回退 K_eff 拍求和(跳过占位拍) */
|
||||
idx = fq_head;
|
||||
for (i = 0u; i < k_eff; i++) {
|
||||
if (idx == 0u) {
|
||||
idx = FREQ_AVG_TAPS;
|
||||
}
|
||||
idx--;
|
||||
if (fq_d_ok[idx] != 0u) {
|
||||
sum += fq_d[idx];
|
||||
n_ok++;
|
||||
}
|
||||
}
|
||||
if (n_ok == 0u) {
|
||||
return fq_last;
|
||||
}
|
||||
return (sum / (float)n_ok) * FREQ_HZ_PER_RAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:频率测量层初始化(幂等)
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功
|
||||
* 限定条件:内核启动前调用(无 RTOS 依赖)
|
||||
* 函数说明:清增量环/占位标志/计数/读数/相位基准/有效标志,并复位源解析。
|
||||
*/
|
||||
int freq_init(void) {
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0u; i < FREQ_AVG_TAPS; i++) {
|
||||
fq_d[i] = 0.0f;
|
||||
fq_d_ok[i] = 0u;
|
||||
}
|
||||
fq_head = 0u;
|
||||
fq_count = 0u;
|
||||
fq_last = 0.0f;
|
||||
fq_prev_re = 0.0f;
|
||||
fq_prev_im = 0.0f;
|
||||
fq_prev_valid = 0u;
|
||||
fq_src_idx[0] = 0xFFu;
|
||||
fq_src_idx[1] = 0xFFu;
|
||||
fq_src_resolved = 0u;
|
||||
fq_prev_src = 0u;
|
||||
fq_started = 0u;
|
||||
fq_gate_ok = 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:每拍测频步进
|
||||
* 入口参数:无
|
||||
* 返 回 值:无
|
||||
* 限定条件:仅采集消费者任务上下文(adcTask),须在 phasor_run() 之后同拍调用
|
||||
* 函数说明:流程 = 选源(UA->IA 过幅值门)-> 共轭乘积 P = Z_now·conj(Z_prev)
|
||||
* 取角(免显式解缠绕)-> 钳制(假值拍占位 ok=0)-> 自适应窗均值。
|
||||
* 相量未就绪/全部候选门失败:本拍不更新(读数保持)、清 gate、废基准。
|
||||
* 基准在"断流"或**源切换**后重建(UA/IA 相位无共同基准)。
|
||||
*/
|
||||
void freq_run(void) {
|
||||
phasor_t ph = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float pr;
|
||||
float pi;
|
||||
float dphi;
|
||||
float f_inst;
|
||||
uint8_t pick = FREQ_SRC_N; /* FREQ_SRC_N = 无合格源 */
|
||||
uint8_t p;
|
||||
|
||||
if (fq_src_resolved == 0u) {
|
||||
fq_resolve_src();
|
||||
}
|
||||
|
||||
/* 源选择(每拍动态):优先级 UA(C02) -> IA(C01),取第一个过幅值门的。
|
||||
* 台架只注电流时 UA 门失败 -> 自动落 IA;两路都无幅值 = 无有效波形。 */
|
||||
for (p = 0u; p < FREQ_SRC_N; p++) {
|
||||
if ((fq_src_idx[p] == 0xFFu) || (phasor_get(fq_src_idx[p], &ph) != 0)) {
|
||||
continue;
|
||||
}
|
||||
if (ph.fAmSQ >= FREQ_AMP_MIN) {
|
||||
pick = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pick >= FREQ_SRC_N) {
|
||||
/* 相量未就绪或全部候选幅值门失败(浮空/断线 rms≈0 -> 相位无意义):
|
||||
* 停更新、清 gate、废基准(读数保持) */
|
||||
fq_prev_valid = 0u;
|
||||
fq_gate_ok = 0u;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fq_prev_valid == 0u) || (pick != fq_prev_src)) {
|
||||
/* 首拍 / 断流后首拍 / 源切换:只建基准,不出数 */
|
||||
fq_prev_re = ph.fRe;
|
||||
fq_prev_im = ph.fIm;
|
||||
fq_prev_src = pick;
|
||||
fq_prev_valid = 1u;
|
||||
fq_gate_ok = 0u;
|
||||
return;
|
||||
}
|
||||
|
||||
/* P = Z_now * conj(Z_prev):arg(P) = 本拍相对上拍的相位增量
|
||||
* (纯音 = +2*pi*f/Fs;|P| 大 = 信号强,钳制用角不受幅值影响) */
|
||||
pr = (ph.fRe * fq_prev_re) + (ph.fIm * fq_prev_im);
|
||||
pi = (ph.fIm * fq_prev_re) - (ph.fRe * fq_prev_im);
|
||||
fq_prev_re = ph.fRe;
|
||||
fq_prev_im = ph.fIm;
|
||||
|
||||
dphi = atan2f(pi, pr);
|
||||
f_inst = dphi * FREQ_HZ_PER_RAD;
|
||||
|
||||
/* 假值钳制:单拍折算越出 35~65 Hz(采样间隙窗滑 2 拍 -> ≈2f=90~110、
|
||||
* 相位跳变、深度畸变过渡段)-> 占位不入和,读数保持;基准已推进。
|
||||
* ⚠ 钳制带刻意宽于系统合理带(见 freq.h FREQ_INST_* 注释):贴边会
|
||||
* 非对称丢弃泄漏纹波半周造成均值偏置。 */
|
||||
if ((f_inst < FREQ_INST_MIN_HZ) || (f_inst > FREQ_INST_MAX_HZ)) {
|
||||
fq_push(0.0f, 0u);
|
||||
} else {
|
||||
fq_push(dphi, 1u);
|
||||
}
|
||||
|
||||
if (fq_started != 0u) {
|
||||
fq_last = fq_read_freq();
|
||||
fq_gate_ok = 1u;
|
||||
} else {
|
||||
fq_gate_ok = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读测得频率(见 freq.h)
|
||||
* 入口参数:out - 输出(NULL -> -1)
|
||||
* 返 回 值:0 已有读数 / -1 参数错 / -2 从未启动收敛
|
||||
* 限定条件:任意上下文(float 单字读原子)
|
||||
*/
|
||||
int freq_get(float *out) {
|
||||
if (out == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (fq_started == 0u) {
|
||||
*out = 0.0f;
|
||||
return -2;
|
||||
}
|
||||
*out = fq_last;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读"本拍有效"标志(见 freq.h)
|
||||
* 入口参数:无
|
||||
* 返 回 值:1 新鲜 / 0 未启动或幅值门失效
|
||||
* 限定条件:无
|
||||
*/
|
||||
uint8_t freq_valid(void) {
|
||||
return fq_gate_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读最近一次有效读数(见 freq.h)
|
||||
* 入口参数:无
|
||||
* 返 回 值:Hz;从未收敛返回 0.0
|
||||
* 限定条件:无
|
||||
*/
|
||||
float freq_last(void) {
|
||||
return fq_last;
|
||||
}
|
||||
+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 */
|
||||
+39
-11
@@ -8,6 +8,7 @@
|
||||
#include "prot_elm.h"
|
||||
#include "prot_set.h"
|
||||
#include "phasor.h"
|
||||
#include "freq.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -15,21 +16,33 @@
|
||||
typedef struct {
|
||||
const char *name; /* 日志名 */
|
||||
uint8_t over; /* 1 = 过量(越上限动作),0 = 欠量(越下限动作) */
|
||||
uint8_t src; /* 测量源:0 = phasor 基波有效值,1 = freq 测频 */
|
||||
prot_set_id_t en_id; /* 投退定值 */
|
||||
prot_set_id_t set_id; /* 动作定值 */
|
||||
prot_set_id_t dly_id; /* 延时定值(ms) */
|
||||
prot_set_id_t ret_id; /* 返回系数定值(RET_OVER / RET_UNDER) */
|
||||
const char *pprefix; /* 相量实例名前缀("C01"…),run() 适配层取数用 */
|
||||
const char *pprefix; /* 相量实例名前缀("C01"…);src=1 时为 0(不用) */
|
||||
} pe_cfg_t;
|
||||
|
||||
/* 测量源枚举(pe_cfg_t.src) */
|
||||
#define PE_SRC_PHASOR 0u
|
||||
#define PE_SRC_FREQ 1u
|
||||
|
||||
/* freq 无有效读数时给欠量元件的"必不启动"填充值(远大于任何欠量定值上限) */
|
||||
#define PE_MEAS_BIG 1e30f
|
||||
|
||||
static const pe_cfg_t k_pe[PE_COUNT] = {
|
||||
{ "OC1-IA", 1u, PS_OC1_EN, PS_OC1_CUR, PS_OC1_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OC2-IA", 1u, PS_OC2_EN, PS_OC2_CUR, PS_OC2_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OC3-IA", 1u, PS_OC3_EN, PS_OC3_CUR, PS_OC3_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OL-IA", 1u, PS_OL_EN, PS_OL_CUR, PS_OL_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OV-UA", 1u, PS_OV_EN, PS_OV_VOL, PS_OV_DLY, PS_RET_OVER, "C02" },
|
||||
{ "UV-UA", 0u, PS_UV_EN, PS_UV_VOL, PS_UV_DLY, PS_RET_UNDER, "C02" },
|
||||
{ "3U0-UN", 1u, PS_3U0_EN, PS_3U0_VOL, PS_3U0_DLY, PS_RET_OVER, "C03" },
|
||||
{ "OC1-IA", 1u, PE_SRC_PHASOR, PS_OC1_EN, PS_OC1_CUR, PS_OC1_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OC2-IA", 1u, PE_SRC_PHASOR, PS_OC2_EN, PS_OC2_CUR, PS_OC2_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OC3-IA", 1u, PE_SRC_PHASOR, PS_OC3_EN, PS_OC3_CUR, PS_OC3_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OL-IA", 1u, PE_SRC_PHASOR, PS_OL_EN, PS_OL_CUR, PS_OL_DLY, PS_RET_OVER, "C01" },
|
||||
{ "OV-UA", 1u, PE_SRC_PHASOR, PS_OV_EN, PS_OV_VOL, PS_OV_DLY, PS_RET_OVER, "C02" },
|
||||
{ "UV-UA", 0u, PE_SRC_PHASOR, PS_UV_EN, PS_UV_VOL, PS_UV_DLY, PS_RET_UNDER, "C02" },
|
||||
{ "3U0-UN", 1u, PE_SRC_PHASOR, PS_3U0_EN, PS_3U0_VOL, PS_3U0_DLY, PS_RET_OVER, "C03" },
|
||||
/* 过频/低频(L3-5,B8):测量量 = freq 层测得频率(Hz),返回系数独立
|
||||
* (RET_OF/RET_UF——频率滞回带与电气量不同量级,勿共用 0.95/1.05) */
|
||||
{ "OF-F", 1u, PE_SRC_FREQ, PS_OF_EN, PS_OF_HZ, PS_OF_DLY, PS_RET_OF, 0 },
|
||||
{ "UF-F", 0u, PE_SRC_FREQ, PS_UF_EN, PS_UF_HZ, PS_UF_DLY, PS_RET_UF, 0 },
|
||||
};
|
||||
|
||||
/* 运行时状态(picked/asserted 的对外快照 + 延时起点 + 相量实例索引缓存) */
|
||||
@@ -89,7 +102,8 @@ void prot_elm_init(void) {
|
||||
s_rt[i].picked = 0u;
|
||||
s_rt[i].asserted = 0u;
|
||||
s_rt[i].t0 = 0u;
|
||||
s_rt[i].pidx = pe_find_phasor(k_pe[i].pprefix);
|
||||
/* src=FREQ 的行无相量绑定(pprefix=0)→ pidx 置未解析(run 不查它) */
|
||||
s_rt[i].pidx = (k_pe[i].pprefix != 0) ? pe_find_phasor(k_pe[i].pprefix) : 0xFFu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,11 +186,15 @@ void prot_elm_eval(uint32_t now_ms, const float meas[PE_COUNT]) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:适配层——从 phasor 使用区取 rms 组装测量数组后调判据核
|
||||
* 函数功能:适配层——按实例表 src 组装测量数组后调判据核
|
||||
* 入口参数:now_ms - 单调时基(tick ms)
|
||||
* 返 回 值:无
|
||||
* 限定条件:仅保护消费者任务上下文;工程量↔码值换算 = 1(L1-5 标定未实现)
|
||||
* 函数说明:任一相量未就绪(未满一周波)→ **整拍跳过**,不引入陈旧/半新测量。
|
||||
* 函数说明:src=PHASOR 行任一相量未就绪(未满一周波)→ **整拍跳过**,
|
||||
* 不引入陈旧/半新测量(B5 口径)。src=FREQ 行:freq 有有效读数
|
||||
* (started 且幅值门 OK)→ 填测得频率;否则按方向填"必不启动"值
|
||||
* (过量 0 / 欠量 PE_MEAS_BIG → 滞回比较恒假 → 元件复位),
|
||||
* 不整拍跳过——频率源缺失不得饿死电流/电压类元件。
|
||||
*/
|
||||
void prot_elm_run(uint32_t now_ms) {
|
||||
float meas[PE_COUNT];
|
||||
@@ -184,6 +202,16 @@ void prot_elm_run(uint32_t now_ms) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0u; i < (uint32_t)PE_COUNT; i++) {
|
||||
if (k_pe[i].src == PE_SRC_FREQ) {
|
||||
float f;
|
||||
|
||||
if ((freq_get(&f) == 0) && (freq_valid() != 0u)) {
|
||||
meas[i] = f;
|
||||
} else {
|
||||
meas[i] = (k_pe[i].over != 0u) ? 0.0f : PE_MEAS_BIG;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((s_rt[i].pidx == 0xFFu) || (phasor_get(s_rt[i].pidx, &p) != 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
+10
-2
@@ -9,7 +9,13 @@
|
||||
* OL 过负荷(A 相 IA,L3-3)
|
||||
* OV/UV 过压/欠压(UA,L3-4)
|
||||
* 3U0 零序过压(UN 外部零序电压通道,L3-6)
|
||||
* ⚠ 过频/低频(L3-5)随频率测量算法(P2)落地,本表暂不含。
|
||||
* 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 独立定值,等价);
|
||||
@@ -30,7 +36,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* 元件实例编号(枚举序 = 实例表序 = 测量数组序) */
|
||||
/* 元件实例编号(枚举序 = 实例表序 = 测量数组序;尾追加,编号不回收) */
|
||||
typedef enum {
|
||||
PE_OC1 = 0, /* 过流 Ⅰ 段(IA) */
|
||||
PE_OC2, /* 过流 Ⅱ 段(IA) */
|
||||
@@ -39,6 +45,8 @@ typedef enum {
|
||||
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;
|
||||
|
||||
|
||||
+17
-6
@@ -47,6 +47,17 @@ const prot_set_meta_t g_prot_set_meta[PS_ID_COUNT] = {
|
||||
{ 0x8825, 0u, PROT_SET_Q10(100.0), PROT_SET_Q10(1.0), PROT_SET_Q10(10000.0) },
|
||||
{ 0x8826, 0u, PROT_SET_Q10(5.0), PROT_SET_Q10(1.0), PROT_SET_Q10(10.0) },
|
||||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* 全局软压板投退(YK12,B7) */
|
||||
/* --- 过频 / 低频(L3-5,B8):地址全 0 = 本地项(参考逐项地址不在
|
||||
* 本仓库,规划 §5.1 仅示"8820~8882 含低压/低频"类目);
|
||||
* 默认值/范围**拟定**(DTU 馈线典型量级),待对照参考出厂表修订 --- */
|
||||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* OF 投退(本地) */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(51.0), PROT_SET_Q10(50.0), PROT_SET_Q10(55.0) }, /* OF 动作频率 Hz */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(1000.0), 0, PROT_SET_Q10(60000) }, /* OF 延时 ms */
|
||||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* UF 投退(本地) */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(49.0), PROT_SET_Q10(45.0), PROT_SET_Q10(50.0) }, /* UF 动作频率 Hz */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(1000.0), 0, PROT_SET_Q10(60000) }, /* UF 延时 ms */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(0.99), PROT_SET_Q10(0.9), PROT_SET_Q10(1.0) }, /* 过频返回系数 */
|
||||
{ 0x0000, 0u, PROT_SET_Q10(1.01), PROT_SET_Q10(1.0), PROT_SET_Q10(1.1) }, /* 低频返回系数 */
|
||||
};
|
||||
|
||||
static uint32_t s_val[2][PS_ID_COUNT]; /* 双缓冲值区(uint32 承载 int32,校验和按字覆盖)
|
||||
@@ -55,7 +66,7 @@ static uint32_t s_val[2][PS_ID_COUNT]; /* 双缓冲值区(uint32 承载 int3
|
||||
static volatile uint8_t s_act; /* 活跃区下标(0/1);交换 = 单字存储 */
|
||||
static uint32_t s_sum; /* 封存的正码 */
|
||||
static uint32_t s_sumn; /* 封存的反码 */
|
||||
static uint32_t s_bad_mask; /* load_raw/parse 越界位图 */
|
||||
static uint64_t s_bad_mask; /* load_raw/parse 越界位图(B8:40 项 → 64 位) */
|
||||
static uint8_t s_locked; /* 定值非法 → 闭锁(L5-1) */
|
||||
static uint8_t s_dirty; /* 未落盘定值改动(B7 持久化脏标志) */
|
||||
|
||||
@@ -105,7 +116,7 @@ void prot_set_init(void) {
|
||||
* 入口参数:无
|
||||
* 返 回 值:bit i = 第 i 项越界(load_raw 失败后有效)
|
||||
*/
|
||||
uint32_t prot_set_bad_mask(void) {
|
||||
uint64_t prot_set_bad_mask(void) {
|
||||
return s_bad_mask;
|
||||
}
|
||||
|
||||
@@ -117,7 +128,7 @@ uint32_t prot_set_bad_mask(void) {
|
||||
* 越界项经 prot_set_bad_mask() 指认。
|
||||
*/
|
||||
int prot_set_load_raw(const int32_t *vals) {
|
||||
uint32_t mask = 0u;
|
||||
uint64_t mask = 0u;
|
||||
uint32_t cur_act;
|
||||
uint32_t *shadow;
|
||||
uint32_t i;
|
||||
@@ -127,7 +138,7 @@ int prot_set_load_raw(const int32_t *vals) {
|
||||
}
|
||||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||||
if ((vals[i] < g_prot_set_meta[i].min) || (vals[i] > g_prot_set_meta[i].max)) {
|
||||
mask |= (1uL << i);
|
||||
mask |= (1uLL << i);
|
||||
}
|
||||
}
|
||||
s_bad_mask = mask;
|
||||
@@ -390,7 +401,7 @@ int prot_set_store_parse(const uint8_t *p_buf, uint32_t len) {
|
||||
uint32_t cur_act;
|
||||
uint32_t *shadow;
|
||||
uint32_t sum = 0u;
|
||||
uint32_t mask = 0u;
|
||||
uint64_t mask = 0u;
|
||||
uint32_t i;
|
||||
|
||||
if ((p_buf == 0) || (len != PROT_SET_BLOB_SZ)) {
|
||||
@@ -422,7 +433,7 @@ int prot_set_store_parse(const uint8_t *p_buf, uint32_t len) {
|
||||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||||
if (((int32_t)shadow[i] < g_prot_set_meta[i].min)
|
||||
|| ((int32_t)shadow[i] > g_prot_set_meta[i].max)) {
|
||||
mask |= (1uL << i);
|
||||
mask |= (1uLL << i);
|
||||
}
|
||||
}
|
||||
s_bad_mask = mask;
|
||||
|
||||
+22
-5
@@ -70,6 +70,20 @@ typedef enum {
|
||||
PS_CT_SEC, /* 0x8826 CT 二次额定(A) */
|
||||
/* --- 全局软压板(B7):104 YK12 两段式投退;0 = 保护退出服务 --- */
|
||||
PS_GLOBAL_EN, /* 本地项(无通讯地址,经 YK12 写、C_RD 不可读) */
|
||||
/* --- 过频 / 低频(L3-5,B8):投退/动作频率/延时 + 各自返回系数 ---
|
||||
* ⚠ 通讯地址:参考 g_tCommSetParaTab 的保护定值段(0x8820~0x8882)
|
||||
* 含"低压/低频"类目(规划 §5.1),但 OF/UF 的**逐项地址不在本仓库**
|
||||
* (参考 Comm 源文件未随仓库提供)→ 按"找不到对应即本地项"规则全部
|
||||
* comm_addr=0 登记本地项(104 C_SE 不可寻址),待对照参考表后补。
|
||||
* ⚠ 默认值/范围为**拟定**(同全表口径,规划 §5.4 落实进度注)。 */
|
||||
PS_OF_EN, /* 过频投退(本地) */
|
||||
PS_OF_HZ, /* 过频动作频率(Hz) */
|
||||
PS_OF_DLY, /* 过频延时(ms) */
|
||||
PS_UF_EN, /* 低频投退(本地) */
|
||||
PS_UF_HZ, /* 低频动作频率(Hz) */
|
||||
PS_UF_DLY, /* 低频延时(ms) */
|
||||
PS_RET_OF, /* 过频返回系数(返回门槛 = 定值 × 系数,<1;本地) */
|
||||
PS_RET_UF, /* 低频返回系数(返回门槛 = 定值 × 系数,>1;本地) */
|
||||
PS_ID_COUNT
|
||||
} prot_set_id_t;
|
||||
|
||||
@@ -97,8 +111,9 @@ void prot_set_init(void);
|
||||
*/
|
||||
int prot_set_load_raw(const int32_t *vals);
|
||||
|
||||
/* 越界位图(bit i = 第 i 项越界;load_raw 失败后有效) */
|
||||
uint32_t prot_set_bad_mask(void);
|
||||
/* 越界位图(bit i = 第 i 项越界;load_raw 失败后有效。B8:项数 32→40,
|
||||
* 位图随项数扩为 64 位——消费方仅主机单测,板端闭锁走 prot_set_locked()) */
|
||||
uint64_t prot_set_bad_mask(void);
|
||||
|
||||
/*
|
||||
* 函数功能:单项写入(范围校验;**不自动重封校验和**——批量改完必须
|
||||
@@ -137,10 +152,12 @@ void prot_set_dirty_mark(void); /* 写盘失败回置(等下轮重试)
|
||||
/* ---------------- 持久化 blob(B7;纯逻辑,主机可单测) ----------------
|
||||
* 格式(小端):magic 'PSET'(4B) + version u16 + count u16
|
||||
* + values u32×count + sum32 u32 + ~sum32 u32
|
||||
* 版本 1;count 必须等于 PS_ID_COUNT(定值表增项 → 旧 blob 整体作废,
|
||||
* 编号不回收但格式换代,宁可用出厂默认也不半装载)。 */
|
||||
* 版本 2(B8:定值表 32→40 项,OF/UF 尾追加);count 必须等于 PS_ID_COUNT
|
||||
* (定值表增项 → 旧 blob 整体作废,编号不回收但格式换代,宁可用出厂默认
|
||||
* 也不半装载)。ver 1 旧档在 parse 即被版本闸拒绝 → 出厂默认 + 日志
|
||||
* (措辞 = "version change",非 CORRUPT,见 prot_set_store/default_task)。 */
|
||||
#define PROT_SET_BLOB_MAGIC 0x50534554uL /* 字节序 P,S,E,T */
|
||||
#define PROT_SET_BLOB_VER 1u
|
||||
#define PROT_SET_BLOB_VER 2u
|
||||
#define PROT_SET_BLOB_HDR_SZ 8u /* magic(4)+ver(2)+cnt(2) */
|
||||
#define PROT_SET_BLOB_SZ (PROT_SET_BLOB_HDR_SZ + 4u * (uint32_t)PS_ID_COUNT + 8u)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user