EdgexAgent/device-gps-go/run/driver/binary_protocol.go

266 lines
7.2 KiB
Go
Raw Normal View History

2025-07-10 20:30:06 +08:00
package driver
import (
"encoding/binary"
"fmt"
)
// GroupID 消息组ID枚举
type GroupID uint8
const (
NMEA_GID GroupID = 0xF0 // NMEA标准语句
BIN_RES_GID GroupID = 0x05 // 二进制协议,响应消息
BIN_CFG_GID GroupID = 0x06 // 二进制协议,配置消息
)
// NMEA_SUB_ID NMEA子消息ID枚举
type NMEA_SUB_ID uint8
const (
NMEA_GGA_SID NMEA_SUB_ID = 0x00 // GGA全球定位系统定位数据
NMEA_GLL_SID NMEA_SUB_ID = 0x01 // GLL地理位置—纬度和经度
NMEA_GSA_SID NMEA_SUB_ID = 0x02 // GSAGNSS精度因子DOP与有效卫星
NMEA_GRS_SID NMEA_SUB_ID = 0x03 // GRSGNSS距离残差
NMEA_GSV_SID NMEA_SUB_ID = 0x04 // GSV可视的GNSS卫星
NMEA_RMC_SID NMEA_SUB_ID = 0x05 // RMC推荐的最少专有GNSS数据
NMEA_VTG_SID NMEA_SUB_ID = 0x06 // VTG相对于地面的实际航向和速度
NMEA_ZDA_SID NMEA_SUB_ID = 0x07 // ZDA时间与日期。主要用于输出UTC时间日、月、年但不支持输出本地时区信息
NMEA_GST_SID NMEA_SUB_ID = 0x08 // GSTGNSS伪距误差统计
)
// BIN_RES_SID 二进制响应消息子ID枚举
type BIN_RES_SID uint8
const (
BM_NAK_SID BIN_RES_SID = 0x00 // NAK否认消息
BM_ACK_SID BIN_RES_SID = 0x01 // ACK确认消息
)
// BIN_CFG_SID 二进制配置消息子ID枚举
type BIN_CFG_SID uint8
const (
BM_PRT_SID BIN_CFG_SID = 0x00 // PRT通信接口配置
BM_MSG_SID BIN_CFG_SID = 0x01 // MSG消息输出速率配置
BM_PPS_SID BIN_CFG_SID = 0x07 // PPSPPS配置
BM_CFG_SID BIN_CFG_SID = 0x09 // CFG清除/保存当前配置
BM_DOP_SID BIN_CFG_SID = 0x0A // DOP导航定位DOP阈值配置
BM_ELEV_SID BIN_CFG_SID = 0x0B // ELEV导航定位的卫星仰角阈值配置
BM_NAVSAT_SID BIN_CFG_SID = 0x0C // NAVSAT卫星系统使能状态配置
BM_SPDHOLD_SID BIN_CFG_SID = 0x0F // SPDHOLD静态速度阈值配置
BM_EPHSAVE_SID BIN_CFG_SID = 0x10 // EPHSAVEFlash中的星历存储状态配置
BM_SIMPLERST_SID BIN_CFG_SID = 0x40 // SIMPLERSTGNSS引擎启动/关闭/复位配置
BM_SLEEP_SID BIN_CFG_SID = 0x41 // SLEEPGNSS模块进入休眠状态配置
BM_PWRCTL_SID BIN_CFG_SID = 0x42 // PWRCTL模块功率控制模式和定位频率配置
)
// CFG_MSG 配置消息结构体
type CFG_MSG struct {
Data [32]byte // 数据缓冲区
DataLen int // 数据长度
}
// BinaryMessage 二进制消息基础结构
type BinaryMessage struct {
Header uint16 // 消息头
GroupID GroupID // 组ID
SubID uint8 // 子ID
Length uint16 // 消息长度
Payload []byte // 消息载荷
CRC uint16 // 校验和
}
// MessageConfig 消息配置结构
type MessageConfig struct {
GroupID GroupID // 消息组ID
SubID NMEA_SUB_ID // 消息子ID
OutRate uint8 // 输出速率
}
// QlCheckQuectel 计算Quectel校验和 (与C代码保持一致)
func QlCheckQuectel(data []byte) uint16 {
if data == nil || len(data) < 4 {
return 0
}
var chk1 uint8 = 0
var chk2 uint8 = 0
// 从索引2开始计算校验和
for i := 2; i < len(data); i++ {
chk1 = chk1 + data[i]
chk2 = chk2 + chk1
}
chk1 = chk1 & 0xFF
chk2 = chk2 & 0xFF
return uint16(chk1) | (uint16(chk2) << 8)
}
// CfgMsgSetOutRate 设置消息输出速率
func CfgMsgSetOutRate(gid GroupID, sid NMEA_SUB_ID, outRate uint8) *CFG_MSG {
msg := &CFG_MSG{
DataLen: 0,
}
// 构建配置消息 (与C代码保持一致)
msgData := []byte{
0xF1, 0xD9, // 帧头
0x06, 0x01, // 消息组ID 消息子ID
0x03, 0x00, // 长度(字节)
0x00, 0x00, 0x00, // 有效载荷:消息组ID 消息子ID 输出速率
0x00, 0x00, // 校验码:CHK1 CHK2
}
// 设置载荷数据
msgData[6] = uint8(gid) // 目标消息组ID
msgData[7] = uint8(sid) // 目标消息子ID
msgData[8] = outRate // 输出速率
// 计算校验和 (不包括最后2字节的校验码)
checksum := QlCheckQuectel(msgData[:len(msgData)-2])
msgData[9] = uint8(checksum & 0xFF)
msgData[10] = uint8(checksum >> 8)
// 复制到CFG_MSG结构
copy(msg.Data[:], msgData)
msg.DataLen = len(msgData)
return msg
}
// CfgMsgQueOutRate 查询消息输出速率
func CfgMsgQueOutRate(gid GroupID, sid NMEA_SUB_ID) *CFG_MSG {
msg := &CFG_MSG{
DataLen: 0,
}
// 构建查询消息 (与C代码保持一致)
msgData := []byte{
0xF1, 0xD9, // 帧头
0x06, 0x01, // 消息组ID 消息子ID
0x02, 0x00, // 长度(字节)
0x00, 0x00, // 有效载荷:消息组ID 消息子ID
0x00, 0x00, // 校验码:CHK1 CHK2
}
// 设置载荷数据
msgData[6] = uint8(gid) // 目标消息组ID
msgData[7] = uint8(sid) // 目标消息子ID
// 计算校验和 (不包括最后2字节的校验码)
checksum := QlCheckQuectel(msgData[:len(msgData)-2])
msgData[len(msgData)-2] = uint8(checksum & 0xFF)
msgData[len(msgData)-1] = uint8(checksum >> 8)
// 复制到CFG_MSG结构
copy(msg.Data[:], msgData)
msg.DataLen = len(msgData)
return msg
}
// ParseBinaryMessage 解析二进制消息
func ParseBinaryMessage(data []byte) (*BinaryMessage, error) {
if len(data) < 8 {
return nil, fmt.Errorf("消息长度不足")
}
// 检查消息头
if data[0] != 0xB5 || data[1] != 0x62 {
return nil, fmt.Errorf("无效的消息头")
}
msg := &BinaryMessage{
Header: binary.LittleEndian.Uint16(data[0:2]),
GroupID: GroupID(data[2]),
SubID: data[3],
Length: binary.LittleEndian.Uint16(data[4:6]),
}
// 检查消息长度
expectedLen := int(msg.Length) + 8 // 头部6字节 + 载荷 + 校验和2字节
if len(data) < expectedLen {
return nil, fmt.Errorf("消息数据不完整")
}
// 提取载荷
if msg.Length > 0 {
msg.Payload = make([]byte, msg.Length)
copy(msg.Payload, data[6:6+msg.Length])
}
// 提取校验和
crcOffset := 6 + int(msg.Length)
msg.CRC = binary.LittleEndian.Uint16(data[crcOffset : crcOffset+2])
// 验证校验和
checksumData := data[2:crcOffset]
expectedCRC := QlCheckQuectel(checksumData)
if msg.CRC != expectedCRC {
return nil, fmt.Errorf("校验和错误: 期望 %04X, 实际 %04X", expectedCRC, msg.CRC)
}
return msg, nil
}
// ToBytes 将CFG_MSG转换为字节数组
func (msg *CFG_MSG) ToBytes() []byte {
if msg.DataLen <= 0 || msg.DataLen > len(msg.Data) {
return nil
}
result := make([]byte, msg.DataLen)
copy(result, msg.Data[:msg.DataLen])
return result
}
// String 返回CFG_MSG的字符串表示
func (msg *CFG_MSG) String() string {
if msg.DataLen <= 0 {
return "CFG_MSG{empty}"
}
return fmt.Sprintf("CFG_MSG{DataLen: %d, Data: %X}", msg.DataLen, msg.Data[:msg.DataLen])
}
// GetGroupIDString 获取GroupID的字符串描述
func (gid GroupID) String() string {
switch gid {
case NMEA_GID:
return "NMEA"
case BIN_RES_GID:
return "BIN_RES"
case BIN_CFG_GID:
return "BIN_CFG"
default:
return fmt.Sprintf("UNKNOWN(0x%02X)", uint8(gid))
}
}
// GetNMEASubIDString 获取NMEA子ID的字符串描述
func (sid NMEA_SUB_ID) String() string {
switch sid {
case NMEA_GGA_SID:
return "GGA"
case NMEA_GLL_SID:
return "GLL"
case NMEA_GSA_SID:
return "GSA"
case NMEA_GRS_SID:
return "GRS"
case NMEA_GSV_SID:
return "GSV"
case NMEA_RMC_SID:
return "RMC"
case NMEA_VTG_SID:
return "VTG"
case NMEA_ZDA_SID:
return "ZDA"
case NMEA_GST_SID:
return "GST"
default:
return fmt.Sprintf("UNKNOWN(0x%02X)", uint8(sid))
}
}