/* * test_stable_settle.c — 上电自检基准判稳 gcc 隔离单测 (vd960Loop V1.03) * * 验证对象: TaskLoop.c vd1_task_per_channel() 稳定期判稳逻辑 * (对齐 DLD154V4B V4.23, 2026-09-02 实测通过后同步) * * 语义: * 绿灯上电自检快闪 → 仅当"基准值真正稳定"才停止: * - 每窗(100样本≈1s)完成时比较窗口平均 Origin 漂移, ≤ Origin×0.1% → settle++ * - 判稳 = 最少 128 样本 + 连续 2 窗漂移达标; 硬兜底 500 样本(≈5s) * - 判稳退出/init_vd_single/有限存在超时/安全复位(LC_Reset=1) 均清零计数 * → 二次稳定期从零开始 (修掉原"残留计数 1 样本瞬间判稳") * * 编译运行: * gcc tests/test_stable_settle.c -o /tmp/test_stable_settle && /tmp/test_stable_settle */ #include #include #include /* ---- 与 TaskLoop.h / TaskLoop.c 一致的宏与结构 (隔离副本) ---- */ #define STABLE_SAMPLES 128 // 稳定期最少样本数 (128×10ms≈1.3s) #define STABLE_ORIGIN_PPT 1 // 基准稳定判据: 窗口均值漂移 ≤ Origin×0.1% #define STABLE_SETTLE_WINDOWS 2 // 连续 2 窗(100样本/窗≈1s)漂移达标 → 判稳 #define STABLE_MAX_SAMPLES 500 // 硬兜底 ≈5s typedef struct { uint32_t loop_Origin; uint32_t loop_Value; uint32_t loop_CAPVD; uint32_t loop_ORG_SUM; uint16_t loop_ORG_CNT; uint16_t stable_cnt; uint16_t settle_cnt; uint8_t loop_stable; } Unit; /* ---- 与 TaskLoop.c update_moving_average 同逻辑副本 ---- */ static uint8_t update_moving_average(uint32_t *p_sum, uint16_t *p_cnt, uint32_t *p_origin, uint32_t new_value, uint16_t window) { *p_sum += new_value; (*p_cnt)++; if (*p_cnt >= window) { *p_origin = *p_sum / window; *p_cnt = 0; *p_sum = 0; return 1; } return 0; } /* init_vd_single 等价 (含 V1.03 清零) */ static void unit_init(Unit *u, uint32_t origin) { u->loop_Origin = origin; u->loop_Value = 0; u->loop_CAPVD = 0; u->loop_ORG_SUM = 0; u->loop_ORG_CNT = 0; u->stable_cnt = 0; u->settle_cnt = 0; u->loop_stable = 0; } /* vd1_task_per_channel() 稳定期分支的精确副本 (V1.03) * 含函数入口守卫: Origin==0 直接 return (首个测量窗确立 Origin 前不推进) */ static void stable_step(Unit *u, uint32_t value) { uint32_t _prev_origin = u->loop_Origin; uint8_t _win_done; if (u->loop_Origin == 0) return; u->loop_CAPVD = value; _win_done = update_moving_average(&u->loop_ORG_SUM, &u->loop_ORG_CNT, &u->loop_Origin, u->loop_CAPVD, 100); u->stable_cnt++; if (_win_done) { uint32_t _drift = (u->loop_Origin > _prev_origin) ? (u->loop_Origin - _prev_origin) : (_prev_origin - u->loop_Origin); uint32_t _band = (uint32_t)(u->loop_Origin * STABLE_ORIGIN_PPT / 1000); if (_drift <= _band) { if (u->settle_cnt < 0xFFFF) u->settle_cnt++; } else { u->settle_cnt = 0; } } if ((u->stable_cnt >= STABLE_SAMPLES && u->settle_cnt >= STABLE_SETTLE_WINDOWS) || u->stable_cnt >= STABLE_MAX_SAMPLES) { u->loop_stable = 1; u->stable_cnt = 0; u->settle_cnt = 0; } } /* 打样本直至判稳, 返回判稳时的累计样本数; 上限 5000 防死循环 */ static int run_until_stable(Unit *u, uint32_t (*gen)(Unit *u, int i)) { int i; for (i = 1; i <= 5000; i++) { stable_step(u, gen(u, i)); if (u->loop_stable) return i; } return -1; } static uint32_t gen_const(Unit *u, int i) { (void)u; (void)i; return 100000UL; } static uint32_t gen_spike(Unit *u, int i) { (void)u; return (i == 50) ? 200000UL : 100000UL; } static uint32_t gen_drift(Unit *u, int i) { (void)u; return 100000UL + 2UL * (uint32_t)i; } static int expect_stable(int n, int exp, const char *tag) { printf("%-24s 判稳样本=%d (期望 %d)%s\n", tag, n, exp, (n == exp) ? "" : " <-- MISMATCH"); return n == exp; } int main(void) { Unit u; int n, fail = 0; /* 1. 常量基准: 窗1@100 settle=1, 窗2@200 settle=2 → 200 判稳 */ unit_init(&u, 100000UL); n = run_until_stable(&u, gen_const); fail += !expect_stable(n, 200, "1. 常量值"); /* 2. 首窗尖峰(第50样本 200000): 窗1均值101000 漂移>0.1% 清零; * 窗2均值100000 与101000差1000 仍>band → 清零; 窗3/窗4 漂移0 * settle=1@300, settle=2@400 → 400 判稳 (扰动后需2个干净窗口) */ unit_init(&u, 100000UL); n = run_until_stable(&u, gen_spike); fail += !expect_stable(n, 400, "2. 首窗尖峰"); /* 3. 持续漂移(每样本+2): 每窗漂移200 > band(≈100) → 永远不达标, * 500 样本硬兜底强制判稳 */ unit_init(&u, 100000UL); n = run_until_stable(&u, gen_drift); fail += !expect_stable(n, 500, "3. 持续漂移(硬兜底)"); /* 4. 二次稳定期防呆: 判稳后模拟 有限存在/安全复位 重学路径 * (loop_stable=0 + 计数清零, 即 C2/C3 新增的两处清零), * 常量基准须重新满 200 样本判稳, 绝非 1 样本瞬间判稳 */ unit_init(&u, 100000UL); n = run_until_stable(&u, gen_const); if (n == 200) { u.loop_stable = 0; /* 重学复位 (同 LC_Reset=1 路径) */ u.stable_cnt = 0; u.settle_cnt = 0; u.loop_ORG_CNT = 0; u.loop_ORG_SUM = 0; n = run_until_stable(&u, gen_const); fail += !expect_stable(n, 200, "4. 二次稳定期(重学复位)"); } else { printf("4. 二次稳定期 前置失败 (首轮 n=%d)\n", n); fail++; } /* 5. Origin==0 边界 (vd1 入口 return, 稳定期不应推进) */ unit_init(&u, 0UL); stable_step(&u, 100000UL); if (u.stable_cnt != 0 || u.settle_cnt != 0) { printf("5. Origin=0 守卫 FAIL (stable_cnt=%u settle_cnt=%u)\n", u.stable_cnt, u.settle_cnt); fail++; } else { printf("%-24s OK (不推进自检计数)\n", "5. Origin=0 守卫"); } if (fail == 0) { printf("ALL PASS (test_stable_settle)\n"); return 0; } printf("FAILED (%d)\n", fail); return 1; }