feat(adc): 新增变比维 r 三维折算,录波段格式升 v2
采样板注入 Ua=5V/Ia=0.5A 读数偏小,根因是固件只输出引脚电压、 缺「端子→引脚」变比。折算改三维: 工程量 = (code + b) × k[µV/码] × 1e-6 × r k 保持 ROM 引脚 LSB,b 为零点,r 为端子变比(int32 Q16)。 变比不能并进 k:UA 端子域 k 达 1.5e5 µV/码、Q16 溢出 int32; r 单独存 Q16 不溢出。两点标定改标 r,k 仍是板级事实。 - 新开单段 [adc_cal_sec](8 键 r_ua..r_in),cfg 段数达 8/8 上限 - 一次变比复用 prot_set 四项,不新开段;r=0 表示本路不折算 - ROM 默认带台架实测变比(电压族 1000、电流族 29.4118), 烧进固件即显二次值;VALID 仍须两点标定,两者互不派生 - eng 判据改「每路要么已配变比、要么削顶不显示」,不再看 VALID - 104 地址 0x8050~0x8057 写 r,run_par_cal_r_set 写值与置脏同函数 - 录波 v2:通道表加 int32 r_q16,头 128→160、整段 5250→5282; code_to_milli 用余数分解换等价单次圆整,20 万组大整数对拍 0 不符 - 文档同步:采集与算法层/通道映射/故障录波/网络配置/配置文件/ 产品需求/内存台账,历史带日期实测数字按冻结口径不改 - 单测 13 套全绿,/keil-build 0 错误 0 警告(256024/31760/ 2080/152368),RAM 实测回填内存内容分配策略 §6.1
This commit is contained in:
@@ -11,19 +11,20 @@
|
||||
* 哨兵漏放,只有离线端能用独立推导的期望值 + 坏文件注入一次性钉死。
|
||||
*
|
||||
* 覆盖点(TC-ADCCAL-xxx;属主登记处 = 本目录,族 TC-HOST-ADCCAL):
|
||||
* TC-ADCCAL-001 init 注册两段:名称/键数/data_size/无回调/默认表指向
|
||||
* TC-ADCCAL-002 UA(TPAFE)折算:0 / 8192 / 32767 / -32768 对 10V/65536 独立推导
|
||||
* TC-ADCCAL-003 UB(STM32)折算:0 / 2048 / 4095 对 3.3V/4096
|
||||
* TC-ADCCAL-004 其余 6 路各 1 点(合计 8 通道 × ≥3 码值点,标定折算落地 DoD)
|
||||
* TC-ADCCAL-001 init 注册三段:名称/键数/data_size/无回调/默认表指向
|
||||
* TC-ADCCAL-002 UA(TPAFE)折算:0 / 8192 / 32767 / -32768,期望 = 引脚值 × 电压族变比
|
||||
* TC-ADCCAL-003 UB(STM32)折算:0 / 2048 / 4095,期望 = 引脚值 × 电压族变比
|
||||
* TC-ADCCAL-004 其余 6 路各 1 点(变比按族逐路挑:UC/UN 电压、IA/IB/IC/IN 电流)
|
||||
* TC-ADCCAL-005 TEMP 透传 + flags/k_uv 恒 0;sem 越界守卫
|
||||
* TC-ADCCAL-006 默认 flags 三态:VALID 全 0;CLIPPED = UB/UC/IB/IC/IN(0xE6)
|
||||
* TC-ADCCAL-007 k_uv 精确值:152.587890625 / 805.6640625(Q16 无舍入验证)
|
||||
* TC-ADCCAL-008 set():合法带边界含端点 / 越界拒绝 RAM 不动 / TEMP 拒绝
|
||||
* TC-ADCCAL-009 合法带哨兵回退:坏 k/坏 b → ROM 值 + 每通道一次 [ERR] +
|
||||
* flags 不报 VALID(即便位图置位)+ init 重置观测窗
|
||||
* TC-ADCCAL-014 零点 b 生效(set 路径):(code+b)×k 逐项可算
|
||||
* TC-ADCCAL-015 两点标定公式:合成数据还原 k/b + 写回往返 + 参数/斜率/
|
||||
* 零点越界(-2)守卫(两点标定的算术核,台架注入待跑)
|
||||
* TC-ADCCAL-014 零点 b 生效(set 路径):(code+b)×k×r 逐项可算
|
||||
* TC-ADCCAL-015 两点标定公式:合成 r/b 回算(独立推导、相对误差 1e-6)+
|
||||
* 写回往返(set k/b + set_ratio 后两点复现端子值)+ 参数/斜率/
|
||||
* k 合法域/v 越 ±1e9/r < 1.0(-1)/零点越界(-2)守卫
|
||||
* TC-ADCCAL-016 rail_ths 门限:TPAFE ±32112 / STM32 4013·81 / TEMP 不监视
|
||||
* TC-ADCCAL-017 init 幂等:重复调用 RAM 复位默认 + 重新注册
|
||||
* —— 以下走真实 cfg_file 引擎(标定落盘与整定入口 DoD)——
|
||||
@@ -36,6 +37,13 @@
|
||||
* 闭锁 / 两路齐第三路缺仍闭锁(全或无)/ 三路齐放行且 UB 仍无 VALID
|
||||
* (证明它是"三路"而不是 feed_eng 那个"8 路")/ 复位后立刻回闭锁
|
||||
* (现查、无自有状态可残留)
|
||||
* TC-ADCCAL-024 变比三维读数口初值 = ROM 默认(电压族 1000.0 / 电流族 29.4118),
|
||||
* ratio_configured 全 1,TEMP/越界恒 0
|
||||
* TC-ADCCAL-025 set_ratio(sem, 0) 成功路径(返 0):该路输出退化到引脚电压、
|
||||
* ratio_configured 归 0、VALID 清;再写回非 0 即恢复
|
||||
* TC-ADCCAL-026 set_ratio 拒绝面:比值 < 1.0 拒(-1,RAM 不动)、TEMP 拒、越界通道拒
|
||||
* TC-ADCCAL-027 坏文件注入 [adc_cal_sec]:r_ua=0 与 r_ua=1(< 1.0)都不折算、
|
||||
* VALID 不亮;未列键保留 ROM 变比(= 只坏一路不牵连别路)
|
||||
* —— 以下四项是"改了没落盘"的可诊断化(道G task-43 第②项;四态**定义只在
|
||||
* App/algo/adc_cal.h 一处**,本表只描述断言、不重定义)——
|
||||
* TC-ADCCAL-019 观测件不得反过来制造故障:get_stats(NULL) 静默返回、随时可读
|
||||
@@ -176,12 +184,57 @@ static int s_total = 0, s_passed = 0, s_failed = 0;
|
||||
|
||||
/* ===================== 独立推导的期望值 ===================== */
|
||||
/* 期望一律用 **double 直接除**(10/65536、3.3/4096),不引用被测的 Q16 常量,
|
||||
* 满足标定折算落地 DoD「期望值由 LSB 常数独立算得(不是抄实现)」;容差只需盖住
|
||||
* float32 中间积舍入(相对 ~1e-7)与打印粒度。 */
|
||||
static double exp_tpafe(int32_t code) { return (double)code * (10.0 / 65536.0); }
|
||||
static double exp_stm32(int32_t code) { return (double)code * (3.3 / 4096.0); }
|
||||
static double exp_q16(int32_t code, int32_t b, int32_t k_q16) {
|
||||
return ((double)code + (double)b) * ((double)k_q16 / 65536.0) / 1.0e6;
|
||||
* 满足标定折算落地 DoD「期望值由 LSB 常数独立算得(不是抄实现)」。三层口径:
|
||||
* ① 引脚域 exp_pin():按通道来源给 LSB——TPAFE 10 V 跨度 / 65536 码,
|
||||
* STM32 0~3.3 V / 4096 码;
|
||||
* ② 端子(二次)域 exp_eng():引脚值 × 该路变比。变比按族在此**独立写出十进制
|
||||
* 常量**(电压族 1000.0 V/V、电流族 29.4118 A/V = 1927532/65536 回落两位),
|
||||
* 不引 ADC_CAL_R_Q16_* 宏——引宏就等于把实现里的数抄进期望,正是 DoD 禁止的;
|
||||
* ③ 整定域 exp_q16():先按 Q16 系数还原引脚电压,再乘该路变比。
|
||||
* ⚠ 变比必须**按通道**挑,不能一刀切:TPAFE 侧 UA/UN 是电压族、IA 是电流族;
|
||||
* STM32 侧 UB/UC 是电压族、IB/IC/IN 是电流族(V0.1 双转换器分配,见
|
||||
* docs/硬件平台/ADC通道映射说明.md 与 adc_cal.c 的 ROM 表)。
|
||||
* 容差只需盖住 float32 中间积舍入(相对 ~1e-7)与打印粒度。 */
|
||||
#define EXP_R_VOLT (1000.0) /* 电压族变比 V/V */
|
||||
#define EXP_R_CURR (29.4118) /* 电流族变比 A/V(1927532/65536 的十进制口径) */
|
||||
|
||||
static double exp_ratio(adc_semantic_t sem) {
|
||||
switch (sem) {
|
||||
case ADC_SEM_UA:
|
||||
case ADC_SEM_UB:
|
||||
case ADC_SEM_UC:
|
||||
case ADC_SEM_UN:
|
||||
return EXP_R_VOLT;
|
||||
case ADC_SEM_IA:
|
||||
case ADC_SEM_IB:
|
||||
case ADC_SEM_IC:
|
||||
case ADC_SEM_IN:
|
||||
return EXP_R_CURR;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 引脚域:来源决定 LSB(TPAFE 是 16 位双极性 10 V 跨度,STM32 是单极性 3.3 V) */
|
||||
static double exp_pin(int32_t code, adc_semantic_t sem) {
|
||||
switch (sem) {
|
||||
case ADC_SEM_UA:
|
||||
case ADC_SEM_UN:
|
||||
case ADC_SEM_IA:
|
||||
return (double)code * (10.0 / 65536.0);
|
||||
default:
|
||||
return (double)code * (3.3 / 4096.0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 端子域 = 引脚域 × 该路变比 */
|
||||
static double exp_eng(int32_t code, adc_semantic_t sem) {
|
||||
return exp_pin(code, sem) * exp_ratio(sem);
|
||||
}
|
||||
|
||||
/* 整定域:Q16 系数还原引脚电压,再乘该路变比(= adc_cal_apply 的三维口径) */
|
||||
static double exp_q16(adc_semantic_t sem, int32_t code, int32_t b, int32_t k_q16) {
|
||||
return ((double)code + (double)b) * ((double)k_q16 / 65536.0) / 1.0e6 * exp_ratio(sem);
|
||||
}
|
||||
static int near_d(double got, double want) {
|
||||
double d = got - want;
|
||||
@@ -222,9 +275,9 @@ static const char *vfs_get(void) { return (const char *)s_vfs; }
|
||||
|
||||
/* ===================== 用例(白盒组) ===================== */
|
||||
|
||||
/* TC-ADCCAL-001:init 注册两段(名称/键数/data_size/无回调) */
|
||||
/* TC-ADCCAL-001:init 注册三段(名称/键数/data_size/无回调) */
|
||||
static void tc_001_init_registers(void) {
|
||||
printf("[TC-ADCCAL-001] init registers [adc_cal]/[adc_cal_ofs]\n");
|
||||
printf("[TC-ADCCAL-001] init registers [adc_cal]/[adc_cal_ofs]/[adc_cal_sec]\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
CHECK(s_sections[0].used == 1u, "section slot0 used");
|
||||
@@ -236,31 +289,38 @@ static void tc_001_init_registers(void) {
|
||||
"sec1 name = adc_cal_ofs");
|
||||
CHECK(s_sections[1].key_count == 8u, "sec1 keys = 8b");
|
||||
CHECK(s_sections[1].data_size == 32u, "sec1 data_size = 32");
|
||||
CHECK(s_sections[2].used == 0u, "only two sections registered");
|
||||
CHECK(s_sections[2].used == 1u && strcmp(s_sections[2].name, "adc_cal_sec") == 0,
|
||||
"sec2 name = adc_cal_sec (ratio dimension)");
|
||||
CHECK(s_sections[2].key_count == 8u, "sec2 keys = 8r");
|
||||
CHECK(s_sections[2].data_size == 32u, "sec2 data_size = 32");
|
||||
CHECK(s_sections[2].p_cb == NULL, "sec2 no callback");
|
||||
CHECK(s_sections[3].used == 0u, "only three sections registered");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-002/003/004:8 通道 × ≥3 码值点折算,期望独立推导(标定折算落地 DoD) */
|
||||
/* TC-ADCCAL-002/003/004:8 通道 × ≥3 码值点折算,期望独立推导(标定折算落地 DoD)
|
||||
* v2.0 起输出是**二次值**(引脚值 × 该路变比):TPAFE 侧 UA/UN 走电压族 1000、
|
||||
* IA 走电流族 29.4118;STM32 侧 UB/UC 走电压族、IB/IC/IN 走电流族。 */
|
||||
static void tc_002_004_apply(void) {
|
||||
printf("[TC-ADCCAL-002] UA (TPAFE) apply\n");
|
||||
printf("[TC-ADCCAL-002] UA (TPAFE, voltage family) apply\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 0), exp_tpafe(0)), "UA code 0 = 0V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 8192), exp_tpafe(8192)), "UA 8192 = 1.25V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 32767), exp_tpafe(32767)), "UA full scale minus 1 (bipolar top code)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, -32768), exp_tpafe(-32768)), "UA -FS (bipolar)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 0), exp_eng(0, ADC_SEM_UA)), "UA code 0 = 0V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 8192), exp_eng(8192, ADC_SEM_UA)), "UA 8192 = 1.25V pin -> 1250V terminal");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 32767), exp_eng(32767, ADC_SEM_UA)), "UA full scale minus 1 (bipolar top code)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, -32768), exp_eng(-32768, ADC_SEM_UA)), "UA -FS (bipolar)");
|
||||
|
||||
printf("[TC-ADCCAL-003] UB (STM32) apply\n");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 0), exp_stm32(0)), "UB code 0 = 0V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 2048), exp_stm32(2048)), "UB mid = 1.65V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 4095), exp_stm32(4095)), "UB +FS 3.2992V");
|
||||
printf("[TC-ADCCAL-003] UB (STM32, voltage family) apply\n");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 0), exp_eng(0, ADC_SEM_UB)), "UB code 0 = 0V");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 2048), exp_eng(2048, ADC_SEM_UB)), "UB mid = 1.65V pin -> 1650V terminal");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 4095), exp_eng(4095, ADC_SEM_UB)), "UB +FS 3.2992V pin");
|
||||
|
||||
printf("[TC-ADCCAL-004] other six channels one mid point each\n");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UC, 123), exp_stm32(123)), "UC 123");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UN, -10000), exp_tpafe(-10000)), "UN -10000");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IA, 11452), exp_tpafe(11452)), "IA 11452 (bench first-frame code)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IB, 4000), exp_stm32(4000)), "IB 4000");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IC, 1), exp_stm32(1)), "IC 1");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IN, 4094), exp_stm32(4094)), "IN 4094");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UC, 123), exp_eng(123, ADC_SEM_UC)), "UC 123 (voltage family)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UN, -10000), exp_eng(-10000, ADC_SEM_UN)), "UN -10000 (voltage family)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IA, 11452), exp_eng(11452, ADC_SEM_IA)), "IA 11452 (current family, bench first-frame code)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IB, 4000), exp_eng(4000, ADC_SEM_IB)), "IB 4000 (current family)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IC, 1), exp_eng(1, ADC_SEM_IC)), "IC 1 (current family)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IN, 4094), exp_eng(4094, ADC_SEM_IN)), "IN 4094 (current family)");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-005:TEMP 透传与守卫 */
|
||||
@@ -315,7 +375,7 @@ static void tc_008_set(void) {
|
||||
adc_cal_init();
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, 10500000L, -12L) == 0, "set in-band ok");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) != 0u, "VALID set after accepted");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_q16(1000, -12, 10500000)),
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_q16(ADC_SEM_UA, 1000, -12, 10500000)),
|
||||
"apply uses accepted k/b");
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, 5000000L, 0L) == 0, "ROM/2 boundary accepted");
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, 20000000L, 0L) == 0, "ROM*2 boundary accepted");
|
||||
@@ -337,7 +397,7 @@ static void tc_009_sanitize(void) {
|
||||
adc_cal_init();
|
||||
s_kcfg.k[0] = 30000000L; /* > ROM*2:模拟坏文件/坏标定已写入 RAM */
|
||||
s_kcfg.valid |= 0x01u; /* 位图也说「已标定」——更要用哨兵兜住 */
|
||||
want = exp_tpafe(1000);
|
||||
want = exp_eng(1000, ADC_SEM_UA);
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), want), "out-of-band k falls back to ROM");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u, "VALID suppressed while k out of band");
|
||||
CHECK(g_caltest_err_count == 1, "first bad observation logs exactly one ERR");
|
||||
@@ -346,7 +406,7 @@ static void tc_009_sanitize(void) {
|
||||
CHECK(g_caltest_err_count == 1, "repeat observations do not spam ERR");
|
||||
/* b 越界同哨兵(k 合法、b 坏) */
|
||||
s_bcfg.b[1] = 99999L;
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 1000), exp_stm32(1000)), "bad b -> ROM + b=0");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 1000), exp_eng(1000, ADC_SEM_UB)), "bad b -> ROM + b=0");
|
||||
CHECK(g_caltest_err_count == 2, "bad b noted on its own channel");
|
||||
/* init 重置留痕位 = 新观测窗 */
|
||||
adc_cal_init();
|
||||
@@ -366,43 +426,66 @@ static void tc_00a_b_effect(void) {
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
CHECK(adc_cal_set(ADC_SEM_IA, 10000000L, -77L) == 0, "set IA b=-77");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IA, 1077), exp_q16(1077, -77, 10000000)),
|
||||
"IA apply = (code-77)*ROMk");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IA, 1077), exp_q16(ADC_SEM_IA, 1077, -77, 10000000)),
|
||||
"IA apply = (code-77)*ROMk*r_curr");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-015:两点标定公式 + 写回往返 */
|
||||
static void tc_00b_two_point(void) {
|
||||
int32_t kq = 0, bb = 0;
|
||||
const int32_t k0 = 10000000L;
|
||||
const int32_t b0 = -30L;
|
||||
int32_t c1 = -250, c2 = 32000;
|
||||
int32_t rq = 0, bb = 0;
|
||||
const int32_t k0 = 10000000L; /* 该路生效 k(TPAFE 152.587890625 uV/code) */
|
||||
const double r0 = 1000.0; /* 待标定的端子 vs 引脚变比(电压族量级) */
|
||||
const int32_t b0 = -30L; /* 真零点,码值 */
|
||||
const int32_t c1 = 300, c2 = 800; /* 两点码值(合端子 41 V / 117 V 量级) */
|
||||
int32_t v1, v2;
|
||||
int rc;
|
||||
|
||||
printf("[TC-ADCCAL-015] two-point formula recovers synthetic k/b\n");
|
||||
printf("[TC-ADCCAL-015] two-point formula recovers synthetic r/b\n");
|
||||
/* 合成测量:v = (c + b0) * k0/65536 µV(lround 模拟量化读数) */
|
||||
v1 = (int32_t)my_lround(((double)c1 + (double)b0) * ((double)k0 / 65536.0));
|
||||
v2 = (int32_t)my_lround(((double)c2 + (double)b0) * ((double)k0 / 65536.0));
|
||||
rc = adc_cal_two_point(c1, c2, v1, v2, &kq, &bb);
|
||||
/* 合成测量:端子 uV = (c + b0) x (k0/65536) uV/code x r0(lround 模拟量化读数) */
|
||||
v1 = (int32_t)my_lround(((double)c1 + (double)b0) * ((double)k0 / 65536.0) * r0);
|
||||
v2 = (int32_t)my_lround(((double)c2 + (double)b0) * ((double)k0 / 65536.0) * r0);
|
||||
rc = adc_cal_two_point(c1, c2, v1, v2, k0, &rq, &bb);
|
||||
CHECK(rc == 0, "two_point ok");
|
||||
CHECK(kq > (k0 - 5) && kq < (k0 + 5), "k recovered within +-5 q16");
|
||||
CHECK(((double)rq > (r0 * 65536.0 * (1.0 - 1.0e-6))) &&
|
||||
((double)rq < (r0 * 65536.0 * (1.0 + 1.0e-6))),
|
||||
"r recovered within 1e-6 relative");
|
||||
CHECK(bb > (b0 - 3) && bb < (b0 + 3), "b recovered within +-3 codes");
|
||||
/* 写回往返:set 后 apply(c2) 应还原 v2(µV 级) */
|
||||
/* 写回往返:set(k,b) 置位 + set_ratio 定量纲,两点都应复现端子值 */
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, kq, bb) == 0, "set recovered coeffs");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, c2), (double)v2 / 1.0e6), "roundtrip v(c2) = measured");
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, k0, bb) == 0, "set recovered k/b");
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_UA, rq) == 0, "set recovered ratio");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) != 0u,
|
||||
"VALID needs both writes (k/b band + ratio configured)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, c1), (double)v1 / 1.0e6),
|
||||
"roundtrip v(c_lo) = measured");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, c2), (double)v2 / 1.0e6),
|
||||
"roundtrip v(c_hi) = measured");
|
||||
|
||||
printf("[TC-ADCCAL-015] two_point guards\n");
|
||||
CHECK(adc_cal_two_point(100, 100, 0L, 100000L, &kq, &bb) == -1, "dc=0 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 50000L, 50000L, &kq, &bb) == -1, "dv=0 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 100000L, 50000L, &kq, &bb) == -1, "inverted slope rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, NULL, &bb) == -1, "NULL k rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, &kq, NULL) == -1, "NULL b rejected");
|
||||
/* 真零点 +9000 码 > B_MAX:斜率对但 b 越界 -> -2(先查前端,不用系数凑) */
|
||||
v1 = (int32_t)my_lround((100.0 + 9000.0) * ((double)k0 / 65536.0));
|
||||
v2 = (int32_t)my_lround((2000.0 + 9000.0) * ((double)k0 / 65536.0));
|
||||
CHECK(adc_cal_two_point(100, 2000, v1, v2, &kq, &bb) == -2, "b beyond +-8192 -> -2");
|
||||
CHECK(adc_cal_two_point(100, 100, 0L, 100000L, k0, &rq, &bb) == -1, "dc=0 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 50000L, 50000L, k0, &rq, &bb) == -1, "dv=0 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 100000L, 50000L, k0, &rq, &bb) == -1, "inverted slope rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, 0L, &rq, &bb) == -1, "k_q16 = 0 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, 100000001L, &rq, &bb) == -1, "k_q16 > 1e8 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, k0, NULL, &bb) == -1, "NULL r rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, k0, &rq, NULL) == -1, "NULL b rejected");
|
||||
/* v 超 +-1e9 uV(= +-1000 V):几乎必然把 mV/码值当 uV 填了 */
|
||||
CHECK(adc_cal_two_point(100, 200, 1000000000L, 1000100000L, k0, &rq, &bb) == -1,
|
||||
"v beyond +1e9 rejected");
|
||||
CHECK(adc_cal_two_point(100, 200, -1000100000L, -1000000000L, k0, &rq, &bb) == -1,
|
||||
"v beyond -1e9 rejected");
|
||||
/* 算出的变比 < 1.0(dc=100、k=1e7 时 dv 仅 1000 uV -> r 约 0.065)-> -1 */
|
||||
CHECK(adc_cal_two_point(100, 200, 0L, 1000L, k0, &rq, &bb) == -1, "computed r < 1.0 -> -1");
|
||||
/* 真零点 +9000 码 > B_MAX:斜率对(r = 1.5)但 b 越界 -> -2(先查前端,不用系数凑) */
|
||||
v1 = (int32_t)my_lround((100.0 + 9000.0) * ((double)k0 / 65536.0) * 1.5);
|
||||
v2 = (int32_t)my_lround((2000.0 + 9000.0) * ((double)k0 / 65536.0) * 1.5);
|
||||
CHECK(adc_cal_two_point(100, 2000, v1, v2, k0, &rq, &bb) == -2, "b beyond +-8192 -> -2");
|
||||
/* 真零点 +9000 码 > B_MAX:斜率对(r = 1.5)但 b 越界 -> -2(先查前端,不用系数凑) */
|
||||
v1 = (int32_t)my_lround((100.0 + 9000.0) * ((double)k0 / 65536.0) * 1.5);
|
||||
v2 = (int32_t)my_lround((2000.0 + 9000.0) * ((double)k0 / 65536.0) * 1.5);
|
||||
CHECK(adc_cal_two_point(100, 2000, v1, v2, k0, &rq, &bb) == -2, "b beyond +-8192 -> -2");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-016:贴轨门限表 */
|
||||
@@ -423,14 +506,83 @@ static void tc_00d_idempotent(void) {
|
||||
printf("[TC-ADCCAL-017] re-init restores defaults\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
(void)adc_cal_set(ADC_SEM_UA, 12345678L, 5L);
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_q16(1000, 5, 12345678)), "pre-check tuned");
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, 12345678L, 5L) == 0, "pre-check tuned");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_q16(ADC_SEM_UA, 1000, 5, 12345678)), "pre-check tuned");
|
||||
adc_cal_init(); /* 幂等重入:RAM 复位 + 再注册 */
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_tpafe(1000)), "re-init back to ROM");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 1000), exp_eng(1000, ADC_SEM_UA)), "re-init back to ROM");
|
||||
CHECK(adc_cal_flags(ADC_SEM_UA) == 0u, "re-init clears VALID too");
|
||||
}
|
||||
|
||||
/* ===================== 用例(引擎集成组,标定落盘与整定入口 DoD) ===================== */
|
||||
/* TC-ADCCAL-024:变比维读数口初值 = ROM 默认(出厂即配,不是缺省 0) */
|
||||
static void tc_024_ratio_defaults(void) {
|
||||
printf("[TC-ADCCAL-024] ratio readouts start at ROM defaults\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
/* 电压族 1000.0 V/V、电流族 29.4118 A/V——十进制口径在此独立写出,
|
||||
* Q16 原值由 比值 × 65536 就地算出(1927532 = 29.4118 x 65536 的回落) */
|
||||
CHECK(near_d((double)adc_cal_ratio(ADC_SEM_UA), EXP_R_VOLT), "UA ratio = 1000.0 V/V");
|
||||
CHECK(near_d((double)adc_cal_ratio(ADC_SEM_UN), EXP_R_VOLT), "UN ratio = 1000.0 V/V");
|
||||
CHECK(near_d((double)adc_cal_ratio(ADC_SEM_IA), EXP_R_CURR), "IA ratio = 29.4118 A/V");
|
||||
CHECK(near_d((double)adc_cal_ratio(ADC_SEM_IN), EXP_R_CURR), "IN ratio = 29.4118 A/V");
|
||||
CHECK(adc_cal_ratio_q16(ADC_SEM_UA) == (int32_t)my_lround(EXP_R_VOLT * 65536.0),
|
||||
"UA ratio q16 = 65536000 (LSB exact)");
|
||||
CHECK(adc_cal_ratio_q16(ADC_SEM_IA) == (int32_t)my_lround(EXP_R_CURR * 65536.0),
|
||||
"IA ratio q16 = 1927532 (LSB exact)");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 1u, "UA ratio configured at boot");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_IA) == 1u, "IA ratio configured at boot");
|
||||
/* TEMP 不参与线性标定;越界通道同守卫 */
|
||||
CHECK((adc_cal_ratio(ADC_SEM_TEMP) == 0.0f) && (adc_cal_ratio_q16(ADC_SEM_TEMP) == 0) &&
|
||||
(adc_cal_ratio_configured(ADC_SEM_TEMP) == 0u), "TEMP ratio all zero");
|
||||
CHECK((adc_cal_ratio((adc_semantic_t)ADC_SEM_COUNT) == 0.0f) &&
|
||||
(adc_cal_ratio_q16((adc_semantic_t)ADC_SEM_COUNT) == 0) &&
|
||||
(adc_cal_ratio_configured((adc_semantic_t)ADC_SEM_COUNT) == 0u),
|
||||
"OOB sem ratio all zero");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-025:set_ratio(sem, 0) = 显式关掉本路折算(成功路径,不是拒绝) */
|
||||
static void tc_025_ratio_zero_disables(void) {
|
||||
printf("[TC-ADCCAL-025] set_ratio(0) disables scaling and drops VALID\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
/* 先走正规 k/b 整定把 VALID 点亮(r 出厂已配 → 三条件齐) */
|
||||
CHECK(adc_cal_set(ADC_SEM_UA, ADC_CAL_K_Q16_TPAFE, 0L) == 0, "seed k/b accepted");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) != 0u,
|
||||
"VALID lit with ROM ratio already configured");
|
||||
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_UA, 0L) == 0, "set_ratio(0) accepted (explicit off)");
|
||||
/* 折算退化到引脚域:期望 = 码值 × 10/65536,与变比无关 */
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 8192), exp_pin(8192, ADC_SEM_UA)),
|
||||
"apply falls back to pin volts (x1000 gone)");
|
||||
CHECK((adc_cal_ratio(ADC_SEM_UA) == 0.0f) && (adc_cal_ratio_q16(ADC_SEM_UA) == 0),
|
||||
"ratio readouts report 0 (not a third out-of-band state)");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 0u, "ratio_configured = 0");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u,
|
||||
"VALID cleared by r = 0 (three-condition rule)");
|
||||
|
||||
/* 写回非 0 即恢复(r 与 k/b 无先后依赖,现算三条件) */
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_UA, (int32_t)my_lround(EXP_R_VOLT * 65536.0)) == 0,
|
||||
"set_ratio back to 1000 accepted");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 8192), exp_eng(8192, ADC_SEM_UA)),
|
||||
"apply scales again");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) != 0u, "VALID back on");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-026:set_ratio 的拒绝面(比值 < 1.0 / TEMP / 越界通道;RAM 不动) */
|
||||
static void tc_026_ratio_set_guards(void) {
|
||||
float before;
|
||||
|
||||
printf("[TC-ADCCAL-026] set_ratio rejects sub-1.0 / TEMP / OOB, RAM intact\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
before = adc_cal_apply(ADC_SEM_UA, 1000);
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_UA, 60000L) == -1, "ratio 0.9155 (< 1.0) rejected");
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_UA, -65536L) == -1, "negative ratio rejected");
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_TEMP, 65536000L) == -1, "TEMP rejected");
|
||||
CHECK(adc_cal_set_ratio((adc_semantic_t)ADC_SEM_COUNT, 65536000L) == -1, "OOB sem rejected");
|
||||
CHECK(adc_cal_apply(ADC_SEM_UA, 1000) == before, "RAM untouched after all rejects");
|
||||
CHECK(adc_cal_ratio_q16(ADC_SEM_UA) == (int32_t)my_lround(EXP_R_VOLT * 65536.0),
|
||||
"UA ratio still ROM default after rejects");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-010:文件值经 load 生效 + VALID */
|
||||
static void tc_010_file_apply(void) {
|
||||
@@ -442,7 +594,7 @@ static void tc_010_file_apply(void) {
|
||||
CHECK(cfg_file_load() == 0, "load ok");
|
||||
CHECK(adc_cal_k_uv(ADC_SEM_UA) > 183.105f && adc_cal_k_uv(ADC_SEM_UA) < 183.106f,
|
||||
"UA k = 12000000 q16 = 183.10546875 uV");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 6557), exp_q16(6557, -40, 12000000)),
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 6557), exp_q16(ADC_SEM_UA, 6557, -40, 12000000)),
|
||||
"apply uses file k and negative file b");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) != 0u, "VALID from file bitmap");
|
||||
/* 未列键保留默认 */
|
||||
@@ -457,8 +609,8 @@ static void tc_011_file_bad(void) {
|
||||
adc_cal_init();
|
||||
vfs_put("[adc_cal]\r\nk_ua=999\r\nk_ub=-999\r\nvalid=7\r\n");
|
||||
CHECK(cfg_file_load() == 0, "load ok (values structurally valid INT)");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 500), exp_tpafe(500)), "UA sanitized to ROM");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 500), exp_stm32(500)), "UB sanitized to ROM");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 500), exp_eng(500, ADC_SEM_UA)), "UA sanitized to ROM");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UB, 500), exp_eng(500, ADC_SEM_UB)), "UB sanitized to ROM");
|
||||
CHECK(adc_cal_flags(ADC_SEM_UA) == 0u, "UA VALID denied despite bitmap bit (k out of band)");
|
||||
CHECK(adc_cal_flags(ADC_SEM_UB) == ADC_CAL_F_CLIPPED, "UB keeps only CLIPPED");
|
||||
CHECK(g_caltest_err_count >= 2, "both sanitizations logged");
|
||||
@@ -480,7 +632,7 @@ static void tc_012_file_corrupt(void) {
|
||||
big[sizeof(big) - 1u] = '\0';
|
||||
vfs_put(big);
|
||||
CHECK(cfg_file_load() == 0, "load survives corruption");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 4096), exp_tpafe(4096)), "corrupt -> ROM coefficients");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 4096), exp_eng(4096, ADC_SEM_UA)), "corrupt -> ROM coefficients");
|
||||
CHECK(adc_cal_flags(ADC_SEM_UA) == 0u, "corrupt -> no VALID anywhere (DoD wording)");
|
||||
CHECK(s_syslog_count >= 1, "corruption left self-test trail");
|
||||
}
|
||||
@@ -492,10 +644,44 @@ static void tc_013_section_missing(void) {
|
||||
adc_cal_init();
|
||||
vfs_put("; only a comment, no managed sections\n");
|
||||
CHECK(cfg_file_load() == 0, "load ok with unrelated file");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IN, 2048), exp_stm32(2048)), "IN still ROM");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IN, 2048), exp_eng(2048, ADC_SEM_IN)), "IN still ROM");
|
||||
CHECK(adc_cal_flags(ADC_SEM_IA) == 0u, "no VALID with missing sections");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-027:坏文件注入 [adc_cal_sec]——r 越带/为 0 都不折算、VALID 不亮 */
|
||||
static void tc_027_file_ratio_bad(void) {
|
||||
printf("[TC-ADCCAL-027] bad [adc_cal_sec] entries -> pin volts, no VALID\n");
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
/* r_ua=0:该路退回引脚域;位图与 k 都合法,挡不到它——只有变比条件能挡 */
|
||||
vfs_put("[adc_cal]\r\nk_ua=10000000\r\nvalid=1\r\n"
|
||||
"[adc_cal_sec]\r\nr_ua=0\r\n");
|
||||
CHECK(cfg_file_load() == 0, "load ok (r_ua = 0)");
|
||||
CHECK(adc_cal_ratio_q16(ADC_SEM_UA) == 0, "r_ua = 0 read back as 0");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 0u, "r_ua = 0 -> not configured");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 500), exp_pin(500, ADC_SEM_UA)),
|
||||
"r_ua = 0 -> apply stays in pin domain");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u,
|
||||
"r_ua = 0 vetoes VALID even with valid bit + k in band");
|
||||
/* 未列键保留 ROM 变比:坏一路不牵连别路 */
|
||||
CHECK(near_d((double)adc_cal_ratio(ADC_SEM_IA), EXP_R_CURR), "IA ratio still ROM default");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_IA, 500), exp_eng(500, ADC_SEM_IA)),
|
||||
"IA still scales (only UA was zeroed)");
|
||||
|
||||
/* r_ua=1:比值 1/65536 < 1.0,同样不折算 + 不报 VALID */
|
||||
reset_all();
|
||||
adc_cal_init();
|
||||
vfs_put("[adc_cal]\r\nk_ua=10000000\r\nvalid=1\r\n"
|
||||
"[adc_cal_sec]\r\nr_ua=1\r\n");
|
||||
CHECK(cfg_file_load() == 0, "load ok (r_ua = 1)");
|
||||
CHECK(adc_cal_ratio_q16(ADC_SEM_UA) == 0, "sub-1.0 ratio reported as 0 (no third state)");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 0u, "sub-1.0 ratio -> not configured");
|
||||
CHECK(near_d(adc_cal_apply(ADC_SEM_UA, 500), exp_pin(500, ADC_SEM_UA)),
|
||||
"sub-1.0 ratio -> apply stays in pin domain");
|
||||
CHECK((adc_cal_flags(ADC_SEM_UA) & ADC_CAL_F_VALID) == 0u, "sub-1.0 ratio vetoes VALID");
|
||||
CHECK(g_caltest_err_count >= 1, "sub-1.0 ratio left its own ERR trace");
|
||||
}
|
||||
|
||||
/* TC-ADCCAL-018:未标定闭锁判定单点(「数据可信」未标定即闭锁 / 道F task-16)
|
||||
* 为什么值得单独钉:这条谓词此前在 App/task/protect_task.c 与
|
||||
* App/app/ui_data_feed.c 各写一份,**没有任何一份被主机测到**(ui_data 套件只编
|
||||
@@ -614,6 +800,10 @@ int main(void) {
|
||||
tc_013_section_missing();
|
||||
tc_018_prot_blocked();
|
||||
tc_019_023_persist_obs();
|
||||
tc_024_ratio_defaults();
|
||||
tc_025_ratio_zero_disables();
|
||||
tc_026_ratio_set_guards();
|
||||
tc_027_file_ratio_bad();
|
||||
|
||||
printf("\n=== ADC Cal Layer Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
|
||||
@@ -435,8 +435,10 @@ static int register_fill(int n)
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* TC-CONFIG-056/057:段数与键数容量钉(道G task-8——[comm] 上线后生产占 6/8,
|
||||
* 容量与"满则拒"行为必须先在这里显式化,别让第 7 段的接入者在真机上才发现顶格) */
|
||||
/* TC-CONFIG-056/057:段数与键数容量钉(2026-11-11 [adc_cal_sec] 上线后生产已占满
|
||||
* 8/8:adc_cal / adc_cal_ofs / adc_cal_sec / run_par / comm / network / ftp / sntp,
|
||||
* 与全仓 8 处 cfg_file_register() 调用一一对应——下一个受管段无处可放,
|
||||
* 必须先在真机上暴露"满则拒"的语义,而不是等第 9 段的接入者现场发现顶格) */
|
||||
static void tc_capacity(void)
|
||||
{
|
||||
printf("[TC-CONFIG-056] section capacity: MAX_SECTIONS pinned, 9th rejected\n");
|
||||
@@ -444,8 +446,10 @@ static void tc_capacity(void)
|
||||
char nm[8];
|
||||
cfg_section_reg_t r;
|
||||
|
||||
/* 数字钉:段上限必须是 8(cfg_file.h:39);改数字 = 容量台账变化,必须
|
||||
* 同步 docs/子系统设计/配置文件设计说明.md 与本用例生产占位说明 */
|
||||
/* 数字钉:段上限必须是 8(cfg_file.h:43);改数字 = 容量台账变化,必须
|
||||
* 同步 docs/子系统设计/配置文件设计说明.md 与本用例生产占位说明。
|
||||
* ⚠ 生产已占满 8/8(见本函数头注释)——这个数字一旦被改大,
|
||||
* 同时要复核 [adc_cal_sec] 之后的段不会被静默拒 */
|
||||
CHECK(CFG_FILE_MAX_SECTIONS == 8u, "CFG_FILE_MAX_SECTIONS pinned to 8");
|
||||
vfs_reset();
|
||||
CHECK(register_fill((int)CFG_FILE_MAX_SECTIONS) == 1, "filling exactly 8 sections all accepted");
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
* ROM 系数覆写(#undef/#define):把"板级理论系数"替换为合成端子侧口径
|
||||
* (10 mV/码,K_DIM),模拟两点标定完成后的 ROM/默认状态——合法带随之
|
||||
* 锚定合成值,adc_cal_set 走正规路径置 VALID,测试不白盒改引擎内存。
|
||||
* 变比 r 同步合成化为 1.0:本套件证的是「折算链别丢量纲」,不是变比数值;
|
||||
* r = 1.0 让上面那对互逆换算(eng ↔ code)继续成立,也就与
|
||||
* docs/算法/采集与算法层设计说明.md §13.6 的合成口径一致。r ≠ 1.0 的
|
||||
* 放大效应单独由 TC-PROTDIM-011 钉(那时故意让判据输入被放大 1000 倍)。
|
||||
*
|
||||
* 覆盖点(族 TC-HOST-PROTDIM,属主登记处 = 本目录):
|
||||
* TC-PROTDIM-001 标定链基线:0.5 A/100 V/0 V 注入 → 七类元件全静默(含
|
||||
@@ -36,6 +40,11 @@
|
||||
* 14.4 保持(ret 14.25)、14.1 释放
|
||||
* TC-PROTDIM-010 聚合与独立性:OC1+OV 同拍动作位图 = 双位;撤 IA →
|
||||
* OC1 返回、OV 保持(互不污染)
|
||||
* TC-PROTDIM-011 变比确实进折算(不是只存在配置里):把 IA 的 r 从合成 1.0
|
||||
* 改成 1000.0 后,同一批码值的判据输入被放大 1000 倍 →
|
||||
* 0.5 A 立刻顶穿 OC1(10 A);改回 1.0 后恢复静默。
|
||||
* 反错配方向:若哪天有人把 r 从 apply() 里摘掉、只留配置段,
|
||||
* 本用例立刻翻红。
|
||||
*
|
||||
* 通过:全部 TC PASS(main 返回 0)。
|
||||
* 创建日期:2026-10-25(道A 量纲回归用例)
|
||||
@@ -91,12 +100,17 @@ static int s_total = 0, s_passed = 0, s_failed = 0;
|
||||
* K_DIM = 655360000 Q16 = 10000 µV/码 = 10 mV/码:
|
||||
* 电压类 100 V rms → 峰值 141.42 V → 14142 码(TPAFE 半量程内,量级刻意
|
||||
* 贴近端子变比两点标定完成后的真实形态);电流类二次值经变换器落到同一定标。
|
||||
* B_IA = -30 码(零点偏移,经 apply 精确对消——验证 b 参与全链而非仅落盘)。 */
|
||||
* B_IA = -30 码(零点偏移,经 apply 精确对消——验证 b 参与全链而非仅落盘)。
|
||||
* r 的合成口径 = 1.0(Q16 = 65536):见文件头「变比 r 同步合成化为 1.0」那段。 */
|
||||
#include "adc_cal.h"
|
||||
#undef ADC_CAL_K_Q16_TPAFE
|
||||
#undef ADC_CAL_K_Q16_STM32
|
||||
#undef ADC_CAL_R_Q16_VOLT
|
||||
#undef ADC_CAL_R_Q16_CURR
|
||||
#define ADC_CAL_K_Q16_TPAFE (655360000L)
|
||||
#define ADC_CAL_K_Q16_STM32 (655360000L)
|
||||
#define ADC_CAL_R_Q16_VOLT (65536L) /* 合成口径 r = 1.0:本套件证"链条别丢量纲",不是变比数值 */
|
||||
#define ADC_CAL_R_Q16_CURR (65536L)
|
||||
#define K_DIM ADC_CAL_K_Q16_TPAFE
|
||||
#define B_IA_DIM (-30L)
|
||||
|
||||
@@ -456,6 +470,42 @@ static void tc_010_mask(void) {
|
||||
CHECK(prot_elm_active_mask() == (1uL << PE_OV), "010: mask = OV only");
|
||||
}
|
||||
|
||||
/* ===================== TC-PROTDIM-011 变比确实进折算 ===================== */
|
||||
/* 这条是「r 进折算」的反错配锁:配置段里写着 1000.0 还不够,必须证明它真的
|
||||
* 乘进了 adc_cal_apply——否则 r 就退化成「只存在配置里的一个数」,屏上与判据
|
||||
* 都会拿着引脚电压冒充二次值。手法:同一批码值,只改 IA 的变比,OC 判据
|
||||
* 收到的量必须恰好放大 1000 倍(期望值独立写出,不引实现常量)。 */
|
||||
static void tc_011_ratio_scales(void) {
|
||||
uint32_t i;
|
||||
double got;
|
||||
|
||||
printf("[TC-PROTDIM-011] ratio enters apply(): 1000x on IA trips OC1\n");
|
||||
dim_setup();
|
||||
isolate_oc_family(); /* 只留 OC1(10 A,dly=0 出厂档) */
|
||||
|
||||
/* 基线:合成口径 r = 1.0 -> 0.5 A 远低于 OC1 定值,静默 */
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.5, 100.0, 0.0); }
|
||||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||||
"011: r = 1.0 -> 0.5 A silent (baseline)");
|
||||
|
||||
/* 把 IA 变比改成 1000.0(Q16 = 1000 x 65536):同一码值,折算输出 x1000 */
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_IA, 65536000L) == 0, "011: IA ratio set to 1000.0");
|
||||
got = (double)adc_cal_apply(ADC_SEM_IA, dim_code(0.5, (double)B_IA_DIM));
|
||||
CHECK((got > 0.5 * 1000.0 * 0.999) && (got < 0.5 * 1000.0 * 1.001),
|
||||
"011: apply(code for 0.5 A) = 500 A (ratio really multiplies)");
|
||||
|
||||
/* 同一物理注入现在应顶穿 OC1(10 A) —— 变比真到了判据手里 */
|
||||
for (i = 0u; i < 5u; i++) { dim_beat(0.5, 100.0, 0.0); }
|
||||
CHECK((pick_of(PE_OC1) != 0) && (assert_of(PE_OC1) != 0),
|
||||
"011: same 0.5 A now trips OC1(10 A) -> ratio reached the criteria");
|
||||
|
||||
/* 改回 1.0:判据看到的又只是 0.5 A,应当返回 */
|
||||
CHECK(adc_cal_set_ratio(ADC_SEM_IA, 65536L) == 0, "011: IA ratio back to 1.0");
|
||||
for (i = 0u; i < 45u; i++) { dim_beat(0.5, 100.0, 0.0); }
|
||||
CHECK((pick_of(PE_OC1) == 0) && (assert_of(PE_OC1) == 0),
|
||||
"011: ratio back to 1.0 -> OC1 returns (the effect is reversible)");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Prot Dim Regression Tests ===\n");
|
||||
adc_cal_init(); /* 先把 RAM 系数置成合成 ROM(各 TC 内还会重跑) */
|
||||
@@ -469,6 +519,7 @@ int main(void) {
|
||||
tc_008_uv();
|
||||
tc_009_3u0();
|
||||
tc_010_mask();
|
||||
tc_011_ratio_scales();
|
||||
printf("\n=== Prot Dim Regression Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
return (s_failed == 0) ? 0 : 1;
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
* TC-RUNPAR-002 死区写往返 + 分档上限(V/A/Hz)+ NaN/负值拒 + 定点量化
|
||||
* TC-RUNPAR-003 落盘链:set→脏→store_save 成功清脏 + 文件含 9 键 +
|
||||
* load 回读往返 + 外部改文件值生效 + 无脏 save 空操作
|
||||
* TC-RUNPAR-004 104 读映射:0x8030~0x8038 命中 / 空隙与邻段 -1 / k·b 回显
|
||||
* TC-RUNPAR-004 104 读映射:0x8030~0x8038 命中 / 空隙与邻段 -1 / k·b·r 回显
|
||||
* TC-RUNPAR-005 104 两段式:选择段不提交不置脏,执行段提交置脏
|
||||
* TC-RUNPAR-006 k/b 直通:合法带接受与拒绝 / 非正 k 拒 / b 单侧 RMW 保 k /
|
||||
* 非法带值经 load 注入后回显 = 生效值(哨兵视图)
|
||||
* TC-RUNPAR-007 init 幂等:重复调用复位默认 + 清脏;重注册被引擎拒(留痕)
|
||||
* TC-RUNPAR-008 引擎副本 = 生效值(无回调):load 原地改副本即时可见
|
||||
* TC-RUNPAR-009 变比 r 的 104 读写:0x8050~0x8057 写 2000.0 回读一致 / 写 0
|
||||
* 接受且回读 0 / 0.5 与 -1 与 40000 拒 / 0x8058 非本段 / 选择段
|
||||
* 粗守只挡 NaN·Inf(0.5 在选择段返 0,执行段才拒——两段可不同)
|
||||
*
|
||||
* 通过:全部 TC PASS(main 返回 0)。
|
||||
* 创建日期:2026-10-25(道D 运行参数段)
|
||||
@@ -279,7 +282,11 @@ static void tc_104_read(void)
|
||||
"TC-RUNPAR-004 k echo last channel (IN, STM32 ROM)");
|
||||
CHECK((run_par_104_read(0x8048u, &v) == 0) && (v == 0.0f),
|
||||
"TC-RUNPAR-004 b echo factory zero");
|
||||
CHECK(run_par_104_read(0x8050u, &v) == -1, "TC-RUNPAR-004 above b block not owned");
|
||||
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
|
||||
"TC-RUNPAR-004 r echo first slot = ROM voltage family 1000.0");
|
||||
CHECK((run_par_104_read(0x8054u, &v) == 0) && (fabsf(v - 29.4118f) <= 1e-3f),
|
||||
"TC-RUNPAR-004 r echo current family = 29.4118");
|
||||
CHECK(run_par_104_read(0x8058u, &v) == -1, "TC-RUNPAR-004 above r block not owned");
|
||||
}
|
||||
|
||||
static void tc_104_two_step(void)
|
||||
@@ -376,6 +383,80 @@ static void tc_copy_is_effective(void)
|
||||
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-008 load itself is not a local edit");
|
||||
}
|
||||
|
||||
/* TC-RUNPAR-009:变比 r 的 104 读写(0x8050~0x8057)与两段式粗守面 */
|
||||
static void tc_104_ratio(void)
|
||||
{
|
||||
float v;
|
||||
|
||||
vfs_reset();
|
||||
reset_all_rp();
|
||||
/* 写 2000.0:电压族量级(2 倍于 ROM 默认 1000),回读 = 生效值 */
|
||||
CHECK(run_par_104_write(0x8050u, 2000.0f, 0u) == 0, "TC-RUNPAR-009 r 2000.0 accepted");
|
||||
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 2000.0f) <= 0.01f),
|
||||
"TC-RUNPAR-009 r readback = 2000.0");
|
||||
CHECK((adc_cal_ratio(ADC_SEM_UA) > 1999.99f) && (adc_cal_ratio(ADC_SEM_UA) < 2000.01f),
|
||||
"TC-RUNPAR-009 r reached adc_cal (write-through)");
|
||||
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-009 r execute marks dirty");
|
||||
|
||||
/* 写 0 = 显式关掉本路折算(成功路径,不是拒绝) */
|
||||
CHECK(run_par_104_write(0x8050u, 0.0f, 0u) == 0, "TC-RUNPAR-009 r 0 accepted (explicit off)");
|
||||
CHECK((run_par_104_read(0x8050u, &v) == 0) && (v == 0.0f), "TC-RUNPAR-009 r readback = 0");
|
||||
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 0u,
|
||||
"TC-RUNPAR-009 r 0 -> channel reports not configured");
|
||||
|
||||
/* 拒绝面:0 与 >=1.0 之外、以及非有限值 */
|
||||
CHECK(run_par_104_write(0x8050u, 0.5f, 0u) == -1, "TC-RUNPAR-009 r 0.5 rejected");
|
||||
CHECK(run_par_104_write(0x8050u, -1.0f, 0u) == -1, "TC-RUNPAR-009 r -1 rejected");
|
||||
CHECK(run_par_104_write(0x8050u, 40000.0f, 0u) == -1, "TC-RUNPAR-009 r 40000 rejected");
|
||||
{
|
||||
volatile float fnan = 0.0f;
|
||||
fnan = fnan / fnan;
|
||||
CHECK(run_par_104_write(0x8050u, fnan, 0u) == -1, "TC-RUNPAR-009 r NaN rejected");
|
||||
CHECK(run_par_104_write(0x8050u, fnan, 1u) == -1, "TC-RUNPAR-009 r NaN rejected in select too");
|
||||
}
|
||||
/* 上限端点(32767.99)在带内,接受 */
|
||||
CHECK(run_par_104_write(0x8050u, 32767.99f, 0u) == 0, "TC-RUNPAR-009 r upper band endpoint accepted");
|
||||
|
||||
/* 选择段:只粗守(NaN/Inf),0.5 这类"执行段会拒"的值在选择段返 0 */
|
||||
CHECK(run_par_104_write(0x8051u, 0.5f, 1u) == 0,
|
||||
"TC-RUNPAR-009 select only guards finiteness (0.5 passes)");
|
||||
CHECK((run_par_104_read(0x8051u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
|
||||
"TC-RUNPAR-009 select did not touch the ratio");
|
||||
CHECK(run_par_104_write(0x8051u, 0.5f, 0u) == -1, "TC-RUNPAR-009 execute rejects the same 0.5");
|
||||
CHECK((run_par_104_read(0x8051u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
|
||||
"TC-RUNPAR-009 rejected execute left the ratio intact");
|
||||
|
||||
/* 段边界:0x8057 是 r 块末地址,0x8058 起非本段 */
|
||||
CHECK(run_par_104_write(0x8057u, 29.4118f, 0u) == 0, "TC-RUNPAR-009 last r slot writable");
|
||||
CHECK(run_par_104_write(0x8058u, 2.0f, 0u) == -2, "TC-RUNPAR-009 0x8058 passes through to prot_set");
|
||||
CHECK(run_par_104_read(0x8058u, &v) == -1, "TC-RUNPAR-009 0x8058 not owned on read");
|
||||
|
||||
/* 落盘往返:脏 → save → 文件带 r_ua 键 → load 回读生效 */
|
||||
CHECK(run_par_104_write(0x8050u, 2000.0f, 0u) == 0, "TC-RUNPAR-009 seed 2000.0 before save");
|
||||
CHECK(run_par_store_save() == 0, "TC-RUNPAR-009 save ok");
|
||||
CHECK((run_par_dirty_peek() == 0u) && (s_vfs_exists == 1),
|
||||
"TC-RUNPAR-009 save cleared dirty and wrote file");
|
||||
CHECK(strstr((const char *)s_vfs, "r_ua=131072000") != NULL,
|
||||
"TC-RUNPAR-009 file carries r_ua=131072000 (= 2000 x 65536)");
|
||||
(void)run_par_104_write(0x8050u, 1000.0f, 0u); /* 覆盖内存态 */
|
||||
CHECK(cfg_file_load() == 0, "TC-RUNPAR-009 engine reload ok");
|
||||
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 2000.0f) <= 0.01f),
|
||||
"TC-RUNPAR-009 reload restores saved ratio");
|
||||
|
||||
/* 回显 = 生效值视图:文件塞越带 r(< 1.0)→ 哨兵按 0 报,不回显原始坏值 */
|
||||
{
|
||||
static const char s_badr[] =
|
||||
"[adc_cal_sec]\r\nr_ua=1\r\n";
|
||||
(void)memcpy(s_vfs, s_badr, sizeof(s_badr) - 1u);
|
||||
s_vfs_len = sizeof(s_badr) - 1u;
|
||||
s_vfs_exists = 1;
|
||||
s_vfs_mtime++;
|
||||
}
|
||||
CHECK(cfg_file_load() == 0, "TC-RUNPAR-009 poisoned ratio file loaded");
|
||||
CHECK((run_par_104_read(0x8050u, &v) == 0) && (v == 0.0f),
|
||||
"TC-RUNPAR-009 echo = effective (out-of-band reported as 0)");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("=== RunPar Layer Host Tests ===\n");
|
||||
@@ -387,6 +468,7 @@ int main(void)
|
||||
tc_104_cal_proxy();
|
||||
tc_init_idempotent();
|
||||
tc_copy_is_effective();
|
||||
tc_104_ratio();
|
||||
printf("=== RunPar Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
return (s_failed == 0) ? 0 : 1;
|
||||
|
||||
+114
-44
@@ -74,15 +74,18 @@ App/algo/wave_store.c must agree byte-for-byte -- if they ever diverge,
|
||||
report it, do not silently re-rule from one side):
|
||||
- CRC-16/MODBUS table-free (0x8005 reflected / init 0xFFFF / xorout 0),
|
||||
both CRC fields stored LOW byte first; anchor "123456789" -> 0x4B37.
|
||||
Header CRC covers [0, 30 + 12*ch_count); data CRC covers the data area
|
||||
and sits at end of file; 8ch/320pts whole segment = 5250 B.
|
||||
- Channel table entry (12 B): sem 1 + src 1 + bits 1 + availability 1
|
||||
Header CRC covers [0, 30 + 16*ch_count); data CRC covers the data area
|
||||
and sits at end of file; 8ch/320pts whole segment = 5282 B.
|
||||
- Channel table entry (16 B, v2): sem 1 + src 1 + bits 1 + availability 1
|
||||
(= adc_cal_flags(sem) low 8 bits, raw, no invented bit meanings) +
|
||||
k_q16 i32 (uV/code * 65536) + b_codes i32 (code domain). There is NO
|
||||
k_q16 i32 (uV/code * 65536) + b_codes i32 (code domain) +
|
||||
r_q16 i32 (ratio * 65536, 0 = no scaling on this channel). There is NO
|
||||
separate LSB field: LSB == k_q16, uV = k_q16/65536, never re-scaled.
|
||||
mV = (code + b_codes) * k_q16/65536/1000 -- exactly the
|
||||
adc_cal_apply() formula. Header eng only LABELS the unit (0 =
|
||||
pin-domain, 1 secondary, 2 primary); it never changes arithmetic.
|
||||
mV = (code + b_codes) * k_q16/65536/1000, then * r_q16/65536 when
|
||||
r_q16 != 0 -- exactly the adc_cal_apply() formula, computed in the same
|
||||
TWO integer steps as the C side (one expression overflows int64).
|
||||
Header eng only LABELS the unit (0 = pin-domain, 1 = secondary,
|
||||
2 = primary); it never changes arithmetic.
|
||||
- flags_seq bit s = ADC_CAL_F_VALID of sem s; clip_mask bit s = sem s
|
||||
(same mapping as App/algo/meas.h; V0.1 constant 0xE6 = UB UC IB IC IN).
|
||||
avail is the RAW pass-through of adc_cal_flags(): VALID and CLIPPED are
|
||||
@@ -91,17 +94,19 @@ report it, do not silently re-rule from one side):
|
||||
VALID/CLIPPED relation nor on avail-vs-clip_mask drift -- those get a
|
||||
soft note only ("VALID+CLIPPED(both)" in dump/fft, a WARN line in dump),
|
||||
no exit-code change, nothing refused.
|
||||
eng likewise derives from flags_seq on the board (feed_eng(): all 8
|
||||
electrical channels VALID -> secondary(1), else pin-domain(0)); a
|
||||
pairing mismatch here is also a SOFT WARN only, never a rejection.
|
||||
- v1 pins ch_count = 8 (TEMP not recorded; TEMP needs a version bump
|
||||
together with the TEMP-in-flags question); PC must verify table entry
|
||||
eng derives from the per-channel RATIO on the board (feed_eng(): every
|
||||
electrical channel either has r configured or is clipped-and-not-shown);
|
||||
a pairing mismatch here is also a SOFT WARN only, never a rejection.
|
||||
- v1/v2 pins ch_count = 8 (TEMP not recorded; TEMP needs a version bump
|
||||
together with the TEMP-in-flags question); v2 = v1 + i32 r_q16 per
|
||||
channel-table entry (12 -> 16 B, head 128 -> 160, segment 5250 -> 5282).
|
||||
PC must verify table entry
|
||||
i carries sem == i (ascending UA..IN), mismatch voids the whole
|
||||
segment; data column i == table entry i; samples int16 LE, TPAFE two's
|
||||
complement, STM32 0..4095 stored positive with NO midpoint bias
|
||||
(biasing it would fabricate bipolarity).
|
||||
- t0_ms / wall / frame_seq are u32 little-endian. ver must equal magic
|
||||
byte 2 and both must equal 1, else refuse.
|
||||
byte 2 and both must equal FMT_VER, else refuse.
|
||||
"""
|
||||
import argparse
|
||||
import math
|
||||
@@ -114,7 +119,7 @@ import tempfile
|
||||
# ---- format constants (design section 6.2, little-endian) -------------------
|
||||
MAGIC_W = 0x57 # 'W'
|
||||
MAGIC_R = 0x52 # 'R'
|
||||
FMT_VER = 1 # byte 2 of magic AND byte 3 (ver) both == 1
|
||||
FMT_VER = 2 # byte 2 of magic AND byte 3 (ver) both == 2
|
||||
SEG_LABEL_SAMPLE = "WAVE%02d" % 0 # sample segment label, produced by format so
|
||||
# this file never carries a hardcoded letter+digit identifier token
|
||||
|
||||
@@ -133,10 +138,11 @@ OFF_FLAGS = 23 # 1 validity bitmap (see Q5)
|
||||
OFF_CLIP = 24 # 2 clip bitmap, bit s = sem s (see Q6)
|
||||
OFF_FSEQ = 26 # 4 frame_seq at segment head
|
||||
HDR_FIXED = 30 # bytes before channel table
|
||||
ENT_SIZE = 12 # bytes per channel-table entry
|
||||
ENT_SIZE = 16 # bytes per channel-table entry (v2: 12 -> 16)
|
||||
ENT_SEM, ENT_SRC, ENT_BITS, ENT_AVAIL = 0, 1, 2, 3
|
||||
ENT_KQ = 4 # i32 Q16 uV/code == the LSB (sections 6.2/6.3)
|
||||
ENT_B = 8 # i32 code-domain zero offset
|
||||
ENT_RQ16 = 12 # i32 Q16 ratio (terminal/primary ratio); 0 = no scaling
|
||||
|
||||
SRC_TPAFE, SRC_STM32 = 0, 1
|
||||
SRC_NAMES = {SRC_TPAFE: "TPAFE5160", SRC_STM32: "STM32-ADC1"}
|
||||
@@ -198,17 +204,55 @@ def put_crc16(value):
|
||||
# ---- decode helpers ----------------------------------------------------------
|
||||
def lsb_uv(ent):
|
||||
"""LSB in micro-volts per code = k_q16 / 65536 (section 6.3: k_q16 IS
|
||||
the LSB; never re-multiplied)."""
|
||||
the LSB; never re-multiplied). This is the PIN-side slope only -- the
|
||||
terminal ratio (r_q16) is a separate factor, see code_to_mv()."""
|
||||
return ent["k_q16"] / 65536.0
|
||||
|
||||
|
||||
def ent_scale_mv(ent):
|
||||
"""Floating-point mV per code in the RECORDING DOMAIN (test expectation
|
||||
helper): k slope via lsb_uv(), times the Q16 ratio when r_q16 != 0.
|
||||
Analytic expectations (e.g. sine RMS) use this so they stay in step with
|
||||
code_to_mv() without duplicating the two-step integer rounding."""
|
||||
base = lsb_uv(ent) / 1000.0
|
||||
if ent["r_q16"] == 0:
|
||||
return base
|
||||
return base * ent["r_q16"] / 65536.0
|
||||
|
||||
|
||||
def code_to_mv(ent, code):
|
||||
"""Code -> mV via exactly the adc_cal_apply() formula:
|
||||
value = (code + b) * k[us/code] * 1e-6 [V] -> mV = that * 1000.
|
||||
The header eng field only labels the unit (0 = pin-domain); per
|
||||
section 6.3 it must never change this arithmetic, and the PC must not
|
||||
re-guess units from the current firmware state."""
|
||||
return (code + ent["b_codes"]) * lsb_uv(ent) / 1000.0
|
||||
"""Code -> engineering millivolt, mathematically identical to the C side
|
||||
(App/algo/wave_store.c wave_store_code_to_milli); a divergent rounding
|
||||
rule would make the two independent implementations disagree on the last
|
||||
digit of every sample, so the rule is mirrored on purpose.
|
||||
|
||||
t = (code + b_codes) * k_q16 uV * 65536, up to ~2.2e12
|
||||
r_q16 == 0 -> mv = round_half_away(t / 65536000) (= 1000 * 2**16)
|
||||
r_q16 != 0 -> mv = round_half_away(t * r_q16 / (1000 * 2**32))
|
||||
|
||||
One rounding, at the end, in the recording DOMAIN: secondary mV/mA when
|
||||
r_q16 != 0, pin mV when r_q16 == 0. The header eng field only labels
|
||||
which of those it is; it never changes this arithmetic.
|
||||
|
||||
Why the C side cannot write that one expression: t * r_q16 reaches
|
||||
4.6e21, past int64 (9.2e18), so it splits t = 1000*q + s and recombines
|
||||
the remainders -- algebraically the same single rounding. Python has
|
||||
arbitrary-precision ints, so this reader evaluates the exact value and
|
||||
keeps the two implementations in step.
|
||||
"""
|
||||
t = (code + ent["b_codes"]) * ent["k_q16"]
|
||||
r = ent["r_q16"]
|
||||
if r == 0:
|
||||
return _div_round(t, 65536000)
|
||||
return _div_round(t * r, 1000 * (1 << 32))
|
||||
|
||||
|
||||
def _div_round(num, den):
|
||||
"""Integer division rounding half AWAY from zero -- mirrors
|
||||
wave_store.c wave_st_div_round() including the negative branch."""
|
||||
if num >= 0:
|
||||
return (num + den // 2) // den
|
||||
return -((-num + den // 2) // den)
|
||||
|
||||
|
||||
def rail_thresholds(ent):
|
||||
@@ -303,6 +347,7 @@ def parse_segment(buf, name="segment"):
|
||||
"avail": buf[o + ENT_AVAIL],
|
||||
"k_q16": struct.unpack_from("<i", buf, o + ENT_KQ)[0],
|
||||
"b_codes": struct.unpack_from("<i", buf, o + ENT_B)[0],
|
||||
"r_q16": struct.unpack_from("<i", buf, o + ENT_RQ16)[0],
|
||||
})
|
||||
|
||||
samples = []
|
||||
@@ -472,20 +517,20 @@ def fmt_dump(seg):
|
||||
% (seg["crc_data"], seg["pts"] * seg["chc"] * 2,
|
||||
"low" if CRC_LO_FIRST else "high"))
|
||||
add("-- channel table (%d entries x %d B) --" % (seg["chc"], ENT_SIZE))
|
||||
add("%3s %3s %-5s %-10s %4s %5s %11s %9s %12s %10s"
|
||||
add("%3s %3s %-5s %-10s %4s %5s %11s %9s %11s %12s %10s"
|
||||
% ("IDX", "SEM", "NAME", "SRC", "BITS", "AVAIL", "K_Q16",
|
||||
"B_CODES", "LSB_uV", "FLAG"))
|
||||
"B_CODES", "R_Q16", "LSB_uV", "FLAG"))
|
||||
for ent in seg["entries"]:
|
||||
fl = []
|
||||
if ent["avail"] & ADC_CAL_F_VALID:
|
||||
fl.append("VALID")
|
||||
if ent["avail"] & ADC_CAL_F_CLIPPED:
|
||||
fl.append("CLIPPED")
|
||||
add("%3d %3d %-5s %-10s %4d 0x%02X %11d %9d %12.6f %10s"
|
||||
add("%3d %3d %-5s %-10s %4d 0x%02X %11d %9d %11d %12.6f %10s"
|
||||
% (ent["idx"], ent["sem"], sem_name(ent["sem"]),
|
||||
SRC_NAMES.get(ent["src"], "SRC%d" % ent["src"]),
|
||||
ent["bits"], ent["avail"], ent["k_q16"], ent["b_codes"],
|
||||
lsb_uv(ent), "|".join(fl) if fl else "."))
|
||||
ent["r_q16"], lsb_uv(ent), "|".join(fl) if fl else "."))
|
||||
# lead ruling 2026-10-29: soft notes, never a reject, never an exit code
|
||||
both = [sem_name(e["sem"]) for e in seg["entries"]
|
||||
if (e["avail"] & (ADC_CAL_F_VALID | ADC_CAL_F_CLIPPED))
|
||||
@@ -583,9 +628,11 @@ def fmt_fft(seg, cycle, all_cycles, only_ch):
|
||||
SRC_NAMES.get(ent["src"], "?"), "ALL",
|
||||
sum(rmss) / len(rmss), min(rmss), max(rmss),
|
||||
len(rmss)))
|
||||
lines.append("note: mV = (code + b_codes) * k_q16/65536/1000 "
|
||||
"(adc_cal_apply formula, unit labeled by header eng, "
|
||||
"never re-guessed); full-cycle window, coefficients = "
|
||||
lines.append("note: mV = (code + b_codes) * k_q16/65536/1000, then "
|
||||
"* r_q16/65536 when r_q16 != 0 (adc_cal_apply formula, same "
|
||||
"two integer steps as the C side; result is in the "
|
||||
"recording domain, labeled by header eng, never "
|
||||
"re-guessed); full-cycle window, coefficients = "
|
||||
"sin/cos(2*pi*k/N)*sqrt(2)/N as in App/algo/phasor.c; "
|
||||
"phase = atan2(im,re) deg")
|
||||
return lines
|
||||
@@ -795,7 +842,7 @@ def cmd_plot(args):
|
||||
|
||||
|
||||
# ---- synthetic segment builders (selftest) ------------------------------------
|
||||
def syn_entries(chc=8, avail_mode="v01"):
|
||||
def syn_entries(chc=8, avail_mode="v01", ratio_mode="pin"):
|
||||
"""avail = raw pass-through of adc_cal_flags(). Modes (lead ruling
|
||||
2026-10-29 asks for the first two as interop shapes):
|
||||
v01 = V0.1 board truth, pre-calibration: TPAFE trio 0x00, STM32 five
|
||||
@@ -803,15 +850,34 @@ def syn_entries(chc=8, avail_mode="v01"):
|
||||
both = deliberately VALID+CLIPPED on the five: 0x01 TPAFE / 0x03 STM32
|
||||
(legal: soft note only, must still parse clean)
|
||||
none = all-zero avail under clip_mask 0xE6 (avail/header drift; soft
|
||||
WARN only, must still parse clean)"""
|
||||
WARN only, must still parse clean)
|
||||
|
||||
ratio_mode picks the v2 r_q16 snapshot (ROM defaults from adc_cal.h):
|
||||
rom = board defaults: voltage family 65536000 (1000.0), current family
|
||||
1927532 (29.4118 A/V) -- sem 0/3 voltage (UA/UN), 4 current (IA)
|
||||
pin = all zero r_q16 (no scaling; output stays pin-domain mV)
|
||||
one = all 65536 (ratio exactly 1.0: must equal the old pin-domain value)
|
||||
"""
|
||||
pa_t, pa_s = {"v01": (0x00, 0x02), "both": (0x01, 0x03),
|
||||
"none": (0x00, 0x00)}[avail_mode]
|
||||
rom = {0: 65536000, 3: 65536000, 4: 1927532}
|
||||
ents = []
|
||||
for sem in range(chc):
|
||||
if sem in (0, 3, 4):
|
||||
ents.append((sem, SRC_TPAFE, 16, pa_t, 10000000, 0))
|
||||
kq16 = 10000000
|
||||
src, bits, avail = SRC_TPAFE, 16, pa_t
|
||||
else:
|
||||
ents.append((sem, SRC_STM32, 12, pa_s, 52800000, 0))
|
||||
kq16 = 52800000
|
||||
src, bits, avail = SRC_STM32, 12, pa_s
|
||||
if ratio_mode == "rom":
|
||||
rq16 = rom.get(sem, 0)
|
||||
elif ratio_mode == "pin":
|
||||
rq16 = 0
|
||||
elif ratio_mode == "one":
|
||||
rq16 = 65536
|
||||
else:
|
||||
raise ValueError("unknown ratio_mode %r" % (ratio_mode,))
|
||||
ents.append((sem, src, bits, avail, kq16, 0, rq16))
|
||||
return ents
|
||||
|
||||
|
||||
@@ -842,7 +908,7 @@ def syn_samples(pts=320, pps=20, chc=8, rail=True):
|
||||
|
||||
def build_synthetic(chc=8, pts=320, pps=20, trig_src=1, clip=0x00E6,
|
||||
flags=0x00, wall=0, t0=123456, fseq=987654321,
|
||||
avail_mode="v01", eng=0, rail=True):
|
||||
avail_mode="v01", eng=0, rail=True, ratio_mode="pin"):
|
||||
hdr = bytearray()
|
||||
hdr += bytes([MAGIC_W, MAGIC_R, FMT_VER, FMT_VER])
|
||||
hdr += struct.pack("<H", 1000)
|
||||
@@ -856,10 +922,10 @@ def build_synthetic(chc=8, pts=320, pps=20, trig_src=1, clip=0x00E6,
|
||||
hdr += struct.pack("<H", clip)
|
||||
hdr += struct.pack("<I", fseq)
|
||||
assert len(hdr) == HDR_FIXED
|
||||
for sem, src, bits, avail, kq16, b_codes in syn_entries(chc,
|
||||
avail_mode)[:chc]:
|
||||
for sem, src, bits, avail, kq16, b_codes, rq16 in syn_entries(
|
||||
chc, avail_mode, ratio_mode)[:chc]:
|
||||
hdr += bytes([sem, src, bits, avail])
|
||||
hdr += struct.pack("<ii", kq16, b_codes)
|
||||
hdr += struct.pack("<iii", kq16, b_codes, rq16)
|
||||
hdr += put_crc16(crc16_modbus(hdr))
|
||||
data = b"".join(struct.pack("<%dh" % pts, *col)
|
||||
for col in syn_samples(pts, pps, chc, rail))
|
||||
@@ -915,7 +981,7 @@ def selftest():
|
||||
# parser (both CRC gates passing inside parse == implicit PASS)
|
||||
img = build_synthetic()
|
||||
seg = parse_segment(img, name="synth8")
|
||||
ck.check("size 8ch/320pts == 5250", seg["size"] == 5250,
|
||||
ck.check("size 8ch/320pts == 5282", seg["size"] == 5282,
|
||||
"got %d" % seg["size"])
|
||||
fields = dict(fs_hz=1000, pps=20, chc=8, pts=320, pre=160, trig_src=1,
|
||||
trig_elm=0, t0_ms=123456, wall=0, eng=0, flags_seq=0,
|
||||
@@ -923,14 +989,18 @@ def selftest():
|
||||
for k in sorted(fields):
|
||||
ck.check("field %s round-trip" % k, seg[k] == fields[k],
|
||||
"got %r" % (seg[k],))
|
||||
ck.check("table entry 0 == sem0/src0/bits16/avail0x00/k_q16 10000000",
|
||||
ck.check("table entry 0 == sem0/src0/bits16/avail0x00/k_q16 10000000/"
|
||||
"r_q16 0 (pin mode)",
|
||||
(seg["entries"][0]["sem"], seg["entries"][0]["src"],
|
||||
seg["entries"][0]["bits"], seg["entries"][0]["avail"],
|
||||
seg["entries"][0]["k_q16"]) == (0, 0, 16, 0x00, 10000000))
|
||||
ck.check("table entry 1 == sem1/src1/bits12/avail0x02/k_q16 52800000",
|
||||
seg["entries"][0]["k_q16"],
|
||||
seg["entries"][0]["r_q16"]) == (0, 0, 16, 0x00, 10000000, 0))
|
||||
ck.check("table entry 1 == sem1/src1/bits12/avail0x02/k_q16 52800000/"
|
||||
"r_q16 0 (pin mode)",
|
||||
(seg["entries"][1]["sem"], seg["entries"][1]["src"],
|
||||
seg["entries"][1]["bits"], seg["entries"][1]["avail"],
|
||||
seg["entries"][1]["k_q16"]) == (1, 1, 12, 0x02, 52800000))
|
||||
seg["entries"][1]["k_q16"],
|
||||
seg["entries"][1]["r_q16"]) == (1, 1, 12, 0x02, 52800000, 0))
|
||||
# per-entry availability (low 8 bits of adc_cal_flags) vs header
|
||||
# clip_mask: the CLIPPED bits must reconcile, bit s = sem s both sides
|
||||
clip_from_avail = 0
|
||||
@@ -947,7 +1017,7 @@ def selftest():
|
||||
img_b = build_synthetic(avail_mode="both", flags=0xFF, fseq=71)
|
||||
segb = parse_segment(img_b, name="synth-both") # raises if wrongly voided
|
||||
ck.check("both-form (flags 0xFF, avail 0x01/0x03) parses, not rejected",
|
||||
segb["size"] == 5250)
|
||||
segb["size"] == 5282)
|
||||
ck.check("dump both-form carries VALID+CLIPPED(both) note",
|
||||
"VALID+CLIPPED(both)" in "\n".join(fmt_dump(segb)))
|
||||
fftb = "\n".join(fmt_fft(segb, None, False, range(segb["chc"])))
|
||||
@@ -1051,7 +1121,7 @@ def selftest():
|
||||
mv = channel_mv(seg, ci)
|
||||
win = mv[-pps:]
|
||||
rms1, ang1 = phasor_rms_ang(win, 1, pps)
|
||||
want_rms = amp_codes * lsb_uv(ent) / 1000.0 / math.sqrt(2.0)
|
||||
want_rms = amp_codes * ent_scale_mv(ent) / math.sqrt(2.0)
|
||||
ck.near("fft ch%d RMS" % ci, rms1, want_rms, 0.01)
|
||||
ck.near("fft ch%d phase (atan2(im,re) convention)" % ci, ang1,
|
||||
phi_deg, 0.01, abs_tol=2.0)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
|
||||
* 用例:形态与预算钉死:int16×320×8 = 5120、两半各 160、
|
||||
* sizeof(wave_seg_meta_t) = 136、sizeof(wave_ch_desc_t) = 12
|
||||
* sizeof(wave_seg_meta_t) = 172、sizeof(wave_ch_desc_t) = 16
|
||||
* 用例:init 复位语义:IDLE / 不可读 / 计数与轮转号归零
|
||||
* 用例:参数守卫:push / request / latch / read / get_stat 的非法入参
|
||||
* 用例:触发到封段的时序:封段恰在触发后第 160 拍,事件一次性
|
||||
@@ -162,8 +162,11 @@ int main(void) {
|
||||
CHECK(((uint32_t)WAVE_RING_PTS * (uint32_t)WAVE_RING_CH * 2u) == WAVE_RING_BYTES, "环长等式");
|
||||
CHECK(WAVE_RING_PTS == (WAVE_RING_PRE + WAVE_RING_POST), "段长 = 预触发 + 后触发");
|
||||
CHECK(WAVE_RING_PRE == 160u, "预触发 160 点 = 8 周波 @20 点/周波");
|
||||
CHECK(sizeof(wave_ch_desc_t) == (size_t)WAVE_ST_CH_BYTES, "通道表一项正好 12 字节");
|
||||
CHECK(sizeof(wave_seg_meta_t) == 136u, "段头快照 136 字节(本卡静态增量台账用)");
|
||||
CHECK(sizeof(wave_ch_desc_t) == (size_t)WAVE_ST_CH_BYTES, "通道表一项正好 16 字节(v2 加 r_q16 后)");
|
||||
/* 段头快照的内存形态 = 固定头 26 + ch[9] × 16 = 170,按 4 字节对齐落到 172
|
||||
* (v1 是 26 + 9×12 = 134 → 136)。这份数供静态增量台账引用,写死是为了让
|
||||
* 「结构一改就红」,不是抄某个文档的算术。 */
|
||||
CHECK(sizeof(wave_seg_meta_t) == 172u, "段头快照 172 字节(本卡静态增量台账用)");
|
||||
|
||||
/* ---------- 用例:init 复位 ---------- */
|
||||
printf("-- 用例:init 复位语义 --\n");
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
做什么:
|
||||
用本机 MSVC 编译 test/host/wave_store/wave_store_test.c(它以 #include 纳入
|
||||
**生产源文件** App/algo/wave_store.c),在 PC 上离线跑 CRC-16/MODBUS 规范锚点、
|
||||
整段长度等式(32 + 12*ch + pts*ch*2 + 2 == 5250)、段头编解码往返、拒绝清单、
|
||||
整段长度等式(32 + 16*ch + pts*ch*2 + 2 == 5282)、段头编解码往返、拒绝清单、
|
||||
按通道分列的偏移换算、打包游标三段推进与四条拒绝路径、自产字节自检、段文件名
|
||||
轮转与反解,失败即非 0 退出。零硬件、零烧录、毫秒级。顺带把一段合成段写到 %TEMP%
|
||||
供 PC 侧读段脚本(test/host/wave_read.py)做只读互验(不参与判定)。
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
*
|
||||
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
|
||||
* 用例:CRC-16/MODBUS:规范锚点 0x4B37、自检、增量与一次算完等价、空输入
|
||||
* 用例:尺寸等式(§6.3 / lead 点名):32 + 12*ch + pts*ch*2 + 2 == 5250,
|
||||
* 头 CRC 位置 126、数据区起点 128、9 路 1000 点变体同样成立
|
||||
* 用例:尺寸等式(§6.3 / lead 点名):32 + 16*ch + pts*ch*2 + 2 == 5282,
|
||||
* 头 CRC 位置 158、数据区起点 160、9 路 1000 点变体同样成立
|
||||
* 用例:meta_init 默认值与通道表:sem = 下标、conv/bits 两片转换器映射、
|
||||
* 封存瞬间事实留 0;0xE6 的算术交叉验证(通道顺序证据)
|
||||
* 用例:段头编解码往返:13 个标量 + 8 项表逐字段,含负系数与大 wall
|
||||
@@ -63,19 +63,40 @@ static wave_seg_meta_t s_meta;
|
||||
static wave_seg_meta_t s_back;
|
||||
|
||||
/*
|
||||
* 段头单位标签与逐路有效位图的**平台派生式**(本体在 App/app/ui_data_feed.c 的 feed_eng(),
|
||||
* 段头填写在 App/task/adc_task.c 的 532 至 553 行):电气 8 路全 VALID 才升二次值档位,
|
||||
* 否则停在引脚域。本层不参与这条派生(wave_store 不链 adc_cal),放这里只为钉一件事:
|
||||
* **夹具必须与平台派生式自洽**。一份真机写不出来的字节,验的是两个读者对不可能状态的
|
||||
* 行为,正好掩盖真正的分歧(lead 裁定 2026-10-29,本套上一版就把 eng 留成引脚域而
|
||||
* flags_seq 全置位,那份构造段没资格当互验夹具)。
|
||||
* 段头单位标签的**平台派生式**(本体在 App/task/adc_task.c 的封存交棒块,
|
||||
* 口径说明在 App/app/ui_data_feed.c 的 feed_eng()):每一路电气量「要么已配变比、
|
||||
* 要么削顶(页面本来就显 '-',不参与整帧量纲断言)」,八路全满足才升二次值档位。
|
||||
*
|
||||
* ⚠ v2 之前这条派生式看的是 flags_seq(八路全 VALID);加了变比维之后判据换成
|
||||
* **r_q16 是否已配**:VALID 回答「准不准」、r 回答「什么单位」,V0.1 五路削顶永远
|
||||
* 拿不到 VALID,用 VALID 当量纲判据会让档位永不解除。本夹具必须跟着平台派生式走
|
||||
* (lead 裁定 2026-10-29:一份真机写不出来的字节,验的是两个读者对不可能状态的行为,
|
||||
* 正好掩盖真正的分歧)。削顶位取表内 avail 的 CLIPPED 位(= adc_cal_flags 原值透传)。
|
||||
*/
|
||||
static uint8_t expect_eng(uint8_t a_flags_seq) {
|
||||
return (a_flags_seq == 0xFFu) ? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
|
||||
static uint8_t expect_eng(const wave_seg_meta_t *p_m) {
|
||||
uint8_t i;
|
||||
uint8_t all_ok = 1u;
|
||||
|
||||
for (i = 0u; i < p_m->ch_count; i++) {
|
||||
if (((p_m->ch[i].avail & 0x02u) == 0u) && (p_m->ch[i].r_q16 == 0)) {
|
||||
all_ok = 0u;
|
||||
}
|
||||
}
|
||||
return (all_ok != 0u) ? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
|
||||
}
|
||||
|
||||
static int eng_selfconsistent(const wave_seg_meta_t *p_m) {
|
||||
return (p_m->eng == expect_eng(p_m->flags_seq)) ? 1 : 0;
|
||||
return (p_m->eng == expect_eng(p_m)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 字节级:把整头解出来再看派生式是否成立(判据只看已落进字节的事实) */
|
||||
static int head_eng_consistent(const uint8_t *p_head) {
|
||||
wave_seg_meta_t m;
|
||||
|
||||
if (wave_store_head_parse(p_head, (uint32_t)WAVE_ST_HEAD_SZ_CUR, &m) != WAVE_ST_OK) {
|
||||
return 0;
|
||||
}
|
||||
return (m.eng == expect_eng(&m)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 一份「像样的」段头:格式侧默认值 + 封存瞬间事实(与 PC 侧口径一致) */
|
||||
@@ -104,6 +125,11 @@ static void fill_meta(wave_seg_meta_t *p_m) {
|
||||
* 解码器不据此判废(lead 裁定 2026-10-29)。削顶位与 clip_mask 的对应关系仍要保持
|
||||
* 自洽(bit1 = 削顶,聚合起来正好 0xE6),那是数据自身的一致性,不是格式不变式。 */
|
||||
p_m->ch[i].avail = (uint8_t)(tpa ? 0x01u : 0x03u);
|
||||
/* v2:变比维。本份「已标定后仍削顶」形态里,非削顶的三路 TPAFE 已配变比
|
||||
* (ROM 电压族 65536000 = 1000.0),削顶的五路片内 ADC 也照 ROM 电流族
|
||||
* (1927532)填上 —— 削顶与否不影响整帧量纲断言,但填了才与 eng = 二次值
|
||||
* 自洽(见 expect_eng)。真值形态(一路都没配变比、eng = 引脚域)由 v01 覆盖。 */
|
||||
p_m->ch[i].r_q16 = tpa ? 65536000 : 1927532;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +232,8 @@ static int meta_same(const wave_seg_meta_t *p_a, const wave_seg_meta_t *p_b) {
|
||||
for (i = 0u; i < p_a->ch_count; i++) {
|
||||
if ((p_a->ch[i].sem != p_b->ch[i].sem) || (p_a->ch[i].conv != p_b->ch[i].conv) ||
|
||||
(p_a->ch[i].bits != p_b->ch[i].bits) || (p_a->ch[i].avail != p_b->ch[i].avail) ||
|
||||
(p_a->ch[i].k_q16 != p_b->ch[i].k_q16) || (p_a->ch[i].b_codes != p_b->ch[i].b_codes)) {
|
||||
(p_a->ch[i].k_q16 != p_b->ch[i].k_q16) || (p_a->ch[i].b_codes != p_b->ch[i].b_codes) ||
|
||||
(p_a->ch[i].r_q16 != p_b->ch[i].r_q16)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -241,23 +268,23 @@ int main(void) {
|
||||
|
||||
/* ---------- 用例:尺寸等式 ---------- */
|
||||
printf("-- 用例:尺寸等式 --\n");
|
||||
CHECK(WAVE_ST_HEAD_CRC_POS(8u) == 126u, "头 CRC 位置 = 30 + 12*8");
|
||||
CHECK(WAVE_ST_HEAD_BYTES(8u) == 128u, "整头 = 128 字节");
|
||||
CHECK(WAVE_ST_HEAD_CRC_POS(8u) == 158u, "头 CRC 位置 = 30 + 16*8");
|
||||
CHECK(WAVE_ST_HEAD_BYTES(8u) == 160u, "整头 = 160 字节");
|
||||
CHECK(WAVE_ST_HEAD_SZ_CUR == WAVE_ST_HEAD_BYTES((uint8_t)WAVE_ST_CH_NUM), "当前值宏与参数化宏同源");
|
||||
CHECK(WAVE_ST_DATA_BYTES(8u, 320u) == 5120u, "数据区 = 5120 字节");
|
||||
CHECK(WAVE_ST_SEG_TOTAL(8u, 320u) == 5250u, "整段 = 5250 字节");
|
||||
CHECK(WAVE_ST_SEG_TOTAL(8u, 320u) == 5282u, "整段 = 5282 字节");
|
||||
CHECK(WAVE_ST_SEG_SZ_CUR == WAVE_ST_SEG_TOTAL((uint8_t)WAVE_ST_CH_NUM, (uint16_t)WAVE_ST_PTS_DEF),
|
||||
"整段当前值宏与参数化宏同源");
|
||||
CHECK((32u + (12u * 8u) + (320u * 8u * 2u) + 2u) == 5250u,
|
||||
"等式 32 + 12*ch + pts*ch*2 + 2 == 5250(接线道拿它比对 f_write 写回数)");
|
||||
CHECK(WAVE_ST_SEG_TOTAL(9u, 1000u) == (32u + 108u + 18000u + 2u), "9 路 1000 点变体同式(每周波点数是数据)");
|
||||
CHECK((32u + (16u * 8u) + (320u * 8u * 2u) + 2u) == 5282u,
|
||||
"等式 32 + 16*ch + pts*ch*2 + 2 == 5282(接线道拿它比对 f_write 写回数)");
|
||||
CHECK(WAVE_ST_SEG_TOTAL(9u, 1000u) == (32u + 144u + 18000u + 2u), "9 路 1000 点变体同式(每周波点数是数据)");
|
||||
fill_meta(&s_meta);
|
||||
CHECK(wave_store_seg_size(&s_meta) == 5250u, "seg_size 按头字段算出 5250");
|
||||
CHECK(wave_store_head_bytes(8u) == 128u, "head_bytes(8)");
|
||||
CHECK(wave_store_seg_size(&s_meta) == 5282u, "seg_size 按头字段算出 5282");
|
||||
CHECK(wave_store_head_bytes(8u) == 160u, "head_bytes(8)");
|
||||
CHECK((wave_store_head_bytes(0u) == 0u) && (wave_store_head_bytes(10u) == 0u), "非法通道数返回 0");
|
||||
CHECK(wave_store_data_off(8u, 320u, 0u, 0u) == 128u, "第 0 列点 0 从整头之后开始");
|
||||
CHECK(wave_store_data_off(8u, 320u, 1u, 0u) == (128u + 640u), "按通道分列:第 1 列 = 128 + 320*2");
|
||||
CHECK(wave_store_data_off(8u, 320u, 7u, 319u) == (5250u - 2u - 2u), "最后一列最后一点贴数据 CRC");
|
||||
CHECK(wave_store_data_off(8u, 320u, 0u, 0u) == 160u, "第 0 列点 0 从整头之后开始");
|
||||
CHECK(wave_store_data_off(8u, 320u, 1u, 0u) == (160u + 640u), "按通道分列:第 1 列 = 160 + 320*2");
|
||||
CHECK(wave_store_data_off(8u, 320u, 7u, 319u) == (5282u - 2u - 2u), "最后一列最后一点贴数据 CRC");
|
||||
CHECK(wave_store_data_off(0u, 320u, 0u, 0u) == 0xFFFFFFFFu, "通道数非法 -> 哨兵");
|
||||
CHECK(wave_store_data_off(8u, 320u, 8u, 0u) == 0xFFFFFFFFu, "列号越界 -> 哨兵");
|
||||
CHECK(wave_store_data_off(8u, 320u, 0u, 320u) == 0xFFFFFFFFu, "点越界 -> 哨兵");
|
||||
@@ -286,13 +313,14 @@ int main(void) {
|
||||
if ((s_back.ch[i].conv != want_conv[i]) || (s_back.ch[i].bits != want_bits[i])) {
|
||||
ok_map = 0;
|
||||
}
|
||||
if ((s_back.ch[i].avail != 0u) || (s_back.ch[i].k_q16 != 0) || (s_back.ch[i].b_codes != 0)) {
|
||||
if ((s_back.ch[i].avail != 0u) || (s_back.ch[i].k_q16 != 0) ||
|
||||
(s_back.ch[i].b_codes != 0) || (s_back.ch[i].r_q16 != 0)) {
|
||||
ok_zero = 0;
|
||||
}
|
||||
}
|
||||
CHECK(ok_sem != 0, "sem = 下标(PC 侧必须校验的一条)");
|
||||
CHECK(ok_map != 0, "conv/bits 映射:UA UN IA = TPAFE 16 位,其余五路 = 片内 ADC 12 位");
|
||||
CHECK(ok_zero != 0, "avail / k_q16 / b_codes 留 0 等 adcTask 现取现填(本层不链 adc_cal)");
|
||||
CHECK(ok_zero != 0, "avail / k_q16 / b_codes / r_q16 留 0 等 adcTask 现取现填(本层不链 adc_cal)");
|
||||
}
|
||||
{
|
||||
uint16_t m = (uint16_t)((1u << 1) | (1u << 2) | (1u << 5) | (1u << 6) | (1u << 7));
|
||||
@@ -326,6 +354,8 @@ int main(void) {
|
||||
for (i = 0u; i < v01.ch_count; i++) {
|
||||
/* 三路 TPAFE 既不VALID也不削顶;五路片内 ADC 削顶但未标定 */
|
||||
v01.ch[i].avail = (uint8_t)(((i == 0u) || (i == 3u) || (i == 4u)) ? 0x00u : 0x02u);
|
||||
/* 真值形态:一路都没配变比(V0.1 无 CT/PT 变比),与 eng = 引脚域自洽 */
|
||||
v01.ch[i].r_q16 = 0;
|
||||
}
|
||||
/* 注意:head_build 成功时返回的是写出的字节数(不是 WAVE_ST_OK)——
|
||||
* 与 wave_ring_read_ch 同一类「返回实际长度」的契约 */
|
||||
@@ -361,25 +391,44 @@ int main(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK(buf[WAVE_ST_OFF_ENG] == expect_eng(buf[WAVE_ST_OFF_FLAGS]),
|
||||
"构造形态落进字节之后 eng 与逐路有效位图仍自洽");
|
||||
CHECK(head_eng_consistent(buf) != 0,
|
||||
"构造形态落进字节之后 eng 与逐路变比仍自洽");
|
||||
fill_meta(&v01);
|
||||
v01.flags_seq = 0x00u;
|
||||
v01.eng = (uint8_t)WAVE_ENG_PIN;
|
||||
for (i = 0u; i < v01.ch_count; i++) {
|
||||
v01.ch[i].avail = (uint8_t)(((i == 0u) || (i == 3u) || (i == 4u)) ? 0x00u : 0x02u);
|
||||
v01.ch[i].r_q16 = 0; /* 一路都没配变比 -> 引脚域 */
|
||||
}
|
||||
if ((wave_store_head_build(&v01, buf, (uint32_t)sizeof(buf)) == (int)WAVE_ST_HEAD_SZ_CUR) &&
|
||||
(buf[WAVE_ST_OFF_ENG] == (uint8_t)WAVE_ENG_PIN) &&
|
||||
(buf[WAVE_ST_OFF_FLAGS] == 0x00u)) {
|
||||
CHECK(buf[WAVE_ST_OFF_ENG] == expect_eng(buf[WAVE_ST_OFF_FLAGS]),
|
||||
"本波真值形态落进字节之后 eng 仍停引脚域(0x00 不是 0xFF,不许谎报二次值)");
|
||||
CHECK(head_eng_consistent(buf) != 0,
|
||||
"本波真值形态落进字节之后 eng 仍停引脚域(变比全 0,不许谎报二次值)");
|
||||
} else {
|
||||
CHECK(0, "本波真值形态编码后 eng 与逐路有效位图字节不符");
|
||||
}
|
||||
CHECK(expect_eng(0xFFu) == (uint8_t)WAVE_ENG_SEC, "派生式:电气 8 路全有效才升二次值档位");
|
||||
CHECK(expect_eng(0x00u) == (uint8_t)WAVE_ENG_PIN, "派生式:一路都没标定则停引脚域");
|
||||
CHECK(expect_eng(0x7Fu) == (uint8_t)WAVE_ENG_PIN, "派生式:部分标定(缺 IN 那一路)整帧仍算引脚域");
|
||||
CHECK(eng_selfconsistent(&v01) != 0, "夹具自洽:真值形态(全零 / 引脚域)");
|
||||
CHECK(eng_selfconsistent(&both) != 0, "夹具自洽:构造形态(全有效 / 二次值)");
|
||||
CHECK(eng_selfconsistent(&v01) != 0, "夹具自洽:真值形态(变比全 0 / 引脚域)");
|
||||
CHECK(eng_selfconsistent(&both) != 0, "夹具自洽:构造形态(变比全配 / 二次值)");
|
||||
CHECK(eng_selfconsistent(&s_meta) != 0, "夹具自洽:本套所有合成段共用的那份段头");
|
||||
/* 派生式的三档探针(用段头对象探,不依赖字节):元本体是「每路要么已配变比、
|
||||
* 要么削顶不显示」——削顶那几路配不配变比都不影响结论 */
|
||||
{
|
||||
wave_seg_meta_t p;
|
||||
|
||||
fill_meta(&p);
|
||||
CHECK(expect_eng(&p) == (uint8_t)WAVE_ENG_SEC,
|
||||
"派生式:非削顶的八路都配了变比 -> 二次值档位");
|
||||
fill_meta(&p);
|
||||
p.ch[7].avail = 0x00u; /* IN 非削顶 */
|
||||
p.ch[7].r_q16 = 0; /* 且没配变比 */
|
||||
CHECK(expect_eng(&p) == (uint8_t)WAVE_ENG_PIN,
|
||||
"派生式:一路非削顶且无变比 -> 整帧仍算引脚域");
|
||||
fill_meta(&p);
|
||||
p.ch[7].r_q16 = 0; /* 只清变比,仍削顶 */
|
||||
CHECK(expect_eng(&p) == (uint8_t)WAVE_ENG_SEC,
|
||||
"派生式:削顶那路配不配变比都不影响(页面本来就显 '-',不参与整帧量纲断言)");
|
||||
}
|
||||
/* 反证本层**不参与**单位标签派生:故意造一份违反派生式的段头(全有效却仍标引脚域),
|
||||
* 编码与解码都该照样通过——判废只留给完整性判据,派生式不一致是 PC 侧软告警的活。
|
||||
* 这份只当探针用,绝不写进任何互验产物(真机写不出这串字节) */
|
||||
@@ -409,24 +458,38 @@ int main(void) {
|
||||
fill_meta(&s_meta);
|
||||
s_meta.ch[0].k_q16 = 10000000;
|
||||
s_meta.ch[0].b_codes = -8192;
|
||||
s_meta.ch[0].r_q16 = 0;
|
||||
s_meta.ch[7].k_q16 = 52800000;
|
||||
s_meta.ch[7].b_codes = 8191;
|
||||
/* v2:变比原值往返(含 ROM 默认的电压族 65536000 = 1000.0 V/V 与电流族
|
||||
* 1927532 = 29.4118 A/V);0 也要能往返(= 本路不折算,不是缺省) */
|
||||
s_meta.ch[0].r_q16 = 65536000;
|
||||
s_meta.ch[1].r_q16 = 1927532;
|
||||
s_meta.ch[2].r_q16 = 0;
|
||||
s_meta.wall = 0xFFFFFFFFu;
|
||||
s_meta.frame_seq = 0u;
|
||||
{
|
||||
uint32_t hn = build_head();
|
||||
CHECK(hn == 128u, "head_build 产出整头 128 字节");
|
||||
CHECK(hn == 160u, "head_build 产出整头 160 字节");
|
||||
CHECK((s_img[0] == (uint8_t)WAVE_ST_MAGIC0) && (s_img[1] == (uint8_t)WAVE_ST_MAGIC1) &&
|
||||
(s_img[2] == (uint8_t)WAVE_ST_VER), "魔数 = 57 52 01(第三字节就是版本号)");
|
||||
(s_img[2] == (uint8_t)WAVE_ST_VER), "魔数 = 57 52 02(第三字节就是版本号)");
|
||||
CHECK(WAVE_ST_VER == 2u, "格式版本 = 2(通道表 16 字节/路)");
|
||||
CHECK(s_img[WAVE_ST_OFF_VER] == s_img[WAVE_ST_OFF_MAGIC + 2u], "ver 字段与魔数第三字节同值");
|
||||
CHECK((s_img[30u + (12u * 8u)] != 0u) || (s_img[31u + (12u * 8u)] != 0u), "头 CRC 落在 126/127");
|
||||
CHECK((s_img[30u + (16u * 8u)] != 0u) || (s_img[31u + (16u * 8u)] != 0u), "头 CRC 落在 158/159");
|
||||
{
|
||||
uint16_t crc = wave_store_crc(s_img, 126u);
|
||||
CHECK((s_img[126] == (uint8_t)(crc & 0xFFu)) && (s_img[127] == (uint8_t)(crc >> 8)),
|
||||
uint16_t crc = wave_store_crc(s_img, 158u);
|
||||
CHECK((s_img[158] == (uint8_t)(crc & 0xFFu)) && (s_img[159] == (uint8_t)(crc >> 8)),
|
||||
"头 CRC 低字节在前(§6.3)");
|
||||
}
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_OK, "head_parse OK");
|
||||
CHECK(meta_same(&s_meta, &s_back) != 0, "13 个标量 + 8 项表逐字段往返一致(含负系数)");
|
||||
CHECK(meta_same(&s_meta, &s_back) != 0, "13 个标量 + 8 项表逐字段往返一致(含负系数与 r_q16)");
|
||||
/* v2 新增断言:段头往返后 r_q16 原值不变(int32 原值,不经 float) */
|
||||
CHECK((s_back.ch[0].r_q16 == 65536000) && (s_back.ch[1].r_q16 == 1927532) &&
|
||||
(s_back.ch[2].r_q16 == 0), "段头往返后 r_q16 原值不变(1000.0 / 29.4118 / 不折算)");
|
||||
/* r_q16 落在 12 字节偏移、小端 int32 */
|
||||
CHECK((s_img[30u + WAVE_ST_CH_OFF_RQ16 + 0u] == 0x00u) &&
|
||||
(s_img[30u + WAVE_ST_CH_OFF_RQ16 + 3u] == 0x03u),
|
||||
"r_q16 在表项偏移 12、小端(65536000 = 0x03E80000)");
|
||||
}
|
||||
|
||||
/* ---------- 用例:拒绝清单 ---------- */
|
||||
@@ -476,7 +539,7 @@ int main(void) {
|
||||
m.trig_src = 5u;
|
||||
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "触发源 5 拒");
|
||||
fill_meta(&m);
|
||||
CHECK(wave_store_head_build(&m, s_img, 127u) == WAVE_ST_E_PARAM, "缓冲装不下整头拒");
|
||||
CHECK(wave_store_head_build(&m, s_img, 159u) == WAVE_ST_E_PARAM, "缓冲装不下整头拒");
|
||||
CHECK(wave_store_head_build(NULL, s_img, IMG_CAP) == WAVE_ST_E_PARAM, "空段头指针拒");
|
||||
CHECK(wave_store_seg_size(NULL) == 0u, "seg_size 空指针返 0");
|
||||
}
|
||||
@@ -485,18 +548,18 @@ int main(void) {
|
||||
{
|
||||
uint32_t hn = build_head();
|
||||
uint8_t save = s_img[0];
|
||||
CHECK(hn == 128u, "参考头重编 OK");
|
||||
CHECK(hn == 160u, "参考头重编 OK");
|
||||
s_img[0] = (uint8_t)'X';
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT, "魔数第一字节错 -> 格式错");
|
||||
s_img[0] = save;
|
||||
s_img[WAVE_ST_OFF_VER] = 2u;
|
||||
s_img[WAVE_ST_OFF_VER] = 3u;
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT,
|
||||
"ver 与魔数第三字节不一致 -> 格式错(不许按新布局猜)");
|
||||
s_img[WAVE_ST_OFF_VER] = (uint8_t)WAVE_ST_VER;
|
||||
s_img[WAVE_ST_OFF_MAGIC + 2u] = 2u;
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT, "版本号 2 -> 拒");
|
||||
s_img[WAVE_ST_OFF_MAGIC + 2u] = 1u;
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT, "版本号 1(老 v1 段)-> 拒");
|
||||
s_img[WAVE_ST_OFF_MAGIC + 2u] = (uint8_t)WAVE_ST_VER;
|
||||
CHECK(wave_store_head_parse(s_img, 127u, &s_back) == WAVE_ST_E_PARAM, "缓冲不足整头 -> 参数错");
|
||||
CHECK(wave_store_head_parse(s_img, 159u, &s_back) == WAVE_ST_E_PARAM, "缓冲不足整头 -> 参数错");
|
||||
CHECK(wave_store_head_parse(s_img, 7u, &s_back) == WAVE_ST_E_PARAM, "连通道数字节都读不到 -> 参数错");
|
||||
s_img[40] = (uint8_t)(s_img[40] ^ 0xA5u);
|
||||
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_CRC, "头覆盖区被改一字节 -> CRC 不符");
|
||||
@@ -520,7 +583,8 @@ int main(void) {
|
||||
{
|
||||
wave_ch_desc_t d;
|
||||
int32_t mv = 0;
|
||||
d.sem = 0u; d.conv = 0u; d.bits = 16u; d.avail = 1u; d.k_q16 = 10000000; d.b_codes = 0;
|
||||
d.sem = 0u; d.conv = 0u; d.bits = 16u; d.avail = 1u;
|
||||
d.k_q16 = 10000000; d.b_codes = 0; d.r_q16 = 0;
|
||||
CHECK(wave_store_code_to_milli(&d, 0, &mv) == WAVE_ST_OK, "TPAFE 零点折算 OK");
|
||||
CHECK(mv == 0, "零点 = 0 mV");
|
||||
(void)wave_store_code_to_milli(&d, 9261, &mv);
|
||||
@@ -539,6 +603,36 @@ int main(void) {
|
||||
CHECK(mv == 0, "系数没填时幅值归零(形状仍在,PC 侧会看到平线)");
|
||||
CHECK(wave_store_code_to_milli(NULL, 0, &mv) == WAVE_ST_E_PARAM, "空表项拒");
|
||||
CHECK(wave_store_code_to_milli(&d, 0, NULL) == WAVE_ST_E_PARAM, "空输出拒");
|
||||
|
||||
/* ---- v2:变比维(口径锚点三条,都必须与 PC 解码器同解) ---- */
|
||||
d.k_q16 = 10000000;
|
||||
d.b_codes = 0;
|
||||
(void)wave_store_code_to_milli(&d, 9261, &mv);
|
||||
{
|
||||
int32_t pin = mv; /* r_q16 = 0:引脚域基准 */
|
||||
d.r_q16 = 65536; /* 比值 1.0 */
|
||||
(void)wave_store_code_to_milli(&d, 9261, &mv);
|
||||
CHECK((pin == 1413) && (mv == pin), "r_q16 = 65536(1.0)与 r_q16 = 0 的引脚域逐位一致");
|
||||
d.r_q16 = 0;
|
||||
(void)wave_store_code_to_milli(&d, -9261, &mv);
|
||||
CHECK(mv == -1413, "r_q16 = 0 退化成引脚域(负半周同理)");
|
||||
d.r_q16 = 65536;
|
||||
(void)wave_store_code_to_milli(&d, -9261, &mv);
|
||||
CHECK(mv == -1413, "r_q16 = 1.0 时负半周同样与引脚域一致(不是 -1414)");
|
||||
}
|
||||
d.r_q16 = 65536000; /* 1000.0 V/V:ROM 电压族默认 */
|
||||
(void)wave_store_code_to_milli(&d, 9261, &mv);
|
||||
/* 精确值 = 1413.116 mV × 1000 = 1413116(**不是** 先圆整再乘的 1413000 ——
|
||||
* 这正是「只在最后圆整一次」的可观测证据:中间圆整会少掉 116 个毫单位) */
|
||||
CHECK(mv == 1413116, "r_q16 = 65536000(1000.0)时放大 1000 倍(1413.116 -> 1413116)");
|
||||
(void)wave_store_code_to_milli(&d, -9261, &mv);
|
||||
CHECK(mv == -1413116, "放大 1000 倍在负半周同样成立(符号不错)");
|
||||
d.r_q16 = 1927532; /* 29.4118 A/V:ROM 电流族默认 */
|
||||
d.k_q16 = 10000000;
|
||||
(void)wave_store_code_to_milli(&d, 9261, &mv);
|
||||
/* 1413 mV * 29.4118 = 41562 毫单位(二次域 mA) */
|
||||
CHECK(mv == 41562, "r_q16 = 1927532(29.4118)时 9261 码 -> 41562 毫单位(二次 mA)");
|
||||
d.r_q16 = 0;
|
||||
}
|
||||
|
||||
/* ---------- 用例:打包游标 ---------- */
|
||||
@@ -550,15 +644,15 @@ int main(void) {
|
||||
s_read_calls = 0u;
|
||||
s_read_points = 0u;
|
||||
CHECK(pack_whole(256u, &len, &rc) == 0, "整段跑完(256 字节分片)");
|
||||
CHECK(s_img[WAVE_ST_OFF_ENG] == expect_eng(s_img[WAVE_ST_OFF_FLAGS]),
|
||||
"整段头字节自洽:写出去的 eng 与逐路有效位图满足平台派生式");
|
||||
CHECK(len == 5250u, "实际产出字节数 = 5250");
|
||||
CHECK(head_eng_consistent(s_img) != 0,
|
||||
"整段头字节自洽:写出去的 eng 与逐路变比满足平台派生式");
|
||||
CHECK(len == 5282u, "实际产出字节数 = 5282");
|
||||
CHECK(s_read_points == (320u * 8u), "数据源被取的点数 = 320*8(不多取不少取)");
|
||||
CHECK(s_read_calls == 40u, "每列 5 批 x 8 列 = 40 次回调(批量取数,不是逐点)");
|
||||
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_OK, "自产字节过整段自检");
|
||||
CHECK(meta_same(&s_meta, &s_back) != 0, "自检回填的段头与原段头一致");
|
||||
{
|
||||
uint16_t dcrc = wave_store_crc(&s_img[128], 5120u);
|
||||
uint16_t dcrc = wave_store_crc(&s_img[160], 5120u);
|
||||
CHECK((s_img[len - 2u] == (uint8_t)(dcrc & 0xFFu)) && (s_img[len - 1u] == (uint8_t)(dcrc >> 8)),
|
||||
"数据 CRC 覆盖数据区、低字节在前、贴在文件尾");
|
||||
}
|
||||
@@ -566,8 +660,8 @@ int main(void) {
|
||||
{
|
||||
uint32_t len = 0u;
|
||||
int rc = 0;
|
||||
CHECK(pack_whole(128u, &len, &rc) == 0, "128 字节分片(= 整头那么大)也能跑完");
|
||||
CHECK(len == 5250u, "128 分片总长仍 5250(产出与分片大小无关)");
|
||||
CHECK(pack_whole(160u, &len, &rc) == 0, "160 字节分片(= 整头那么大)也能跑完");
|
||||
CHECK(len == 5282u, "160 分片总长仍 5282(产出与分片大小无关)");
|
||||
}
|
||||
{
|
||||
uint32_t len = 0u;
|
||||
@@ -579,10 +673,10 @@ int main(void) {
|
||||
uint32_t n = 99u;
|
||||
int rc;
|
||||
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_OK, "pack_start OK");
|
||||
CHECK(wave_store_pack_next(&ctx, s_img, 127u, &n) == WAVE_ST_E_PARAM, "头一步容量不足 -> E_PARAM");
|
||||
CHECK(wave_store_pack_next(&ctx, s_img, 159u, &n) == WAVE_ST_E_PARAM, "头一步容量不足 -> E_PARAM");
|
||||
CHECK(n == 0u, "拒绝时一个字节都不产(不写半截头)");
|
||||
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
|
||||
CHECK((rc == WAVE_ST_OK) && (n == 128u), "同一游标重试成功(拒绝不留副作用)");
|
||||
CHECK((rc == WAVE_ST_OK) && (n == 160u), "同一游标重试成功(拒绝不留副作用)");
|
||||
rc = wave_store_pack_next(&ctx, s_img, 1u, &n);
|
||||
CHECK(rc == WAVE_ST_E_PARAM, "数据区容量装不下一个样本 -> 拒");
|
||||
CHECK(n == 0u, "同样不产半个样本的字节");
|
||||
@@ -603,7 +697,7 @@ int main(void) {
|
||||
int rc;
|
||||
CHECK(wave_store_pack_start(&ctx, &s_meta, NULL, NULL) == WAVE_ST_OK, "无数据源也能 start(只产头)");
|
||||
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
|
||||
CHECK((rc == WAVE_ST_OK) && (n == 128u), "头能产");
|
||||
CHECK((rc == WAVE_ST_OK) && (n == 160u), "头能产");
|
||||
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
|
||||
CHECK((rc == WAVE_ST_E_STATE) && (n == 0u),
|
||||
"没有数据源却进数据区 -> 拒且不产空字节(产空字节 = 伪造一条直线)");
|
||||
@@ -742,7 +836,7 @@ int main(void) {
|
||||
char path[512];
|
||||
FILE *fp;
|
||||
size_t wrote;
|
||||
CHECK(len == 5250u, "产物长度 5250");
|
||||
CHECK(len == 5282u, "产物长度 5282");
|
||||
if ((tmp != NULL) && (strlen(tmp) < 400u)) {
|
||||
strcpy(path, tmp);
|
||||
strcat(path, "\\wave_algo_synth8.bin");
|
||||
@@ -751,8 +845,8 @@ int main(void) {
|
||||
wrote = fwrite(s_img, 1u, len, fp);
|
||||
(void)fclose(fp);
|
||||
CHECK(wrote == (size_t)len, "合成段落盘成功");
|
||||
CHECK(s_img[WAVE_ST_OFF_ENG] == expect_eng(s_img[WAVE_ST_OFF_FLAGS]),
|
||||
"互验产物自洽:这份构造段的单位标签与逐路有效位图对得上(真机写得出)");
|
||||
CHECK(head_eng_consistent(s_img) != 0,
|
||||
"互验产物自洽:这份构造段的单位标签与逐路变比对得上(真机写得出)");
|
||||
/* 形态在日志里说清楚:本套是「同时带有效与削顶」的构造形态,
|
||||
* 本波真值形态(逐路有效位图全零、片内 ADC 只带削顶位)由 wave_trig 那套给 */
|
||||
printf(" [INFO] 互验产物(构造形态 = 已标定后仍削顶:逐路有效位图 0xFF、avail 0x01 / 0x03、"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* 而那正是「无波可录」的时刻。用 1 kHz 逐拍模拟在主机上跑满整段(360 拍一段)成本几乎为零。
|
||||
*
|
||||
* 桩的边界:本套不桩任何东西——wave_ring.c 与 wave_store.c 零平台依赖,
|
||||
* 封存瞬间事实(adc_cal 的 avail / k_q16 / b_codes)由本测试按 §6.3 口径
|
||||
* 封存瞬间事实(adc_cal 的 avail / k_q16 / b_codes / r_q16)由本测试按 §6.3 口径
|
||||
* 手工填进 wave_seal_cal_t,模拟接线道那一拍该交棒的内容。
|
||||
* 真 adc_cal、真 FatFs、真 FTP、注入对表属台架(docs/子系统设计/故障录波设计说明.md §10)。
|
||||
*
|
||||
@@ -198,6 +198,10 @@ static void cal_init(void) {
|
||||
s_cal.ch[j].avail = (uint8_t)((tpa != 0) ? 0x00 : 0x02);
|
||||
s_cal.ch[j].k_q16 = (tpa != 0) ? 10000000 : 52800000;
|
||||
s_cal.ch[j].b_codes = 0;
|
||||
/* v2:变比原值。本套是 V0.1 未标定形态,一路都还没配变比 -> 全 0
|
||||
* (= 本路不折算,输出停在引脚域,与 s_cal.eng = WAVE_ENG_PIN 自洽);
|
||||
* 非 0 变比的往返与折算由 test/host/wave_store 那套与它的 PC 解码器覆盖。 */
|
||||
s_cal.ch[j].r_q16 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +500,7 @@ int main(void) {
|
||||
CHECK(trigger_and_seal((uint8_t)WAVE_TRIG_PROTECT, 12u, 200u) != 0, "全链路:先滚 200 拍再触发封段");
|
||||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "全链路:交棒封存瞬间事实");
|
||||
CHECK(pack_seg() == 0, "全链路:打包游标跑到 DONE(数据源就是 wave_ring 的读口)");
|
||||
CHECK(s_len == (uint32_t)WAVE_ST_SEG_SZ_CUR, "整段字节数 = 5250(§6.3 等式)");
|
||||
CHECK(s_len == (uint32_t)WAVE_ST_SEG_SZ_CUR, "整段字节数 = 5282(§6.3 等式)");
|
||||
CHECK(wave_store_seg_check(s_buf, s_len, NULL) == WAVE_ST_OK, "自产段过整段自检(两个 CRC 都对)");
|
||||
CHECK(wave_store_seg_check(s_buf, s_len, &s_m) == WAVE_ST_OK, "自检并回填段头");
|
||||
CHECK((s_buf[0] == (uint8_t)WAVE_ST_MAGIC0) && (s_buf[1] == (uint8_t)WAVE_ST_MAGIC1) &&
|
||||
@@ -546,15 +550,18 @@ int main(void) {
|
||||
if (wave_st_get32(&s_buf[pos + WAVE_ST_CH_OFF_BCODE]) != (uint32_t)s_cal.ch[j].b_codes) {
|
||||
ok_tbl = 0;
|
||||
}
|
||||
if (wave_st_get32(&s_buf[pos + WAVE_ST_CH_OFF_RQ16]) != (uint32_t)s_cal.ch[j].r_q16) {
|
||||
ok_tbl = 0;
|
||||
}
|
||||
}
|
||||
CHECK(ok_tbl != 0, "通道表 8 项:sem = 下标 + conv/bits/avail/k_q16/b_codes 全部落进字节");
|
||||
CHECK(ok_tbl != 0, "通道表 8 项:sem = 下标 + conv/bits/avail/k_q16/b_codes/r_q16 全部落进字节");
|
||||
}
|
||||
{
|
||||
uint16_t hcrc = wave_store_crc(s_buf, WAVE_ST_HEAD_CRC_POS((uint8_t)WAVE_ST_CH_NUM));
|
||||
|
||||
CHECK((s_buf[WAVE_ST_HEAD_CRC_POS((uint8_t)WAVE_ST_CH_NUM)] == (uint8_t)(hcrc & 0xFFu)) &&
|
||||
(s_buf[WAVE_ST_HEAD_CRC_POS((uint8_t)WAVE_ST_CH_NUM) + 1u] == (uint8_t)(hcrc >> 8)),
|
||||
"头 CRC 覆盖 [0, 30+12*ch) 且低字节在前");
|
||||
"头 CRC 覆盖 [0, 30+16*ch) 且低字节在前");
|
||||
}
|
||||
{
|
||||
uint16_t dcrc = wave_store_crc(&s_buf[WAVE_ST_HEAD_SZ_CUR],
|
||||
|
||||
Reference in New Issue
Block a user