feat(104遥测): 卡9改读L2快照+死区运行化2点确认 + 卡11绑频0x401D(道D M5/M6)

卡 9(App/protocol/iec104/iec104_pts.*,「数据可信」,docs/子系统设计/
IEC104上行设计说明.md §3.1/§4 同步):
- 数据源换轨:板端默认 YC 源从直读 phasor 改为**整体透传 L2 遥测快照**
  (meas_get 双区无锁;"新不新、能不能信"只在发布侧判一次,M5-② 落地);
  iec104_pts_src_t 表尾追加 yc_read_ex(带品质)——旧注入源走 legacy 退路,
  品质由返回值推导,行为与卡 9 前**完全一致**(存量假源用例零改动)
- 品质映射 s_q_map:MEAS_QDS_* 低字节=线上 QDS 同值直传;CLIPPED→IV
  (「削顶通道 104 恒 IV」口径不变、从点表绑死 -1 改品质驱动——UB/UC/IB/
  IC/IN 五路现绑快照槽,V0.2/N-18 削顶消失后**零改动自动启用**);IV 优先
  NT;快照从未发布(开机 <200ms)→ IV|NT;无效信息体清零(旧线上口径保持)
- 死区运行化:运行时副本 s_yc_db(ROM 默认 = 点表列,卡 6 额定 0.5% 档不变)
  + iec104_pts_yc_set_deadband()/iec104_pts_yc_deadband()——卡 10 运行参数段
  (8020H)的接线点已留;非法 idx/负值/NaN 拒绝、成功时清该点待确认计数
- COV 遥测腿升级(lead 裁②"卡 9 是消费者"):判定 = 相对**上次上送值**(非
  上次读值,防慢漂移吃基准)连续 2 个新测量点越死区才确认上送;回摆入带即
  撤销候选;品质翻转当拍直报(含 0x401D 的 IV 进出)。测量点推进:板端按
  快照 seq 去重(100ms poll vs 200ms 快照——不去重则同一帧把单点噪声计满
  2 点,抗噪语义失效);注入源=每次调用一测点(单测步进自持)

卡 11:YC 表加 src 类型(PTS_SRC_MEAS/FREQ);0x401D 绑快照 freq_hz(与
  freq_get 同源);snap.freq_valid==0 → IV 清零(卡 11 DoD 原句)

测试与文档:test/host/iec104 97→127 断言——TC-008 语义更新为 2 点确认;
新 TC-IEC104-025~028(024 号被存量 B7 用例裸标签占用,让号并改用全家长
形态)板端默认源走**真实 L2 链**(ring→phasor→freq→meas,非桩):未发布
NT/值透传/CLIPPED→IV 清零/绑频收敛 ±0.02Hz/未新鲜 IV/死区运行档压旧档/
回摆撤销/seq 去重同帧不计数/换帧只变品质按集合直报;id_registry 族游标
023→028;IEC104 上行设计说明 §3.1 表+注/§4/§7/§8 第 4 条/变更记录同步。
无新 .c(uvprojx 零改动);PC 主站读回一致与频率 ±0.01Hz 属网络对端验证
——步骤清单已发 lead 排窗,本轮零台架动作。

gate:@build 0 错 0 警(Code=218128 RO=21624 RW=1272 ZI=132864);增量 vs
adeef7d:Flash +348B、RAM(RW+ZI) +48B(低于 96B 预估,链接器并洞),
IRAM1 58.5% 不变;iec104Task 栈 worst=1456B margin=1104B(43%) PASS 无抬栈
(stack_report);--all-files 全量实跑:其余钩子全绿(含修掉一处 cppcheck
variableScope 后),仅 mem-budget + mem-numbers 两红 = 台账受管区 vs .map
漂移(回填属 lead 单写者区,批 1/道D-1 同口径):
gate: only mem-budget/mem-numbers pending lead ledger backfill
BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过(未触碰带 BOM 文件)。
This commit is contained in:
2026-10-25 12:03:29 +08:00
parent adeef7d21b
commit acc547efaf
5 changed files with 512 additions and 118 deletions
+195 -3
View File
@@ -17,7 +17,8 @@
* TC-IEC104-005 OPEN 态 I 帧忽略(序号不消费)
* TC-IEC104-006 总召全流程:actcon→4 分片(首召 COT=4)→actterm;二召 COT=2;RBT→COT=4
* TC-IEC104-007 遥信变位 COV(COT=3);无效位不触发
* TC-IEC104-008 遥测死区 COV:死区内不发 / 越死区发
* TC-IEC104-008 遥测死区 COV(卡 9 起 = 2 点确认):死区内不发 / 首越只挂起 /
* 连续第 2 越点上送
* TC-IEC104-009 k 窗:12 未确认停发;S 确认后恢复
* TC-IEC104-010 w 阈:收满 8 发 S
* TC-IEC104-011 t2 到期发 S
@@ -33,6 +34,17 @@
* TC-IEC104-021 C_TS / C_RP actcon
* TC-IEC104-022 周期上送 COT=1(YC 组 + 频率)
* TC-IEC104-023 k 窗满 COV 不丢事件(pend 暂存,窗开后补发)
* —— 以下为「数据可信」卡 9/卡 11(道D,2026-10-25;板端默认源 = 真实
L2 链 ring→phasor→freq→meas,非桩)——
* TC-IEC104-025 板端源读快照(024 号被存量 B7 用例的裸标签占用):未发布
* IV|NT / 值=基波有效值 / 削顶 CLIPPED
* →线上 IV 清零 / 干净路品质 0 / 频率未新鲜 IV
* TC-IEC104-026 0x401D 绑频:收敛后 ±0.02 Hz / freq_valid==0 → IV 清零
* (卡 11 DoD 原句)
* TC-IEC104-027 死区运行时化:set/get + 非法拒 + 运行档压住旧档步长 +
* 2 点确认/回摆撤销/init 复位 ROM 默认
* TC-IEC104-028 品质翻转直报(假源)+ 板端 seq 去重(同帧不计两点)+
* 换帧只变品质时按集合直报
*
* 通过:全部 TC PASS(main 返回 0)。
* 创建日期:2026-10-12(B4)
@@ -54,6 +66,7 @@ static int s_total = 0, s_passed = 0, s_failed = 0;
#include "sample_ring.c"
#include "phasor.c"
#include "freq.c"
#include "meas.c" /* 卡 9:板端默认 YC 源 = L2 遥测快照(真实链,非桩) */
#include "prot_elm.c"
#include "iec104_pts.c"
#include "iec104_link.c"
@@ -515,11 +528,14 @@ static void tc_cov_yc(void)
* 旧拟定码值死区 10 已由「数据可信」卡 6 重定) */
iec104_link_poll(&g_lk, now + 100u);
CHECK(cap_i_count() == 0, "TC-008 inside the deadband nothing is uploaded");
s_f_yc[0] = 101.25f; /* 101.25-100.4=0.85 > 0.5 */
s_f_yc[0] = 101.25f; /* 101.25-100=1.25 > 0.5,第 1 越点 */
iec104_link_poll(&g_lk, now + 200u);
CHECK(cap_i_count() == 0, "TC-IEC104-008 first deadband crossing only arms (2-point confirm)");
iec104_link_poll(&g_lk, now + 300u); /* 第 2 个连续测点仍越 -> 确认上送 */
CHECK((cap_i_count() == 1) && (cap_i(0, &c) == 0) && (c.type == 13u)
&& (c.cot == IEC104_COT_SPONT) && (c.ioa == 0x4011u)
&& (iec104_get_float(c.obj) == 101.25f), "TC-008 crossing the deadband uploads");
&& (iec104_get_float(c.obj) == 101.25f),
"TC-IEC104-008 second consecutive crossing uploads (Card 9 confirm)");
}
static void feed_rd(uint32_t now)
@@ -912,6 +928,178 @@ static void tc_cov_kfull(void)
&& (c.cot == IEC104_COT_SPONT), "TC-023 once the window opens the backlogged event is sent, none lost");
}
/* ========== 卡 9/卡 11:板端默认源(真实 L2 链:ring→phasor→freq→meas) ========== */
/* 与 App/task/adc_task.c 同型的单拍编排:压环 → phasor_run →(可选 freq_run)。
* 9 路同幅错相正弦(整周波 → 各路基波有效值 = A/√2,与相位无关,期望好算) */
static double s_ph2 = 0.0;
static void snap_beats(int n, double amp, int run_freq)
{
float f[ADC_SEM_COUNT];
int i;
int s;
for (i = 0; i < n; i++) {
for (s = 0; s < (int)ADC_SEM_TEMP; s++) {
f[s] = (float)(amp * sin(s_ph2 + (double)s * 0.35));
}
f[ADC_SEM_TEMP] = 1234.0f;
s_ph2 += 2.0 * 3.14159265358979323846 * 50.0 / 1000.0; /* 50 Hz */
(void)sample_ring_push_frame(f, (uint8_t)ADC_SEM_COUNT);
phasor_run();
if (run_freq != 0) {
freq_run();
}
}
}
/* 200 ms 发布拍(读同拍 freq 状态,adc_task v3.2 编排的镜像) */
static void snap_pub(uint32_t ms, uint32_t clip_mask)
{
meas_tick(ms, freq_valid(), freq_last(), clip_mask);
}
static void tc_pts_snap(void)
{
float v;
uint8_t q;
reset_all(1000u);
iec104_pts_bind_src(NULL); /* 板端默认源 = 真实快照链 */
(void)phasor_init(); /* 环 + 相量双区清零 */
(void)meas_init();
CHECK((iec104_pts_yc_read(0u, &v, &q) == -1)
&& (q == (uint8_t)(IEC104_QDS_IV | IEC104_QDS_NT)) && (v == 0.0f),
"TC-IEC104-025 before first publish -> IV|NT, value zero");
snap_beats(20, 100.0, 0);
snap_pub(200u, 0xE6u); /* 削顶位图同板端默认构建(cl=0xE6) */
CHECK((iec104_pts_yc_read(0u, &v, &q) == 0) && (q == 0u)
&& (fabsf(v - 70.711f) < 0.1f),
"TC-IEC104-025 UA = snapshot fundamental RMS (engineering domain)");
CHECK((iec104_pts_yc_read(3u, &v, &q) == 0) && (q == 0u),
"TC-IEC104-025 UN (bit3 not in 0xE6) stays valid");
CHECK((iec104_pts_yc_read(4u, &v, &q) == 0) && (q == 0u),
"TC-IEC104-025 IA valid");
CHECK((iec104_pts_yc_read(1u, &v, &q) == -1) && (q == IEC104_QDS_IV)
&& (v == 0.0f), "TC-IEC104-025 clipped UB: CLIPPED maps to wire IV, value zero");
CHECK((iec104_pts_yc_read(2u, &v, &q) == -1) && (q == IEC104_QDS_IV),
"TC-IEC104-025 clipped UC IV");
CHECK((iec104_pts_yc_read(5u, &v, &q) == -1) && (q == IEC104_QDS_IV),
"TC-IEC104-025 clipped IB IV");
CHECK((iec104_pts_yc_read(8u, &v, &q) == -1) && (q == IEC104_QDS_IV),
"TC-IEC104-025 freq point published but not fresh -> IV (Card 11 rule)");
}
static void tc_pts_freq(void)
{
float v;
uint8_t q;
int k;
reset_all(1000u);
iec104_pts_bind_src(NULL);
(void)phasor_init();
(void)freq_init();
(void)meas_init();
for (k = 0; k < 5; k++) { /* 100 拍:测频启动收敛需 20 拍 */
snap_beats(20, 100.0, 1);
snap_pub((uint32_t)(k + 1) * 200u, 0u);
}
CHECK(freq_valid() == 1u, "TC-IEC104-026 drive precondition: freq converged");
CHECK((iec104_pts_yc_read(8u, &v, &q) == 0) && (q == 0u)
&& (fabsf(v - 50.0f) <= 0.02f),
"TC-IEC104-026 0x401D carries freq reading within 0.02 Hz of synthetic 50.000");
for (k = 0; k < 3; k++) { /* 浮空:幅值门落下 */
snap_beats(20, 0.0, 1);
snap_pub((uint32_t)(6 + k) * 200u, 0u);
}
CHECK((freq_valid() == 0u) && (iec104_pts_yc_read(8u, &v, &q) == -1)
&& (q == IEC104_QDS_IV) && (v == 0.0f),
"TC-IEC104-026 freq_valid==0 -> IV with zeroed value (Card 11 DoD)");
}
static void tc_db_runtime(void)
{
iec104_pts_cov_t cov;
reset_all(1000u); /* 假源在场 = 每次 cov_check 一测点 */
CHECK((iec104_pts_yc_set_deadband(0u, 2.0f) == 0)
&& (iec104_pts_yc_deadband(0u) == 2.0f),
"TC-IEC104-027 runtime deadband write + readback");
CHECK((iec104_pts_yc_set_deadband(9u, 1.0f) == -1)
&& (iec104_pts_yc_set_deadband(0u, -0.1f) == -1)
&& (iec104_pts_yc_deadband(0u) == 2.0f),
"TC-IEC104-027 bad idx / negative rejected, RAM untouched");
CHECK(iec104_pts_yc_deadband(8u) == 0.5f, "TC-IEC104-027 freq point ROM default kept");
s_f_yc[0] = 100.0f;
iec104_pts_cov_check(&cov); /* 建线 rep=100(档 2.0) */
s_f_yc[0] = 101.0f; /* 越旧档 0.5、未越运行档 2.0 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 runtime db gates the old step");
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 2nd point under 2.0 still silent");
iec104_pts_init(); /* 恢复 ROM 默认档(0.5) */
iec104_pts_bind_src(&g_fake);
s_f_yc[0] = 100.0f;
iec104_pts_cov_check(&cov); /* 建线 */
s_f_yc[0] = 100.6f; /* d=0.6 > 0.5,第 1 越 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 crossing arms only (2-point)");
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-027 second consecutive crossing confirms");
s_f_yc[0] = 101.4f; /* 新基准 100.6 起 d=0.8 第 1 越 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 pullback test step 1 armed");
s_f_yc[0] = 100.8f; /* 回摆入死区 -> 撤销候选 */
iec104_pts_cov_check(&cov);
s_f_yc[0] = 101.4f; /* 重新起算:单点不报 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 pullback cleared pending count");
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-027 two fresh crossings confirm again");
}
static void tc_cov_qds_seq(void)
{
iec104_pts_cov_t cov;
reset_all(1000u); /* 假源段:品质翻转 */
s_f_yc[1] = 50.0f;
iec104_pts_cov_check(&cov);
s_f_yc_rc[1] = -1;
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0002u) != 0u, "TC-IEC104-028 valid->IV reported at once");
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0002u) == 0u, "TC-IEC104-028 IV steady state not repeated");
s_f_yc_rc[1] = 0;
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0002u) != 0u, "TC-IEC104-028 IV->valid recovery reported at once");
iec104_pts_init(); /* 板端段:seq 去重 */
iec104_pts_bind_src(NULL);
(void)phasor_init();
(void)meas_init();
snap_beats(20, 100.0, 0);
snap_pub(200u, 0u); /* seq=1 */
iec104_pts_cov_check(&cov); /* 建线帧 */
CHECK(cov.yc_changed == 0u, "TC-IEC104-028 baseline frame silent");
snap_beats(20, 200.0, 0);
snap_pub(400u, 0u); /* seq=2:第 1 越点 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-028 one new frame arms only");
iec104_pts_cov_check(&cov);
iec104_pts_cov_check(&cov); /* 无换帧:不得计为第 2 点 */
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-028 same frame never counts twice");
snap_beats(20, 200.0, 0);
snap_pub(600u, 0u); /* seq=3:连续第 2 越点 */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-028 second new frame confirms");
snap_pub(800u, 0xE6u); /* 换帧仅变品质(削顶注入) */
iec104_pts_cov_check(&cov);
CHECK((cov.yc_changed & 0x00E6u) == 0x00E6u,
"TC-IEC104-028 clipped flip per frame reported as a set");
}
int main(void)
{
printf("=== IEC104 Protocol Stack Host Tests ===\n");
@@ -939,6 +1127,10 @@ int main(void)
tc_ts_rp();
tc_periodic();
tc_cov_kfull();
tc_pts_snap();
tc_pts_freq();
tc_db_runtime();
tc_cov_qds_seq();
printf("=== IEC104 Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;