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:
wangfq
2026-09-03 10:50:26 +08:00
parent 433b7f472d
commit 92f675e2d3
9 changed files with 465 additions and 228 deletions
+196 -196
View File
@@ -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;
}