Files
wandering1 4ceb5b41e6 docs(任务规划): 故障录波卡片登记簿销项——判据并入设计说明后删除
用户指令(2026-10-30):确认录波任务完成度,把除实际操作之外的所有
步骤做完,然后删除规划文件,避免已完成的工作被误读为未做。

审计结论:代码面确已全部收口——wave_read.py --selftest 92/92、
三套主机单测 113/170/110 复跑全 rc=0、bench 自抓与 PC 读段脚本在仓、
数据可信/复刻明细/短板评测/UI 两处回写均已做过。**唯一未跑的是
板级台架那一轮(实际操作)**,判据不得丢,故先迁后删:

- 设计说明 §9 承接落盘道失败留痕名全清单(13+1 条,打印点现值
  default_task.c:816/:831 实测核对)
- 设计说明 §10 承接原卡 2/卡 3 的板级段(触发来路一行、回放页
  一行)与本波执行顺序图(mermaid,原规划 §3 逐字迁)
- §4/§11 头部定位、§11 尾注、§14 关系行改属主口径
- 全库 32 处引用改挂设计说明 §10/§9(18 文件):任务规划 README
  独立行(顺带订正单测数 100→113 为实测)、登记簿、数据可信 ×2、
  复刻明细、产品需求 ×2、ADC 注入 ×3、短板评测 ×2、页面补齐规划、
  App/algo README、配图生成器注释、录波三套单测注释 ×6
- git rm docs 该规划文件;人类索引任务规划 6→5 个文件(沿革注照
  屏板链路先例走裸句,不留反引号死引用)

验证:残留两形态(任务规划/故障录波、裸文件名)全树扫描 0;
mermaid 预检新发 0;doc-links 11 个被改文件逐文件 PASS;生成器
注释改动重跑产物逐字节不变(svg 60a0dd39e4);三套单测与
wave_read selftest 复跑全绿;18 文件 BOM/CR/U+FFFD 全零。

本笔不含固件编译单元(test/host 仅注释行与文档),构建留待合并
轮统一跑;板级轮启动判据与顺序见设计说明 §10。
2026-10-30 18:20:37 +08:00

773 lines
41 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:录波段格式层(段文件字节布局编解码 + 打包游标 + 段名轮转)主机侧单元测试
* 被测对象:真实源文件(#include 原样纳入,测生产代码而非副本):App/algo/wave_store.c
* 该源是纯逻辑(无 HAL / CMSIS / RTOS / FatFs / 日志)→ **无需任何桩目录**。
*
* 为什么要主机侧(而不是只等台架):段文件是**证据**,一旦写进 NAND 就再也说不清
* 「当时想表达什么」。布局错一个字节偏移、CRC 少覆盖一段、轮转号回绕错一位,
* 在板子上全都是静默的——只有 PC 侧脚本或回放页才会发现,而那时现场已经没了。
* 所以字节级判据(整段长度等式、CRC 锚点、编解码往返、拒绝清单)必须钉在主机侧。
* 需要板子的部分(真 FAT 落盘、FTP 拉回、注入对表)见 docs/子系统设计/故障录波设计说明.md §10。
*
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
* 用例:CRC-16/MODBUS:规范锚点 0x4B37、自检、增量与一次算完等价、空输入
* 用例:尺寸等式(§6.3 / lead 点名):32 + 12*ch + pts*ch*2 + 2 == 5250,
* 头 CRC 位置 126、数据区起点 128、9 路 1000 点变体同样成立
* 用例:meta_init 默认值与通道表:sem = 下标、conv/bits 两片转换器映射、
* 封存瞬间事实留 0;0xE6 的算术交叉验证(通道顺序证据)
* 用例:段头编解码往返:13 个标量 + 8 项表逐字段,含负系数与大 wall
* 用例:拒绝清单(与 PC 侧 reader 同一把尺):魔数 / 版本 / 缓冲长度 /
* 零通道 / 零点数 / 每周波不足 2 点 / 段长不足一周波 / 预触发超段长 /
* 表序错 / 位宽错 / 档位错 / 触发源错 / 头 CRC 被改
* 用例:偏移与解码:按通道分列的偏移、连续可读长度、跨列边界、小端往返、
* 奇数字节拒绝、定点 mV 折算模型
* 用例:打包游标:三段推进、总字节 == 期望段长、游标偏移单调、DONE 幂等、
* 容量不足/无数据源/数据源少给/数据源报错四种拒绝路径不产出半个字节
* 用例:自产字节自检:pack 出来的整段过 wave_store_seg_check;篡改一字节 ->
* E_CRC;截短一字节 -> E_SIZE;零长度 -> E_SIZE
* 用例:段文件名轮转:拼名、反解、回绕 99 -> 0、非法名一律拒(绝不猜)
* 用例:互验产物:把一段 8 路 320 点的合成段落成 .bin 供 PC 侧脚本只读校验
* (不参与判定,写失败只打印)
*
* 通过:全部 CHECK 通过(main 返回 0)。
* 创建日期:2026-10-29(故障录波 task-1)
*/
/* 主机测试专用:本文件的互验产物要用 stdio/stdlib/string 的
* 传统形态(MSVC 会对 strcpy/fopen 报 C4996,测试代码不受生产侧禁用清单约束) */
#define _CRT_SECURE_NO_WARNINGS 1
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ===================== 断言 ===================== */
static int s_total = 0, s_passed = 0, s_failed = 0;
#define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
} while (0)
/* ===================== 被测代码(生产源原样纳入) ===================== */
#include "wave_store.c"
/* ===================== 公共夹具 ===================== */
#define IMG_CAP (WAVE_ST_SEG_SZ_CUR + 256u)
static uint8_t s_img[IMG_CAP];
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 全置位,那份构造段没资格当互验夹具)。
*/
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 int eng_selfconsistent(const wave_seg_meta_t *p_m) {
return (p_m->eng == expect_eng(p_m->flags_seq)) ? 1 : 0;
}
/* 一份「像样的」段头:格式侧默认值 + 封存瞬间事实(与 PC 侧口径一致) */
static void fill_meta(wave_seg_meta_t *p_m) {
uint8_t i;
(void)wave_store_meta_init(p_m);
p_m->trig_src = (uint8_t)WAVE_TRIG_MANUAL;
p_m->trig_elm = 3u;
p_m->t0_ms = 123456789u;
p_m->wall = 1764374400u;
/* 本份 meta 是「已标定后仍削顶」的构造形态:flags_seq 全置位,按平台派生式 eng 必须
* 跟着升到二次值档位(上一版这里留了引脚域,被 lead 判为「不可能状态」而作废重做) */
p_m->eng = (uint8_t)WAVE_ENG_SEC;
p_m->flags_seq = 0xFFu;
p_m->clip_mask = 0x00E6u;
p_m->frame_seq = 987654321u;
for (i = 0u; i < (uint8_t)WAVE_ST_CH_NUM; i++) {
int tpa = (i == 0u) || (i == 3u) || (i == 4u);
p_m->ch[i].k_q16 = tpa ? 10000000 : 52800000;
p_m->ch[i].b_codes = tpa ? -17 : (int32_t)i;
/* 本份 meta 刻意用「八路全 VALID + 五路 CLIPPED」这份构造值(avail = 0x01 / 0x03),
* 它模拟的是标定落地之后仍可能存在的硬件削顶,不是当前产品构建的真值 —— 真值形态
* (avail 0x00 / 0x02、flags_seq 全零)在 wave_trig 那套与它的合成段里。
* 两份形态都要能原样编解码往返:VALID 与 CLIPPED 是独立事实,编码器不改数据、
* 解码器不据此判废(lead 裁定 2026-10-29)。削顶位与 clip_mask 的对应关系仍要保持
* 自洽(bit1 = 削顶,聚合起来正好 0xE6),那是数据自身的一致性,不是格式不变式。 */
p_m->ch[i].avail = (uint8_t)(tpa ? 0x01u : 0x03u);
}
}
/* 打包用的假数据源:按列给可辨认的斜坡(与 wave_ring 的读口同形态) */
static uint32_t s_read_calls;
static uint32_t s_read_points;
static int32_t fake_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
uint32_t i;
(void)p_user;
s_read_calls++;
s_read_points += a_cnt;
if ((a_ch >= (uint8_t)WAVE_ST_CH_NUM) || ((a_from + a_cnt) > (uint32_t)WAVE_ST_PTS_DEF)) {
return WAVE_ST_E_RANGE;
}
for (i = 0u; i < a_cnt; i++) {
p_dst[i] = (int16_t)((int32_t)a_ch * 311 + (int32_t)a_from + (int32_t)(i * 3));
}
return (int32_t)a_cnt;
}
static int32_t short_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
(void)p_user;
(void)a_ch;
(void)a_from;
(void)p_dst;
return (a_cnt > 1u) ? ((int32_t)(a_cnt - 1u)) : 0; /* 少给一个点 */
}
static int32_t err_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
(void)p_user;
(void)a_ch;
(void)a_from;
(void)a_cnt;
(void)p_dst;
return WAVE_ST_E_RANGE;
}
/* 把游标跑到 DONE,返回 -1 表示中途报错;镜像与长度回填 */
static int pack_whole(uint32_t a_cap, uint32_t *p_len, int *p_last_rc) {
wave_pack_ctx_t ctx;
uint32_t off = 0u;
uint32_t len;
int rc;
*p_len = 0u;
rc = wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL);
if (rc != WAVE_ST_OK) {
*p_last_rc = rc;
return -1;
}
for (;;) {
uint32_t n = 0u;
uint32_t cap = a_cap;
if ((off + cap) > IMG_CAP) {
cap = IMG_CAP - off;
}
rc = wave_store_pack_next(&ctx, &s_img[off], cap, &n);
if ((rc < 0) || (n > cap)) {
*p_last_rc = rc;
return -1;
}
off += n;
if (rc == WAVE_ST_DONE) {
break;
}
if (off >= IMG_CAP) {
*p_last_rc = WAVE_ST_E_SIZE;
return -1;
}
}
len = off;
*p_len = len;
*p_last_rc = rc;
/* 游标报的总长与实际产出必须相等(接线道拿这条比对 f_write 写回字节数) */
if (wave_store_pack_total(&ctx) != len) {
return -1;
}
if (wave_store_pack_offset(&ctx) != len) {
return -1;
}
return 0;
}
static int meta_same(const wave_seg_meta_t *p_a, const wave_seg_meta_t *p_b) {
uint8_t i;
if ((p_a->fs_hz != p_b->fs_hz) || (p_a->pts_per_cycle != p_b->pts_per_cycle) ||
(p_a->ch_count != p_b->ch_count) || (p_a->pts_per_ch != p_b->pts_per_ch) ||
(p_a->pre_pts != p_b->pre_pts) || (p_a->trig_src != p_b->trig_src) ||
(p_a->trig_elm != p_b->trig_elm) || (p_a->t0_ms != p_b->t0_ms) ||
(p_a->wall != p_b->wall) || (p_a->eng != p_b->eng) ||
(p_a->flags_seq != p_b->flags_seq) || (p_a->clip_mask != p_b->clip_mask) ||
(p_a->frame_seq != p_b->frame_seq)) {
return 0;
}
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)) {
return 0;
}
}
return 1;
}
/* 编好整头放进 s_img,返回头字节数(失败返回 0) */
static uint32_t build_head(void) {
int n = wave_store_head_build(&s_meta, s_img, IMG_CAP);
return (n > 0) ? (uint32_t)n : 0u;
}
/* ===================== 主测 ===================== */
int main(void) {
printf("=== Wave Store Tests:\n");
/* ---------- 用例:CRC-16/MODBUS ---------- */
printf("-- 用例:CRC-16/MODBUS --\n");
CHECK(wave_store_crc_selftest() == WAVE_ST_OK, "自检通过(锚点 123456789 = 0x4B37)");
{
static const uint8_t s_kat[9] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
uint16_t one = wave_store_crc(s_kat, 9u);
uint16_t inc = wave_store_crc_update(WAVE_ST_CRC_INIT, s_kat, 4u);
inc = wave_store_crc_update(inc, &s_kat[4], 5u);
CHECK(one == 0x4B37u, "规范锚点值本身(不是自算自验)");
CHECK(inc == one, "增量与一次算完等价(落盘按 256 字节分片必须同值)");
CHECK(wave_store_crc(s_kat, 0u) == WAVE_ST_CRC_INIT, "零长度 = 初值(判废归调用方)");
CHECK(wave_store_crc_update(WAVE_ST_CRC_INIT, NULL, 8u) == WAVE_ST_CRC_INIT, "空指针不当数据");
CHECK(WAVE_ST_CRC_POLY == 0xA001u, "反射多项式 0xA001(不是 0x8005 正序)");
CHECK(WAVE_ST_CRC_XOROUT == 0u, "异或输出恒 0");
}
/* ---------- 用例:尺寸等式 ---------- */
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_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_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 点变体同式(每周波点数是数据)");
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_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(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, "点越界 -> 哨兵");
CHECK(wave_store_data_bytes_avail(320u, 300u, 40u) == 40u, "连续可读长度夹到列尾(20 点 = 40 字节)");
CHECK(wave_store_data_bytes_avail(320u, 0u, 1000u) == 640u, "整列 320 点 = 640 字节");
CHECK(wave_store_data_bytes_avail(320u, 320u, 1u) == 0u, "列尾之后没有字节可读");
/* ---------- 用例:meta_init 与 0xE6 交叉验证 ---------- */
printf("-- 用例:meta_init 与通道顺序 --\n");
CHECK(wave_store_meta_init(NULL) == WAVE_ST_E_PARAM, "meta_init 空指针");
CHECK(wave_store_meta_init(&s_back) == WAVE_ST_OK, "meta_init OK");
CHECK((s_back.fs_hz == 1000u) && (s_back.pts_per_cycle == 20u) && (s_back.ch_count == 8u) &&
(s_back.pts_per_ch == 320u) && (s_back.pre_pts == 160u), "默认值 = §6.2 的 1 kHz / 20 / 8 / 320 / 160");
CHECK(s_back.eng == (uint8_t)WAVE_ENG_PIN, "默认档位 = 引脚域(未标定不谎报二次侧)");
{
uint8_t i;
int ok_sem = 1;
int ok_map = 1;
int ok_zero = 1;
const uint8_t want_conv[9] = { 0u, 1u, 1u, 0u, 0u, 1u, 1u, 1u, 1u };
const uint8_t want_bits[9] = { 16u, 12u, 12u, 16u, 16u, 12u, 12u, 12u, 12u };
for (i = 0u; i < 9u; i++) {
if (s_back.ch[i].sem != i) {
ok_sem = 0;
}
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)) {
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)");
}
{
uint16_t m = (uint16_t)((1u << 1) | (1u << 2) | (1u << 5) | (1u << 6) | (1u << 7));
CHECK(m == 0x00E6u, "0xE6 的算术 = UB UC IB IC IN 五个语义位(通道顺序的交叉验证)");
fill_meta(&s_meta);
{
uint8_t i;
uint16_t from_table = 0u;
for (i = 0u; i < s_meta.ch_count; i++) {
if ((s_meta.ch[i].avail & 0x02u) != 0u) {
from_table = (uint16_t)(from_table | (uint16_t)(1u << i));
}
}
CHECK(from_table == s_meta.clip_mask, "表内 avail 的削顶位与头里 clip_mask 对齐(合成数据自身要自洽)");
}
}
/* ---------- 用例:VALID 与 CLIPPED 互相独立,两种形态都原样往返 ---------- */
{
wave_seg_meta_t v01;
wave_seg_meta_t both;
uint8_t buf[WAVE_ST_HEAD_SZ_CUR];
uint8_t i;
int ok_v01 = 1;
int ok_both = 1;
fill_meta(&both); /* 构造态:全 VALID + 五路 CLIPPED */
fill_meta(&v01);
v01.flags_seq = 0x00u; /* 本波真值:未标定 → 逐路有效位图全零 */
v01.eng = (uint8_t)WAVE_ENG_PIN; /* 派生式跟着落档:未升二次值 */
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);
}
/* 注意:head_build 成功时返回的是写出的字节数(不是 WAVE_ST_OK)——
* 与 wave_ring_read_ch 同一类「返回实际长度」的契约 */
if (wave_store_head_build(&v01, buf, (uint32_t)sizeof(buf)) != (int)WAVE_ST_HEAD_SZ_CUR) {
ok_v01 = 0;
}
if (wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) != WAVE_ST_OK) {
ok_v01 = 0;
} else {
if (s_back.flags_seq != 0x00u) {
ok_v01 = 0;
}
for (i = 0u; i < s_back.ch_count; i++) {
if (s_back.ch[i].avail != v01.ch[i].avail) {
ok_v01 = 0;
}
}
}
/* 注意:head_build 成功时返回的是写出的字节数(不是 WAVE_ST_OK)——
* 与 wave_ring_read_ch 同一类「返回实际长度」的契约 */
if (wave_store_head_build(&both, buf, (uint32_t)sizeof(buf)) != (int)WAVE_ST_HEAD_SZ_CUR) {
ok_both = 0;
}
if (wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) != WAVE_ST_OK) {
ok_both = 0;
} else {
if (s_back.flags_seq != 0xFFu) {
ok_both = 0;
}
for (i = 0u; i < s_back.ch_count; i++) {
if (s_back.ch[i].avail != both.ch[i].avail) {
ok_both = 0;
}
}
}
CHECK(buf[WAVE_ST_OFF_ENG] == expect_eng(buf[WAVE_ST_OFF_FLAGS]),
"构造形态落进字节之后 eng 与逐路有效位图仍自洽");
fill_meta(&v01);
v01.flags_seq = 0x00u;
v01.eng = (uint8_t)WAVE_ENG_PIN;
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,不许谎报二次值)");
} 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(&s_meta) != 0, "夹具自洽:本套所有合成段共用的那份段头");
/* 反证本层**不参与**单位标签派生:故意造一份违反派生式的段头(全有效却仍标引脚域),
* 编码与解码都该照样通过——判废只留给完整性判据,派生式不一致是 PC 侧软告警的活。
* 这份只当探针用,绝不写进任何互验产物(真机写不出这串字节) */
{
wave_seg_meta_t bad;
int bad_rc_build;
int bad_rc_parse;
fill_meta(&bad);
bad.flags_seq = 0xFFu;
bad.eng = (uint8_t)WAVE_ENG_PIN;
bad_rc_build = wave_store_head_build(&bad, buf, (uint32_t)sizeof(buf));
bad_rc_parse = (bad_rc_build == (int)WAVE_ST_HEAD_SZ_CUR) ?
wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) : (int)WAVE_ST_E_SIZE;
CHECK((bad_rc_build == (int)WAVE_ST_HEAD_SZ_CUR) && (bad_rc_parse == WAVE_ST_OK) &&
(s_back.eng == (uint8_t)WAVE_ENG_PIN) && (s_back.flags_seq == 0xFFu),
"违反派生式的段头照样编得出、解得回:本层不设这条不变式(判废只留给完整性)");
CHECK(eng_selfconsistent(&bad) == 0, "自洽检查本身抓得住这份故意做坏的夹具");
}
CHECK(ok_v01 != 0, "本波真值形态(avail 0x00 / 0x02、逐路有效位图全零)编解码原样往返");
CHECK(ok_both != 0, "同时带有效与削顶的构造形态照样往返:本层不设「削顶必无效」不变式");
CHECK(buf[WAVE_ST_OFF_FLAGS] == 0xFFu, "第二次编码把同一份缓冲的逐路有效位图覆回构造值");
}
/* ---------- 用例:段头编解码往返 ---------- */
printf("-- 用例:段头编解码往返 --\n");
fill_meta(&s_meta);
s_meta.ch[0].k_q16 = 10000000;
s_meta.ch[0].b_codes = -8192;
s_meta.ch[7].k_q16 = 52800000;
s_meta.ch[7].b_codes = 8191;
s_meta.wall = 0xFFFFFFFFu;
s_meta.frame_seq = 0u;
{
uint32_t hn = build_head();
CHECK(hn == 128u, "head_build 产出整头 128 字节");
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(第三字节就是版本号)");
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");
{
uint16_t crc = wave_store_crc(s_img, 126u);
CHECK((s_img[126] == (uint8_t)(crc & 0xFFu)) && (s_img[127] == (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 项表逐字段往返一致(含负系数)");
}
/* ---------- 用例:拒绝清单 ---------- */
printf("-- 用例:拒绝清单 --\n");
{
wave_seg_meta_t m;
fill_meta(&m);
m.ch_count = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "零通道拒(长度不成立)");
fill_meta(&m);
m.ch_count = 10u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "通道数超上限拒");
fill_meta(&m);
m.pts_per_ch = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "零点数拒(即使 CRC 能自洽)");
fill_meta(&m);
m.pre_pts = 321u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "预触发超段长拒");
fill_meta(&m);
m.pts_per_cycle = 1u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE,
"每周波不足 2 点拒(与 PC 侧 reader 同一把尺)");
fill_meta(&m);
m.pts_per_ch = 10u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE,
"段长不足一个整周期拒(基波无从取窗)");
fill_meta(&m);
m.fs_hz = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "采样率 0 拒");
fill_meta(&m);
m.ch[1].sem = 6u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "表序错(sem != 下标)拒");
fill_meta(&m);
m.ch[2].bits = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "位宽 0 拒");
fill_meta(&m);
m.ch[2].bits = 33u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "位宽超域拒");
fill_meta(&m);
m.ch[3].conv = 2u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "转换器号非法拒");
fill_meta(&m);
m.eng = 3u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "折算档位 3 拒");
fill_meta(&m);
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(NULL, s_img, IMG_CAP) == WAVE_ST_E_PARAM, "空段头指针拒");
CHECK(wave_store_seg_size(NULL) == 0u, "seg_size 空指针返 0");
}
/* 解码侧的拒绝(先造一份合法头,再逐项破坏) */
fill_meta(&s_meta);
{
uint32_t hn = build_head();
uint8_t save = s_img[0];
CHECK(hn == 128u, "参考头重编 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;
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] = (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, 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 不符");
CHECK(wave_store_head_parse(NULL, hn, &s_back) == WAVE_ST_E_PARAM, "空缓冲指针");
CHECK(wave_store_head_parse(s_img, hn, NULL) == WAVE_ST_E_PARAM, "空输出指针");
}
/* ---------- 用例:小端解码与定点折算 ---------- */
printf("-- 用例:解码与折算 --\n");
{
static const uint8_t s_le[8] = { 0x00u, 0x80u, 0xFFu, 0xFFu, 0x2Du, 0x23u, 0x00u, 0x00u };
int16_t out[4];
uint32_t got = 0u;
CHECK(wave_store_le_decode(s_le, 8u, out, 4u, &got) == WAVE_ST_OK, "解码 OK");
CHECK((got == 4u) && (out[0] == -32768) && (out[1] == -1) && (out[2] == 9005) && (out[3] == 0),
"小端按补码解释:0x8000 -> -32768、0xFFFF -> -1");
CHECK(wave_store_le_decode(s_le, 7u, out, 4u, &got) == WAVE_ST_E_PARAM, "奇数字节拒(不丢尾巴)");
CHECK(wave_store_le_decode(s_le, 8u, out, 3u, &got) == WAVE_ST_E_PARAM, "容量不足拒");
CHECK(wave_store_le_decode(NULL, 2u, out, 1u, &got) == WAVE_ST_E_PARAM, "空指针拒");
}
{
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;
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);
CHECK(mv == 1413, "TPAFE LSB 152.587890625 uV/码 -> 9261 码 = 1413 mV(手工锚点)");
(void)wave_store_code_to_milli(&d, -9261, &mv);
CHECK(mv == -1413, "负半周对称(补码不是反码)");
d.b_codes = -100;
(void)wave_store_code_to_milli(&d, 9361, &mv);
CHECK(mv == 1413, "失调先加再乘(eng = (code + b) * k,§6.3)");
d.b_codes = 0;
d.k_q16 = 52800000; /* 片内 ADC:805.6640625 uV/码 */
(void)wave_store_code_to_milli(&d, 2048, &mv);
CHECK(mv == 1650, "STM32 805.6640625 uV/码 -> 2048 码 = 1650 mV(手工锚点)");
d.k_q16 = 0;
(void)wave_store_code_to_milli(&d, 2048, &mv);
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, "空输出拒");
}
/* ---------- 用例:打包游标 ---------- */
printf("-- 用例:打包游标 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
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(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);
CHECK((s_img[len - 2u] == (uint8_t)(dcrc & 0xFFu)) && (s_img[len - 1u] == (uint8_t)(dcrc >> 8)),
"数据 CRC 覆盖数据区、低字节在前、贴在文件尾");
}
}
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(128u, &len, &rc) == 0, "128 字节分片(= 整头那么大)也能跑完");
CHECK(len == 5250u, "128 分片总长仍 5250(产出与分片大小无关)");
}
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(1u, &len, &rc) != 0, "容量 1 字节装不下整头 -> 首步就拒");
}
{
wave_pack_ctx_t ctx;
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(n == 0u, "拒绝时一个字节都不产(不写半截头)");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_OK) && (n == 128u), "同一游标重试成功(拒绝不留副作用)");
rc = wave_store_pack_next(&ctx, s_img, 1u, &n);
CHECK(rc == WAVE_ST_E_PARAM, "数据区容量装不下一个样本 -> 拒");
CHECK(n == 0u, "同样不产半个样本的字节");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
/* 恢复容量后这一拍产满 256 字节:数据区每次进循环都拿「批量上限」与
* 「容量剩余」里小的那个,256 字节 = 128 点 > 批量 64 点 → 两次取数才产满 */
CHECK((rc == WAVE_ST_OK) && (n == 256u), "恢复容量后继续推进(产满 256 字节)");
while (rc == WAVE_ST_OK) {
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
}
CHECK(rc == WAVE_ST_DONE, "跑到 DONE");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_DONE) && (n == 0u), "DONE 之后幂等(重复调不再产字节)");
}
{
wave_pack_ctx_t ctx;
uint32_t n = 0u;
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), "头能产");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_E_STATE) && (n == 0u),
"没有数据源却进数据区 -> 拒且不产空字节(产空字节 = 伪造一条直线)");
}
{
wave_pack_ctx_t ctx;
uint32_t n = 0u;
int rc;
CHECK(wave_store_pack_start(&ctx, &s_meta, short_reader, NULL) == WAVE_ST_OK, "少给点数的数据源 start OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_OK, "头一步 OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_E_STATE, "数据源少给一个点 -> 拒(不拿半批凑长度)");
CHECK(wave_store_pack_start(&ctx, &s_meta, err_reader, NULL) == WAVE_ST_OK, "报错数据源 start OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_E_RANGE, "数据源的错误码原样透传");
}
{
wave_pack_ctx_t ctx;
fill_meta(&s_meta);
s_meta.ch[0].sem = 5u;
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_E_FORMAT,
"pack_start 也跑同一把尺(表序错别想搬数据)");
s_meta.ch[0].sem = 0u;
s_meta.pts_per_ch = 0u;
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_E_SIZE,
"零点数在 start 就拒");
CHECK(wave_store_pack_start(NULL, &s_meta, fake_reader, NULL) == WAVE_ST_E_PARAM, "空游标拒");
CHECK(wave_store_pack_start(&ctx, NULL, fake_reader, NULL) == WAVE_ST_E_PARAM, "空段头拒");
CHECK((wave_store_pack_offset(NULL) == 0u) && (wave_store_pack_total(NULL) == 0u), "空游标查询返 0");
}
/* ---------- 用例:整段自检 ---------- */
printf("-- 用例:整段自检与判废 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(256u, &len, &rc) == 0, "参考段重跑 OK");
CHECK(wave_store_seg_check(s_img, 0u, &s_back) == WAVE_ST_E_SIZE, "零长度 -> 判废(不许画东西)");
CHECK(wave_store_seg_check(s_img, len - 1u, &s_back) == WAVE_ST_E_SIZE, "少一字节 -> 长度不符");
CHECK(wave_store_seg_check(s_img, len + 1u, &s_back) == WAVE_ST_E_SIZE, "多一字节 -> 长度不符");
CHECK(wave_store_seg_check(s_img, 300u, &s_back) == WAVE_ST_E_SIZE, "半截段 -> 长度不符");
{
uint8_t save = s_img[1000];
s_img[1000] = (uint8_t)(s_img[1000] ^ 0x5Au);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "数据区改一字节 -> 数据 CRC 不符");
s_img[1000] = save;
}
{
uint8_t save = s_img[len - 2u];
s_img[len - 2u] = (uint8_t)(s_img[len - 2u] ^ 0x01u);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "数据 CRC 自身被改 -> 不符");
s_img[len - 2u] = save;
}
{
uint8_t save = s_img[14];
s_img[14] = (uint8_t)(s_img[14] + 1u);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "段首毫秒被改 -> 头 CRC 拦下");
s_img[14] = save;
}
CHECK(wave_store_seg_check(NULL, len, &s_back) == WAVE_ST_E_PARAM, "空镜像指针");
CHECK(wave_store_seg_check(s_img, len, NULL) == WAVE_ST_OK, "段头输出可空(只判废的调用方)");
}
/* ---------- 用例:段名轮转 ---------- */
printf("-- 用例:段名轮转 --\n");
{
char path[WAVE_ST_PATH_MAX];
uint8_t idx = 99u;
/* 名字长度 = 16 个字符(/wave/ 6 + WAVE 4 + 两位 2 + .BIN 4),缓冲要 17 字节;
* 本条断言在 PATH_MAX 曾写错成 16 时抓出了 seg_path 内部 buf[] 的越界写 */
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 0u) == 16, "段号 0 拼出 16 字符");
CHECK(strcmp(path, "/wave/WAVE00.BIN") == 0, "严格 8.3:/wave/WAVE00.BIN");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 7u) == 16, "段号 7");
CHECK(strcmp(path, "/wave/WAVE07.BIN") == 0, "个位补零");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 99u) == 16, "段号 99");
CHECK(strcmp(path, "/wave/WAVE99.BIN") == 0, "最大号");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 100u) == WAVE_ST_E_RANGE, "段号 100 越界");
CHECK(wave_store_seg_path(path, 16u, 3u) == WAVE_ST_E_PARAM, "容量 16 装不下「16 字符 + 结束符」 -> 拒");
CHECK(wave_store_seg_path(path, 17u, 3u) == 16, "容量刚好 17 -> 通过");
CHECK(wave_store_seg_path(NULL, 17u, 3u) == WAVE_ST_E_PARAM, "空指针拒");
CHECK(wave_store_idx_from_name("/wave/WAVE07.BIN", &idx) == WAVE_ST_OK, "反解整路径");
CHECK(idx == 7u, "反解得 7");
/* 字面量用宏拼出来(运行时仍是同一个名字):本仓「字母紧跟数字」是编号门禁的
* 裸形态判据,测试数据里的自造号不该去撞它 */
CHECK(wave_store_idx_from_name(WAVE_ST_PFX "42" WAVE_ST_EXT, &idx) == WAVE_ST_OK, "反解纯文件名");
CHECK(idx == 42u, "反解得 42");
/* cppcheck 把「字符串字面量之后接宏」的拼接形态判成配置解析失败(unknownMacro
* 是配置类报错,不是代码缺陷;上一行的同族拼接它就不报)。本仓先例 =
* App/util/sys_clock.c 的带理由行内压制,这里同样写明理由而不是改测试。 */
// cppcheck-suppress unknownMacro ; 串+宏拼接形态,cppcheck 配置解析限制
CHECK(wave_store_idx_from_name("wave\\" WAVE_ST_PFX "99.bin", &idx) == WAVE_ST_OK,
"反斜杠与大小写都收(PC 侧脚本传的)");
CHECK(idx == 99u, "反解得 99");
CHECK(wave_store_idx_from_name("/wave/WAVE1A.BIN", &idx) == WAVE_ST_E_NAME, "非数字号 -> 拒");
CHECK(wave_store_idx_from_name("/wave/WAVE.BIN", &idx) == WAVE_ST_E_NAME, "缺号 -> 拒");
CHECK(wave_store_idx_from_name("/wave/WAVE007.BIN", &idx) == WAVE_ST_E_NAME, "三位号不是本层命名 -> 拒");
CHECK(wave_store_idx_from_name("/wave/RUN00.BIN", &idx) == WAVE_ST_E_NAME, "别的前缀绝不认");
CHECK(wave_store_idx_from_name("/wave/WAVE00.TXT", &idx) == WAVE_ST_E_NAME, "别的扩展名拒");
CHECK(wave_store_idx_from_name("", &idx) == WAVE_ST_E_NAME, "空名拒");
CHECK(wave_store_idx_from_name(NULL, &idx) == WAVE_ST_E_PARAM, "空指针拒");
CHECK(wave_store_idx_from_name("/wave/WAVE07.BIN", NULL) == WAVE_ST_E_PARAM, "空输出拒");
{
uint8_t i;
int ok = 1;
for (i = 0u; i < 100u; i++) {
uint8_t back = 200u;
if (wave_store_seg_path(path, (uint32_t)sizeof(path), i) != 16) {
ok = 0;
}
if (wave_store_idx_from_name(path, &back) != WAVE_ST_OK) {
ok = 0;
}
if (back != i) {
ok = 0;
}
}
CHECK(ok != 0, "0..99 全部拼名 -> 反解往返一致");
}
CHECK(wave_store_next_idx(0u) == 1u, "轮转 0 -> 1");
CHECK(wave_store_next_idx(98u) == 99u, "轮转 98 -> 99");
CHECK(wave_store_next_idx(99u) == 0u, "轮转 99 -> 0(回绕)");
CHECK(wave_store_next_idx(200u) == 0u, "越界号归零而不是回绕出野号");
}
/* ---------- 用例:互验产物(不参与判定) ---------- */
printf("-- 用例:互验产物 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
if (pack_whole(256u, &len, &rc) == 0) {
const char *tmp = getenv("TEMP");
char path[512];
FILE *fp;
size_t wrote;
CHECK(len == 5250u, "产物长度 5250");
if ((tmp != NULL) && (strlen(tmp) < 400u)) {
strcpy(path, tmp);
strcat(path, "\\wave_algo_synth8.bin");
fp = fopen(path, "wb");
if (fp != NULL) {
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]),
"互验产物自洽:这份构造段的单位标签与逐路有效位图对得上(真机写得出)");
/* 形态在日志里说清楚:本套是「同时带有效与削顶」的构造形态,
* 本波真值形态(逐路有效位图全零、片内 ADC 只带削顶位)由 wave_trig 那套给 */
printf(" [INFO] 互验产物(构造形态 = 已标定后仍削顶:逐路有效位图 0xFF、avail 0x01 / 0x03、"
"单位标签 %u 即二次值):%s\n",
(unsigned int)s_img[WAVE_ST_OFF_ENG], path);
} else {
printf(" [INFO] 互验产物跳过:%s 打不开\n", path);
}
}
} else {
CHECK(0, "互验产物:打包失败");
}
}
printf("\n=== Wave Store Tests: %d/%d PASSED (failed=%d) ===\n", s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}