merge: task/cal-lockout-dim 合入 v2-on-ui
任务 cal-lockout(1 个提交)经 task-worktree 回收: - feat(保护标定): 卡5 未标定闭锁 + 卡6 下游三常数重定 + 卡7 量纲回归单测
This commit is contained in:
@@ -354,6 +354,17 @@ repos:
|
||||
language: system
|
||||
files: ^(App/algo/adc_cal\.(c|h)|App/algo/adc_health\.(c|h)|App/cfg/cfg_file\.(c|h)|App/task/adc_task\.c|test/host/adc_cal/.*|test/host/common/unit_test_harness\.py)$
|
||||
pass_filenames: false
|
||||
# 7h3) 量纲回归(「数据可信」卡 7,道A)主机侧单元测试(2026-10-25 加)
|
||||
# 背景:台架曾长期缺"不该动作时不动作"的判据,码值/工程量错配曾以
|
||||
# 恒假动作的形态混过验收。本钩子用真实链(adc_cal→ring→phasor→
|
||||
# prot_elm)+ 显式换算的合成波把七类元件的量纲钉死:折算点被旁路
|
||||
# 或量纲再错位,断言立刻翻转。files 覆盖链上全部生产源与两处编排。
|
||||
- id: prot-dim-unit-test
|
||||
name: 量纲回归单测(主机侧)
|
||||
entry: python test/host/prot_dim/run_unit_test.py
|
||||
language: system
|
||||
files: ^(App/algo/(adc_cal|prot_elm|prot_set|phasor|sample_ring|freq)\.(c|h)|App/task/(adc_task|protect_task)\.c|test/host/prot_dim/.*|test/host/common/unit_test_harness\.py)$
|
||||
pass_filenames: false
|
||||
|
||||
|
||||
# 7i) 保护定值模型(P1 / L5-1 载体)主机侧单元测试(2026-10-11 加)
|
||||
|
||||
+9
-3
@@ -49,9 +49,15 @@
|
||||
#define FREQ_INST_MIN_HZ 35.0f
|
||||
#define FREQ_INST_MAX_HZ 65.0f
|
||||
|
||||
/* 幅值门限(有效值,**原始码值口径**,L1-5 标定未实现):低于该值判
|
||||
* "无有效波形"。⚠ 拟定值(远小于正常波形千级码值、远大于浮空噪声),
|
||||
* 标定落地后须按工程量口径复核(同 prot_set 默认值"拟定"口径)。 */
|
||||
/* 幅值门限(有效值,**二次值 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 上限。实际平均窗长**自适应**
|
||||
|
||||
+2
-1
@@ -189,7 +189,8 @@ void prot_elm_eval(uint32_t now_ms, const float meas[PE_COUNT]) {
|
||||
* 函数功能:适配层——按实例表 src 组装测量数组后调判据核
|
||||
* 入口参数:now_ms - 单调时基(tick ms)
|
||||
* 返 回 值:无
|
||||
* 限定条件:仅保护消费者任务上下文;工程量↔码值换算 = 1(L1-5 标定未实现)
|
||||
* 限定条件:仅保护消费者任务上下文;测量已是 L1.5 工程量(未标定域由
|
||||
* protectTask 卡 5 闭锁挡在门外,本函数不感知标定态)
|
||||
* 函数说明:src=PHASOR 行任一相量未就绪(未满一周波)→ **整拍跳过**,
|
||||
* 不引入陈旧/半新测量(B5 口径)。src=FREQ 行:freq 有有效读数
|
||||
* (started 且幅值门 OK)→ 填测得频率;否则按方向填"必不启动"值
|
||||
|
||||
+4
-1
@@ -22,7 +22,10 @@
|
||||
* - 返回**即时**(参考 GRly_TimeRelay 带 10 ms 返回延时——V0.1 无出口硬件,
|
||||
* 抖动只影响日志,P4 接出口时再补返回延时);
|
||||
* - 复压/方向/无压无流闭锁(dwSetFyCtrl 类)随 P2/P3 元件扩展;
|
||||
* - 工程量↔码值换算系数暂为 1(L1-5 标定未实现,同 phasor 层口径)。
|
||||
* - 测量输入 = adc_task 经 adc_cal_apply() 折算后的工程量(L1.5,卡 2 已落地):
|
||||
* 未标定(VALID=0)时单位只是 ADC 引脚电压——该域下判据必然错配,由
|
||||
* protectTask 的未标定闭锁(卡 5)停走整条判据链,元件核本身不掺和
|
||||
* 标定语义(保持纯逻辑);标定后单位 = 二次值,定值口径才名副其实。
|
||||
*
|
||||
* 线程约定:init 内核启动前或保护消费者任务上下文;eval/run 仅保护消费者任务
|
||||
* 上下文(B5 起 = protectTask);request_reset 任意上下文(跨上下文复归请求,
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
* ⚠ 默认值/范围为拟定值(参考 EEPROM 出厂默认表不在本仓库,规划 §10.3 待补);
|
||||
* 通讯地址逐项对照参考 Comm/Pub/Const_CommSetPara.c g_tCommSetParaTab。
|
||||
*
|
||||
* 量纲口径(「数据可信」卡 6 复核,2026-10-25):A/V 项 = **二次值**工程量,
|
||||
* 标称基准 100 V / 1 A(UA/UB/UC 相电压、3U0、OV/UV 同域——OV 120 = 1.2×Un、
|
||||
* UV 20 = 0.2×Un、3U0 15 = 0.15×Un;OC1/2/3 = 10/5/2 ×Ir、OL = 1.0×Ir,
|
||||
* 三段递升与过负荷低于速断均自洽)。ADC 帧仅在 adc_cal VALID 置位后才处
|
||||
* 于此域(未标定 = 引脚电压域,由卡 5 闭锁停走判据,见 protect_task.c);
|
||||
* min/max 范围覆盖 0~4×标称,无需随本卡改动数值——换算依据与三处下游
|
||||
* 常数同源,见 docs/任务规划/数据可信.md §2.3。
|
||||
*
|
||||
* 创建日期:2026-10-11
|
||||
*/
|
||||
#include "prot_set.h"
|
||||
|
||||
@@ -69,6 +69,10 @@ typedef enum {
|
||||
SOE_T_PLATE_IN,
|
||||
SOE_T_SAMPLE_BLOCK,
|
||||
SOE_T_SAMPLE_RESUME,
|
||||
/* 未标定闭锁(「数据可信」卡 5,2026-10-25 道A 追加):UA/UN/IA 的 VALID
|
||||
* 未全置位 → 判据停走;与采样闭锁分型,边沿各留一条记录 */
|
||||
SOE_T_CAL_BLOCK,
|
||||
SOE_T_CAL_RESUME,
|
||||
SOE_T_COUNT /* 哨兵值,非有效类型 */
|
||||
} soe_type_t;
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
* 遥测死区)。点表口径见 iec104_pts.h 文件头与设计文档 §3。
|
||||
* 线程约定:init/bind 建服务前;其余仅 104 服务任务上下文(快照非原子)。
|
||||
* 创建日期:2026-10-12(B4)
|
||||
* 修改记录:
|
||||
* 2026-10-25 DTU 项目组 卡 6(「数据可信」):YC 死区由拟定码值域重定为
|
||||
* 二次值工程量域(额定 0.5 % 规则),判据依据见
|
||||
* k_yc 表头注释;数值随卡 9 迁运行参数,本表将退化为
|
||||
* ROM 默认载体。
|
||||
*/
|
||||
#include "iec104_pts.h"
|
||||
|
||||
@@ -16,23 +21,31 @@
|
||||
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
|
||||
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
|
||||
|
||||
/* ---------------- YC 表(§1.5 点号 4011+;死区 = 拟定码值,L1-5 后复核) -- */
|
||||
/* ---------------- YC 表(§1.5 点号 4011+;死区 = 二次值工程量域) --------- */
|
||||
typedef struct {
|
||||
uint32_t ioa;
|
||||
int8_t sem; /* 绑定的 phasor 语义通道(-1 = 无 phasor 源,恒无效) */
|
||||
float deadband; /* 死区(工程量绝对值) */
|
||||
float deadband; /* 死区(二次值工程量绝对值,「数据可信」卡 6 重定) */
|
||||
} pts_yc_def_t;
|
||||
|
||||
/* 死区规则 = 额定二次值的 0.5 %(「数据可信」§2.3 表第 2 行建议规则,卡 6 采纳;
|
||||
* 额定基准 100 V / 1 A 与 App/algo/prot_set.c 头部同源,勿在此另立口径):
|
||||
* 电压类 0.5 % × 100 V = 0.5 V 电流类 0.5 % × 1 A = 0.005 A
|
||||
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
|
||||
* 卡 10 运行参数段可调)。IV 品质点(sem=-1)数值仅为将来启用预留同型。
|
||||
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
|
||||
* 按本域判定无意义但也不会漏报——104 侧 IV/数据可信度语义由 M5 快照层
|
||||
* (卡 8/9)统一处理,本卡只把"量纲错位"这一处常量债还掉。 */
|
||||
static const pts_yc_def_t k_yc[] = {
|
||||
{ 0x4011u, (int8_t)ADC_SEM_UA, 10.0f }, /* 1Ua */
|
||||
{ 0x4012u, -1, 10.0f }, /* 1Ub:V0.1 削顶 → IV */
|
||||
{ 0x4013u, -1, 10.0f }, /* 1Uc:同上 */
|
||||
{ 0x4014u, (int8_t)ADC_SEM_UN, 10.0f }, /* 1U0(外部零序专用通道) */
|
||||
{ 0x4015u, (int8_t)ADC_SEM_IA, 10.0f }, /* 1Ia */
|
||||
{ 0x4016u, -1, 10.0f }, /* 1Ib */
|
||||
{ 0x4017u, -1, 10.0f }, /* 1Ic */
|
||||
{ 0x4018u, -1, 10.0f }, /* 1I0 */
|
||||
{ 0x401Du, -1, 0.5f }, /* 1f:L1-7 未实现 → IV */
|
||||
{ 0x4011u, (int8_t)ADC_SEM_UA, 0.5f }, /* 1Ua:0.5 % × 100 V */
|
||||
{ 0x4012u, -1, 0.5f }, /* 1Ub:V0.1 削顶 → IV */
|
||||
{ 0x4013u, -1, 0.5f }, /* 1Uc:同上 */
|
||||
{ 0x4014u, (int8_t)ADC_SEM_UN, 0.5f }, /* 1U0(外部零序专用通道) */
|
||||
{ 0x4015u, (int8_t)ADC_SEM_IA, 0.005f }, /* 1Ia:0.5 % × 1 A */
|
||||
{ 0x4016u, -1, 0.005f }, /* 1Ib */
|
||||
{ 0x4017u, -1, 0.005f }, /* 1Ic */
|
||||
{ 0x4018u, -1, 0.005f }, /* 1I0 */
|
||||
{ 0x401Du, -1, 0.5f }, /* 1f:Hz 绝对死区(非额定比)→ IV */
|
||||
};
|
||||
#define YC_N ((uint8_t)(sizeof(k_yc) / sizeof(k_yc[0])))
|
||||
|
||||
|
||||
+80
-18
@@ -8,7 +8,16 @@
|
||||
* 可忽略;失配 → prot_set 内部闭锁,eval 自动复位全元件);
|
||||
* ③ prot_elm_run()(phasor rms → 判据步进)+ 动作位图边沿日志;
|
||||
* ④ 全局软压板 PS_GLOBAL_EN(104 YK12 两段式投退,B7):退出 =
|
||||
* 复位元件 + 停判据(与采样闭锁同构,独立边沿日志)。
|
||||
* 复位元件 + 停判据(与采样闭锁同构,独立边沿日志);
|
||||
* ⑤ 未标定闭锁(「数据可信」卡 5,M4 唯一新增行为变更):UA/UN/IA
|
||||
* 的 adc_cal VALID 位未全置位 → 工程量还只是 ADC 引脚电压
|
||||
* (eng=0),拿去比二次值定值必然错判 → 复位元件 + 本拍不 eval。
|
||||
* 默认构建(未标定)恒闭锁是**期望行为**:边沿用 DBG_INFO 而非
|
||||
* [ERR](未标定是出厂常态、不是故障;每次开机都走这条沿,
|
||||
* [ERR] 还会误触 @test 禁用标记);台架旁路只走编译期宏
|
||||
* DTU_BENCH(「数据可信」§4.3:不进 cfg、不进 104,本仓任何
|
||||
* 提交目标都不定义它)。判定优先级 = 采样可信在前、标定可用
|
||||
* 在后(数据都旧了谈不上标定)。
|
||||
* 定值/元件初始化在 app_main_init(内核前,B5);本任务只消费。
|
||||
* 线程约定:prot_elm_run / prot_set_selfcheck 仅本任务上下文(prot_elm.h);
|
||||
* 跨上下文复归(104 YK11)经 prot_elm_request_reset 落请求。
|
||||
@@ -20,6 +29,12 @@
|
||||
* adc_health 的 stall 位依赖 adcTask 每拍跑 check(),
|
||||
* 节拍源一停本任务就再没有任何人判"数据新不新",
|
||||
* 而相量冻在最后一帧上还会被本任务继续用于判据
|
||||
* v1.2 2026-10-25 DTU 项目组 卡 5:新增第⑤路「未标定闭锁」(adc_cal_flags
|
||||
* VALID 口径,docs/任务规划/数据可信.md §4.3):
|
||||
* 采样恢复分支内、巡检/压板之前判;边沿 INFO 对
|
||||
* + SOE_T_CAL_BLOCK/RESUME(不用 [ERR]:未标定是
|
||||
* 常态非故障,且 @test 禁用 [ERR]);DTU_BENCH
|
||||
* 编译期旁路(本仓目标不定义 = 产品恒强制闭锁)
|
||||
*/
|
||||
#include "protect_task.h"
|
||||
|
||||
@@ -31,6 +46,7 @@
|
||||
|
||||
#include "adc_input.h" /* adc_input_stm32_seq():DMA 完成计数(中断侧写) */
|
||||
#include "adc_health.h"
|
||||
#include "adc_cal.h" /* 卡 5:adc_cal_flags() VALID = 未标定闭锁源 */
|
||||
#include "console_log.h" /* dbg_console_writer_ready():启动锚点行门控(B6) */
|
||||
#include "prot_elm.h"
|
||||
#include "prot_set.h"
|
||||
@@ -60,6 +76,33 @@ static adc_health_age_t s_age_a1; /* STM32 车道 */
|
||||
static uint32_t s_prev_active; /* 上拍动作位图(边沿日志用) */
|
||||
static uint8_t s_prev_hf_lock; /* 上拍采样闭锁态(边沿日志用) */
|
||||
static uint8_t s_prev_plate_out; /* 上拍全局软压板退出态(边沿日志用,B7) */
|
||||
static uint8_t s_prev_cal_lock; /* 上拍未标定闭锁态(边沿日志用,卡 5) */
|
||||
|
||||
/*
|
||||
* 函数功能:未标定闭锁判定(「数据可信」卡 5 / §4.3)
|
||||
* 入口参数:无
|
||||
* 返 回 值:1 - 闭锁(判据停走);0 - 放行 uint8_t
|
||||
* 限定条件:仅本任务上下文(adc_cal_flags 纯读,任意上下文可读)
|
||||
* 函数说明:判据 = UA/UN/IA 三路 VALID **全**置位——恰好是 V0.1 唯一有效的
|
||||
* 三个测量通道(N-18);UB/UC/IB/IC/IN 即使将来标定也另受 CLIPPED
|
||||
* 品质约束(M5 快照层处理),不掺和进本闭锁源。VALID 随 cfg
|
||||
* load/poll 与卡 12 标定自动翻转,本函数无自有状态(每拍现查,
|
||||
* 无陈旧标志位)。台架调试旁路 = 编译期 -DDTU_BENCH(§4.3 决策 1
|
||||
* 的例外通道,仅本地台架构建自行定义;产品恒强制)。
|
||||
*/
|
||||
/* 形态说明:用 #ifndef DTU_BENCH 把"产品默认路径"留在主分支(而非 #ifdef
|
||||
* 双分支)——cppcheck 对未定义宏取真分支,双分支写法会把它引到旁路分支上
|
||||
* 报 knownConditionTrueFalse 假信号(实测 2026-10-25)。 */
|
||||
static uint8_t s_cal_locked(void) {
|
||||
#ifndef DTU_BENCH
|
||||
if (((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u) ||
|
||||
((adc_cal_flags(ADC_SEM_UN) & ADC_CAL_F_VALID) == 0u) ||
|
||||
((adc_cal_flags(ADC_SEM_IA) & ADC_CAL_F_VALID) == 0u)) {
|
||||
return 1u;
|
||||
}
|
||||
#endif
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:动作位图变化沿日志 + SOE 投递(逐变化元件)
|
||||
@@ -153,29 +196,48 @@ void protect_task(void *argument) {
|
||||
SOE_ELM_NONE, 0u);
|
||||
s_prev_hf_lock = 0u;
|
||||
}
|
||||
/* L5-1 巡检:定值区与封存校验和不符 → prot_set 闭锁(下一拍起
|
||||
* eval 自动复位全元件)。B7 收口:104 定值写改走
|
||||
* prot_set_set_sealed(影子区写+封存后单字交换),selfcheck 另有
|
||||
* seqlock 式复核(提交撞见 = 本轮跳过),误锁窗口已消除。 */
|
||||
(void)prot_set_selfcheck();
|
||||
/* 全局软压板(PS_GLOBAL_EN,104 YK12 两段式投退,B7):退出 =
|
||||
* 保护停止判据并复位元件状态(重新投入从干净快照起步) */
|
||||
if (prot_set_get_ctl(PS_GLOBAL_EN) == 0u) {
|
||||
if (s_prev_plate_out == 0u) {
|
||||
DBG_INFO("global plate OUT -> protection disabled");
|
||||
(void)soe_store_post((uint8_t)SOE_T_PLATE_OUT,
|
||||
/* 卡 5:未标定闭锁——夹在「采样可信」与「巡检/压板」之间:
|
||||
* eng=0 域(引脚电压)拿去比二次值定值必然错判(UV 尤甚),
|
||||
* 标定 VALID 未全置位前整条判据链停走(「数据可信」§4.3 决策 1) */
|
||||
if (s_cal_locked() != 0u) {
|
||||
if (s_prev_cal_lock == 0u) {
|
||||
DBG_INFO("uncalibrated -> protection blocked (UA/UN/IA VALID not all set)");
|
||||
(void)soe_store_post((uint8_t)SOE_T_CAL_BLOCK,
|
||||
SOE_ELM_NONE, 0u);
|
||||
s_prev_plate_out = 1u;
|
||||
s_prev_cal_lock = 1u;
|
||||
}
|
||||
prot_elm_init();
|
||||
} else {
|
||||
if (s_prev_plate_out != 0u) {
|
||||
DBG_INFO("global plate IN -> protection enabled");
|
||||
(void)soe_store_post((uint8_t)SOE_T_PLATE_IN,
|
||||
if (s_prev_cal_lock != 0u) {
|
||||
DBG_INFO("calibration VALID -> protection unlocked");
|
||||
(void)soe_store_post((uint8_t)SOE_T_CAL_RESUME,
|
||||
SOE_ELM_NONE, 1u);
|
||||
s_prev_plate_out = 0u;
|
||||
s_prev_cal_lock = 0u;
|
||||
}
|
||||
/* L5-1 巡检:定值区与封存校验和不符 → prot_set 闭锁(下一拍起
|
||||
* eval 自动复位全元件)。B7 收口:104 定值写改走
|
||||
* prot_set_set_sealed(影子区写+封存后单字交换),selfcheck 另有
|
||||
* seqlock 式复核(提交撞见 = 本轮跳过),误锁窗口已消除。 */
|
||||
(void)prot_set_selfcheck();
|
||||
/* 全局软压板(PS_GLOBAL_EN,104 YK12 两段式投退,B7):退出 =
|
||||
* 保护停止判据并复位元件状态(重新投入从干净快照起步) */
|
||||
if (prot_set_get_ctl(PS_GLOBAL_EN) == 0u) {
|
||||
if (s_prev_plate_out == 0u) {
|
||||
DBG_INFO("global plate OUT -> protection disabled");
|
||||
(void)soe_store_post((uint8_t)SOE_T_PLATE_OUT,
|
||||
SOE_ELM_NONE, 0u);
|
||||
s_prev_plate_out = 1u;
|
||||
}
|
||||
prot_elm_init();
|
||||
} else {
|
||||
if (s_prev_plate_out != 0u) {
|
||||
DBG_INFO("global plate IN -> protection enabled");
|
||||
(void)soe_store_post((uint8_t)SOE_T_PLATE_IN,
|
||||
SOE_ELM_NONE, 1u);
|
||||
s_prev_plate_out = 0u;
|
||||
}
|
||||
prot_elm_run(now);
|
||||
}
|
||||
prot_elm_run(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1708,6 +1708,28 @@
|
||||
"owner_note": "B 层口径(用户裁定 2026-10-21,同 TC-HOST-ADCHEALTH 先例;2026-10-25 道A 卡2 建族):属主登记处 = 单测代码目录,不逐号补文档",
|
||||
"cursor_note": "游标按建族时实测最大号收录(2026-10-25):越界即视为未登记的冒号牌"
|
||||
},
|
||||
{
|
||||
"name": "TC-HOST-PROTDIM",
|
||||
"class": "TC",
|
||||
"title": "主机侧单测用例号:量纲回归(test/host/prot_dim,道A 卡7)",
|
||||
"pattern": "\\bTC-PROTDIM-[0-9]{1,4}[a-z]?\\b",
|
||||
"digit_width": [
|
||||
2,
|
||||
4
|
||||
],
|
||||
"owner_docs_glob": [
|
||||
"test/host/prot_dim/*"
|
||||
],
|
||||
"scope": "global",
|
||||
"frozen": false,
|
||||
"cursor": "TC-PROTDIM-010",
|
||||
"cursor_owner_only": true,
|
||||
"check_dangling": true,
|
||||
"allow_orphans": true,
|
||||
"subitem_ok": true,
|
||||
"owner_note": "B 层口径(用户裁定 2026-10-21,同 TC-HOST-ADCHEALTH/TC-HOST-ADCCAL 先例;2026-10-25 道A 卡7 建族):属主登记处 = 单测代码目录,不逐号补文档",
|
||||
"cursor_note": "游标按建族时实测最大号收录(2026-10-25):越界即视为未登记的冒号牌"
|
||||
},
|
||||
{
|
||||
"name": "TC-HOST-IEC104",
|
||||
"class": "TC",
|
||||
|
||||
@@ -511,14 +511,15 @@ static void tc_cov_yc(void)
|
||||
handshake(now);
|
||||
s_f_yc[0] = 100.0f;
|
||||
iec104_link_poll(&g_lk, now); /* 快照 = 100 */
|
||||
s_f_yc[0] = 105.0f; /* 死区 10 内 */
|
||||
s_f_yc[0] = 100.4f; /* 死区 0.5 V 内(卡 6 二次值口径:0.5 %×100 V,
|
||||
* 旧拟定码值死区 10 已由「数据可信」卡 6 重定) */
|
||||
iec104_link_poll(&g_lk, now + 100u);
|
||||
CHECK(cap_i_count() == 0, "TC-008 inside the deadband nothing is uploaded");
|
||||
s_f_yc[0] = 130.0f; /* 130-105=25 > 10 */
|
||||
s_f_yc[0] = 101.25f; /* 101.25-100.4=0.85 > 0.5 */
|
||||
iec104_link_poll(&g_lk, now + 200u);
|
||||
CHECK((cap_i_count() == 1) && (cap_i(0, &c) == 0) && (c.type == 13u)
|
||||
&& (c.cot == IEC104_COT_SPONT) && (c.ioa == 0x4011u)
|
||||
&& (iec104_get_float(c.obj) == 130.0f), "TC-008 crossing the deadband uploads");
|
||||
&& (iec104_get_float(c.obj) == 101.25f), "TC-008 crossing the deadband uploads");
|
||||
}
|
||||
|
||||
static void feed_rd(uint32_t now)
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 模块名称:量纲回归主机侧单测(「数据可信」卡 7 · 道A)
|
||||
* 被测对象:真实源文件(#include 原样纳入,全链路 L1.5→L2→L3):
|
||||
* adc_cal.c + prot_set.c + sample_ring.c + phasor.c + freq.c +
|
||||
* prot_elm.c
|
||||
* 平台依赖桩:dbg_log.h(stub/)、cfg_file_register(捕获计数)、
|
||||
* adc_input_desc(V0.1 权威来源表,同 adc_cal 套件)。
|
||||
*
|
||||
* 本卡的存在理由(短板评测 + 「数据可信」§2.2):台架此前"通过"是因为没有
|
||||
* 一条判据检查**不该动作时不动作**——"码值当工程量"的错配曾让 OC 拿 ~11000
|
||||
* 码值比 10 A 定值恒假动作。本套件把这条固化成回归:用例从 **ADC 码值** 出发、
|
||||
* **显式写出码值↔二次值换算**,走真实 折算→采样环→全周傅氏→元件判据 链;
|
||||
* 任何一环把量纲弄丢(比如折算点被旁路),断言立刻翻转。
|
||||
*
|
||||
* ROM 系数覆写(#undef/#define):把"板级理论系数"替换为合成端子侧口径
|
||||
* (10 mV/码,K_DIM),模拟卡 12 标定完成后的 ROM/默认状态——合法带随之
|
||||
* 锚定合成值,adc_cal_set 走正规路径置 VALID,测试不白盒改引擎内存。
|
||||
*
|
||||
* 覆盖点(族 TC-HOST-PROTDIM,属主登记处 = 本目录):
|
||||
* TC-PROTDIM-001 标定链基线:0.5 A/100 V/0 V 注入 → 七类元件全静默(含
|
||||
* OL:0.5 < 1 A 定值)+ IA 相量有效值 = 0.5 A ±0.5 %
|
||||
* TC-PROTDIM-002 反错配对照:同一批码值,raw 直灌 → OC1 假动作(复现历史
|
||||
* 事故);经 adc_cal_apply → OC1 静默(折算点不可旁路)
|
||||
* TC-PROTDIM-003 OC1(10 A,dly=0):9.995 不动作 / 10.5 启动即动作 /
|
||||
* 9.6 滞回保持 / 9.49 释放
|
||||
* TC-PROTDIM-004 OC2(5 A,500 ms):6 A 启动,启动后 499 ms 不动作、
|
||||
* 501 ms 动作;4.8 保持(ret 4.75)、4.6 释放
|
||||
* TC-PROTDIM-005 OC3(2 A,3000 ms):同上时序(2.1 A)
|
||||
* TC-PROTDIM-006 OL(1 A,10 s):1.05 A 启动、9999/10001 ms 时序、
|
||||
* 0.96 保持(ret 0.95)、0.93 释放
|
||||
* TC-PROTDIM-007 OV(120 V,10 s):100 V 静默;121 V 启动、到点动作;
|
||||
* 115 保持(ret 114)、113 释放
|
||||
* TC-PROTDIM-008 UV(20 V,10 s,欠量方向):21 V 静默;19.9 V 启动、
|
||||
* 到点动作;20.5 保持(ret 21)、21.6 释放
|
||||
* TC-PROTDIM-009 3U0(15 V,5 s):14 V 静默;15.1 启动到点;
|
||||
* 14.4 保持(ret 14.25)、14.1 释放
|
||||
* TC-PROTDIM-010 聚合与独立性:OC1+OV 同拍动作位图 = 双位;撤 IA →
|
||||
* OC1 返回、OV 保持(互不污染)
|
||||
*
|
||||
* 通过:全部 TC PASS(main 返回 0)。
|
||||
* 创建日期:2026-10-25(道A 卡 7)
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 桩头先于生产源码 */
|
||||
#include "dbg_log.h"
|
||||
int g_dimtest_err_count = 0;
|
||||
int g_dimtest_info_count = 0;
|
||||
int g_dimtest_debug_count = 0;
|
||||
|
||||
#include "cfg_file.h"
|
||||
|
||||
static int s_reg_count = 0;
|
||||
|
||||
int cfg_file_register(const cfg_section_reg_t *p_reg) {
|
||||
(void)p_reg;
|
||||
s_reg_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* V0.1 权威来源表(docs/硬件平台/ADC通道映射说明.md §3/§5,同 adc_cal 套件桩) */
|
||||
#include "adc_input.h"
|
||||
|
||||
static const adc_channel_desc_t s_desc_stub[ADC_SEM_COUNT] = {
|
||||
{ ADC_SRC_TPAFE, 6u }, { ADC_SRC_STM32, 14u }, { ADC_SRC_STM32, 3u },
|
||||
{ ADC_SRC_TPAFE, 5u }, { ADC_SRC_TPAFE, 2u }, { ADC_SRC_STM32, 6u },
|
||||
{ ADC_SRC_STM32, 1u }, { ADC_SRC_STM32, 2u }, { ADC_SRC_STM32, 5u },
|
||||
};
|
||||
|
||||
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
|
||||
if ((unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
|
||||
return NULL;
|
||||
}
|
||||
return &s_desc_stub[(unsigned)ch];
|
||||
}
|
||||
|
||||
/* ---- 测试框架(同 prot_elm 套件) ---- */
|
||||
static int s_total = 0, s_passed = 0, s_failed = 0;
|
||||
|
||||
#define CHECK(cond, name) do { \
|
||||
s_total++; \
|
||||
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
|
||||
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
|
||||
} while (0)
|
||||
|
||||
/* ---- 合成"标定后"板级口径(覆写 adc_cal.h 的 ROM 常量,必须在纳入 .c 之前) ----
|
||||
* K_DIM = 655360000 Q16 = 10000 µV/码 = 10 mV/码:
|
||||
* 电压类 100 V rms → 峰值 141.42 V → 14142 码(TPAFE 半量程内,量级刻意
|
||||
* 贴近卡 12 端子变比标定后的真实形态);电流类二次值经变换器落到同一定标。
|
||||
* B_IA = -30 码(零点偏移,经 apply 精确对消——验证 b 参与全链而非仅落盘)。 */
|
||||
#include "adc_cal.h"
|
||||
#undef ADC_CAL_K_Q16_TPAFE
|
||||
#undef ADC_CAL_K_Q16_STM32
|
||||
#define ADC_CAL_K_Q16_TPAFE (655360000L)
|
||||
#define ADC_CAL_K_Q16_STM32 (655360000L)
|
||||
#define K_DIM ADC_CAL_K_Q16_TPAFE
|
||||
#define B_IA_DIM (-30L)
|
||||
|
||||
/* ===================== 被测生产源文件 ===================== */
|
||||
#include "prot_set.c"
|
||||
#include "sample_ring.c"
|
||||
#include "phasor.c"
|
||||
#include "freq.c"
|
||||
#include "prot_elm.c"
|
||||
#include "adc_cal.c"
|
||||
|
||||
/* ===================== 波形/时基(同 prot_elm 套件 run() 级用例口径) ===================== */
|
||||
static uint32_t s_t = 0u;
|
||||
|
||||
static long my_lround(double x) { return (long)((x >= 0.0) ? (x + 0.5) : (x - 0.5)); }
|
||||
|
||||
/* 显式换算(卡 7 的核心手法,两处方向都要有):
|
||||
* code = round(eng[V 或 A] × 65536×1e6 / k_q16) − b (期望 → 码值)
|
||||
* eng = (code + b) × k_q16 / 65536 / 1e6 = adc_cal_apply (码值 → 工程量)
|
||||
* 上面两式必须互逆——若实现(adc_cal_apply)与期望推导脱钩,全部 TC 翻转。 */
|
||||
static int32_t dim_code(double eng, double b_codes) {
|
||||
return (int32_t)(my_lround(eng * 65536.0e6 / (double)K_DIM) - b_codes);
|
||||
}
|
||||
|
||||
/* 一拍:三相量按 50 Hz 正弦(峰值 = rms×√2),其余电气量通道给 0 码值,
|
||||
* TEMP 透传 0;随后跑真实 L2 链。 */
|
||||
static void dim_beat(double ia_rms, double ua_rms, double un_rms) {
|
||||
float frame[ADC_SEM_COUNT];
|
||||
double s50 = sin(2.0 * 3.14159265358979 * 50.0 * (double)s_t / 1000.0);
|
||||
int32_t ci_ia, ci_ua, ci_un;
|
||||
uint8_t c;
|
||||
|
||||
ci_ia = dim_code(ia_rms * 1.4142135623730951 * s50, (double)B_IA_DIM);
|
||||
ci_ua = dim_code(ua_rms * 1.4142135623730951 * s50, 0.0);
|
||||
ci_un = dim_code(un_rms * 1.4142135623730951 * s50, 0.0);
|
||||
|
||||
frame[ADC_SEM_IA] = adc_cal_apply(ADC_SEM_IA, ci_ia);
|
||||
frame[ADC_SEM_UA] = adc_cal_apply(ADC_SEM_UA, ci_ua);
|
||||
frame[ADC_SEM_UN] = adc_cal_apply(ADC_SEM_UN, ci_un);
|
||||
for (c = 0u; c < (uint8_t)ADC_SEM_COUNT; c++) {
|
||||
if ((c != (uint8_t)ADC_SEM_IA) && (c != (uint8_t)ADC_SEM_UA) &&
|
||||
(c != (uint8_t)ADC_SEM_UN)) {
|
||||
frame[c] = adc_cal_apply((adc_semantic_t)c, 0L);
|
||||
}
|
||||
}
|
||||
(void)sample_ring_push_frame(frame, (uint8_t)ADC_SEM_COUNT);
|
||||
phasor_run();
|
||||
freq_run();
|
||||
prot_elm_run(s_t);
|
||||
s_t++;
|
||||
}
|
||||
|
||||
/* TC 起手:定值/元件全复位 + 标定注入(set 正规路径)+ 中性 45 拍收敛 */
|
||||
static void dim_setup(void) {
|
||||
prot_set_init();
|
||||
adc_cal_init();
|
||||
(void)adc_cal_set(ADC_SEM_UA, K_DIM, 0L);
|
||||
(void)adc_cal_set(ADC_SEM_UN, K_DIM, 0L);
|
||||
(void)adc_cal_set(ADC_SEM_IA, K_DIM, B_IA_DIM);
|
||||
prot_elm_init();
|
||||
}
|
||||
|
||||
static void set_en(prot_set_id_t id, int on) {
|
||||
(void)prot_set_set(id, PROT_SET_Q10(on ? 1.0 : 0.0));
|
||||
}
|
||||
|
||||
/* 只留一个电气量元件家族:默认全 EN=1,逐个关到只剩目标 */
|
||||
static void isolate_oc_family(void) {
|
||||
set_en(PS_OC1_EN, 1); set_en(PS_OC2_EN, 0); set_en(PS_OC3_EN, 0);
|
||||
set_en(PS_OL_EN, 0); set_en(PS_OV_EN, 0); set_en(PS_UV_EN, 0);
|
||||
set_en(PS_3U0_EN, 0);
|
||||
}
|
||||
|
||||
static int pick_of(prot_elm_id_t e) {
|
||||
prot_elm_state_t st;
|
||||
prot_elm_state(e, &st);
|
||||
return (st.picked != 0u) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int assert_of(prot_elm_id_t e) {
|
||||
prot_elm_state_t st;
|
||||
prot_elm_state(e, &st);
|
||||
return (st.asserted != 0u) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 把 IA 拉到 far_below 收敛 45 拍(确保 picked 干净归零、t0 无效) */
|
||||
static void settle_oc(double ia_rms, uint32_t beats) {
|
||||
uint32_t i;
|
||||
for (i = 0u; i < beats; i++) { dim_beat(ia_rms, 100.0, 0.0); }
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-001 标定链基线 ===================== */
|
||||
static void tc_001_baseline(void) {
|
||||
phasor_t p;
|
||||
double want_rms;
|
||||
double got;
|
||||
uint32_t i;
|
||||
int quiet = 1;
|
||||
|
||||
printf("[TC-PROTDIM-001] calibrated chain baseline: no element moves\n");
|
||||
dim_setup();
|
||||
for (i = 0u; i < 60u; i++) { dim_beat(0.5, 100.0, 0.0); }
|
||||
|
||||
/* 换算链可验证:C01-IA(idx0)有效值 = 注入 0.5 A ±0.5 %
|
||||
* (b=-30 码应被 apply 精确对消;若折算丢失,这里会是 ~700 量级码值) */
|
||||
want_rms = 0.5;
|
||||
CHECK(phasor_get(0u, &p) == 0, "001: C01-IA phasor ready");
|
||||
got = (double)p.fAmSQ;
|
||||
CHECK((got > want_rms * 0.995) && (got < want_rms * 1.005),
|
||||
"001: IA rms = 0.5 A (secondary domain, b cancelled)");
|
||||
|
||||
for (i = 0u; i < (uint32_t)PE_OC1; i++) {
|
||||
if ((pick_of((prot_elm_id_t)i) != 0) || (assert_of((prot_elm_id_t)i) != 0)) {
|
||||
quiet = 0;
|
||||
}
|
||||
}
|
||||
CHECK(quiet, "001: OC1/OC2/OC3/OL/OV/UV/3U0 all silent at 0.5A/100V/0V");
|
||||
CHECK(prot_elm_active_mask() == 0u, "001: active mask 0");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-002 反错配对照 ===================== */
|
||||
static void tc_002_mismatch_trap(void) {
|
||||
float raw[ADC_SEM_COUNT];
|
||||
uint32_t i;
|
||||
|
||||
printf("[TC-PROTDIM-002] raw-codes-as-engineering trap / apply guard\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OC2_EN, 0);
|
||||
|
||||
/* (a) 复现历史事故:把"码值本身"当工程量灌环(等价于折算点被旁路)。
|
||||
* 3.36 A 物理电流的码值 ~672 → 比 OC1(10 A) → 必假动作 */
|
||||
for (i = 0u; i < (uint32_t)ADC_SEM_COUNT; i++) {
|
||||
raw[i] = 0.0f;
|
||||
}
|
||||
for (i = 0u; i < 45u; i++) {
|
||||
double s50 = sin(2.0 * 3.14159265358979 * 50.0 * (double)s_t / 1000.0);
|
||||
raw[ADC_SEM_IA] = (float)((double)dim_code(3.36 * 1.4142135623730951 * s50,
|
||||
(double)B_IA_DIM));
|
||||
(void)sample_ring_push_frame(raw, (uint8_t)ADC_SEM_COUNT);
|
||||
phasor_run();
|
||||
prot_elm_run(s_t);
|
||||
s_t++;
|
||||
}
|
||||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||||
"002a: raw code domain falsely trips OC1 (documents the incident)");
|
||||
|
||||
/* (b) 同一物理量经正规折算链:3.36 A < 10 A → 必须静默。
|
||||
* (若生产链有人绕过 adc_cal_apply,本断言与 001 一起翻红) */
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(3.36, 100.0, 0.0); }
|
||||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||||
"002b: same physical current via apply() stays silent");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-003 OC1:滞回 + dly0 ===================== */
|
||||
static void tc_003_oc1(void) {
|
||||
printf("[TC-PROTDIM-003] OC1 10A/dly0 hysteresis\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
|
||||
settle_oc(3.0, 45u);
|
||||
settle_oc(9.995, 45u);
|
||||
CHECK(pick_of(PE_OC1) == 0, "003: 9.995 A below set: no pickup");
|
||||
|
||||
settle_oc(10.5, 45u);
|
||||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||||
"003: 10.5 A -> picked AND asserted (dly=0 same-beat)");
|
||||
|
||||
settle_oc(9.6, 45u);
|
||||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||||
"003: 9.6 A inside hysteresis (ret=9.5): holds");
|
||||
|
||||
settle_oc(9.49, 45u);
|
||||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||||
"003: 9.49 A below return threshold: drops out");
|
||||
}
|
||||
|
||||
/* 过量定时限通用核:启动拍受控(先远低收敛),到点前后各验一次 */
|
||||
static void timed_over_case(prot_elm_id_t e, prot_set_id_t cur_id, double trip,
|
||||
double hold, double drop, float dly_ms,
|
||||
const char *tag) {
|
||||
uint32_t t_pick = 0u;
|
||||
uint32_t i;
|
||||
uint32_t settle;
|
||||
char nm[96];
|
||||
int pre_ok = 1, post_ok = 0, hold_ok = 0, drop_ok = 0, trip_ok = 0;
|
||||
|
||||
(void)cur_id;
|
||||
(void)tag;
|
||||
/* 1) 远低收敛;2) 阶跃到 trip,逐拍轮询到 picked(记录启动拍) */
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.1 * trip, 100.0, 0.0); }
|
||||
for (i = 0u; i < 400u; i++) {
|
||||
dim_beat(trip, 100.0, 0.0);
|
||||
if (pick_of(e) != 0) { t_pick = s_t - 1u; trip_ok = 1; break; }
|
||||
}
|
||||
if (trip_ok == 0) {
|
||||
(void)snprintf(nm, sizeof(nm), "%s: step to trip level picks up", tag);
|
||||
CHECK(0, nm);
|
||||
return;
|
||||
}
|
||||
/* 3) 到点前 2 ms 不许动作、到点后 2 ms 必须动作(同幅值持续注入) */
|
||||
settle = 0u;
|
||||
for (i = t_pick; i < (t_pick + (uint32_t)dly_ms - 2u); i++) {
|
||||
dim_beat(trip, 100.0, 0.0);
|
||||
settle++;
|
||||
}
|
||||
(void)settle;
|
||||
if (assert_of(e) != 0) { pre_ok = 0; }
|
||||
for (i = 0u; i < 5u; i++) { dim_beat(trip, 100.0, 0.0); }
|
||||
post_ok = assert_of(e);
|
||||
|
||||
(void)snprintf(nm, sizeof(nm), "%s: no assert before setpoint-2ms", tag);
|
||||
CHECK(pre_ok != 0 && post_ok != 0, nm);
|
||||
|
||||
/* 4) 返回系数滞回带内保持、跌破释放 */
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(hold, 100.0, 0.0); }
|
||||
hold_ok = (pick_of(e) != 0) && (assert_of(e) != 0);
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(drop, 100.0, 0.0); }
|
||||
drop_ok = (pick_of(e) == 0) && (assert_of(e) == 0);
|
||||
|
||||
(void)snprintf(nm, sizeof(nm), "%s: holds inside return band", tag);
|
||||
CHECK(hold_ok != 0, nm);
|
||||
(void)snprintf(nm, sizeof(nm), "%s: returns below return threshold", tag);
|
||||
CHECK(drop_ok != 0, nm);
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-004~006 OC2/OC3/OL ===================== */
|
||||
static void tc_004_oc2(void) {
|
||||
printf("[TC-PROTDIM-004] OC2 5A/500ms timed+return\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OC2_EN, 1);
|
||||
timed_over_case(PE_OC2, PS_OC2_CUR, 6.0, 4.8, 4.6, 500.0f, "004:OC2");
|
||||
}
|
||||
|
||||
static void tc_005_oc3(void) {
|
||||
printf("[TC-PROTDIM-005] OC3 2A/3000ms timed+return\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OC3_EN, 1);
|
||||
timed_over_case(PE_OC3, PS_OC3_CUR, 2.1, 1.95, 1.89, 3000.0f, "005:OC3");
|
||||
}
|
||||
|
||||
static void tc_006_ol(void) {
|
||||
printf("[TC-PROTDIM-006] OL 1A/10s timed+return\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OL_EN, 1);
|
||||
timed_over_case(PE_OL, PS_OL_CUR, 1.05, 0.96, 0.93, 10000.0f, "006:OL");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-007 OV(电压域走 UA) ===================== */
|
||||
static void tc_007_ov(void) {
|
||||
uint32_t t_pick = 0u;
|
||||
uint32_t i;
|
||||
int ok;
|
||||
|
||||
printf("[TC-PROTDIM-007] OV 120V/10s (UA secondary domain)\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OV_EN, 1);
|
||||
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 0.0); }
|
||||
CHECK(pick_of(PE_OV) == 0, "007: nominal 100 V silent");
|
||||
for (i = 0u; i < 400u; i++) {
|
||||
dim_beat(0.0, 121.0, 0.0);
|
||||
if (pick_of(PE_OV) != 0) { t_pick = s_t - 1u; break; }
|
||||
}
|
||||
CHECK(pick_of(PE_OV) != 0, "007: 121 V picks up");
|
||||
for (i = t_pick; i < (t_pick + 9998u); i++) { dim_beat(0.0, 121.0, 0.0); }
|
||||
ok = (assert_of(PE_OV) == 0);
|
||||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 121.0, 0.0); }
|
||||
CHECK((ok != 0) && (assert_of(PE_OV) != 0),
|
||||
"007: asserts exactly at +10000ms window (not earlier)");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 115.0, 0.0); }
|
||||
CHECK(assert_of(PE_OV) != 0, "007: 115 V holds (ret=114)");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 113.0, 0.0); }
|
||||
CHECK((pick_of(PE_OV) == 0) && (assert_of(PE_OV) == 0),
|
||||
"007: 113 V returns");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-008 UV(欠量方向) ===================== */
|
||||
static void tc_008_uv(void) {
|
||||
uint32_t t_pick = 0u;
|
||||
uint32_t i;
|
||||
int ok;
|
||||
|
||||
printf("[TC-PROTDIM-008] UV 20V/10s under-direction\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_UV_EN, 1);
|
||||
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 21.0, 0.0); }
|
||||
CHECK(pick_of(PE_UV) == 0, "008: 21 V above set(20): silent");
|
||||
for (i = 0u; i < 400u; i++) {
|
||||
dim_beat(0.0, 19.9, 0.0);
|
||||
if (pick_of(PE_UV) != 0) { t_pick = s_t - 1u; break; }
|
||||
}
|
||||
CHECK(pick_of(PE_UV) != 0, "008: 19.9 V picks up");
|
||||
for (i = t_pick; i < (t_pick + 9998u); i++) { dim_beat(0.0, 19.9, 0.0); }
|
||||
ok = (assert_of(PE_UV) == 0);
|
||||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 19.9, 0.0); }
|
||||
CHECK((ok != 0) && (assert_of(PE_UV) != 0), "008: asserts at +10000ms");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 20.5, 0.0); }
|
||||
CHECK(assert_of(PE_UV) != 0, "008: 20.5 V holds under return(21)");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 21.6, 0.0); }
|
||||
CHECK((pick_of(PE_UV) == 0) && (assert_of(PE_UV) == 0), "008: 21.6 V returns");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-009 3U0(UN 通道) ===================== */
|
||||
static void tc_009_3u0(void) {
|
||||
uint32_t t_pick = 0u;
|
||||
uint32_t i;
|
||||
int ok;
|
||||
|
||||
printf("[TC-PROTDIM-009] 3U0 15V/5s via UN channel\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_3U0_EN, 1);
|
||||
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.0); }
|
||||
CHECK(pick_of(PE_3U0) == 0, "009: 14 V below set(15): silent");
|
||||
for (i = 0u; i < 400u; i++) {
|
||||
dim_beat(0.0, 100.0, 15.1);
|
||||
if (pick_of(PE_3U0) != 0) { t_pick = s_t - 1u; break; }
|
||||
}
|
||||
CHECK(pick_of(PE_3U0) != 0, "009: 15.1 V picks up");
|
||||
for (i = t_pick; i < (t_pick + 4998u); i++) { dim_beat(0.0, 100.0, 15.1); }
|
||||
ok = (assert_of(PE_3U0) == 0);
|
||||
for (i = 0u; i < 5u; i++) { dim_beat(0.0, 100.0, 15.1); }
|
||||
CHECK((ok != 0) && (assert_of(PE_3U0) != 0), "009: asserts at +5000ms");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.4); }
|
||||
CHECK(assert_of(PE_3U0) != 0, "009: 14.4 V holds (ret=14.25)");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.0, 100.0, 14.1); }
|
||||
CHECK((pick_of(PE_3U0) == 0) && (assert_of(PE_3U0) == 0), "009: 14.1 V returns");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-010 聚合位图与独立性 ===================== */
|
||||
static void tc_010_mask(void) {
|
||||
uint32_t i;
|
||||
|
||||
printf("[TC-PROTDIM-010] dual assert mask independence\n");
|
||||
dim_setup();
|
||||
isolate_oc_family();
|
||||
set_en(PS_OV_EN, 1);
|
||||
(void)prot_set_set(PS_OV_DLY, PROT_SET_Q10(0.0)); /* 同拍聚合,聚焦位图 */
|
||||
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(10.5, 121.0, 0.0); }
|
||||
CHECK((assert_of(PE_OC1) != 0) && (assert_of(PE_OV) != 0),
|
||||
"010: OC1+OV both asserted");
|
||||
CHECK(prot_elm_active_mask() == ((1uL << PE_OC1) | (1uL << PE_OV)),
|
||||
"010: active mask = bit0|bit4 exactly");
|
||||
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(3.0, 121.0, 0.0); }
|
||||
CHECK((assert_of(PE_OC1) == 0) && (assert_of(PE_OV) != 0),
|
||||
"010: IA withdrawn -> OC1 returns, OV untouched");
|
||||
CHECK(prot_elm_active_mask() == (1uL << PE_OV), "010: mask = OV only");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Prot Dim Regression Tests ===\n");
|
||||
adc_cal_init(); /* 先把 RAM 系数置成合成 ROM(各 TC 内还会重跑) */
|
||||
tc_001_baseline();
|
||||
tc_002_mismatch_trap();
|
||||
tc_003_oc1();
|
||||
tc_004_oc2();
|
||||
tc_005_oc3();
|
||||
tc_006_ol();
|
||||
tc_007_ov();
|
||||
tc_008_uv();
|
||||
tc_009_3u0();
|
||||
tc_010_mask();
|
||||
printf("\n=== Prot Dim Regression Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
return (s_failed == 0) ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""量纲回归(卡 7,道A)主机侧单元测试运行器(L0.5,纯标准库)。
|
||||
|
||||
做什么:
|
||||
MSVC 编译 test/host/prot_dim/prot_dim_test.c(#include 纳入真实链:
|
||||
adc_cal + prot_set + sample_ring + phasor + freq + prot_elm),在 PC 上
|
||||
用"码值→二次值显式换算"的全链路合成波驱动七类保护元件,钉死
|
||||
「不该动作时不动作」。工具链细节委托 unit_test_harness.py。
|
||||
|
||||
为什么要这个套件(数据可信 §2.2 / 卡 7):
|
||||
台架此前"通过"是因为没有一条判据检查不该动作时不动作——"码值当工程量"
|
||||
的错配曾让 OC 恒假动作。本套件把换算写进用例:折算点被旁路或量纲再错位,
|
||||
断言立刻翻转;纯合成数据,零硬件零台架。
|
||||
|
||||
用法(仓库根目录):
|
||||
python test/host/prot_dim/run_unit_test.py
|
||||
|
||||
退出码:0=PASS / 1=测试失败 / 2=环境错误。
|
||||
设计依据:docs/任务规划/数据可信.md 卡 7(M4 退出判据⑥);
|
||||
创建日期:2026-10-25
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
|
||||
sys.path.insert(0, os.path.join(ROOT, "test", "host", "common"))
|
||||
|
||||
from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
|
||||
|
||||
# 顺序敏感:stub 先(dbg_log.h 覆盖);App/algo 供被测链与 adc_input.h;
|
||||
# App/cfg 供 cfg_file.h(adc_cal.c 的接口头,注册函数打桩)。
|
||||
BUILD = TestBuild(
|
||||
name="Prot Dim Regression Tests",
|
||||
src=os.path.join(HERE, "prot_dim_test.c"),
|
||||
include_dirs=[
|
||||
os.path.join(HERE, "stub"),
|
||||
os.path.join(ROOT, "App", "algo"),
|
||||
os.path.join(ROOT, "App", "cfg"),
|
||||
HERE,
|
||||
],
|
||||
summary_marker="=== Prot Dim Regression Tests:",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run_host_unit_test(BUILD))
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 主机侧单测桩:dbg_log.h(prot_dim 量纲回归套件)
|
||||
* 模块功能:为 adc_cal.c 提供主机可编译的日志宏(映射为计数)。
|
||||
* ⚠ 仅供 test/host/prot_dim/ 使用,不参与 MCU 构建。同 adc_cal 套件先例。
|
||||
* 创建日期:2026-10-25
|
||||
*/
|
||||
|
||||
#ifndef DBG_LOG_H
|
||||
#define DBG_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern int g_dimtest_err_count;
|
||||
extern int g_dimtest_info_count;
|
||||
extern int g_dimtest_debug_count;
|
||||
|
||||
#define DBG_INFO(fmt, ...) do { g_dimtest_info_count++; } while (0)
|
||||
#define DBG_DEBUG(fmt, ...) do { g_dimtest_debug_count++; } while (0)
|
||||
#define DBG_ERROR(fmt, ...) do { g_dimtest_err_count++; } while (0)
|
||||
|
||||
#endif /* DBG_LOG_H */
|
||||
Reference in New Issue
Block a user