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
+255 -94
View File
@@ -1,8 +1,9 @@
/*
* 模块名称:IEC 60870-5-104 点表与数据源适配(实现)
* 模块功能:实现 iec104_pts.h——YC/YX/YK/定值点表(ROM)、板端默认源
* (phasor / prot_elm / prot_set)、COV 快照比对(遥信变位 +
* 遥测死区)。点表口径见 iec104_pts.h 文件头与设计文档 §3。
* (**L2 遥测快照**(卡 9)/ prot_elm / prot_set)、COV 比对(遥信变位 +
* 遥测死区 2 点确认 + 品质变化)。点表口径见 iec104_pts.h 文件头与
* 设计文档 §3。
* 线程约定:init/bind 建服务前;其余仅 104 服务任务上下文(快照非原子)。
* 创建日期:2026-10-12(B4)
* 修改记录:
@@ -10,47 +11,71 @@
* 二次值工程量域(额定 0.5 % 规则),判据依据见
* k_yc 表头注释;数值随卡 9 迁运行参数,本表将退化为
* ROM 默认载体。
* 2026-10-25 DTU 项目组 卡 9/卡 11(「数据可信」,道D):YC 板端源改读
* L2 遥测快照(App/algo/meas.h,meas_get 无锁双区);
* 削顶五路由快照品质 CLIPPED 驱动线上 IV(不再绑死 -1,
* V0.2/N-18 零改动自动启用);0x401d 绑频率源(卡 11);
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
* 单测 = test/host/iec104 TC-IEC104-025~028。
*/
#include "iec104_pts.h"
#include <string.h>
#include "iec104_asdu.h" /* QDS/DIQ 品质位常量 */
#include "phasor.h" /* 板端源:遥测 = 基波有效值 */
#include "meas.h" /* 板端源:遥测 = L2 遥测快照(卡 9;品质域位同源) */
#include "prot_elm.h" /* 板端源:遥信 = 元件状态 */
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
/* ---------------- YC 表(§1.5 点号 4011+;死区 = 二次值工程量域) --------- */
/* ---------------- YC 表(§1.5 点号 4011+;死区列 = ROM 默认值) ------------ */
/* 源类型(「数据可信」卡 11:YC 表加 src 类型区分相量/频率) */
#define PTS_SRC_MEAS 0u /* L2 快照槽 sem(电气量,yc/qds 直读) */
#define PTS_SRC_FREQ 1u /* 快照频率(freq_valid 门 → IV) */
typedef struct {
uint32_t ioa;
int8_t sem; /* 绑定的 phasor 语义通道(-1 = 无 phasor 源,恒无效) */
float deadband; /* 死区(二次值工程量绝对值,「数据可信」卡 6 重定) */
int8_t sem; /* 快照 yc 槽 = 语义通道(-1 = 非测量量点,当前仅频率) */
float db_default; /* 死区 ROM 默认(运行时生效值 = s_yc_db,卡 10 覆盖) */
uint8_t src; /* PTS_SRC_* 源类型 */
} pts_yc_def_t;
/* 死区规则 = 额定二次值的 0.5 %(「数据可信」§2.3 表第 2 行建议规则,卡 6 采纳;
* 额定基准 100 V / 1 A 与 App/algo/prot_set.c 头部同源,勿在此另立口径):
* 电压类 0.5 % × 100 V = 0.5 V 电流类 0.5 % × 1 A = 0.005 A
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
* 卡 10 运行参数段可调)。IV 品质点(sem=-1)数值仅为将来启用预留同型。
* 卡 10 运行参数段可调)。自卡 9 起本表**只作 ROM 默认载体**:运行时死区 =
* s_yc_db 副本;削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
* 数据可信.md §10 的 N-18 行)。
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
* 按本域判定无意义但也不会漏报——104 侧 IV/数据可信度语义由 M5 快照层
* (卡 8/9)统一处理,本卡只把"量纲错位"这一处常量债还掉。 */
* 数字按本域偏保守(迟报不漏报);标定落地后复核(「数据可信」§2.3 表
* 第 2 行)。判定 = 死区连续 2 点确认(卡 9),见 iec104_pts.h COV 说明。 */
static const pts_yc_def_t k_yc[] = {
{ 0x4011u, (int8_t)ADC_SEM_UA, 0.5f }, /* 1Ua:0.5 % × 100 V */
{ 0x4012u, -1, 0.5f }, /* 1Ub:V0.1 削顶 → IV */
{ 0x4013u, -1, 0.5f }, /* 1Uc:同上 */
{ 0x4014u, (int8_t)ADC_SEM_UN, 0.5f }, /* 1U0(外部零序专用通道) */
{ 0x4015u, (int8_t)ADC_SEM_IA, 0.005f }, /* 1Ia:0.5 % × 1 A */
{ 0x4016u, -1, 0.005f }, /* 1Ib */
{ 0x4017u, -1, 0.005f }, /* 1Ic */
{ 0x4018u, -1, 0.005f }, /* 1I0 */
{ 0x401Du, -1, 0.5f }, /* 1f:Hz 绝对死区(非额定比)→ IV */
{ 0x4011u, (int8_t)ADC_SEM_UA, 0.5f, PTS_SRC_MEAS }, /* 1Ua:0.5 % × 100 V */
{ 0x4012u, (int8_t)ADC_SEM_UB, 0.5f, PTS_SRC_MEAS }, /* 1Ub:削顶 → 品质驱动 IV */
{ 0x4013u, (int8_t)ADC_SEM_UC, 0.5f, PTS_SRC_MEAS }, /* 1Uc:同上 */
{ 0x4014u, (int8_t)ADC_SEM_UN, 0.5f, PTS_SRC_MEAS }, /* 1U0(外部零序专用通道) */
{ 0x4015u, (int8_t)ADC_SEM_IA, 0.005f, PTS_SRC_MEAS }, /* 1Ia:0.5 % × 1 A */
{ 0x4016u, (int8_t)ADC_SEM_IB, 0.005f, PTS_SRC_MEAS }, /* 1Ib:削顶 → 品质驱动 IV */
{ 0x4017u, (int8_t)ADC_SEM_IC, 0.005f, PTS_SRC_MEAS }, /* 1Ic:同上 */
{ 0x4018u, (int8_t)ADC_SEM_IN, 0.005f, PTS_SRC_MEAS }, /* 1I0:同上 */
{ 0x401Du, -1, 0.5f, PTS_SRC_FREQ }, /* 1f:卡 11 绑频率源,
* freq_valid=0 → IV */
};
#define YC_N ((uint8_t)(sizeof(k_yc) / sizeof(k_yc[0])))
/* phasor 实例下标缓存(init 时按 sem+harm==1 查表;-1 = 未找到) */
static int8_t s_yc_phasor_idx[YC_N];
/* ===== 遥测 COV 运行时状态(卡 9;仅 iec104Task 上下文访问,无并发) =====
* 判据链:新测量点 → |v − 上次上送值| > 死区?→ 连续 2 点确认 → 置变化位。
* 基准 = **上次上送值**(非上次读值——否则慢漂移永不出死区,参考系统同款);
* 品质(线上 qds)翻转 = 事件:不参与 2 点计数、当拍直报。 */
static float s_yc_db[YC_N]; /* 死区运行时副本(ROM 默认 = k_yc 列) */
static float s_yc_rep[YC_N]; /* 上次上送值(建线/确认上送时更新) */
static uint8_t s_yc_repq[YC_N]; /* 上次上送的线上 qds */
static uint8_t s_yc_cnf[YC_N]; /* 越死区连续计数(1 = 待确认;2 = 确认) */
static uint32_t s_yc_seq; /* COV 已消费到的快照 seq(板端源测点去重) */
static uint8_t s_yc_seqmode; /* 1 = 板端默认源(测点 = meas 换帧) */
/* ---------------- YX 表(IOA = 1..33;双点 = 开关位置类) ------------------ */
#define YX_N 33u
@@ -61,63 +86,65 @@ static int8_t s_yc_phasor_idx[YC_N];
| (1uLL << 17u) | (1uLL << 22u) | (1uLL << 30u)))
/* ---------------- 数据源(默认 = 板端;bind_src 可整体替换) -------------- */
static int s_src_yc_read(uint8_t idx, float *out);
static int s_src_yc_read_ex(uint8_t idx, float *out, uint16_t *qds);
static uint64_t s_src_yx_bits(void);
static uint64_t s_src_yx_valid(void);
static int s_src_yk_exec(uint32_t ioa, uint8_t dpc, uint8_t select);
static int s_src_set_read(uint32_t addr, float *out);
static int s_src_set_write(uint32_t addr, float v, uint8_t select);
/* 板端默认源:遥测走**带品质**的 ex 通道(legacy yc_read 槽置 NULL——
* s_yc_point 优先 ex;注入源两种槽位均可,见 iec104_pts.h) */
static const iec104_pts_src_t k_default_src = {
s_src_yc_read, s_src_yx_bits, s_src_yx_valid,
s_src_yk_exec, s_src_set_read, s_src_set_write
NULL, s_src_yx_bits, s_src_yx_valid,
s_src_yk_exec, s_src_set_read, s_src_set_write, s_src_yc_read_ex
};
static const iec104_pts_src_t *s_src;
/* ---------------- COV 快照 ---------------- */
static float s_yc_last[YC_N];
static uint16_t s_yc_have; /* bit i = 已有上次快照 */
/* ---------------- COV 快照(遥信侧;遥测侧状态见上方 s_yc_* 块) --------- */
static uint16_t s_yc_have; /* bit i = 已建立上送基线 */
static uint64_t s_yx_last;
static uint8_t s_yx_snap_ok;
/* ============================ 公开接口 ================================== */
/*
* 函数功能:初始化点表模块(扫 phasor 基波实例映射、恢复默认源、清快照)
* 函数功能:初始化点表模块(死区装 ROM 默认、清 COV/快照状态、恢复默认源)
* 限定条件:内核启动前或 104 服务启动前,调一次
* 函数说明:卡 9 起不再扫 phasor 实例(数据源 = 快照,槽位 = 语义通道,编译期
* 直接可定);重复调用 = 全状态复位(单测 reset 依赖此语义)。
*/
void iec104_pts_init(void) {
uint8_t i;
uint8_t n;
(void)memset(s_yc_phasor_idx, -1, sizeof(s_yc_phasor_idx));
n = phasor_inst_count();
for (i = 0u; i < YC_N; i++) {
uint8_t j;
if (k_yc[i].sem < 0) {
continue;
}
for (j = 0u; j < n; j++) {
const phasor_inst_t *inst = phasor_inst(j);
if ((inst != NULL) && ((uint8_t)inst->sem == (uint8_t)k_yc[i].sem)
&& (inst->harm == 1u)) {
s_yc_phasor_idx[i] = (int8_t)j;
break;
}
}
s_yc_db[i] = k_yc[i].db_default;
}
s_src = &k_default_src;
s_yc_have = 0u;
(void)memset(s_yc_rep, 0, sizeof(s_yc_rep));
(void)memset(s_yc_repq, 0, sizeof(s_yc_repq));
(void)memset(s_yc_cnf, 0, sizeof(s_yc_cnf));
s_yc_have = 0u;
s_yc_seq = 0u;
s_yc_seqmode = 1u;
s_src = &k_default_src;
s_yx_snap_ok = 0u;
s_yx_last = 0u;
s_yx_last = 0u;
}
/*
* 函数功能:替换数据源(NULL = 恢复板端默认源;单测注入假源)
* 函数说明:注入源同时把 COV 测点口径切为**每次调用一测点**(假源无快照节拍,
* 步进由调用方控制);默认源 = 测点按 meas 换帧(seq 去重)。换源
* 不重建基线(基线状态统一由 init 复位,防跨源半截确认序列)。
*/
void iec104_pts_bind_src(const iec104_pts_src_t *src) {
s_src = (src != NULL) ? src : &k_default_src;
if (src != NULL) {
s_src = src;
s_yc_seqmode = 0u;
} else {
s_src = &k_default_src;
s_yc_seqmode = 1u;
}
}
/*
@@ -135,35 +162,105 @@ uint32_t iec104_pts_yc_ioa(uint8_t idx) {
}
/*
* 函数功能:读遥测(值 + 品质 descriptor;源 -2 → NT,其余失败 → IV)
* 返 回 值:0 有效 / -1 无效
* 函数功能:品质域位映射(uint16 meas 域 → uint8 线上 QDS,卡 9)
* 入口参数:q16 - 源侧品质(低字节与线上同值;高字节为 MEAS_QDS_CLIPPED 域内位)
* 返 回 值:线上 QDS 字节 uint8_t
* 函数说明:CLIPPED(硬件削顶,104 无对应位)映射为 IV——「削顶通道 104 恒
* IV」口径(docs/任务规划/数据可信.md §8 风险表末行)改由快照品质
* 驱动,不再靠点表绑死 -1;OV/NT/IV 透传,其余高位丢弃。
*/
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds) {
int rc;
static uint8_t s_q_map(uint16_t q16) {
uint8_t w = (uint8_t)(q16 & (uint16_t)(IEC104_QDS_OV | IEC104_QDS_NT
| IEC104_QDS_IV));
if (v != NULL) {
*v = 0.0f;
if ((q16 & (uint16_t)MEAS_QDS_CLIPPED) != 0u) {
w |= (uint8_t)IEC104_QDS_IV;
}
if ((idx >= YC_N) || (s_src == NULL) || (s_src->yc_read == NULL)) {
if (qds != NULL) {
*qds = IEC104_QDS_IV;
}
return w;
}
/*
* 函数功能:读单点遥测(统一源通道 + 品质归一,yc_read 与 COV 共用唯一读径)
* 入口参数:idx - YC 表下标 v/q - 输出(值 / 线上 qds;调用方保证非空)
* 返 回 值:0 有效;-1 IV(含 CLIPPED 映射);-2 NT(未就绪) int
* 函数说明:ex 源在场 → 品质由源给(板端 = 快照槽位透传);否则退 legacy
* yc_read,品质从返回值推导(-1→IV / -2→NT / 0→无位)——旧注入源
* 行为与卡 9 前**完全一致**(单测假源零改动)。判定顺序 IV > NT:
* 开机首帧 qds=IV|NT 按无效处理(NT 只留给纯暂态)。任何无效 →
* 信息体清零(线上口径:主站只信品质)。
*/
static int s_yc_point(uint8_t idx, float *v, uint8_t *q) {
int rc;
uint16_t q16 = 0u;
if ((idx >= YC_N) || (s_src == NULL)
|| ((s_src->yc_read == NULL) && (s_src->yc_read_ex == NULL))) {
*v = 0.0f;
*q = (uint8_t)IEC104_QDS_IV;
return -1;
}
rc = s_src->yc_read(idx, v);
if (rc == 0) {
if (qds != NULL) {
*qds = 0u;
}
return 0;
if (s_src->yc_read_ex != NULL) {
rc = s_src->yc_read_ex(idx, v, &q16);
} else {
rc = s_src->yc_read(idx, v);
q16 = (rc == -2) ? (uint16_t)MEAS_QDS_NT
: ((rc != 0) ? (uint16_t)MEAS_QDS_IV : 0u);
}
if (v != NULL) {
(void)rc; /* 有效性由归一后的 q 终裁(IV/NT 即无效),源返回值不直通 */
*q = s_q_map(q16);
rc = ((*q & (uint8_t)IEC104_QDS_IV) != 0u) ? -1
: (((*q & (uint8_t)IEC104_QDS_NT) != 0u) ? -2 : 0);
if (rc != 0) {
*v = 0.0f;
}
if (qds != NULL) {
*qds = (rc == -2) ? IEC104_QDS_NT : IEC104_QDS_IV;
return rc;
}
/*
* 函数功能:读遥测(值 + 线上品质位;无效值清零,判据见 s_yc_point)
* 返 回 值:0 有效 / -1 无效(NT 亦无效,品质位与 IV 区分) int
*/
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds) {
float val = 0.0f;
uint8_t q = (uint8_t)IEC104_QDS_IV;
(void)s_yc_point(idx, &val, &q);
if (v != NULL) {
*v = val;
}
return -1;
if (qds != NULL) {
*qds = q;
}
return (q == 0u) ? 0 : -1;
}
/*
* 函数功能:写死区运行时值(「数据可信」卡 9;卡 10 运行参数段的落点入口)
* 入口参数:idx - YC 表下标 db - 工程量绝对死区(≥0) float
* 返 回 值:0 成功;-1 越界/负值/NaN(RAM 不动) int
* 限定条件:**iec104Task 上下文**(配置属主 = defaultTask,卡 10 接线时经
* 104 任务落地本调用,勿跨任务直调——本表状态无锁)
* 函数说明:成功时清零该点待确认计数——跨死区档位不保留半截确认序列;
* 不隐式重建基线(上送基准延续,新死区即刻生效)。
*/
int iec104_pts_yc_set_deadband(uint8_t idx, float db) {
if ((idx >= YC_N) || !(db >= 0.0f) || !(db < 1000000.0f)) {
return -1; /* NaN 被 !(db>=0) 捕获(比较式写法,防 lint 报恒真) */
}
s_yc_db[idx] = db;
s_yc_cnf[idx] = 0u;
return 0;
}
/*
* 函数功能:读当前生效死区
* 入口参数:idx - YC 表下标
* 返 回 值:运行时死区;idx 越界返 0.0 float
* 限定条件:任意上下文(单字 float 读,Cortex-M 原子)
* 函数说明:UI 回显 / 卡 10 读写往返校验用;ROM 默认查点表,勿在此反推。
*/
float iec104_pts_yc_deadband(uint8_t idx) {
return (idx < YC_N) ? s_yc_db[idx] : 0.0f;
}
/*
@@ -238,28 +335,72 @@ void iec104_pts_cov_check(iec104_pts_cov_t *cov) {
s_yx_snap_ok = 1u;
}
/* YC:死区比对(仅对"本次有效 且 上次有快照"的点判定) */
if ((s_src != NULL) && (s_src->yc_read != NULL)) {
uint8_t i;
/* YC(卡 9):测点推进 = 板端源按快照换帧(100 ms poll vs 200 ms 快照,
* 不去重则同一帧计两次 → "连续 2 点确认"退化成"连续 2 次轮询",单帧
* 噪声即可被确认,抗噪语义失效);注入源 = 每次调用一测点。
* 判定 = 死区连续 2 点确认 + 品质翻转直报(口径全文 → iec104_pts.h)。 */
if ((s_src != NULL)
&& ((s_src->yc_read != NULL) || (s_src->yc_read_ex != NULL))) {
uint8_t step = 1u;
for (i = 0u; i < YC_N; i++) {
float v;
if (s_yc_seqmode != 0u) {
uint32_t sq = meas_seq();
if (s_src->yc_read(i, &v) != 0) {
continue;
if (sq == s_yc_seq) {
step = 0u; /* 未换帧:本 poll 不算新测量点 */
} else {
s_yc_seq = sq;
}
if (((s_yc_have >> i) & 1u) != 0u) {
float d = v - s_yc_last[i];
}
if (step != 0u) {
uint8_t i;
if (d < 0.0f) {
d = -d;
for (i = 0u; i < YC_N; i++) {
float v;
uint8_t q;
int rc = s_yc_point(i, &v, &q);
if (((s_yc_have >> i) & 1u) == 0u) {
if (rc == 0) { /* 首个有效读数只建线不上报(初值归总召) */
s_yc_have |= (uint16_t)(1u << i);
s_yc_rep[i] = v;
s_yc_repq[i] = q;
s_yc_cnf[i] = 0u;
}
continue;
}
if (d > k_yc[i].deadband) {
if (q != s_yc_repq[i]) {
/* 品质翻转 = 事件直报(含 0x401d 的 IV 进出,卡 11) */
local.yc_changed |= (uint16_t)(1u << i);
s_yc_repq[i] = q;
s_yc_cnf[i] = 0u;
if (rc == 0) {
s_yc_rep[i] = v;
}
continue;
}
if (rc != 0) {
s_yc_cnf[i] = 0u; /* 无效期间不计数(品质未变 = 状态延续) */
continue;
}
{
float d = v - s_yc_rep[i];
if (d < 0.0f) {
d = -d;
}
if (d > s_yc_db[i]) {
s_yc_cnf[i]++;
if (s_yc_cnf[i] >= 2u) { /* 连续 2 越 → 确认:报 + 移基准 */
local.yc_changed |= (uint16_t)(1u << i);
s_yc_rep[i] = v;
s_yc_cnf[i] = 0u;
}
} else {
s_yc_cnf[i] = 0u; /* 回摆入死区 → 撤销候选(抗噪核心) */
}
}
}
s_yc_last[i] = v;
s_yc_have |= (uint16_t)(1u << i);
}
}
@@ -304,24 +445,44 @@ int iec104_pts_set_write(uint32_t addr, float v, uint8_t select) {
/* ============================ 板端默认源 ================================= */
/*
* 函数功能:板端默认源 - 遥测 = 基波相量有效值(fAmSQ)
* 返 回 值:0 / -1 无 phasor 源(削顶通道/频率未实现)/ -2 未满一周波(NT)
* 函数功能:板端默认源 - 遥测 = L2 遥测快照直读(「数据可信」卡 9/卡 11)
* 入口参数:idx - YC 表下标 out/qds - 值 + uint16 域品质(MEAS_QDS_*)
* 返 回 值:0 / -1 越界或频率未新鲜(IV)/ -2 快照从未发布(NT) int
* 限定条件:iec104Task 上下文(meas_get 为双区无锁读,App/algo/meas.h)
* 函数说明:**不做任何本地判断**——电气路值/品质整体透传快照槽(削顶路快照
* 已置 CLIPPED,s_q_map 映上线 IV;开机首帧 IV|NT 由 s_yc_point 按
* IV 处理),"新不新、能不能信"全在发布侧一处决定(卡 8 设计意图,
* 两处判断必打架)。频率路(PTS_SRC_FREQ):snap.freq_valid==0 →
* IV(卡 11 DoD 原句);有效时值 = snap.freq_hz,与 freq_get() 同源。
*/
static int s_src_yc_read(uint8_t idx, float *out) {
phasor_t p;
int8_t pi;
static int s_src_yc_read_ex(uint8_t idx, float *out, uint16_t *qds) {
meas_snap_t snap;
if ((idx >= YC_N) || (out == NULL)) {
if ((idx >= YC_N) || (out == NULL) || (qds == NULL)) {
return -1;
}
pi = s_yc_phasor_idx[idx];
if (pi < 0) {
return -1; /* 无 phasor 源(削顶通道/频率未实现) */
if (meas_get(&snap) != 0) {
*out = 0.0f; /* 从未发布(开机 <200 ms)= 暂态 */
*qds = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
return -2;
}
if (phasor_get((uint8_t)pi, &p) != 0) {
return -2; /* 未满一周波:未就绪(NT) */
if (k_yc[idx].src == (uint8_t)PTS_SRC_FREQ) {
if (snap.freq_valid == 0u) {
*out = 0.0f;
*qds = (uint16_t)MEAS_QDS_IV;
return -1;
}
*out = snap.freq_hz;
*qds = 0u;
return 0;
}
*out = p.fAmSQ; /* 有效值口径(系数已折算) */
if (k_yc[idx].sem < 0) {
*out = 0.0f; /* 理论不可达:现无未绑测量量点 */
*qds = (uint16_t)MEAS_QDS_IV;
return -1;
}
*out = snap.yc[(uint8_t)k_yc[idx].sem];
*qds = snap.qds[(uint8_t)k_yc[idx].sem];
return 0;
}
+36 -7
View File
@@ -5,9 +5,12 @@
* phasor/prot_elm/prot_set 真实源;主机单测绑假源 → 协议栈全链
* (asdu+pts+link)可离线跑(登记簿 A-4 约束 1)。
* 点表口径(docs/算法/继电保护功能复现规划.md §1.5,验收 = 省电科院点表):
* - YC:IOA 0x4011~0x4018 + 0x401d(频率),短浮点、系数 1;
* ⚠ V0.1 有效通道仅 UA/UN/IA(TPAFE 三路,§1.6)——其余点**存在但置 IV**
* (无效位),频率随 L1-7 落地前同样置 IV;
* - YC:IOA 0x4011~0x4018 + 0x401d(频率),短浮点、系数 1;数据源 =
* L2 遥测快照(App/algo/meas.h,「数据可信」卡 9/11):电气 8 路绑快照槽,
* 有效性由快照品质驱动——V0.1 削顶路(docs/算法/继电保护功能复现规划.md
* §1.6)快照置 CLIPPED → 本层映射为线上 IV(V0.2/N-18 后自动启用,绑定零
* 改动,见 docs/任务规划/数据可信.md §10 行 N-18);0x401d 绑频率源,
* freq_valid()==0 → IV;
* - YX:IOA 1~33(§1.5 表(3));双点 = 开关位置类(YX1~4,本期无硬件 → IV),
* 保护动作类 YX15~23 映射 prot_elm 状态,闭锁类 YX31~33 映射 prot_set;
* - YK:IOA 0x6000+(§1.5 表(3));V0.1 无出口硬件(规划 R7)——
@@ -21,6 +24,11 @@
* 按未知 IOA(COT 14 + EVI)应答,登记为待办(设计文档)。
* 线程约定:init/bind 内核启动前或建服务前;cov_check/read 仅 104 服务任务上下文。
* 创建日期:2026-10-12(B4)
* 修改记录:
* 2026-10-25 DTU 项目组 卡 9/卡 11(「数据可信」,道D):YC 数据源 = L2 遥测
* 快照(削顶路由品质驱动 IV);死区改运行时副本 +
* set/get 入口;COV 遥测腿 = 死区 2 点确认(seq 去重)
* + 品质变化即报;0x401d 绑频率源(freq_valid → IV)。
*/
#ifndef IEC104_PTS_H
#define IEC104_PTS_H
@@ -42,6 +50,11 @@ typedef struct {
int (*set_read)(uint32_t addr, float *out);
/* 定值写:select=1 预置校验(范围)/ 0 执行写入。返回 0 / -1 越界或未知 */
int (*set_write)(uint32_t addr, float v, uint8_t select);
/* 遥测读(带品质,卡 9 追加于表尾——旧源 NULL = 走 yc_read 退路,品质由
* 返回值推导:-1 → IV、-2 → NT、0 → 无位,行为与旧源完全一致)。
* qds = uint16 域位(App/algo/meas.h MEAS_QDS_*:低字节 = 线上 QDS 同值,
* 高字节 CLIPPED 由本层映射线上 IV);返回 0 / -1 IV / -2 NT */
int (*yc_read_ex)(uint8_t idx, float *out, uint16_t *qds);
} iec104_pts_src_t;
/* ---------------- 变化检测结果(cov_check 输出) ---------------- */
@@ -65,8 +78,15 @@ void iec104_pts_bind_src(const iec104_pts_src_t *src);
/* ---------------- YC ---------------- */
uint8_t iec104_pts_yc_count(void);
uint32_t iec104_pts_yc_ioa(uint8_t idx);
/* 读 YC:v 有效值(无效时为 0);qds 输出品质位(IV/NT 等)。返回 0/无效-1 */
/* 读 YC:v 有效值(无效时为 0);qds 输出线上品质位(IV/NT/OV,CLIPPED 已映射
* 为 IV)。返回 0/无效-1(NT 亦无效,品质位区分) */
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds);
/* 死区运行时写(「数据可信」卡 9;卡 10 运行参数段的落点入口):
* db = 工程量绝对值 ≥0;越界/负值/NaN → -1 且 RAM 不动;成功时清零该点
* 待确认计数(跨死区基线不残留半截确认)。仅 104 服务任务上下文调用。 */
int iec104_pts_yc_set_deadband(uint8_t idx, float db);
/* 读当前生效死区(越界 idx 返 0) */
float iec104_pts_yc_deadband(uint8_t idx);
/* ---------------- YX ---------------- */
uint8_t iec104_pts_yx_count(void);
@@ -77,10 +97,19 @@ void iec104_pts_yx_read(uint8_t idx, uint8_t *out);
/* ---------------- COV(每 poll 一次) ---------------- */
/*
* 函数功能:读全表快照并与上次比对(变位 + 死区),输出变化集
* 函数功能:读全表并与上次比对(遥信变位 + 遥测死区 2 点确认 + 品质变化),
* 输出变化集
* 入口参数:cov - 输出(可为 NULL = 只刷快照不取变化)
* 函数说明:首次调用只建快照、变化集全 0(突发由初始化/总召覆盖)。
* 死区 = 点表内置绝对值(§1.5 运行参数 8020H 段本期取内置默认)。
* 函数说明:遥测判据(「数据可信」卡 9,对标参考系统 CalfYcMea 死区 2 点确认,
* 短板评测口径):**连续 2 个新测量点** |v - 上次上送值| > 死区才置变化位
* (单点尖峰不确认——噪声回摆即撤销候选);品质位(线上 qds)相对上次
* 上送值翻转 → 当拍直接置位(不等 2 点,有效性变化必须即时可见,
* 含 0x401d 的 IV 进出)。死区 = 运行时副本(ROM 默认 = 点表列,
* iec104_pts_yc_set_deadband() 覆盖——卡 10 运行参数段的接线点)。
* 测量点定义:板端默认源 = **快照换帧**(meas seq 前进;100 ms poll 对
* 200 ms 快照,同一帧重复比对会计数两次 → seq 去重是确认语义成立的
* 前提);注入源(单测假源)= 每次调用一测点。首次读到有效值只建基线
* 不上报(突发由总召覆盖)。
*/
void iec104_pts_cov_check(iec104_pts_cov_t *cov);