用户反馈: 烧 V4.36.42 后离开 3、4 秒才释放。
定位(46µH/113kHz 日志): 4 次离开全是 Car_OFF(band_timeout), 延迟 3.5~4.7s; RLN 正常
(有车态 = Origin−41, 说明 G1 生效、100ms 自激已消失) ⇒ 问题出在"正常释放路径被挡住"。
根因: G3 的 g_rel_hold_tick-- 误放 if(!loop1_VD_FLAG) 无车分支(原 :1308) ⇒ 有车期间永不递减
⇒ 首次 Car_In 后常驻 30 ⇒ g_rel_hold_tick==0 永假 ⇒ 正常释放被永久挡住, 只能等 2.5s 环带兜底
+ 平滑值抬升 ⇒ 3.5~4.7s。
修复: 倒计时移到 } else { 有车分支顶部 (TaskLoop.c:1531 后、#if USE_FLATNESS_EXIT 之前,
每 tick 必过且在释放判定之前); 误放旧行改注释留痕。REL_MIN_HOLD_TICKS 仍 30(300ms)。
单测: 8/8 全绿(场景 7 语义不变); 新增"镜像能力边界"警告 —— 镜像只验证算法语义, 验证不了固件内
执行路径/放置位置, 此类问题必须靠日志 Car_OFF 原因字段(band_timeout/FLAT/普通)暴露。
文档: devlog V2.80(+条目) / spec 头 V2.62 / manual 表 V2.19 / cmcng.h 4.36.42→4.36.43 REV 43
/ 分析稿 §13 返工记录。
待现场: 离开延迟回到 ~300ms 内; 缓慢接近/停边界仍不跳。
352 lines
19 KiB
C
352 lines
19 KiB
C
/*
|
||
* 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 <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <assert.h>
|
||
#include <stdint.h>
|
||
|
||
/* ===== 镜像固件宏 (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");
|
||
}
|
||
|
||
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();
|
||
|
||
printf("ALL PASS (A+B 同步 / 护栏 / 门控回归 / 上电语义 / 死区 / 抖动 / 低Q自激)\n");
|
||
return 0;
|
||
}
|