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 条仍全过
539 lines
26 KiB
C
539 lines
26 KiB
C
/*
|
||
* 模块名称:录波环(滚动环 + 触发冻结状态机 + 已封存区读口)主机侧单元测试
|
||
* 被测对象:真实源文件(#include 原样纳入,测生产代码而非副本):
|
||
* App/algo/wave_store.c + App/algo/wave_ring.c
|
||
* 两源均为纯逻辑(无 HAL / CMSIS / RTOS / FatFs / 日志)→ **无需任何桩目录**。
|
||
*
|
||
* 为什么要主机侧(而不是只等台架):本层的判据几乎全是**结构与并发**类——
|
||
* 段首基址与点序、SEALED 期间写指针必须真冻结、ACK 前不许重录、滞留超时自弃、
|
||
* 冷却只在 ACK 后计时、读口在未就绪时绝不返回旧数据。这些在台架上要么不可观测
|
||
* (1 ms 一拍的窗口,人工读不到)、要么无法安全注入(真造一次「defaultTask 挂住
|
||
* 两秒」就是把看门狗窗口拿去赌)。主机侧可以拿影子样本序列把它逐点钉死。
|
||
* 需要板子的部分(注入 1 V/50 Hz 后 FTP 拉回对表、实机 FAT 落盘)不在本套件,
|
||
* 见 docs/任务规划/故障录波.md 的「录波数据面最小闭环」退出判据。
|
||
*
|
||
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
|
||
* 用例:形态与预算钉死:int16×320×8 = 5120、两半各 160、
|
||
* sizeof(wave_seg_meta_t) = 136、sizeof(wave_ch_desc_t) = 12
|
||
* 用例:init 复位语义:IDLE / 不可读 / 计数与轮转号归零
|
||
* 用例:参数守卫:push / request / latch / read / get_stat 的非法入参
|
||
* 用例:触发到封段的时序:封段恰在触发后第 160 拍,事件一次性
|
||
* 用例:段内容与点序:8 列 × 320 点逐点等于影子样本,
|
||
* 触发那一拍的样本在点 159、点 160 是其后第一拍(列序 = 语义序)
|
||
* 用例:SEALED 丢样本:push 返 DONE、drop 计点、写指针真冻结、内容不变
|
||
* 用例:封存交棒握手:未 latch 一律不可用;latch 只准一次、不覆盖;
|
||
* latch 内容进段头(eng / flags_seq / clip_mask / wall + 8 路系数)
|
||
* 用例:读窗夹取与跨环回绕;read_cb 的 32 位形态
|
||
* 用例:列号映射:0..7 恒等、TEMP 与越界返 WAVE_ST_E_RANGE
|
||
* 用例:ACK 语义:成功推进 seg_idx 与 seg_total 并起冷却;重复 ACK 拒绝;
|
||
* 未就绪时读口返 E_STATE 而不是旧数据
|
||
* 用例:环未填满就触发:前导点必须是 0(不是上一段的残留),真实样本从
|
||
* 点 129 起(本次上电只有 31 个点可给)
|
||
*
|
||
* 通过:全部 CHECK 通过(main 返回 0)。
|
||
* 创建日期:2026-10-29(故障录波 task-1)
|
||
*/
|
||
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
#include <stdio.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"
|
||
#include "wave_ring.c"
|
||
|
||
/* ===================== 影子样本模型 ===================== */
|
||
#define HIST_N 2048u /* 影子样本环(比录波环大,够跑完整个场景) */
|
||
#define TRIG_K 400u /* 第一段:消费请求那一拍的样本号 */
|
||
|
||
static int16_t s_hist[HIST_N][WAVE_RING_CH];
|
||
static uint32_t s_k; /* 下一个要压的样本号 */
|
||
static uint32_t s_evt_sealed; /* 累计看到多少次 EVT_SEALED */
|
||
static uint32_t s_push_bad; /* push 返回了 OK/DONE 之外的值 */
|
||
static int32_t s_push_last; /* 最近一次 push 的返回值 */
|
||
|
||
/* 每路一条可辨认的斜坡:不同样本、不同列都不重复,负值也覆盖(TPAFE 是双极性补码) */
|
||
static int16_t wave_sig(uint32_t a_k, uint8_t a_col) {
|
||
return (int16_t)(((int32_t)a_k * 7) - 1200 + ((int32_t)a_col * 91));
|
||
}
|
||
|
||
/* 压 n 拍(push 之后 tick,与 adcTask 的接线顺序一致:先写样本再算点数) */
|
||
static void wave_step(uint32_t a_n) {
|
||
uint32_t i;
|
||
|
||
for (i = 0u; i < a_n; i++) {
|
||
int16_t codes[WAVE_RING_CH];
|
||
uint8_t c;
|
||
int32_t rc;
|
||
|
||
for (c = 0u; c < (uint8_t)WAVE_RING_CH; c++) {
|
||
codes[c] = wave_sig(s_k, c);
|
||
s_hist[s_k % HIST_N][c] = codes[c];
|
||
}
|
||
rc = wave_ring_push(codes, (uint8_t)WAVE_RING_CH);
|
||
s_push_last = rc;
|
||
if ((rc != WAVE_ST_OK) && (rc != WAVE_ST_DONE)) {
|
||
s_push_bad++;
|
||
}
|
||
rc = wave_ring_tick(s_k, s_k);
|
||
if (rc == (int32_t)WAVE_RING_EVT_SEALED) {
|
||
s_evt_sealed++;
|
||
}
|
||
s_k++;
|
||
}
|
||
}
|
||
|
||
/* 影子样本 -> 段内点:点 i 对应样本 (a_trig - 159 + i),负数表示当时还没采到 */
|
||
static int32_t wave_pt_sample(uint32_t a_trig, uint32_t a_pt) {
|
||
return ((int32_t)a_trig - (int32_t)(WAVE_RING_PRE - 1u)) + (int32_t)a_pt;
|
||
}
|
||
|
||
/* 整段逐点比对(a_trig = 消费请求那一拍的样本号) */
|
||
static int wave_cmp_all(uint32_t a_trig) {
|
||
int16_t buf[WAVE_RING_PTS];
|
||
uint8_t c;
|
||
|
||
for (c = 0u; c < (uint8_t)WAVE_RING_CH; c++) {
|
||
int32_t n;
|
||
uint32_t i;
|
||
|
||
n = wave_ring_read_ch(c, 0u, (uint16_t)WAVE_RING_PTS, buf);
|
||
if (n != (int32_t)WAVE_RING_PTS) {
|
||
return 0;
|
||
}
|
||
for (i = 0u; i < (uint32_t)WAVE_RING_PTS; i++) {
|
||
int32_t ks = wave_pt_sample(a_trig, i);
|
||
int16_t want = (ks < 0) ? (int16_t)0 : wave_sig((uint32_t)ks, c);
|
||
if (buf[i] != want) {
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* 封好段并交棒(返回消费请求那一拍的样本号) */
|
||
static uint32_t s_trig_first;
|
||
|
||
static void wave_seal_once(uint8_t a_src, uint8_t a_elm) {
|
||
wave_ring_request(a_src, a_elm);
|
||
wave_step(1u); /* 本拍样本 = 点 159,同拍 tick 消费请求钉基址 */
|
||
s_trig_first = s_k - 1u;
|
||
wave_step((uint32_t)WAVE_RING_POST); /* 后触发 160 点,最后一拍封段 */
|
||
}
|
||
|
||
/* 造一份封存瞬间事实 */
|
||
static void wave_fill_cal(wave_seal_cal_t *p_cal, uint8_t a_seq_tag) {
|
||
uint8_t i;
|
||
|
||
p_cal->eng = (uint8_t)WAVE_ENG_PIN;
|
||
p_cal->flags_seq = (uint8_t)(0x50u ^ a_seq_tag);
|
||
p_cal->clip_mask = (uint16_t)0x00E6u;
|
||
p_cal->wall = 1764374400u + (uint32_t)a_seq_tag;
|
||
for (i = 0u; i < (uint8_t)WAVE_ST_CH_MAX; i++) {
|
||
p_cal->ch[i].sem = i;
|
||
p_cal->ch[i].conv = (uint8_t)((i == 0u) || (i == 3u) || (i == 4u)) ? 0u : 1u;
|
||
p_cal->ch[i].bits = (uint8_t)(((i == 0u) || (i == 3u) || (i == 4u)) ? 16u : 12u);
|
||
p_cal->ch[i].avail = (uint8_t)(0x01u | (((i == 1u) || (i == 5u)) ? 0x02u : 0x00u));
|
||
p_cal->ch[i].k_q16 = (int32_t)(((i == 0u) || (i == 3u) || (i == 4u)) ? 10000000L : 52800000L);
|
||
p_cal->ch[i].b_codes = (int32_t)(a_seq_tag + i);
|
||
}
|
||
}
|
||
|
||
static wave_seal_cal_t s_cal;
|
||
|
||
/* ===================== 主测 ===================== */
|
||
int main(void) {
|
||
printf("=== Wave Ring Tests:\n");
|
||
|
||
/* ---------- 用例:形态与预算 ---------- */
|
||
printf("-- 用例:形态与预算 --\n");
|
||
CHECK(sizeof(int16_t) == 2u, "int16 是两字节(不许 float)");
|
||
CHECK(WAVE_RING_BYTES == 5120u, "环 = 5120 字节");
|
||
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 字节(本卡静态增量台账用)");
|
||
|
||
/* ---------- 用例:init 复位 ---------- */
|
||
printf("-- 用例:init 复位语义 --\n");
|
||
wave_ring_init();
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "init 后 IDLE");
|
||
CHECK(wave_ring_sealed() == 0u, "init 后无可读段");
|
||
{
|
||
wave_ring_stat_t st;
|
||
int16_t buf[8];
|
||
CHECK(wave_ring_read_ch(0u, 0u, 8u, buf) == WAVE_ST_E_STATE, "init 后读已封存区被拒");
|
||
CHECK(wave_ring_get_stat(&st) == WAVE_ST_OK, "get_stat OK");
|
||
CHECK((st.seg_idx == 0u) && (st.seg_total == 0u) && (st.ovf_cnt == 0u) &&
|
||
(st.drop_cnt == 0u) && (st.err_cnt == 0u), "init 后计数与轮转号归零");
|
||
}
|
||
|
||
/* ---------- 用例:参数守卫 ---------- */
|
||
printf("-- 用例:参数守卫 --\n");
|
||
CHECK(wave_ring_push(NULL, (uint8_t)WAVE_RING_CH) == WAVE_ST_E_PARAM, "push 空指针");
|
||
{
|
||
int16_t codes[WAVE_RING_CH];
|
||
CHECK(wave_ring_push(codes, 7u) == WAVE_ST_E_PARAM, "push 长度不等于 8 拒绝");
|
||
CHECK(wave_ring_push(codes, 9u) == WAVE_ST_E_PARAM, "push 长度大于 8 拒绝");
|
||
}
|
||
CHECK(wave_ring_request(0u, 0u) == WAVE_ST_E_PARAM, "request 触发源 0 拒绝");
|
||
CHECK(wave_ring_request(4u, 0u) == WAVE_ST_E_PARAM, "request 触发源 4 拒绝");
|
||
CHECK(wave_ring_latch_meta(NULL) == WAVE_ST_E_PARAM, "latch 空指针");
|
||
CHECK(wave_ring_sealed_meta(NULL) == WAVE_ST_E_PARAM, "sealed_meta 空指针");
|
||
CHECK(wave_ring_get_stat(NULL) == WAVE_ST_E_PARAM, "get_stat 空指针");
|
||
CHECK(wave_ring_write_done(0) == WAVE_ST_E_STATE, "没段可 ACK 时拒绝");
|
||
|
||
/* ---------- 用例:触发到封段时序 ---------- */
|
||
printf("-- 用例:触发到封段时序 --\n");
|
||
wave_ring_init();
|
||
s_k = 0u;
|
||
s_evt_sealed = 0u;
|
||
s_push_bad = 0u;
|
||
wave_step(TRIG_K);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "400 拍后仍 IDLE(没请求就不该动)");
|
||
wave_ring_request((uint8_t)WAVE_TRIG_MANUAL, 7u);
|
||
wave_step(1u);
|
||
s_trig_first = s_k - 1u;
|
||
CHECK(s_trig_first == TRIG_K, "请求在下一拍被消费(本层不自己冻结,交给 adcTask 的 tick)");
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_FREEZE, "消费请求即 FREEZE");
|
||
CHECK(s_evt_sealed == 0u, "还没封段");
|
||
CHECK(wave_ring_sealed() == 0u, "FREEZE 期间不可读");
|
||
wave_step((uint32_t)WAVE_RING_POST - 1u);
|
||
CHECK((wave_ring_state() == (uint8_t)WAVE_RING_ST_FREEZE) && (s_evt_sealed == 0u),
|
||
"后触发 159 点时仍未封段");
|
||
wave_step(1u);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_SEALED, "后触发计满 160 点才封段");
|
||
CHECK(s_evt_sealed == 1u, "EVT_SEALED 恰好一次");
|
||
wave_step(1u);
|
||
CHECK(s_evt_sealed == 1u, "事件不重复报(只封段那一拍报)");
|
||
CHECK(s_push_bad == 0u, "全程 push 无非法返回");
|
||
|
||
/* ---------- 用例:a 未 latch 前一律不可用(必须先于内容比对) ---------- */
|
||
printf("-- 用例:封存交棒握手 --\n");
|
||
CHECK(wave_ring_sealed() == 0u, "刚封段还没交棒:不可用");
|
||
{
|
||
wave_ring_stat_t st;
|
||
wave_seg_meta_t meta;
|
||
int16_t buf[8];
|
||
(void)wave_ring_get_stat(&st);
|
||
CHECK(st.meta_ready == 0u, "meta_ready = 0");
|
||
CHECK(wave_ring_read_ch(0u, 0u, 8u, buf) == WAVE_ST_E_STATE, "未 latch 读被拒");
|
||
CHECK(wave_ring_sealed_meta(&meta) == WAVE_ST_E_STATE, "未 latch 段头被拒");
|
||
/* 此刻 latch 是合法时机 */
|
||
wave_fill_cal(&s_cal, 0u);
|
||
s_cal.eng = 9u;
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_E_PARAM, "折算档位非法拒绝");
|
||
s_cal.eng = (uint8_t)WAVE_ENG_PIN;
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "latch 成功");
|
||
CHECK(wave_ring_sealed() == 1u, "latch 后可用(两个标志并成一个)");
|
||
/* 第二次交棒:必须整份拒绝,锁存值一个字节都不许被换掉 */
|
||
{
|
||
uint32_t wall0 = s_cal.wall;
|
||
s_cal.wall = 123u;
|
||
s_cal.clip_mask = 0x0000u;
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_E_STATE, "重复 latch 拒绝");
|
||
CHECK(wave_ring_sealed_meta(&meta) == WAVE_ST_OK, "latch 后段头可读");
|
||
CHECK((meta.wall == wall0) && (meta.clip_mask == 0x00E6u),
|
||
"重复 latch 没有覆盖已锁存值(半路换系数会让头与数据不同源)");
|
||
s_cal.wall = wall0;
|
||
}
|
||
CHECK((meta.fs_hz == (uint16_t)WAVE_ST_FS_DEF) && (meta.pts_per_cycle == (uint8_t)WAVE_ST_PPC_DEF),
|
||
"段头采样率与每周波点数按 §6.2 落值");
|
||
CHECK((meta.ch_count == (uint8_t)WAVE_RING_CH) && (meta.pts_per_ch == (uint16_t)WAVE_RING_PTS) &&
|
||
(meta.pre_pts == (uint16_t)WAVE_RING_PRE), "段头几何三要素与环一致");
|
||
CHECK((meta.trig_src == (uint8_t)WAVE_TRIG_MANUAL) && (meta.trig_elm == 7u),
|
||
"触发源与元件序号进段头");
|
||
CHECK((meta.t0_ms == s_trig_first) && (meta.frame_seq == s_trig_first),
|
||
"段首毫秒与帧序号取触发那一拍(不是封段那一拍)");
|
||
CHECK((meta.flags_seq == s_cal.flags_seq) && (meta.clip_mask == 0x00E6u),
|
||
"有效性位图与削顶位图进段头");
|
||
{
|
||
uint8_t i;
|
||
int ok = 1;
|
||
for (i = 0u; i < (uint8_t)WAVE_RING_CH; i++) {
|
||
if ((meta.ch[i].sem != i) || (meta.ch[i].conv != s_cal.ch[i].conv) ||
|
||
(meta.ch[i].bits != s_cal.ch[i].bits) || (meta.ch[i].avail != s_cal.ch[i].avail) ||
|
||
(meta.ch[i].k_q16 != s_cal.ch[i].k_q16) || (meta.ch[i].b_codes != s_cal.ch[i].b_codes)) {
|
||
ok = 0;
|
||
}
|
||
}
|
||
CHECK(ok != 0, "通道表 8 项逐项进段头(sem = 下标)");
|
||
}
|
||
}
|
||
|
||
/* ---------- 用例:段内容与点序 ---------- */
|
||
printf("-- 用例:段内容与点序 --\n");
|
||
CHECK(wave_cmp_all(s_trig_first) != 0, "8 列 × 320 点逐点等于影子样本(列序 = 语义序)");
|
||
{
|
||
int16_t buf[WAVE_RING_PTS];
|
||
(void)wave_ring_read_ch(0u, 0u, (uint16_t)WAVE_RING_PTS, buf);
|
||
CHECK(buf[WAVE_RING_PRE - 1u] == wave_sig(s_trig_first, 0u),
|
||
"触发那一拍的样本落在点 159(预触发窗最后一点)");
|
||
CHECK(buf[WAVE_RING_PRE] == wave_sig(s_trig_first + 1u, 0u),
|
||
"点 160 是触发之后第一拍");
|
||
CHECK(buf[0] == wave_sig(s_trig_first - (WAVE_RING_PRE - 1u), 0u),
|
||
"点 0 是最老的预触发点(段首基址钉对了)");
|
||
CHECK(buf[1] > buf[0], "斜坡单调 -> 点序没有倒过来");
|
||
}
|
||
|
||
/* ---------- 用例:SEALED 丢样本 + 写指针真冻结 ---------- */
|
||
printf("-- 用例:SEALED 丢样本与写指针冻结 --\n");
|
||
{
|
||
wave_ring_stat_t st0;
|
||
wave_ring_stat_t st1;
|
||
(void)wave_ring_get_stat(&st0);
|
||
wave_step(10u);
|
||
(void)wave_ring_get_stat(&st1);
|
||
CHECK(s_push_last == WAVE_ST_DONE, "SEALED 期间 push 返 DONE(样本被丢)");
|
||
CHECK(st1.drop_cnt == (st0.drop_cnt + (10u * (uint32_t)WAVE_RING_CH)),
|
||
"drop 计数单位 = 点(一次丢一拍加 8)");
|
||
CHECK(st1.seal_hold == (uint16_t)(st0.seal_hold + 10u), "SEALED 滞留按拍累计");
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_SEALED, "滞留未超时仍是 SEALED");
|
||
CHECK(wave_cmp_all(s_trig_first) != 0, "已封存区一个点都没被动过(写指针真冻住)");
|
||
}
|
||
|
||
/* ---------- 用例:读窗夹取 / 跨回绕 / read_cb ---------- */
|
||
printf("-- 用例:读窗夹取与跨回绕 --\n");
|
||
{
|
||
int16_t buf[WAVE_RING_PTS];
|
||
int32_t n;
|
||
uint32_t i;
|
||
int ok;
|
||
|
||
n = wave_ring_read_ch(3u, 300u, 40u, buf);
|
||
CHECK(n == 20, "超出段尾自动夹到剩余点数");
|
||
ok = 1;
|
||
for (i = 0u; i < 20u; i++) {
|
||
int32_t ks = wave_pt_sample(s_trig_first, 300u + i);
|
||
if (buf[i] != wave_sig((uint32_t)ks, 3u)) {
|
||
ok = 0;
|
||
}
|
||
}
|
||
CHECK(ok != 0, "夹出来的 20 点仍与影子样本一致(回绕段尾正确)");
|
||
/* base = 241(400 拍场景)-> 段内跨环尾的点必须靠取模接上 */
|
||
n = wave_ring_read_ch(5u, 70u, 20u, buf);
|
||
ok = 1;
|
||
for (i = 0u; i < (uint32_t)n; i++) {
|
||
int32_t ks = wave_pt_sample(s_trig_first, 70u + i);
|
||
if (buf[i] != wave_sig((uint32_t)ks, 5u)) {
|
||
ok = 0;
|
||
}
|
||
}
|
||
CHECK((n == 20) && (ok != 0), "跨越环尾的窗取模展开正确(段首基址 241)");
|
||
n = wave_ring_read_cb(NULL, 2u, 0u, (uint32_t)WAVE_RING_PTS, buf);
|
||
CHECK(n == (int32_t)WAVE_RING_PTS, "read_cb 32 位形态整段可读");
|
||
CHECK(wave_ring_read_cb(NULL, 2u, 0u, 9999u, buf) == WAVE_ST_E_PARAM, "read_cb 点数超过环长拒绝");
|
||
CHECK(wave_ring_read_cb(NULL, 2u, 9999u, 1u, buf) == WAVE_ST_E_RANGE, "read_cb 起点越界拒绝");
|
||
CHECK(wave_ring_read_ch(8u, 0u, 1u, buf) == WAVE_ST_E_RANGE, "列号越界");
|
||
CHECK(wave_ring_read_ch(0u, (uint16_t)WAVE_RING_PTS, 1u, buf) == WAVE_ST_E_RANGE, "起点越界");
|
||
CHECK(wave_ring_read_ch(0u, 0u, 0u, buf) == WAVE_ST_E_PARAM, "点数为 0");
|
||
CHECK(wave_ring_read_ch(0u, 0u, 1u, NULL) == WAVE_ST_E_PARAM, "读口空指针");
|
||
}
|
||
|
||
/* ---------- 用例:列号映射 ---------- */
|
||
printf("-- 用例:列号映射 --\n");
|
||
{
|
||
int ok = 1;
|
||
uint8_t i;
|
||
for (i = 0u; i < (uint8_t)WAVE_RING_CH; i++) {
|
||
if (wave_ring_col(i) != (int32_t)i) {
|
||
ok = 0;
|
||
}
|
||
}
|
||
CHECK(ok != 0, "0..7 语义号 -> 同列号(本波恒等)");
|
||
CHECK(wave_ring_col(8u) == WAVE_ST_E_RANGE, "TEMP 不录 -> 越界");
|
||
CHECK(wave_ring_col(200u) == WAVE_ST_E_RANGE, "非法语义号 -> 越界");
|
||
}
|
||
|
||
/* ---------- 用例:ACK 成功 + 冷却 + 二次触发 ---------- */
|
||
printf("-- 用例:ACK 与冷却 --\n");
|
||
{
|
||
wave_ring_stat_t st;
|
||
int16_t buf[8];
|
||
CHECK(wave_ring_write_done(0) == WAVE_ST_OK, "ACK 成功");
|
||
(void)wave_ring_get_stat(&st);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_COOL, "ACK 后转冷却");
|
||
CHECK(st.cool_left == (uint16_t)WAVE_RING_COOL_MS, "冷却起表 500 拍");
|
||
CHECK((st.seg_total == 1u) && (st.seg_idx == 1u), "成功才推进轮转号与段计数");
|
||
CHECK(wave_ring_sealed() == 0u, "解冻后立刻不可读");
|
||
CHECK(wave_ring_read_ch(0u, 0u, 8u, buf) == WAVE_ST_E_STATE, "解冻后读被拒(不返旧数据)");
|
||
CHECK(wave_ring_write_done(0) == WAVE_ST_E_STATE, "重复 ACK 拒绝");
|
||
/* 冷却期内再触发:只加 ovf,绝不重录 */
|
||
CHECK(wave_ring_request((uint8_t)WAVE_TRIG_PROTECT, 3u) == WAVE_ST_DONE, "冷却期内请求被忽略");
|
||
(void)wave_ring_get_stat(&st);
|
||
CHECK(st.ovf_cnt == 1u, "ovf 计一次");
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_COOL, "被忽略的请求没有改状态");
|
||
wave_step(100u);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_COOL, "冷却未走完仍在 COOL");
|
||
wave_step(400u);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "冷却走完后回 IDLE");
|
||
}
|
||
|
||
/* ---------- 用例:ACK 失败不推进轮转号 ---------- */
|
||
printf("-- 用例:ACK 失败不推进号 --\n");
|
||
wave_seal_once((uint8_t)WAVE_TRIG_PROTECT, 2u);
|
||
wave_fill_cal(&s_cal, 1u);
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "第二段交棒 OK");
|
||
CHECK(wave_cmp_all(s_trig_first) != 0, "第二段内容 = 第二次故障的影子样本");
|
||
{
|
||
wave_ring_stat_t st;
|
||
CHECK(wave_ring_write_done(-5) == WAVE_ST_OK, "失败 ACK 仍解冻(错误只计数)");
|
||
(void)wave_ring_get_stat(&st);
|
||
CHECK(st.err_cnt == 1u, "err 计一次");
|
||
CHECK((st.seg_idx == 1u) && (st.seg_total == 1u), "失败不推进轮转号与段计数(下段同号)");
|
||
}
|
||
|
||
/* ---------- 用例:滞留超时自弃 ---------- */
|
||
printf("-- 用例:SEALED 滞留超时自弃 --\n");
|
||
wave_step((uint32_t)WAVE_RING_COOL_MS);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "冷却走完回 IDLE");
|
||
wave_seal_once((uint8_t)WAVE_TRIG_BENCH, 0u);
|
||
wave_fill_cal(&s_cal, 2u);
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "第三段交棒 OK");
|
||
wave_step((uint32_t)WAVE_SEAL_TIMEOUT_MS - 1u);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_SEALED, "差一拍还没到超时:仍 SEALED");
|
||
{
|
||
wave_ring_stat_t st0;
|
||
wave_ring_stat_t st1;
|
||
(void)wave_ring_get_stat(&st0);
|
||
wave_step(1u);
|
||
(void)wave_ring_get_stat(&st1);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_COOL, "超时自弃并起冷却(绝不让环永久焊死)");
|
||
CHECK(st1.err_cnt == (st0.err_cnt + 1u), "自弃计 err");
|
||
CHECK(st1.seal_hold == 0u, "滞留计数清零");
|
||
CHECK((st1.seg_idx == st0.seg_idx) && (st1.seg_total == st0.seg_total),
|
||
"自弃不算成功:号与段计数都不动");
|
||
CHECK(wave_ring_sealed() == 0u, "自弃后不可读");
|
||
}
|
||
/* 自弃之后必须还能录下一段 */
|
||
wave_step((uint32_t)WAVE_RING_COOL_MS);
|
||
wave_seal_once((uint8_t)WAVE_TRIG_MANUAL, 0u);
|
||
wave_fill_cal(&s_cal, 3u);
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "自弃之后仍能录第四段");
|
||
CHECK(wave_cmp_all(s_trig_first) != 0, "第四段内容是新的故障(没被上一段污染)");
|
||
|
||
/* ---------- 用例:环未填满就触发 ---------- */
|
||
printf("-- 用例:环未填满就触发 --\n");
|
||
wave_ring_write_done(0);
|
||
wave_step((uint32_t)WAVE_RING_COOL_MS);
|
||
wave_ring_init();
|
||
s_k = 0u;
|
||
s_evt_sealed = 0u;
|
||
wave_step(30u);
|
||
wave_seal_once((uint8_t)WAVE_TRIG_MANUAL, 0u);
|
||
wave_fill_cal(&s_cal, 4u);
|
||
CHECK(wave_ring_latch_meta(&s_cal) == WAVE_ST_OK, "只采了 31 点也能封段交棒");
|
||
{
|
||
int16_t buf[WAVE_RING_PTS];
|
||
uint32_t i;
|
||
int zeros = 1;
|
||
int reals = 1;
|
||
(void)wave_ring_read_ch(0u, 0u, (uint16_t)WAVE_RING_PTS, buf);
|
||
for (i = 0u; i < 129u; i++) {
|
||
if (buf[i] != 0) {
|
||
zeros = 0;
|
||
}
|
||
}
|
||
for (i = 129u; i < (uint32_t)WAVE_RING_PTS; i++) {
|
||
if (buf[i] != wave_sig(i - 129u, 0u)) {
|
||
reals = 0;
|
||
}
|
||
}
|
||
CHECK(zeros != 0, "前导 129 点是 0(不是上一段残留,init 清零生效)");
|
||
CHECK(reals != 0, "真实样本从点 129 起接上(点 159 = 第 30 拍)");
|
||
}
|
||
|
||
printf("-- 用例:挂号载荷单字节往返(lead 评审 2026-10-29 交错缺陷) --\n");
|
||
{
|
||
static const uint8_t k_src[3] = { 1u, 2u, 3u };
|
||
static const uint8_t k_elm[3] = { 0u, 1u, 31u };
|
||
uint32_t a;
|
||
uint32_t b;
|
||
int accept_all = 1;
|
||
int second_rejected = 1;
|
||
int ovf_once = 1;
|
||
int decoded_ok = 1;
|
||
int head_identity = 1;
|
||
|
||
for (a = 0u; a < 3u; a++) {
|
||
for (b = 0u; b < 3u; b++) {
|
||
wave_ring_stat_t st0;
|
||
wave_ring_stat_t st1;
|
||
wave_seg_meta_t meta;
|
||
uint8_t src = k_src[a];
|
||
uint8_t elm = k_elm[b];
|
||
|
||
wave_ring_init();
|
||
s_k = 0u;
|
||
s_evt_sealed = 0u;
|
||
wave_step(20u);
|
||
if (wave_ring_request(src, elm) != WAVE_ST_OK) {
|
||
accept_all = 0;
|
||
}
|
||
/* 交错形状:adcTask 还没 tick(挂号未被消费)时又来了第二个请求。
|
||
* 旧实现在这里已经写完了挂号位、载荷还没写完,本条用例就是那个形状 */
|
||
if (wave_ring_request(k_src[(a + 1u) % 3u], 9u) != WAVE_ST_DONE) {
|
||
second_rejected = 0;
|
||
}
|
||
(void)wave_ring_get_stat(&st0);
|
||
if (st0.ovf_cnt != 1u) {
|
||
ovf_once = 0;
|
||
}
|
||
wave_step(1u);
|
||
(void)wave_ring_get_stat(&st1);
|
||
if ((st1.trig_src != src) || (st1.trig_elm != elm)) {
|
||
decoded_ok = 0;
|
||
}
|
||
wave_step((uint32_t)WAVE_RING_POST);
|
||
wave_fill_cal(&s_cal, 0u);
|
||
if ((wave_ring_latch_meta(&s_cal) != WAVE_ST_OK)
|
||
|| (wave_ring_sealed_meta(&meta) != WAVE_ST_OK)
|
||
|| (meta.trig_src != src) || (meta.trig_elm != elm)) {
|
||
head_identity = 0;
|
||
}
|
||
(void)wave_ring_write_done(0);
|
||
wave_step((uint32_t)WAVE_RING_COOL_MS);
|
||
}
|
||
}
|
||
CHECK(accept_all == 1, "九个触发源与元件号组合全部受理");
|
||
CHECK(second_rejected == 1, "挂号未被消费的窗口里第二个请求一律被拒(交错形状)");
|
||
CHECK(ovf_once == 1, "被拒那次只加一次 ovf,不重录不排队");
|
||
CHECK(decoded_ok == 1, "下一拍解码出的触发源与元件号就是本次投递值");
|
||
CHECK(head_identity == 1, "封段交棒后段头身份仍是本次投递值(文件格式没多一个字节)");
|
||
}
|
||
|
||
printf("-- 用例:元件号越界当场拒且不记 ovf 账 --\n");
|
||
{
|
||
wave_ring_stat_t st;
|
||
|
||
wave_ring_init();
|
||
s_k = 0u;
|
||
s_evt_sealed = 0u;
|
||
wave_step(10u);
|
||
CHECK(wave_ring_request((uint8_t)WAVE_TRIG_PROTECT, 32u) == WAVE_ST_E_PARAM, "元件号 32 拒绝(载荷只有 5 位,越界不许静默截断)");
|
||
CHECK(wave_ring_request((uint8_t)WAVE_TRIG_PROTECT, 255u) == WAVE_ST_E_PARAM, "元件号 255 拒绝");
|
||
CHECK(wave_ring_request(0u, 5u) == WAVE_ST_E_PARAM, "触发源 0 带合法元件号也拒");
|
||
CHECK(wave_ring_request(4u, 5u) == WAVE_ST_E_PARAM, "触发源 4 带合法元件号也拒");
|
||
(void)wave_ring_get_stat(&st);
|
||
CHECK(st.ovf_cnt == 0u, "四条参数错一条都没记进 ovf(参数错不是第二次触发)");
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "参数错没有留下任何挂号");
|
||
wave_step(1u);
|
||
CHECK(wave_ring_state() == (uint8_t)WAVE_RING_ST_IDLE, "空跑一拍仍是 IDLE(没有凭空开录)");
|
||
CHECK(wave_ring_request((uint8_t)WAVE_TRIG_PROTECT, 31u) == WAVE_ST_OK, "越界值拒完之后 31 号元件照样能挂号");
|
||
}
|
||
|
||
printf("\n=== Wave Ring Tests: %d/%d PASSED (failed=%d) ===\n", s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|