EdgexAgent/device-gps-go/run/driver/binary_protocol.go
2025-07-10 20:30:06 +08:00

266 lines
7.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))
}
}