feat(V4B): V4.26 — 线圈电感量告警(一长一短) + 故障补偿 60s→10s (4点拍板)
1. 默认低频档频率合理窗 20~120kHz, 出界即告警(不做档位判定/灰带):
TaskLoop.c 新增 coil_monitor() — 稳定+无车空闲由无车主路径调用,
Origin 反推空场 f, f<20kHz→L偏大/f>120kHz→L偏小, 1s 防抖置位/清除
2. 告警形式: 黄灯"一长一短" 200ms亮/100ms灭/100ms亮/800ms灭 循环 (模式5)
3. 黄灯优先级: 断开快闪 > 故障补偿(学习)常亮 > 电感量一长一短 > N短闪 > 灭
4. 故障补偿等待 ENV_RESYNC_WAIT 6000→1000 (60s→10s): 总补偿 ~66s→~21s
验证: tests/test_coil_range.c 5场景(边界19/20/120/121+防抖) ALL PASS;
test_env_resync.c 适配10s(applied_at=1600tick) 5场景 ALL PASS; 语法 0 error
文档: devlog V2.24/spec V2.5/manual V2.9/design doc v0.3(标记已实现)
修复: 源文件行尾多CR污染(\\r\\r\\r\\r\\n)统一规范化回标准CRLF
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* test_coil_range.c — 线圈电感量合理范围监视 gcc 隔离单测 (DLD154V4B V4.26)
|
||||
*
|
||||
* 验证对象: TaskLoop.c coil_monitor() (V4.26 拍板规格)
|
||||
* 默认低频档口径: 空场频率合理窗 20~120kHz, 出界即判定电感量不合理
|
||||
* g_coil_fault: 0=正常 1=L偏大(f<20kHz) 2=L偏小(f>120kHz)
|
||||
* 防抖: 越界/恢复均连续 100tick(=1s) 才置位/清除 (调用点=稳定无车空闲)
|
||||
*
|
||||
* 镜像宏与固件一致 (改 COIL_* 时同步此处!)
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define COIL_F_OK_MIN_KHZ 20
|
||||
#define COIL_F_OK_MAX_KHZ 120
|
||||
#define COIL_DEF_C_NF 66
|
||||
|
||||
static uint8_t g_coil_fault = 0;
|
||||
static uint16_t g_coil_bad_cnt = 0;
|
||||
|
||||
/* coil_monitor() 纯逻辑镜像 (不含 Origin==0 守卫, 由调用点保证) */
|
||||
static void coil_step(uint32_t f_khz)
|
||||
{
|
||||
uint8_t want = 0;
|
||||
|
||||
if (f_khz < COIL_F_OK_MIN_KHZ)
|
||||
want = 1;
|
||||
else if (f_khz > COIL_F_OK_MAX_KHZ)
|
||||
want = 2;
|
||||
|
||||
if (want == g_coil_fault) {
|
||||
g_coil_bad_cnt = 0;
|
||||
} else {
|
||||
g_coil_bad_cnt++;
|
||||
if (g_coil_bad_cnt >= 100) {
|
||||
g_coil_fault = want;
|
||||
g_coil_bad_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void run_ticks(uint32_t f_khz, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
coil_step(f_khz);
|
||||
}
|
||||
|
||||
static void test_too_large(void)
|
||||
{
|
||||
g_coil_fault = 0; g_coil_bad_cnt = 0;
|
||||
|
||||
/* f=16.3kHz(实测超限线圈) 持续 <1s 不置位 */
|
||||
run_ticks(16, 99);
|
||||
assert(g_coil_fault == 0);
|
||||
/* 满 1s → L 偏大 */
|
||||
coil_step(16);
|
||||
assert(g_coil_fault == 1);
|
||||
/* 保持 → 状态稳定不抖 */
|
||||
run_ticks(16, 1000);
|
||||
assert(g_coil_fault == 1);
|
||||
printf("1. L偏大 (f=16kHz, 1s 防抖置位, 保持稳定) OK\n");
|
||||
}
|
||||
|
||||
static void test_too_small(void)
|
||||
{
|
||||
g_coil_fault = 0; g_coil_bad_cnt = 0;
|
||||
run_ticks(150, 100);
|
||||
assert(g_coil_fault == 2);
|
||||
printf("2. L偏小 (f=150kHz, 1s 防抖置位) OK\n");
|
||||
}
|
||||
|
||||
static void test_recover(void)
|
||||
{
|
||||
g_coil_fault = 1; g_coil_bad_cnt = 0;
|
||||
run_ticks(62, 99);
|
||||
assert(g_coil_fault == 1); /* 恢复防抖 <1s 不清除 */
|
||||
coil_step(62);
|
||||
assert(g_coil_fault == 0); /* 满 1s 恢复正常 */
|
||||
printf("3. 恢复正常 (1s 防抖清除) OK\n");
|
||||
}
|
||||
|
||||
static void test_boundaries(void)
|
||||
{
|
||||
g_coil_fault = 0; g_coil_bad_cnt = 0;
|
||||
run_ticks(19, 100); assert(g_coil_fault == 1); /* 19 → 偏大 */
|
||||
run_ticks(62, 100); assert(g_coil_fault == 0); /* 20~120 正常 */
|
||||
run_ticks(20, 100); assert(g_coil_fault == 0); /* 边界 20 正常 */
|
||||
run_ticks(120, 100); assert(g_coil_fault == 0); /* 边界 120 正常 */
|
||||
run_ticks(121, 100); assert(g_coil_fault == 2); /* 121 → 偏小 */
|
||||
printf("4. 边界 19/20/120/121 OK\n");
|
||||
}
|
||||
|
||||
static void test_jitter_no_alarm(void)
|
||||
{
|
||||
/* 抖动: 16 与 62 交替 (每 50tick) → 计数永远到不了 100 → 不误报 */
|
||||
int i;
|
||||
g_coil_fault = 0; g_coil_bad_cnt = 0;
|
||||
for (i = 0; i < 2000; i++)
|
||||
coil_step((i & 1) ? 62 : 16);
|
||||
assert(g_coil_fault == 0);
|
||||
printf("5. 频率抖动不误报 OK\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_too_large();
|
||||
test_too_small();
|
||||
test_recover();
|
||||
test_boundaries();
|
||||
test_jitter_no_alarm();
|
||||
printf("\nALL PASS (窗口 20~120kHz, 防抖 1s)\n");
|
||||
return 0;
|
||||
}
|
||||
+196
-196
@@ -1,32 +1,43 @@
|
||||
/*
|
||||
* test_env_resync.c — 环境重校准(受控基线更新) gcc 隔离单测 (DLD154V4B V4.24)
|
||||
* test_env_resync.c — 环境重校准(受控基线更新) gcc 隔离单测 (DLD154V4B V4.26)
|
||||
*
|
||||
* 验证对象: TaskLoop.c vd1_task() 无车段 dev>0 分支 (V4.24 两段式)
|
||||
* 验证对象: TaskLoop.c vd1_task() 无车段 dev>0 分支 (V4.24/25 两段式, V4.26 参数更新)
|
||||
* ─ 现场场景 (2026-09-02 实测): 线圈旁金属板A上电 → Origin 学低(128096)
|
||||
* A 拿走 → CAPVD 弹回真空场(128856) dev=+761 → 双向保护(36321a1)永不更新
|
||||
* → B 触发需 809cnt (正常 54cnt) 灵敏度假性降低
|
||||
* ─ 修复: dev>+4dlt 高位连续 60s(ENV_RESYNC_WAIT) 未回摆 → 判定永久环境变化
|
||||
* → 学习基线 B (连续空闲窗均值 + settle 判稳) → 空闲原子替换 Origin
|
||||
* A 拿走 → CAPVD 弹回 128856 dev=+761(0.59%) > +4dlt → 冻结累计
|
||||
* ─ V4.26 拍板: ENV_RESYNC_WAIT 60s→10s(故障补偿提速), 其余语义不变
|
||||
*
|
||||
* 运行: gcc -Wall tests/test_env_resync.c && ./a.out
|
||||
* 期望: 4 场景 ALL PASS (瞬态 60s 内短停不学; 永久变化 ~66s 恢复)
|
||||
* 黄灯语义 (poll_yellow_led): 5s 预判锁存(g_env_warn, 回摆保持) →
|
||||
* 10s 进入学习(g_env_resync, 常亮) → 空闲原子替换 Origin → 空闲 5s 恢复确认 → 灭
|
||||
*
|
||||
* 镜像宏与固件一致(改 ENV_RESYNC_WAIT 时同步此处!)
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*========== 与固件一致的参数宏 (TaskLoop.c vd1_task 内) ==========*/
|
||||
#define STABLE_ORIGIN_PPT 1 // 判稳带: 窗口均值漂移 ≤ Origin×1/1000 (0.1%)
|
||||
#define STABLE_SETTLE_WINDOWS 2 // 连续 2 窗漂移达标
|
||||
#define FREEZE_TIMEOUT 1000 // 打印饱和(dev>0 不再超时更新 Origin)
|
||||
#define ENV_RESYNC_WAIT 6000 // V4.24: dev>+4dlt 高位连续 60s → 判定永久环境变化
|
||||
#define ENV_WARN_WAIT 500 // V4.25: 高位连续 ~5s → 黄灯预判锁存 (TaskLoop.h)
|
||||
#define ENV_RECOVER_WAIT 500 // V4.25: 环境回正常空闲连续 ~5s → 解除预判 (TaskLoop.h)
|
||||
#define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s
|
||||
#define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃
|
||||
#define ENV_WARN_WAIT 500
|
||||
#define ENV_RECOVER_WAIT 500
|
||||
#define ENV_RESYNC_WAIT 1000 /* V4.26: 60s->10s (拍板4) */
|
||||
#define ENV_LEARN_WINDOW 200
|
||||
#define ENV_LEARN_MAX_TICKS 3000
|
||||
#define STABLE_ORIGIN_PPT 1
|
||||
#define STABLE_SETTLE_WINDOWS 2
|
||||
|
||||
/*========== 与固件同名的 env 全局镜像 (TaskLoop.c :80-90) ==========*/
|
||||
#define FREEZE_BAND_MULT 4 /* freeze_band = dlt*4 */
|
||||
|
||||
/* --- 镜像状态 --- */
|
||||
static uint32_t sim_Origin = 128095UL;
|
||||
static uint32_t sim_CAPVD = 128856UL;
|
||||
static uint32_t sim_dlt = 48UL;
|
||||
static uint8_t sim_learn_start_cnt = 0;
|
||||
static uint8_t sim_applied = 0;
|
||||
static uint32_t sim_applied_origin = 0;
|
||||
static uint32_t sim_applied_tick = 0;
|
||||
static uint8_t sim_abort = 0;
|
||||
static uint32_t sim_tick_cnt = 0;
|
||||
|
||||
/* --- g_env_* 镜像 (与固件同名) --- */
|
||||
static uint8_t g_env_resync = 0;
|
||||
static uint16_t g_env_wait_cnt = 0;
|
||||
static uint32_t g_env_sum = 0;
|
||||
@@ -34,19 +45,9 @@ static uint16_t g_env_cnt = 0;
|
||||
static uint32_t g_env_prev = 0;
|
||||
static uint8_t g_env_settle = 0;
|
||||
static uint16_t g_env_tick = 0;
|
||||
static uint8_t g_env_warn = 0;
|
||||
static uint8_t g_env_warn = 0;
|
||||
static uint16_t g_env_recover_cnt = 0;
|
||||
|
||||
/* 场景镜像 (loop1_* 对应量) */
|
||||
static uint32_t sim_Origin;
|
||||
static uint32_t sim_CAPVD;
|
||||
static int32_t sim_dlt; /* 进阈 dlt */
|
||||
|
||||
/* 事件计数 */
|
||||
static int ev_learn_start = 0;
|
||||
static int ev_applied = 0;
|
||||
static int ev_abort = 0;
|
||||
|
||||
static void env_reset(void)
|
||||
{
|
||||
g_env_resync = 0;
|
||||
@@ -56,48 +57,45 @@ static void env_reset(void)
|
||||
g_env_prev = 0;
|
||||
g_env_settle = 0;
|
||||
g_env_tick = 0;
|
||||
g_env_warn = 0;
|
||||
g_env_warn = 0;
|
||||
g_env_recover_cnt = 0;
|
||||
sim_learn_start_cnt = 0;
|
||||
sim_applied = 0;
|
||||
sim_abort = 0;
|
||||
sim_tick_cnt = 0;
|
||||
}
|
||||
|
||||
/* V4.25: 黄灯模式4 条件镜像 (TaskLoop.c poll_yellow_led) — 预判锁存 */
|
||||
static int env_warn_active(void)
|
||||
{
|
||||
return g_env_resync || g_env_warn;
|
||||
return g_env_resync || g_env_warn; /* poll_yellow_led 模式4 条件镜像 */
|
||||
}
|
||||
|
||||
/* 与 TaskLoop.c env_resync_abort/apply 逐行一致 */
|
||||
static void env_resync_abort(void)
|
||||
{
|
||||
/* V4.25: abort 只退出学习, 不清预判锁存 g_env_warn (环境未恢复前黄灯保持) */
|
||||
g_env_resync = 0;
|
||||
g_env_sum = 0;
|
||||
g_env_cnt = 0;
|
||||
g_env_prev = 0;
|
||||
g_env_settle = 0;
|
||||
g_env_tick = 0;
|
||||
sim_abort++;
|
||||
}
|
||||
|
||||
static void env_resync_apply(uint32_t new_origin)
|
||||
{
|
||||
(void)new_origin;
|
||||
/* 固件此处打印 + 替换 Origin + 清 freeze/ORG/entry + slow snap */
|
||||
ev_applied++;
|
||||
env_resync_abort();
|
||||
}
|
||||
|
||||
/*========== 模拟 vd1_task 无车段基线块 env 逻辑 (逐行复刻 V4.24) ==========*/
|
||||
/* vd1_task 无车段 dev 三分支的 env 相关逻辑镜像 (V4.26) */
|
||||
static void sim_tick(void)
|
||||
{
|
||||
int32_t dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin;
|
||||
int32_t freeze_band = sim_dlt * 4;
|
||||
int32_t dev;
|
||||
int32_t freeze_band;
|
||||
|
||||
sim_tick_cnt++;
|
||||
dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin;
|
||||
freeze_band = (int32_t)(sim_dlt * FREEZE_BAND_MULT);
|
||||
|
||||
if (dev < freeze_band && dev > -freeze_band) {
|
||||
/* 对称窗口内 → 正常基线跟踪 (env 相关) */
|
||||
g_env_wait_cnt = 0;
|
||||
if (g_env_resync) ev_abort++;
|
||||
env_resync_abort();
|
||||
/* V4.25: 恢复确认 — 空闲(dev>-dlt)连续 5s → 解除预判锁存 */
|
||||
if (g_env_resync) env_resync_abort();
|
||||
/* V4.25: 恢复确认 — 真空闲(未进进入线)连续 5s 解除预判锁存 */
|
||||
if (dev > -(int32_t)sim_dlt) {
|
||||
if (g_env_warn) {
|
||||
g_env_recover_cnt++;
|
||||
@@ -107,229 +105,231 @@ static void sim_tick(void)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g_env_recover_cnt = 0; /* 目标进入确认区 → 恢复计时重来 */
|
||||
g_env_recover_cnt = 0; /* 目标在进入确认区 → 恢复计时重来 */
|
||||
}
|
||||
} else if (dev < 0) {
|
||||
/* 车方向 → 冻结 (env 相关) */
|
||||
g_env_wait_cnt = 0;
|
||||
if (g_env_resync) ev_abort++;
|
||||
env_resync_abort();
|
||||
g_env_recover_cnt = 0; /* 目标深压 → 恢复计时清零 */
|
||||
g_env_recover_cnt = 0;
|
||||
if (g_env_resync) env_resync_abort();
|
||||
} else {
|
||||
/* dev>0 高位 — V4.24 两段式 / V4.25 预判锁存 */
|
||||
g_env_recover_cnt = 0; /* 高位异常中 → 恢复确认失效 */
|
||||
/* dev>0 高位 — 两段式 (V4.24) + 预判锁存 (V4.25) */
|
||||
g_env_recover_cnt = 0;
|
||||
if (!g_env_resync) {
|
||||
g_env_wait_cnt++;
|
||||
if (g_env_wait_cnt >= ENV_WARN_WAIT)
|
||||
g_env_warn = 1; /* 预判锁存 (只增不清) */
|
||||
g_env_warn = 1; /* 5s 预判锁存 */
|
||||
if (g_env_wait_cnt >= ENV_RESYNC_WAIT) {
|
||||
sim_learn_start_cnt++;
|
||||
g_env_resync = 1;
|
||||
g_env_wait_cnt = 0;
|
||||
g_env_sum = 0;
|
||||
g_env_cnt = 0;
|
||||
g_env_prev = 0;
|
||||
g_env_settle = 0;
|
||||
g_env_tick = 0;
|
||||
ev_learn_start++;
|
||||
g_env_sum = 0; g_env_cnt = 0; g_env_prev = 0;
|
||||
g_env_settle = 0; g_env_tick = 0;
|
||||
}
|
||||
} else {
|
||||
/* 学习中: 窗口均值 + settle 判稳 */
|
||||
g_env_tick++;
|
||||
g_env_sum += sim_CAPVD;
|
||||
g_env_cnt++;
|
||||
if (g_env_cnt >= ENV_LEARN_WINDOW) {
|
||||
uint32_t _m = g_env_sum / g_env_cnt;
|
||||
uint32_t _drift, _band;
|
||||
uint32_t _m = g_env_sum / g_env_cnt;
|
||||
g_env_sum = 0;
|
||||
g_env_cnt = 0;
|
||||
if (g_env_prev != 0) {
|
||||
_drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m);
|
||||
_band = _m * STABLE_ORIGIN_PPT / 1000;
|
||||
if (_drift <= _band) {
|
||||
uint32_t _drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m);
|
||||
uint32_t _band = _m * STABLE_ORIGIN_PPT / 1000;
|
||||
if (_drift <= _band)
|
||||
g_env_settle++;
|
||||
} else {
|
||||
else
|
||||
g_env_settle = 0;
|
||||
}
|
||||
if (g_env_settle >= STABLE_SETTLE_WINDOWS) {
|
||||
sim_Origin = _m; /* env_resync_apply 替换 */
|
||||
env_resync_apply(_m);
|
||||
return; /* 本 tick 不再走进入判定 */
|
||||
sim_applied = 1;
|
||||
sim_applied_origin = _m;
|
||||
sim_applied_tick = sim_tick_cnt;
|
||||
sim_Origin = _m; /* 原子替换 (空闲由上下文保证) */
|
||||
g_env_resync = 0;
|
||||
g_env_wait_cnt = 0;
|
||||
g_env_sum = 0; g_env_cnt = 0;
|
||||
g_env_prev = 0; g_env_settle = 0; g_env_tick = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_env_prev = _m;
|
||||
}
|
||||
if (g_env_tick >= ENV_LEARN_MAX_TICKS) {
|
||||
ev_abort++;
|
||||
env_resync_abort();
|
||||
env_resync_abort(); /* 预算超时放弃 (保留旧 Origin) */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*========== 测试工具 ==========*/
|
||||
static void t_set(uint32_t origin, uint32_t capvd, int32_t dlt)
|
||||
{
|
||||
sim_Origin = origin;
|
||||
sim_CAPVD = capvd;
|
||||
sim_dlt = dlt;
|
||||
env_reset();
|
||||
ev_learn_start = 0;
|
||||
ev_applied = 0;
|
||||
ev_abort = 0;
|
||||
}
|
||||
|
||||
/*========== 场景 ==========*/
|
||||
/* ============ 场景 1: 永久变化 (A拿走) ~21s 恢复 ============ */
|
||||
static void test_permanent_change_recovers(void)
|
||||
{
|
||||
/* ① 常量高位稳定 (A 拿走): Origin=128095, 空场 CAPVD=128856, dev=+761>192 */
|
||||
t_set(128095UL, 128856UL, 48);
|
||||
int tick, warn_at = 0;
|
||||
|
||||
int tick = 0;
|
||||
int warn_at = 0;
|
||||
for (; tick < 7000 && !ev_applied; tick++) {
|
||||
env_reset();
|
||||
sim_Origin = 128095UL; sim_CAPVD = 128856UL;
|
||||
|
||||
for (tick = 0; tick < 4000 && !sim_applied; tick++) {
|
||||
sim_tick();
|
||||
if (!warn_at && env_warn_active())
|
||||
warn_at = tick + 1; /* 首次黄灯预判条件成立 (wait>=500) */
|
||||
warn_at = (int)sim_tick_cnt;
|
||||
}
|
||||
printf("1. 永久变化: warn_at=%d learn=%d applied_at=%d Origin=%u (期望 warn 500, learn>=1000, applied~1600)\n",
|
||||
warn_at, sim_learn_start_cnt, (int)sim_applied_tick, sim_Origin);
|
||||
|
||||
printf("常量高位稳定: 黄灯预判=%d 学习启动=%d 替换=%d 替换tick=%d resync=%d\n",
|
||||
warn_at, ev_learn_start, ev_applied, tick, g_env_resync);
|
||||
assert(warn_at == 500); /* V4.25: 高位 5s 即黄灯常亮预判 */
|
||||
assert(ev_learn_start == 1);
|
||||
assert(ev_applied == 1);
|
||||
assert(tick == 6600); /* 6000 等待 + 3×200 窗 */
|
||||
assert(warn_at == 500); /* 5s 预判锁存 */
|
||||
assert(sim_learn_start_cnt == 1);
|
||||
assert(sim_applied == 1);
|
||||
assert(sim_applied_origin == 128856UL); /* 替换到真空场 */
|
||||
assert(sim_Origin == 128856UL);
|
||||
assert(g_env_resync == 0); /* 学习结束 */
|
||||
assert(env_warn_active() == 1);/* V4.25: 替换后黄灯保持 (恢复确认前不灭) */
|
||||
assert(env_warn_active() == 1); /* 替换后黄灯保持 (等待恢复确认) */
|
||||
|
||||
/* 替换后 dev≈0 → 窗口分支; 空闲连续 5s 恢复确认 → 黄灯灭 */
|
||||
for (int i = 0; i < 500; i++)
|
||||
/* 空闲 5s 恢复确认 → 灭 */
|
||||
for (tick = 0; tick < 600; tick++)
|
||||
sim_tick();
|
||||
assert(ev_applied == 1); /* 不再重复替换 */
|
||||
assert(env_warn_active() == 0);/* 恢复确认 5s 后黄灯灭 */
|
||||
printf(" 场景1 PASS — 永久环境变化 5s 预判锁存, ~66s 自动恢复, 恢复确认后灭\n\n");
|
||||
assert(env_warn_active() == 0);
|
||||
printf(" 恢复确认后黄灯灭 OK\n");
|
||||
}
|
||||
|
||||
static void test_transient_never_learns(void)
|
||||
/* ============ 场景 2: 瞬态高位 (<10s 回摆) 不学, 锁存保持到恢复确认 ============ */
|
||||
static void test_transient_no_learn(void)
|
||||
{
|
||||
/* ② 瞬态高位短停 (铁块模拟): 40s(4000tick) 高位后回摆 → 不学习
|
||||
* 但 5s 后黄灯预判已亮 (wait>=500) — 预判亮 ≠ 进入学习 */
|
||||
t_set(128095UL, 128856UL, 48);
|
||||
for (int i = 0; i < 4000; i++)
|
||||
sim_tick();
|
||||
assert(ev_learn_start == 0);
|
||||
assert(env_warn_active() == 1); /* 黄灯预判已亮 (wait=4000) */
|
||||
printf("瞬态高位短停: 4000tick 时黄灯预判亮=%d (未学习)\n", env_warn_active());
|
||||
int i;
|
||||
|
||||
/* 回摆进窗口 (CAPVD 回 Origin 附近): wait 清零, 但预判锁存保持 (V4.25 核心语义) */
|
||||
sim_CAPVD = 128100UL; /* dev=+5 窗口内 */
|
||||
env_reset();
|
||||
sim_Origin = 128095UL; sim_CAPVD = 128856UL;
|
||||
|
||||
/* 高位 9s (900tick): 预判 5s 亮, 但 <10s 不学习 */
|
||||
for (i = 0; i < 900; i++)
|
||||
sim_tick();
|
||||
assert(env_warn_active() == 1);
|
||||
assert(sim_learn_start_cnt == 0);
|
||||
|
||||
/* 回摆进窗口: wait 清零但预判锁存保持 */
|
||||
sim_CAPVD = 128100UL; /* dev=+5 */
|
||||
sim_tick();
|
||||
assert(g_env_wait_cnt == 0);
|
||||
assert(env_warn_active() == 1); /* 回摆不清黄灯, 保持常亮 */
|
||||
assert(env_warn_active() == 1); /* 回摆不清黄灯 (拍板锁存语义) */
|
||||
|
||||
/* 窗口内连续 499 tick (累计 500 恢复确认) → 环境稳定, 解除预判 */
|
||||
for (int i = 0; i < 499; i++)
|
||||
/* 窗口空闲 499tick (累计500恢复确认) → 灭 */
|
||||
for (i = 0; i < 499; i++)
|
||||
sim_tick();
|
||||
assert(env_warn_active() == 0); /* 新环境稳定 5s, 黄灯灭 */
|
||||
assert(env_warn_active() == 0);
|
||||
assert(g_env_recover_cnt == 0);
|
||||
|
||||
/* 再次高位 59s(5900tick): 5s 后预判重新锁存, 但 <60s 不学习 */
|
||||
/* 再高位 9s → 又锁存, 仍不学习 */
|
||||
sim_CAPVD = 128856UL;
|
||||
for (int i = 0; i < 5900; i++)
|
||||
for (i = 0; i < 900; i++)
|
||||
sim_tick();
|
||||
assert(ev_learn_start == 0);
|
||||
assert(ev_applied == 0);
|
||||
assert(env_warn_active() == 1);
|
||||
assert(sim_learn_start_cnt == 0);
|
||||
assert(sim_applied == 0);
|
||||
assert(sim_Origin == 128095UL);
|
||||
assert(env_warn_active() == 1); /* 高位重现 → 预判重新锁存 */
|
||||
printf("瞬态高位短停: 回摆保持常亮→稳定5s灭→高位重现再锁存 (未学习)\n");
|
||||
printf(" 场景2 PASS — 铁块类瞬态(60s内回摆)不触发重校准, 预判锁存语义正确\n\n");
|
||||
printf("2. 瞬态高位(<10s)不学, 锁存保持, 恢复确认后灭 OK\n");
|
||||
}
|
||||
|
||||
static void test_learn_interrupted_restarts(void)
|
||||
/* ============ 场景 3: 学习期打断重算 + 最终恢复 ============ */
|
||||
static void test_learn_interrupt_retry(void)
|
||||
{
|
||||
/* ③ 学习期打断重算: 60s 后进入学习, 途中目标靠近(回窗) → abort → 重来并最终恢复 */
|
||||
t_set(128095UL, 128856UL, 48);
|
||||
int i;
|
||||
int learn_start_before;
|
||||
|
||||
/* 第一次 60s + 学习 100 tick 后, B 靠近把 CAPVD 压回窗口 */
|
||||
for (int i = 0; i < 6000; i++)
|
||||
sim_tick(); /* 进入学习 */
|
||||
assert(g_env_resync == 1);
|
||||
for (int i = 0; i < 100; i++)
|
||||
sim_tick(); /* 学习窗1 未完 */
|
||||
sim_CAPVD = 128000UL; /* dev=-95 窗口内(B 目标) */
|
||||
sim_tick();
|
||||
assert(g_env_resync == 0); /* 打断退出学习 */
|
||||
assert(ev_abort == 1);
|
||||
assert(env_warn_active() == 1); /* 预判锁存保持 (环境未确认恢复) */
|
||||
assert(g_env_recover_cnt == 0); /* 目标在进入确认区, 恢复计时清零 */
|
||||
env_reset();
|
||||
sim_Origin = 128095UL; sim_CAPVD = 128856UL;
|
||||
|
||||
/* 目标离开, 高位重新 60s → 学习 3 窗 → 替换 */
|
||||
sim_CAPVD = 128856UL;
|
||||
for (int i = 0; i < 7000 && !ev_applied; i++)
|
||||
/* 10s 高位 → 学习开始 */
|
||||
for (i = 0; i < 1000; i++)
|
||||
sim_tick();
|
||||
|
||||
printf("学习期打断: abort=%d 重新学习启动=%d 最终替换tick计数=%d Origin=%lu\n",
|
||||
ev_abort, ev_learn_start, 0, (unsigned long)sim_Origin);
|
||||
assert(ev_applied == 1);
|
||||
assert(ev_learn_start == 2);
|
||||
assert(sim_Origin == 128856UL);
|
||||
assert(env_warn_active() == 1); /* 替换后黄灯保持到恢复确认 */
|
||||
for (int i = 0; i < 500; i++)
|
||||
sim_tick(); /* 窗口恢复确认 5s */
|
||||
assert(env_warn_active() == 0); /* 黄灯灭 */
|
||||
printf(" 场景3 PASS — 学习期打断重算+锁存保持, 恢复确认后灭\n\n");
|
||||
}
|
||||
|
||||
static void test_unstable_learn_gives_up(void)
|
||||
{
|
||||
/* ④ 学习期每窗均值漂移 >0.1% (CAPVD 高位大幅交替 128856±200..400)
|
||||
* → settle 永远到不了 2 → 30s 预算超时放弃, 保留旧 Origin */
|
||||
t_set(128095UL, 128856UL, 48);
|
||||
for (int i = 0; i < 6000; i++)
|
||||
sim_tick(); /* 进入学习 */
|
||||
assert(sim_learn_start_cnt == 1);
|
||||
assert(g_env_resync == 1);
|
||||
|
||||
int tick = 0;
|
||||
for (; tick < 3500 && !ev_applied && g_env_resync; tick++) {
|
||||
/* 交替高值: 窗与窗之间均值漂移 ~200 > band(~129), 且 dev 恒 >192 */
|
||||
sim_CAPVD = ((tick / ENV_LEARN_WINDOW) & 1) ? 129256UL : 129056UL;
|
||||
/* 学习 100 tick 后目标靠近 (dev=-95 进入确认区) → 打断 */
|
||||
for (i = 0; i < 100; i++)
|
||||
sim_tick();
|
||||
sim_CAPVD = 128000UL; /* dev=-95 */
|
||||
sim_tick();
|
||||
assert(g_env_resync == 0); /* 打断退出学习 */
|
||||
assert(sim_abort >= 1);
|
||||
assert(env_warn_active() == 1); /* 预判锁存保持 (环境未确认恢复) */
|
||||
assert(g_env_recover_cnt == 0); /* 目标在进入确认区 → 恢复计时清零 */
|
||||
|
||||
/* 目标离开, 高位重现 → 重新等待 10s + 学习 3 窗 → 替换 */
|
||||
learn_start_before = sim_learn_start_cnt;
|
||||
sim_CAPVD = 128856UL;
|
||||
for (i = 0; i < 4000 && !sim_applied; i++)
|
||||
sim_tick();
|
||||
assert(sim_learn_start_cnt == learn_start_before + 1);
|
||||
assert(sim_applied == 1);
|
||||
assert(sim_Origin == 128856UL);
|
||||
assert(env_warn_active() == 1); /* 替换后保持到恢复确认 */
|
||||
|
||||
/* 空闲 5s → 黄灯灭 */
|
||||
for (i = 0; i < 600; i++)
|
||||
sim_tick();
|
||||
assert(env_warn_active() == 0);
|
||||
printf("3. 学习期打断重算, 最终恢复 OK\n");
|
||||
}
|
||||
|
||||
/* ============ 场景 4: 学习期持续不稳 → 30s 预算放弃 ============ */
|
||||
static void test_learn_unstable_abort(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
env_reset();
|
||||
sim_Origin = 128095UL;
|
||||
|
||||
/* 高位 10s → 学习开始; 然后窗口均值交替漂移 >0.1% (129056/129256) */
|
||||
for (i = 0; i < 1000; i++) {
|
||||
sim_CAPVD = 128856UL;
|
||||
sim_tick();
|
||||
}
|
||||
assert(g_env_resync == 1);
|
||||
|
||||
printf("学习期不稳: 预算放弃 tick=%d applied=%d resync=%d warn=%d Origin=%lu\n",
|
||||
tick, ev_applied, g_env_resync, g_env_warn, (unsigned long)sim_Origin);
|
||||
assert(ev_applied == 0);
|
||||
assert(g_env_resync == 0); /* 超时退出学习 */
|
||||
assert(env_warn_active() == 1); /* 环境仍异常 → 黄灯保持锁存 */
|
||||
assert(ev_abort >= 1);
|
||||
assert(sim_Origin == 128095UL); /* Origin 不被污染 */
|
||||
printf(" 场景4 PASS — 学习不稳定 30s 放弃保留旧 Origin, 黄灯保持异常指示\n\n");
|
||||
for (i = 0; i < 3500; i++) {
|
||||
/* 每窗 200 样本交替 129056 / 129256 → 窗间漂移 200 > band~129 → settle 恒 0 */
|
||||
sim_CAPVD = ((i / 200) & 1) ? 129256UL : 129056UL;
|
||||
sim_tick();
|
||||
}
|
||||
/* 1000(等待) + 3000(学习预算) ≈ 放弃于 tick 4000; 总 tick 4500 < 再学阈值(5000) */
|
||||
assert(sim_learn_start_cnt == 1); /* 只有一次学习启动 */
|
||||
assert(sim_applied == 0); /* 未替换 */
|
||||
assert(sim_Origin == 128095UL); /* 旧 Origin 保留 (不冒险污染) */
|
||||
assert(g_env_resync == 0); /* 预算 30s 放弃 */
|
||||
assert(env_warn_active() == 1); /* 环境仍异常 → 黄灯保持 */
|
||||
printf("4. 学习不稳 30s 放弃, 保留旧 Origin, 黄灯保持 OK\n");
|
||||
}
|
||||
|
||||
static void test_no_learning_after_replace(void)
|
||||
/* ============ 场景 5: 替换后不震荡, 无重复学习 ============ */
|
||||
static void test_no_oscillation_after_apply(void)
|
||||
{
|
||||
/* ⑤ 替换后 Origin≈CAPVD → dev≈0 正常窗口跟踪, 不再触发二次学习 */
|
||||
t_set(128095UL, 128856UL, 48);
|
||||
for (int i = 0; i < 7000 && !ev_applied; i++)
|
||||
sim_tick();
|
||||
assert(ev_applied == 1);
|
||||
int i, lsc;
|
||||
|
||||
int before = ev_learn_start;
|
||||
for (int i = 0; i < 6000; i++)
|
||||
sim_tick(); /* 新 Origin 下高位条件消失 */
|
||||
assert(ev_learn_start == before); /* 无新学习 */
|
||||
assert(env_warn_active() == 0); /* 恢复确认 5s 后黄灯已灭 */
|
||||
printf("替换后稳态: 高位条件消失, 无重复学习, 黄灯灭 (Origin=%lu)\n",
|
||||
(unsigned long)sim_Origin);
|
||||
printf(" 场景5 PASS — 恢复后进入正常跟踪, 不震荡\n\n");
|
||||
env_reset();
|
||||
sim_Origin = 128095UL; sim_CAPVD = 128856UL;
|
||||
|
||||
for (i = 0; i < 4000 && !sim_applied; i++)
|
||||
sim_tick();
|
||||
assert(sim_applied == 1);
|
||||
|
||||
lsc = sim_learn_start_cnt;
|
||||
for (i = 0; i < 6000; i++)
|
||||
sim_tick(); /* 窗口内持续 (dev≈0) */
|
||||
assert(sim_learn_start_cnt == lsc); /* 无重复学习 */
|
||||
assert(sim_applied == 1); /* 未被再次改写 */
|
||||
assert(sim_Origin == 128856UL);
|
||||
assert(env_warn_active() == 0); /* 恢复确认后黄灯已灭 */
|
||||
printf("5. 替换后不震荡无重复学习 OK\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_permanent_change_recovers();
|
||||
test_transient_never_learns();
|
||||
test_learn_interrupted_restarts();
|
||||
test_unstable_learn_gives_up();
|
||||
test_no_learning_after_replace();
|
||||
printf("ALL PASS (5 scenarios)\n");
|
||||
test_transient_no_learn();
|
||||
test_learn_interrupt_retry();
|
||||
test_learn_unstable_abort();
|
||||
test_no_oscillation_after_apply();
|
||||
printf("\nALL PASS (ENV_RESYNC_WAIT=%d)\n", ENV_RESYNC_WAIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user