/* * test_vac_ref_reanchor.c — V4.36.41 补丁草案单测 (gcc 隔离, DLD154V4B) * * 镜像 = TaskLoop.c V4.36.41 草案: vac_ref() + origin_set_atomic() + env_resync_apply() * 覆盖 5 件事: * ① 现象复现: env 重校准(黄灯常亮补偿)后判定参考未同步 ⇒ 进入线被拖低 Δ (修复前) * ② 补丁效果: origin_set_atomic 同步 ⇒ 进入线立即回到 Origin−dlt (与断电上电同口径) * ③ 回归: V4.36.33 的 1×dlt 门控语义不变; 小步跳变不吃掉 EMA 跟踪; 上电 EMA=0 语义 * ④ 新发现(2026-09-11): EMA 整数步进 d/128 有 ±127 counts 死区 ⇒ 真空 EMA 长期停在 * 真空下方 ~127 counts(≈2.6×dlt) ⇒ vac_ref 偏低 ⇒ 触发高度持续漂移(断电才复位) * ⑤ 修复 D(余数累加器) 后 EMA 无死区收敛到 0 counts 偏差 * * 编译: gcc -O0 -Wall -o /tmp/t_vac tests/test_vac_ref_reanchor.c && /tmp/t_vac * 镜像宏与固件一致(改 VAC_/ORIGIN_JUMP_/SENS 时同步此处!) */ #include #include #include #include /* ===== 镜像固件宏 (TaskLoop.h / TaskLoop.c) ===== */ static const uint16_t SensTable[4] = { 337, 71, 38, 25 }; /* SENS3 = 25 */ #define SENS_LEVEL 3 #define VAC_REF_DELTA_DLT 1 /* V4.36.33: vac_ref 重锚阈值 (×dlt) */ #define ORIGIN_JUMP_REANCH_DLT 1 /* V4.36.41 草案: 原子换 Origin 重锚阈值 (×dlt) */ #define VACUUM_EMA_ALFA 128 /* V4.36.31: EMA 平滑因子 (τ≈1.28s@10ms) */ #define REL_BAND_LO_MULT 180 /* 镜像 TaskLoop.h (V4.36.28 现场验证; G2 只夹紧不改它) */ /* ===== 镜像全局 ===== */ static uint32_t loop1_Origin = 0, loop1_CAPVD = 0, loop1_dlt_ORG = 0; static uint32_t g_vacuum_ema = 0; static int32_t g_vac_frac = 0; /* 修复 D: EMA 余数累加器 */ static uint32_t calc_dlt(uint32_t origin) { return (uint32_t)(((uint64_t)origin * SensTable[SENS_LEVEL]) >> 16); } /* ===== 镜像: vac_ref() (TaskLoop.c:287-299) ===== */ static uint32_t vac_ref(void) { if (g_vacuum_ema == 0) return loop1_Origin; if ((int32_t)loop1_Origin - (int32_t)g_vacuum_ema > (int32_t)(loop1_dlt_ORG * VAC_REF_DELTA_DLT)) return g_vacuum_ema; return loop1_Origin; } /* 进入线 (TaskLoop.c:1432 的判定阈值面) */ static uint32_t enter_line(void) { return vac_ref() - loop1_dlt_ORG; } /* ===== 镜像: origin_set_atomic() (V4.36.41 草案, B1 护栏) ===== */ static void origin_set_atomic(uint32_t new_origin) { int32_t _jump = (int32_t)new_origin - (int32_t)loop1_Origin; if (_jump < 0) _jump = -_jump; loop1_Origin = new_origin; if (g_vacuum_ema == 0 || loop1_dlt_ORG == 0 || _jump > (int32_t)(loop1_dlt_ORG * ORIGIN_JUMP_REANCH_DLT)) { g_vacuum_ema = new_origin; g_vac_frac = 0; } } /* ===== 镜像: env_resync_apply() 修复前 / 修复后 ===== */ static void env_resync_apply_old(uint32_t new_origin) /* V4.36.40: 只换 Origin */ { loop1_Origin = new_origin; } static void env_resync_apply_new(uint32_t new_origin) /* V4.36.41 草案 (A+B) */ { origin_set_atomic(new_origin); } /* ===== 镜像: EMA 步进 —— 现状公式 vs 修复 D ===== */ static void ema_step_cur(void) /* V4.36.35 现状: d/128 整数截断 (死区 ±127) */ { int32_t d = (int32_t)loop1_CAPVD - (int32_t)g_vacuum_ema; int32_t lim = (int32_t)(loop1_dlt_ORG * 3); if (d > lim) d = lim; else if (d < -lim) d = -lim; if (d != 0) g_vacuum_ema += (uint32_t)(d / VACUUM_EMA_ALFA); } static void ema_step_fix(void) /* 修复 D: 余数累加 (无死区) */ { int32_t d = (int32_t)loop1_CAPVD - (int32_t)g_vacuum_ema; int32_t lim = (int32_t)(loop1_dlt_ORG * 3); if (d > lim) d = lim; else if (d < -lim) d = -lim; if (d != 0) { g_vac_frac += d; int32_t st = g_vac_frac / VACUUM_EMA_ALFA; g_vac_frac -= st * VACUUM_EMA_ALFA; g_vacuum_ema += (uint32_t)st; } } /* ===== 夹具 ===== */ #define VAC_TRUE 130506UL /* 真实真空 CAPVD (devlog J1 实测) */ #define VAC_ANCHOR 130636UL /* env 学习后 Origin (锚高 130 counts = 2.65×dlt) */ static void reset_all(uint32_t origin) { loop1_Origin = origin; loop1_CAPVD = origin; loop1_dlt_ORG = calc_dlt(origin); g_vacuum_ema = origin; g_vac_frac = 0; } /* ================= 场景 6: vac_ref 硬开关抖动(缓慢接近/悬停)================= * V4.36.41 现场发现: 板缓慢接近时判据"乱跳"。机理 = EMA 跟随近场金属向下时, * vac_ref 的 "min + 1×dlt" 硬开关被反复扫过 ⇒ 进入线跳变(±40~150 counts) ⇒ 抖动。 * mode: 0=旧截断(死区) 1=D余数累加 2=E单向(只上不下); profile: 0=悬停 1=手抖 2=极慢接近 */ static int sim_switch_cnt(int mode, int profile) { uint32_t Origin = VAC_ANCHOR, EMA = VAC_ANCHOR, capvd; int32_t frac = 0, d; int i, cnt500 = 0, prev_ref = -1, sw = 0; for (i = 0; i < 3000; i++) { /* 30s @10ms */ int x; if (profile == 0) x = (i < 400) ? i * 3 / 10 : 120; /* 4s 接近到 x=120 后悬停 */ else if (profile == 1) x = ((i % 300) < 150) ? 20 + (i % 300) * 100 / 150 /* 手抖 20~120, 周期 3s */ : 120 - ((i % 300) - 150) * 100 / 150; else x = ((i < 1000) ? i * 200 / 1000 : 200) + ((i % 200 < 100) ? 8 : -8); capvd = VAC_ANCHOR - (uint32_t)x; d = (int32_t)capvd - (int32_t)EMA; if (d > 3 * (int32_t)loop1_dlt_ORG) d = 3 * (int32_t)loop1_dlt_ORG; else if (d < -3 * (int32_t)loop1_dlt_ORG) d = -3 * (int32_t)loop1_dlt_ORG; ++cnt500; if (cnt500 >= 500) { Origin = capvd; cnt500 = 0; } /* Origin 块平均 5s */ if (d != 0) { if (mode == 0) EMA += (uint32_t)(d / 128); else if (mode == 1) { frac += d; int32_t st = frac / 128; frac -= st * 128; EMA += (uint32_t)st; } else if (d > 0) { frac += d; /* E: 只上不下 */ int32_t st = frac / 128; frac -= st * 128; EMA += (uint32_t)st; } } { int ref = ((int32_t)Origin - (int32_t)EMA > (int32_t)loop1_dlt_ORG) ? (int)EMA : (int)Origin; if (prev_ref >= 0 && ref != prev_ref) ++sw; prev_ref = ref; } } return sw; } static void test_chatter(void) { int p, s_old, s_frac, s_one; for (p = 0; p < 3; p++) { s_old = sim_switch_cnt(0, p); s_frac = sim_switch_cnt(1, p); s_one = sim_switch_cnt(2, p); printf(" [抖动] profile%d vac_ref 切换/30s: 旧死区=%d D余数=%d E单向=%d\n", p, s_old, s_frac, s_one); assert(s_one <= 8); /* E: 抖动回到旧水平 */ assert(s_frac > s_one); /* D 单独: 显著放大 = 本次现场"乱跳" */ } printf("场景 6: vac_ref 抖动 ✓ 通过 (E 单向 ≤8 次/30s; D 余数显著放大, 保持旧死区水平)\n"); } /* ================= 场景 7: 低Q 临界悬停自激(现场日志 154log260911a 复现)================= * ⚠ 能力边界: 本镜像只验证**算法语义**, 验证不了**固件里的执行路径/放置位置**。 * V4.36.42 的 G3 就栽在这: g_rel_hold_tick-- 误放"无车"分支 ⇒ 有车期间常驻 ⇒ 正常释放被永久挡住, * 只能靠 band_timeout(现场实测离开延迟 3.5~4.7s)。此类问题必须靠日志的 Car_OFF 原因字段暴露。 * ⇒ 凡"进入后 N tick 生效"的守卫, 改完先查递减点是否在每 tick 必经路径上。 * 现场: Origin=129254 进表 dlt=49 释表 dlt=23 TEMA≈129220 CAPVD≈129204(深 50) * 现状(V4.36.41): 进门瞬间 dlt_ORG 切成 23 ⇒ vac_ref() 门控由 49 变 23 ⇒ 同意偏差(34)下参考从 * Origin 阶跃到 EMA ⇒ 释放线 EMA-41=129179 比进入线 Origin-49=129205 还浅 * ⇒ 深度 50~58 的静止凹脉冲"进门即满足释放" ⇒ 100ms 周期自激 * G1: 门控用状态无关进表 dlt; G2: 带回夹紧 ≤ 进表 dlt(不动 180=B4.36.28 的 41); G3: 进入后 300ms 禁释放 */ static uint32_t gate_dlt(uint32_t origin, int sens) { return (uint32_t)(((uint64_t)origin * SensTable[sens]) >> 16); } static void sim_flap_x(int mode, int n, uint32_t (*capvd_at)(int), uint32_t org, int32_t ema0, int *p_in, int *p_off) { uint32_t Origin = org; int32_t EMA = ema0, frac = 0; uint32_t dlt_ent = gate_dlt(Origin, SENS_LEVEL), dlt_rel = (uint32_t)(((uint64_t)Origin * 12) >> 16); uint8_t vd = 0; int i, cnt_in = 0, cnt_off = 0, ent = 0, rel = 0, hold = 0, freeze = 0; for (i = 0; i < n; i++) { uint32_t capvd = capvd_at(i), ref, d = dlt_ent; if (mode == 0 && vd) d = dlt_rel; /* 现状: 门控用"当前状态"dlt */ ref = ((int32_t)Origin - EMA > (int32_t)(d * VAC_REF_DELTA_DLT)) ? (uint32_t)EMA : Origin; if (!vd) { if (capvd < ref - dlt_ent) { if (++ent >= 5) { vd = 1; ent = 0; cnt_in++; hold = (mode >= 3) ? 30 : 0; } } else ent = 0; if (freeze > 0) freeze--; /* 释放后 1s 全冻结(REL_ORIGIN_FREEZE_TICKS) */ else if (capvd > Origin - 4*dlt_ent && capvd < Origin + 4*dlt_ent) { /* 对称窗内 EMA 跟踪(D: 余数累加) */ int32_t dd = (int32_t)capvd - EMA, lim = (int32_t)(3*dlt_ent); if (dd > lim) dd = lim; if (dd < -lim) dd = -lim; frac += dd; { int32_t st = frac / VACUUM_EMA_ALFA; frac -= st * VACUUM_EMA_ALFA; EMA += st; } } } else { if (hold > 0) { hold--; rel = 0; } else { uint32_t band = (uint32_t)(((uint64_t)dlt_rel * REL_BAND_LO_MULT) / 100); /* 41 */ if (mode >= 2 && band > dlt_ent) band = dlt_ent - 1; /* G2 兜底夹紧 */ /* mode 4 = 非低Q(QLO=0): 释放线 = Origin − dlt_rel, 不经 vac_ref ⇒ 结构上无重叠 */ uint32_t line = (mode == 4) ? (Origin - dlt_rel) : (ref - band); if (line < capvd) { if (++rel >= 5) { vd = 0; rel = 0; cnt_off++; freeze = 100; } } else rel = 0; } } } *p_in = cnt_in; *p_off = cnt_off; } static void sim_flap(int mode, int n, uint32_t (*capvd_at)(int), int *p_in, int *p_off) { sim_flap_x(mode, n, capvd_at, 129254, 129220, p_in, p_off); } /* 22µH/163kHz 现场参数 */ static uint32_t tl_hold(int i) { (void)i; return 129204; } /* 静止悬停 深 50 */ static uint32_t tl_rebound(int i){ return i < 50 ? 129194 : 129218; } /* 深 60 → 回摆到 Origin-36 */ static uint32_t tl_leave(int i) { return i < 50 ? 129194 : 129254; } /* 深 60 → 撤板 */ static uint32_t tl_hold35(int i) { (void)i; return 129088; } /* 35µH/128kHz: Origin 129141 深 53 */ static uint32_t tl_leave35(int i){ return i < 50 ? 129070 : 129141; } /* 35µH 撤板 */ static void test_lowq_limit_cycle(void) { int i, ci, co, ci22 = 0, ci35 = 0; printf("\n--- 场景 7: 低Q 临界悬停自激(现场参数逐位复现)---\n"); struct { int mode; const char *name; } M[4] = { {0, "现状 V4.36.41"}, {1, "G1"}, {2, "G1+G2"}, {3, "G1+G2+G3"} }; printf(" 静止悬停(深 50) 3s: "); for (i = 0; i < 4; i++) { sim_flap(M[i].mode, 3000, tl_hold, &ci, &co); printf("[%s: Car_In %d / OFF %d] ", M[i].name, ci, co); } printf("\n"); sim_flap(0, 3000, tl_hold, &ci, &co); assert(ci >= 10); /* 现状必然自激(现场日志 ~30 次/3s) */ sim_flap(3, 3000, tl_hold, &ci, &co); assert(ci == 1 && co == 0); /* 修后: 进入一次, 悬停期间不释放 */ sim_flap(3, 3000, tl_leave, &ci, &co); assert(ci == 1 && co == 1); /* 撤板仍能释放 */ sim_flap(2, 3000, tl_rebound, &ci, &co); assert(co >= 1); /* V4.36.28 回摆释放(VACVD Origin-36)不被打破 */ sim_flap(0, 3000, tl_hold, &ci, &co); { ci22 = ci; } printf(" 35µH/128kHz 悬停(深 53) 3s: "); sim_flap_x(0, 3000, tl_hold35, 129141, 129109, &ci, &co); printf("[现状: In %d / OFF %d] ", ci, co); sim_flap_x(3, 3000, tl_hold35, 129141, 129109, &ci, &co); printf("[G1+G2+G3: In %d / OFF %d]\n", ci, co); sim_flap_x(0, 3000, tl_hold35, 129141, 129109, &ci, &co); assert(ci >= 10); /* 35µH 同样自激 */ ci35 = ci; sim_flap_x(3, 3000, tl_hold35, 129141, 129109, &ci, &co); assert(ci == 1 && co == 0); sim_flap_x(3, 3000, tl_leave35, 129141, 129109, &ci, &co); assert(ci == 1 && co == 1); printf(" 现状自激: 22µH Car_In=%d 次/3s, 35µH Car_In=%d 次/3s (≥10 OK, 现场日志同为 ~100ms 周期)\n", ci22, ci35); sim_flap_x(4, 3000, tl_hold, 129254, 129220, &ci, &co); assert(ci == 1 && co == 0); /* 判别预测: 非低Q(f<100kHz) 结构上不自激 */ printf(" 判别预测 OK: 非低Q(f<100kHz) 同深度悬停不自激(1/0) ⇒ 现场换 L≳68µH 应完全不跳\n"); printf(" 修后: 悬停 1/0 OK | 撤板 1/1 OK | V4.36.28 回摆仍释放 OK\n"); } /* ================= 场景 8: 铁板负偏差误释放 (P1) ================= * 现场依据: 2026-09-11 154log260911d (22µH) 两次"铁板还在但被抬到 dev=+33/+96 ⇒ 释放" * 时间线: 深压进入(dev −128) → CAPVD 回升穿过 Origin 停在 +96 (铁板仍在) * 期望: 现状(V4.36.43) ~1.0s 即误释放; P1 冻结离开 ⇒ 持续 5s 后重锚基线再放行(1 次) */ #define SIM_ORG 129310u static uint32_t tl8_iron(int i) { return (i < 100) ? (SIM_ORG - 128u) : (SIM_ORG + 96u); } static uint32_t tl8_leave(int i) { return (i < 200) ? (SIM_ORG - 128u) : (SIM_ORG); } static void sim_iron(int p1_on, uint32_t (*capvd_at)(int), int n, int *p_off_tick, int *p_off_total) { uint32_t dlt_e = gate_dlt(SIM_ORG, SENS_LEVEL); uint32_t dlt_rel = (uint32_t)(((uint64_t)SIM_ORG * 12) >> 16); uint32_t origin = SIM_ORG; int i, ent = 0, rel = 0, hold = 0, neg = 0, off_tick = -1, off_total = 0; uint8_t vd = 0; for (i = 0; i < n; i++) { int32_t capvd = (int32_t)capvd_at(i); if (!vd) { if (capvd < (int32_t)(origin - dlt_e)) { if (++ent >= 5) { vd = 1; ent = 0; hold = 10; } } else ent = 0; } else { if (hold > 0) hold--; if (p1_on) { /* P1: dev > +1×进表dlt 期间冻结离开 */ if (capvd - (int32_t)origin > (int32_t)dlt_e) { if (neg < 500) neg++; else { origin = (uint32_t)capvd; neg = 0; } /* 持续 5s ⇒ 重锚基线后放行 */ } else neg = 0; } if (hold == 0 && neg == 0) { if (capvd > (int32_t)(origin - (dlt_rel * 180) / 100)) { if (++rel >= 5) { vd = 0; rel = 0; off_total++; if (off_tick < 0) off_tick = i; } } else rel = 0; } else rel = 0; } } *p_off_tick = off_tick; *p_off_total = off_total; } static void test_iron_neg_var_release(void) { int t1 = -1, c1 = 0, t2 = -1, c2 = 0, t3 = -1, c3 = 0; sim_iron(0, tl8_iron, 2000, &t1, &c1); /* 现状: 无 P1 */ sim_iron(1, tl8_iron, 2000, &t2, &c2); /* P1: 冻结离开 */ sim_iron(1, tl8_leave, 2000, &t3, &c3); /* P1: 正常撤板(dev→0) */ printf(" 铁板停留 dev=+96: 现状 释放%d次@%.2fs | P1 释放%d次@%.2fs\n", c1, t1 * 0.01, c2, t2 * 0.01); printf(" P1 正常撤板(dev→0): 释放%d次@%.2fs\n", c3, t3 * 0.01); assert(c1 >= 1 && t1 <= 200); /* 现状: ≈1s 内误释放 */ assert(c2 == 1 && t2 >= 550); /* P1: 冻结≥5s 后重锚放行, 且仅 1 次 */ assert(c3 == 1 && t3 <= 260); /* P1: 正常离开不受影响 */ } int main(void) { printf("test_vac_ref_reanchor (V4.36.42: A+B 同步 / D 死区 / E 单向 / G1+G2+G3 低Q自激)\n"); /* ================= 场景 1: 复现 + 补丁效果 ================= */ reset_all(VAC_ANCHOR); uint32_t dlt_anchor = loop1_dlt_ORG; uint32_t dlt_true = calc_dlt(VAC_TRUE); printf("1. 参数: VAC_TRUE=%u(真空) VAC_ANCHOR=%u(补偿后 Origin) Δ=%u counts | dlt=%u(SENS3)\n", (unsigned)VAC_TRUE, (unsigned)VAC_ANCHOR, (unsigned)(VAC_ANCHOR - VAC_TRUE), (unsigned)dlt_anchor); reset_all(VAC_TRUE); /* 补偿前: Origin=真空, EMA=真空 */ env_resync_apply_old(VAC_ANCHOR); /* 修复前: 只换 Origin, EMA 停旧真空 */ uint32_t line_old = enter_line(); printf(" ① 修复前: Origin=%u EMA=%u 进入线=%u | 正确应为 %u ⇒ 低 %u counts" " = %.2f×dlt = %.3f%% Δf/f\n", (unsigned)loop1_Origin, (unsigned)g_vacuum_ema, (unsigned)line_old, (unsigned)(VAC_ANCHOR - dlt_anchor), (unsigned)((VAC_ANCHOR - dlt_anchor) - line_old), ((VAC_ANCHOR - dlt_anchor) - line_old) / (double)dlt_anchor, ((VAC_ANCHOR - dlt_anchor) - line_old) * 100.0 / VAC_ANCHOR); assert(g_vacuum_ema == VAC_TRUE); /* EMA 未同步(缺陷) */ assert(line_old == VAC_TRUE - dlt_true); /* 进入线被留在旧真空 */ assert((VAC_ANCHOR - dlt_anchor) - line_old == 130); /* 整整低 Δ=130 */ reset_all(VAC_TRUE); env_resync_apply_new(VAC_ANCHOR); /* 修复后 (A+B) */ printf(" ② 修复后: Origin=%u EMA=%u 进入线=%u | 与断电上电口径一致 ⇒ 低 0 counts\n", (unsigned)loop1_Origin, (unsigned)g_vacuum_ema, (unsigned)enter_line()); assert(g_vacuum_ema == VAC_ANCHOR); assert(vac_ref() == VAC_ANCHOR); assert(enter_line() == VAC_ANCHOR - dlt_anchor); printf("1. 复现+修复 OK(补偿后进入线由'低 Δ'恢复为 Origin−dlt)\n"); /* ================= 场景 2: B1 护栏(大步重锚 / 小步不动) ================= */ reset_all(VAC_TRUE); origin_set_atomic(VAC_TRUE - 20); /* 步长 20 ≤ 1×dlt ⇒ EMA 不动 */ printf("2. 小步 20(≤1×dlt): Origin=%u EMA=%u ⇒ EMA 未动 ✓\n", (unsigned)loop1_Origin, (unsigned)g_vacuum_ema); assert(g_vacuum_ema == VAC_TRUE); origin_set_atomic(VAC_TRUE + 130); /* 步长 150 > 1×dlt ⇒ 重锚 */ printf(" 大步 150(>1×dlt): Origin=%u EMA=%u ⇒ 已重锚 ✓\n", (unsigned)loop1_Origin, (unsigned)g_vacuum_ema); assert(g_vacuum_ema == VAC_TRUE + 130); printf("2. 护栏 OK(不吃掉 EMA 的正常跟踪,也不容忍大偏差)\n"); /* ================= 场景 3: V4.36.33 门控语义回归 ================= */ reset_all(VAC_ANCHOR); g_vacuum_ema = VAC_ANCHOR - 30; /* 边缘悬停拉低 0.61×dlt */ printf("3. Origin−EMA=30(0.61×dlt): vac_ref=%u ⇒ 用 Origin ✓(门控保留)\n", (unsigned)vac_ref()); assert(vac_ref() == VAC_ANCHOR); g_vacuum_ema = VAC_ANCHOR - 130; /* 老锚错 2.65×dlt */ printf(" Origin−EMA=130(2.65×dlt): vac_ref=%u ⇒ 用 EMA ✓(老场景保留)\n", (unsigned)vac_ref()); assert(vac_ref() == VAC_ANCHOR - 130); printf("3. 门控回归 OK\n"); /* ================= 场景 4: 上电/判稳语义 ================= */ reset_all(VAC_ANCHOR); g_vacuum_ema = 0; /* INIT 清零 */ assert(vac_ref() == VAC_ANCHOR); g_vacuum_ema = loop1_Origin; /* 判稳完成建立初值 */ assert(vac_ref() == VAC_ANCHOR && enter_line() == VAC_ANCHOR - loop1_dlt_ORG); printf("4. 上电(EMA=0→Origin)语义 OK: 进入线=%u\n", (unsigned)enter_line()); /* ================= 场景 5: EMA 整数步进死区(新发现) ================= */ reset_all(VAC_ANCHOR); loop1_CAPVD = VAC_ANCHOR; /* 真空就在 Origin */ g_vacuum_ema = VAC_ANCHOR - 200; /* 被拖低 200 counts 后追赶 */ { int i; for (i = 0; i < 4000; i++) ema_step_cur(); } { uint32_t gap = loop1_Origin - g_vacuum_ema; printf("5. 现状公式 d/128 追赶 4000 样本(40s): EMA=%u 停在真空下方 %u counts" " (%.2f×dlt) ⇒ vac_ref=%s ⇒ 进入线低 %u counts\n", (unsigned)g_vacuum_ema, (unsigned)gap, gap / (double)loop1_dlt_ORG, (vac_ref() == loop1_Origin) ? "Origin" : "EMA", (unsigned)gap); assert(gap == 127); /* 死区: |d|<128 ⇒ 步进 0 */ assert(vac_ref() == g_vacuum_ema); /* 127 > 1×dlt ⇒ 判定参考被拉低 */ assert(enter_line() == g_vacuum_ema - loop1_dlt_ORG); } reset_all(VAC_ANCHOR); loop1_CAPVD = VAC_ANCHOR; g_vacuum_ema = VAC_ANCHOR - 200; g_vac_frac = 0; { int i; for (i = 0; i < 4000; i++) ema_step_fix(); } { uint32_t gap = loop1_Origin - g_vacuum_ema; printf(" 修复 D(余数累加)同条件: EMA=%u 偏差 %u counts ⇒ vac_ref=%s ⇒ 进入线=%u ✓\n", (unsigned)g_vacuum_ema, (unsigned)gap, (vac_ref() == loop1_Origin) ? "Origin" : "EMA", (unsigned)enter_line()); assert(gap == 0); assert(vac_ref() == loop1_Origin); assert(enter_line() == loop1_Origin - loop1_dlt_ORG); } printf("5. 死区实测 OK(现状 127 counts 常驻偏差 → 修复 D 归零)\n"); /* ================= 场景 6: vac_ref 硬开关抖动 ================= */ test_chatter(); /* ================= 场景 7: 低Q 临界悬停自激 ================= */ test_lowq_limit_cycle(); /* ================= 场景 8: 铁板负偏差误释放 (P1) ================= */ test_iron_neg_var_release(); printf("ALL PASS (A+B 同步 / 护栏 / 门控回归 / 上电语义 / 死区 / 抖动 / 低Q自激 / P1负偏差)\n"); return 0; }