d64bbdc57f
数据面(App/algo,纯逻辑,双端编译) - wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask 唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃 兜底——下一次真故障无波可录比丢一段更贵 - wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带 锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度 - 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式 - 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样 纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉 - test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条 自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合 - 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定) 接线(任务层) - adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图), 段头按最低有效位精确记账,不经浮点回算 - adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实; 台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行 - defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到 关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms 补一条告警并记历史峰值(喂狗余量可核) - Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留 超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的, 半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进 - Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到 上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数; 段文件字节布局零改动,三份合成段哈希逐字节不变 - protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体 零改动 人机(160×160 单色屏) - 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器 得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30 - 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与 垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、 单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据 - ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、 手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口 - 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有 60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形 - 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变 - 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏 22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、 仪器用已在跑的单轮工作耗时探针,不新加埋点 台账与文档 - 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936 字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名; 散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建 重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置) - 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据); §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径 风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」 - 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项 - 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划 索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动) - 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件 形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记 「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场 - 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行 227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退 为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与 体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账 同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道 编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串 生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
773 lines
41 KiB
C
773 lines
41 KiB
C
/*
|
||
* 模块名称:录波段格式层(段文件字节布局编解码 + 打包游标 + 段名轮转)主机侧单元测试
|
||
* 被测对象:真实源文件(#include 原样纳入,测生产代码而非副本):App/algo/wave_store.c
|
||
* 该源是纯逻辑(无 HAL / CMSIS / RTOS / FatFs / 日志)→ **无需任何桩目录**。
|
||
*
|
||
* 为什么要主机侧(而不是只等台架):段文件是**证据**,一旦写进 NAND 就再也说不清
|
||
* 「当时想表达什么」。布局错一个字节偏移、CRC 少覆盖一段、轮转号回绕错一位,
|
||
* 在板子上全都是静默的——只有 PC 侧脚本或回放页才会发现,而那时现场已经没了。
|
||
* 所以字节级判据(整段长度等式、CRC 锚点、编解码往返、拒绝清单)必须钉在主机侧。
|
||
* 需要板子的部分(真 FAT 落盘、FTP 拉回、注入对表)见 docs/任务规划/故障录波.md 卡 1。
|
||
*
|
||
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
|
||
* 用例: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;
|
||
}
|