proto(V1.05): variation 由 2B无符号 扩展为 3B有符号

背景: variation 是遥测量, 用于离线跟踪分析车检器算法。原 2B 无符号
存在两个缺陷: (1)CAPVD域量级~131072, 大车全覆盖时偏差可超65535而回绕,
污染数据; (2)绝对值丢失方向, 分不清'车进入'与'反向漂移'。

改动 (三端联动, 同版本发布):
- 协议 DLD960Loop_串口通信协议.md: 变化量 2B->3B有符号补码, 定义
  variation=Origin-CAPVD (正=车/裕量, 负=反向漂移); 每单元11->12B,
  Len 47->51; 补符号扩展说明; 示例报文重算(XOR=FE SUM=38); 修订记录V1.05
- vd960Loop main.c: 组包 uint32->int32, Origin-CAPVD, 3B LE, +-2^23饱和防回绕
- vd960DBN loop_uart_proto.h: variation uint16->int32
- vd960DBN loop_uart_proto.c: 步长11->12, data_len/11->/12, misc偏移右移1B,
  variation 3B解码+bit23符号扩展
- vd960DBN iot_mqtt_srv.c: fast_mode 阈值判断改 abs(variation)>=10, 正负变化都加速

验证: 本地 gcc 隔离单测 编解码往返/符号扩展/饱和/方向语义/fast_mode 全通过;
大车 diff=100000 旧版回绕34464 新版正确保留。字节账: 帧56B < Loop缓冲64B
< DBN缓冲512B < MSS576, 均不溢出。整体固件编译需在 Keil/MounRiver 侧完成。
This commit is contained in:
wangfq
2026-07-14 14:47:39 +08:00
parent db52f92348
commit bfc9646e48
5 changed files with 46 additions and 27 deletions
+15 -10
View File
@@ -214,28 +214,32 @@ SensData:传感数据
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 配置 1 | 1 | freq\_level(2bit), Direction(1bit)freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级 | | 配置 1 | 1 | freq\_level(2bit), Direction(1bit)freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级 |
| 线圈评估条件 | 1 | condition(4bit), loop\_state(1bit),<br>car\_state(1bit),<br>report\_msic(2bit) | loop\_condition 正常(0)有效,环境状态条件(环境状态评估值,值越大,干扰越大),高四位有效。低四位中 ,其中第3位表示线圈状态,0 表示正常,1 表示线圈断开;第 2 位表示有无车,0 表示无车,1 表示有车;低两位表示**杂项类型**,0b00表示时间量,0b01表示线圈断开次数;0b10表示车流量数;0b11表示继电器输出次数 | | 线圈评估条件 | 1 | condition(4bit), loop\_state(1bit),<br>car\_state(1bit),<br>report\_msic(2bit) | loop\_condition 正常(0)有效,环境状态条件(环境状态评估值,值越大,干扰越大),高四位有效。低四位中 ,其中第3位表示线圈状态,0 表示正常,1 表示线圈断开;第 2 位表示有无车,0 表示无车,1 表示有车;低两位表示**杂项类型**,0b00表示时间量,0b01表示线圈断开次数;0b10表示车流量数;0b11表示继电器输出次数 |
| 频率 1 | 3 | frequent | 低字节在前 | | 频率 1 | 3 | frequent | 低字节在前,无符号 |
| 变化量 1 | 2 | variation | 低字节在前 | | 变化量 1 | 3 | variation | **有符号** 3 字节,低字节在前(小端),补码表示。定义 `variation = loop_Origin loop_CAPVD`(基线 − 当前值)。**正值**表示当前值低于基线(车辆/金属进入,检测裕量方向);**负值**表示当前值高于基线(反向漂移/异常抬升)。值域 ±8388607(±2²³−1),发送端已做饱和限幅,不会回绕。<br>**接收端解析**:读入 3 字节后须做符号扩展——`v = b0\|(b1<<8)\|(b2<<16); if (v & 0x800000) v \|= 0xFF000000;` |
| 杂项 | 4 | in\_out\_passtime/cut\_amount/flow\_amount | **杂项类型**,可能是时间量,也可能是线圈断开次数、车流量数,说明:<br>1、时间量有两种类型,通过时间或车间距,5ms为单位。通过时间是从有车开始到车辆离开的时间(上报时car\_state 为0);车间距是 从上一次车辆离开到有车时候的时间(上报时car\_state 为1)。非0有效。<br>2、线圈断开次数。<br>3、车流量数。<br>4、继电器输出次数。 | | 杂项 | 4 | in\_out\_passtime/cut\_amount/flow\_amount | **杂项类型**,可能是时间量,也可能是线圈断开次数、车流量数,说明:<br>1、时间量有两种类型,通过时间或车间距,5ms为单位。通过时间是从有车开始到车辆离开的时间(上报时car\_state 为0);车间距是 从上一次车辆离开到有车时候的时间(上报时car\_state 为1)。非0有效。<br>2、线圈断开次数。<br>3、车流量数。<br>4、继电器输出次数。 |
eg: eg:
`7F 00 2F C0 0C 00 12 00 2A 0F 01 07 00 00 00 00 00 D2 00 75 B7 00 01 00 00 00 00 00 12 00 EC 0C 01 02 00 00 00 00 00 92 00 83 C3 00 02 00 00 00 00 00 E2 34` `7F 00 33 C0 0C 00 12 00 2A 0F 01 07 00 00 00 00 00 00 D2 00 75 B7 00 01 00 00 00 00 00 00 12 00 EC 0C 01 02 00 00 00 00 00 00 92 00 83 C3 00 02 00 00 00 00 00 00 FE 38`
指令解释 指令解释
```python ```python
7F Magic Byte 7F Magic Byte
00 Addr0 default 00 Addr0 default
2F Len, 0x2F=47个字节Cmd + Data 33 Len, 0x33=51个字节Cmd + Data
C0 Cmd C0 Cmd
0C SensType传感数据类型0x0C 多线圈传感信息960Loop为 四路线圈车检器会有4个传感数据单元 0C SensType传感数据类型0x0C 多线圈传感信息960Loop为 四路线圈车检器会有4个传感数据单元
00 SubPkgFlag分包标记00 表示无分包是一条数据完整的记录 00 SubPkgFlag分包标记00 表示无分包是一条数据完整的记录
12 00 2A 0F 01 07 00 00 00 00 00线圈1传感数据单元0b00 高频0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x010F2A频率为69418Hz0x0007变化量为70x00000000时间量为为0 每路线圈单元 = 12 字节config1(1) + eval(1) + freq(3) + variation(3, 有符号) + misc(4)
D2 00 75 B7 00 01 00 00 00 00 00线圈2传感数据单元0b11 0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x00B775频率为46965Hz0x0001变化量为10x00000000时间量为0 12 00 2A 0F 01 07 00 00 00 00 00 00线圈1传感数据单元0b00 0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x010F2A频率为69418Hz0x000007变化量为 +7当前值低于基线正常裕量0x00000000时间量为0
12 00 EC 0C 01 02 00 00 00 00 00线圈3传感数据单元0b00 0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x010CEC频率为68844Hz0x0002变化量为20x00000000时间量为0 D2 00 75 B7 00 01 00 00 00 00 00 00线圈2传感数据单元0b11 0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x00B775频率为46965Hz0x000001变化量为 +10x00000000时间量为0
92 00 83 C3 00 02 00 00 00 00 00线圈4传感数据单元0b10 中低0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x00C383频率为50051Hz0x0002变化量为20x00000000时间量为0 12 00 EC 0C 01 02 00 00 00 00 00 00线圈3传感数据单元0b00 高频0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x010CEC频率为68844Hz0x000002变化量为 +20x00000000时间量为0
E2 34 校验字节 92 00 83 C3 00 02 00 00 00 00 00 00线圈4传感数据单元0b10 中低0b0 触发模式0b1实时频率0b0010 表示2级灵敏度0b0000环境正常0b0线圈正常0b0当前无车0b00 表示当前杂项参数为时间量0x00C383频率为50051Hz0x000002变化量为 +20x00000000时间量为0
FE 38 校验字节XOR=0xFE, SUM=0x38
# 变化量为有符号 3 字节小端补码。若某通道 variation 三字节为 F6 FF FF
# 符号扩展后 = 0xFFFFFFF6 = -10,表示当前值高于基线 10(反向漂移/异常抬升)。
``` ```
**注意,如果有续包,最后一包上位机无返回**。中间续包上位机返回传感类型,当前收到包的序列和子包的数量: **注意,如果有续包,最后一包上位机无返回**。中间续包上位机返回传感类型,当前收到包的序列和子包的数量:
@@ -254,4 +258,5 @@ E2 34 校验字节
| V1.01 | 2026-06-22 | 补充遗漏的接口说明 | wangfq | | V1.01 | 2026-06-22 | 补充遗漏的接口说明 | wangfq |
| V1.02 | 2026-07-02 | 查缺补漏,补充例程说明。 | wangfq | | V1.02 | 2026-07-02 | 查缺补漏,补充例程说明。 | wangfq |
| V1.03 | 2026-07-02 | 0x8A 响应格式 + LEN 计算修正 | wangfq | | V1.03 | 2026-07-02 | 0x8A 响应格式 + LEN 计算修正 | wangfq |
| V1.04 | 2026-07-02 | 主动上报增加继电器输出次数 类型 | wangfq | | V1.04 | 2026-07-02 | 主动上报增加继电器输出次数 类型 | wangfq |
| V1.05 | 2026-07-14 | 多线圈上报(0x0C)变化量 variation 由 2 字节无符号扩展为 **3 字节有符号**(补码, 小端),定义 `variation = Origin CAPVD`(正=车/裕量, 负=反向漂移);每单元 11→12 字节,Len 47→51。接收端须做符号扩展。**Loop 固件与 DBN 固件须同版本发布。** | wangfq |
@@ -97,8 +97,9 @@ typedef struct {
uint8_t loop_state : 1; // 0=正常, 1=断开 uint8_t loop_state : 1; // 0=正常, 1=断开
uint8_t car_state : 1; // 0=无车, 1=有车 uint8_t car_state : 1; // 0=无车, 1=有车
uint8_t misc_type : 2; // 0=时间量, 1=断开次数, 2=车流量, 3=继电器输出次数 uint8_t misc_type : 2; // 0=时间量, 1=断开次数, 2=车流量, 3=继电器输出次数
uint32_t freq; // 频率 (3 bytes, little-endian) uint32_t freq; // 频率 (3 bytes, little-endian, 无符号)
uint16_t variation; // 变化量 (2 bytes, little-endian) int32_t variation; // 变化量 (3 bytes, little-endian, 有符号补码) — V1.05
// = Origin - CAPVD; 正=当前值低于基线(车/裕量), 负=反向漂移
union { union {
uint32_t passtime_ms; // 通过时间/车间距, 50ms单位 (misc_type=0) uint32_t passtime_ms; // 通过时间/车间距, 50ms单位 (misc_type=0)
uint32_t cut_amount; // 线圈断开次数 (misc_type=1) uint32_t cut_amount; // 线圈断开次数 (misc_type=1)
@@ -412,7 +412,10 @@ void iot_mqtt_publish_sensor(void) {
{ {
uint8_t i; uint8_t i;
for (i = 0; i < _cached_sr.coil_count; i++) { for (i = 0; i < _cached_sr.coil_count; i++) {
if (_cached_sr.coils[i].variation >= IOT_MQTT_VARIATION_THRESHOLD) { // variation 为有符号(V1.05): 正=车/裕量, 负=反向漂移, 两向显著变化都加速
int32_t v = _cached_sr.coils[i].variation;
int32_t av = (v >= 0) ? v : -v;
if (av >= IOT_MQTT_VARIATION_THRESHOLD) {
fast_mode = 1; fast_mode = 1;
break; break;
} }
@@ -376,12 +376,12 @@ int lup_parse_set_param_resp(const uint8_t *pkg, uint16_t len, uint8_t *success)
/* /*
* Parse 0xC0/0x0C — 多路线圈传感信息 * Parse 0xC0/0x0C — 多路线圈传感信息
* *
* 每路线圈数据单元 = 11 bytes: * 每路线圈数据单元 = 12 bytes (V1.05):
* [0] 配置1: freq_level(2) | Direction(1) | freq_type(1) | sens(4) * [0] 配置1: freq_level(2) | Direction(1) | freq_type(1) | sens(4)
* [1] 评估条件: condition(4) | loop_state(1) | car_state(1) | misc_type(2) * [1] 评估条件: condition(4) | loop_state(1) | car_state(1) | misc_type(2)
* [2-4] 频率 (3 bytes, LE) * [2-4] 频率 (3 bytes, LE, 无符号)
* [5-6] 变化量 (2 bytes, LE) * [5-7] 变化量 (3 bytes, LE, 有符号补码, = Origin - CAPVD)
* [7-10] 杂项 (4 bytes) * [8-11] 杂项 (4 bytes)
*/ */
int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report) int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report)
{ {
@@ -397,17 +397,17 @@ int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *
report->sub_amount = (sub_pkg >> 4) & 0x0F; report->sub_amount = (sub_pkg >> 4) & 0x0F;
report->sub_sequence = sub_pkg & 0x0F; report->sub_sequence = sub_pkg & 0x0F;
// 计算线圈数: Value = sensType(1) + SubPkgFlag(1) + coil_count * 11 // 计算线圈数: Value = sensType(1) + SubPkgFlag(1) + coil_count * 12
uint16_t val_len = pkg[2]; // LEN field uint16_t val_len = pkg[2]; // LEN field
uint16_t data_offset = 6; // 跳过 7F + Addr + LEN + CMD + SensType + SubPkgFlag uint16_t data_offset = 6; // 跳过 7F + Addr + LEN + CMD + SensType + SubPkgFlag
uint16_t data_len = val_len - 2; // 减去 SensType + SubPkgFlag uint16_t data_len = val_len - 2; // 减去 SensType + SubPkgFlag
uint8_t coil_count = data_len / 11; uint8_t coil_count = data_len / 12;
if (coil_count > LUP_COIL_COUNT) coil_count = LUP_COIL_COUNT; if (coil_count > LUP_COIL_COUNT) coil_count = LUP_COIL_COUNT;
report->coil_count = coil_count; report->coil_count = coil_count;
for (i = 0; i < coil_count; i++) { for (i = 0; i < coil_count; i++) {
const uint8_t *c = pkg + data_offset + i * 11; const uint8_t *c = pkg + data_offset + i * 12;
LUP_CoilSensor *cs = &report->coils[i]; LUP_CoilSensor *cs = &report->coils[i];
cs->freq_level = (c[0] >> 6) & 0x03; cs->freq_level = (c[0] >> 6) & 0x03;
@@ -421,9 +421,15 @@ int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *
cs->misc_type = c[1] & 0x03; cs->misc_type = c[1] & 0x03;
cs->freq = c[2] | ((uint32_t)c[3] << 8) | ((uint32_t)c[4] << 16); cs->freq = c[2] | ((uint32_t)c[3] << 8) | ((uint32_t)c[4] << 16);
cs->variation = c[5] | ((uint16_t)c[6] << 8);
cs->misc.passtime_ms = c[7] | ((uint32_t)c[8] << 8) // 变化量 3B 有符号 (LE) — 读入后须符号扩展 bit23
| ((uint32_t)c[9] << 16) | ((uint32_t)c[10] << 24); {
int32_t v = c[5] | ((int32_t)c[6] << 8) | ((int32_t)c[7] << 16);
if (v & 0x800000) v |= (int32_t)0xFF000000;
cs->variation = v;
}
cs->misc.passtime_ms = c[8] | ((uint32_t)c[9] << 8)
| ((uint32_t)c[10] << 16) | ((uint32_t)c[11] << 24);
} }
return 0; return 0;
@@ -507,12 +507,16 @@ void uart_report_packet_loop_acs(uint8_t flag)
g_pkg_uart_report.pkg[i++] = (uint8_t)((_frequent >> 8) & 0xFF); g_pkg_uart_report.pkg[i++] = (uint8_t)((_frequent >> 8) & 0xFF);
g_pkg_uart_report.pkg[i++] = (uint8_t)((_frequent >> 16) & 0xFF); g_pkg_uart_report.pkg[i++] = (uint8_t)((_frequent >> 16) & 0xFF);
// 变化量 (2B, LE) // 变化量 (3B, LE, 有符号补码) — V1.05
uint32_t variation = (unit->loop_Origin > unit->loop_CAPVD) // 定义: variation = Origin - CAPVD
? (unit->loop_Origin - unit->loop_CAPVD) // 正值 = 当前值低于基线(车辆/金属进入, 检测裕量方向)
: (unit->loop_CAPVD - unit->loop_Origin); // 负值 = 当前值高于基线(反向漂移/异常抬升)
int32_t variation = (int32_t)unit->loop_Origin - (int32_t)unit->loop_CAPVD;
if (variation > 8388607) variation = 8388607; // +2^23-1 饱和, 防回绕
if (variation < -8388608) variation = -8388608; // -2^23 饱和
g_pkg_uart_report.pkg[i++] = (uint8_t)(variation & 0xFF); g_pkg_uart_report.pkg[i++] = (uint8_t)(variation & 0xFF);
g_pkg_uart_report.pkg[i++] = (uint8_t)((variation >> 8) & 0xFF); g_pkg_uart_report.pkg[i++] = (uint8_t)((variation >> 8) & 0xFF);
g_pkg_uart_report.pkg[i++] = (uint8_t)((variation >> 16) & 0xFF);
// 杂项值 (4B, LE) // 杂项值 (4B, LE)
g_pkg_uart_report.pkg[i++] = (uint8_t)(_misc_val[j] & 0xFF); g_pkg_uart_report.pkg[i++] = (uint8_t)(_misc_val[j] & 0xFF);