/* * test_env_resync.c — 环境重校准(受控基线更新) gcc 隔离单测 (DLD154V4B V4.24) * * 验证对象: TaskLoop.c vd1_task() 无车段 dev>0 分支 (V4.24 两段式) * ─ 现场场景 (2026-09-02 实测): 线圈旁金属板A上电 → Origin 学低(128096) * A 拿走 → CAPVD 弹回真空场(128856) dev=+761 → 双向保护(36321a1)永不更新 * → B 触发需 809cnt (正常 54cnt) 灵敏度假性降低 * ─ 修复: dev>+4dlt 高位连续 60s(ENV_RESYNC_WAIT) 未回摆 → 判定永久环境变化 * → 学习基线 B (连续空闲窗均值 + settle 判稳) → 空闲原子替换 Origin * * 运行: gcc -Wall tests/test_env_resync.c && ./a.out * 期望: 4 场景 ALL PASS (瞬态 60s 内短停不学; 永久变化 ~66s 恢复) */ #include #include #include /*========== 与固件一致的参数宏 (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_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s #define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃 /*========== 与固件同名的 env 全局镜像 (TaskLoop.c :80-90) ==========*/ static uint8_t g_env_resync = 0; static uint16_t g_env_wait_cnt = 0; static uint32_t g_env_sum = 0; 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; /* 场景镜像 (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; 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; } /* 与 TaskLoop.c env_resync_abort/apply 逐行一致 */ static void env_resync_abort(void) { g_env_resync = 0; g_env_sum = 0; g_env_cnt = 0; g_env_prev = 0; g_env_settle = 0; g_env_tick = 0; } 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) ==========*/ static void sim_tick(void) { int32_t dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin; int32_t freeze_band = sim_dlt * 4; if (dev < freeze_band && dev > -freeze_band) { /* 对称窗口内 → 正常基线跟踪 (env 相关) */ g_env_wait_cnt = 0; if (g_env_resync) ev_abort++; env_resync_abort(); } else if (dev < 0) { /* 车方向 → 冻结 (env 相关) */ g_env_wait_cnt = 0; if (g_env_resync) ev_abort++; env_resync_abort(); } else { /* dev>0 高位 — V4.24 两段式 */ if (!g_env_resync) { g_env_wait_cnt++; if (g_env_wait_cnt >= ENV_RESYNC_WAIT) { 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++; } } else { 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; 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) { g_env_settle++; } else { g_env_settle = 0; } if (g_env_settle >= STABLE_SETTLE_WINDOWS) { sim_Origin = _m; /* env_resync_apply 替换 */ env_resync_apply(_m); return; /* 本 tick 不再走进入判定 */ } } g_env_prev = _m; } if (g_env_tick >= ENV_LEARN_MAX_TICKS) { ev_abort++; env_resync_abort(); } } } } /*========== 测试工具 ==========*/ 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; } /*========== 场景 ==========*/ static void test_permanent_change_recovers(void) { /* ① 常量高位稳定 (A 拿走): Origin=128095, 空场 CAPVD=128856, dev=+761>192 */ t_set(128095UL, 128856UL, 48); int tick = 0; for (; tick < 7000 && !ev_applied; tick++) sim_tick(); printf("常量高位稳定: 学习启动=%d 替换=%d 替换tick=%d resync=%d\n", ev_learn_start, ev_applied, tick, g_env_resync); assert(ev_learn_start == 1); assert(ev_applied == 1); assert(tick == 6600); /* 6000 等待 + 3×200 窗 */ assert(sim_Origin == 128856UL); assert(g_env_resync == 0); /* 替换后复位, 黄灯灭 */ /* 替换后 dev≈0 → 走窗口分支, 不再进 freeze/学习 */ for (int i = 0; i < 500; i++) sim_tick(); assert(ev_applied == 1); /* 不再重复替换 */ printf(" 场景1 PASS — 永久环境变化 ~66s 自动恢复, Origin 128095->128856\n\n"); } static void test_transient_never_learns(void) { /* ② 瞬态高位短停 (铁块模拟): 40s(4000tick) 高位后回摆 → 不学习 */ t_set(128095UL, 128856UL, 48); for (int i = 0; i < 4000; i++) sim_tick(); assert(ev_learn_start == 0); /* 回摆进窗口 (CAPVD 回 Origin 附近), wait 清零 */ sim_CAPVD = 128100UL; /* dev=+5 窗口内 */ sim_tick(); assert(g_env_wait_cnt == 0); /* 再次高位 59s(5900tick) — 仍不足 60s 连续 → 不学 */ sim_CAPVD = 128856UL; for (int i = 0; i < 5900; i++) sim_tick(); assert(ev_learn_start == 0); assert(ev_applied == 0); assert(sim_Origin == 128095UL); printf("瞬态高位短停: 不学习不替换, Origin 保持 (等待计数=%d)\n", g_env_wait_cnt); printf(" 场景2 PASS — 铁块类瞬态(60s内回摆)不触发重校准\n\n"); } static void test_learn_interrupted_restarts(void) { /* ③ 学习期打断重算: 60s 后进入学习, 途中目标靠近(回窗) → abort → 重来并最终恢复 */ t_set(128095UL, 128856UL, 48); /* 第一次 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); /* 目标离开, 高位重新 60s → 学习 3 窗 → 替换 */ sim_CAPVD = 128856UL; for (int i = 0; i < 7000 && !ev_applied; 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); 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(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; sim_tick(); } printf("学习期不稳: 预算放弃 tick=%d applied=%d resync=%d Origin=%lu\n", tick, ev_applied, g_env_resync, (unsigned long)sim_Origin); assert(ev_applied == 0); assert(g_env_resync == 0); /* 超时放弃, 黄灯灭 */ assert(ev_abort >= 1); assert(sim_Origin == 128095UL); /* Origin 不被污染 */ printf(" 场景4 PASS — 学习不稳定 30s 放弃, 宁可灵敏度低不冒险污染\n\n"); } static void test_no_learning_after_replace(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 before = ev_learn_start; for (int i = 0; i < 6000; i++) sim_tick(); /* 新 Origin 下高位条件消失 */ assert(ev_learn_start == before); /* 无新学习 */ printf("替换后稳态: 高位条件消失, 无重复学习 (Origin=%lu)\n", (unsigned long)sim_Origin); printf(" 场景5 PASS — 恢复后进入正常跟踪, 不震荡\n\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"); return 0; }