package driver import ( "strconv" "strings" ) // NMEA_TYPE 定义NMEA语句类型 type NMEA_TYPE int const ( NMEA_UNKONW_TYPE NMEA_TYPE = iota NMEA_RMC_TYPE NMEA_GGA_TYPE NMEA_GSV_TYPE NMEA_GSA_TYPE NMEA_VTG_TYPE NMEA_GLL_TYPE NMEA_ZDA_TYPE NMEA_GRS_TYPE NMEA_GST_TYPE ) // NMEA 结构体定义基本的NMEA信息 type NMEA struct { /* --------------------------------------- GNSS星系配置 | TalkerID --------------------------------------- GPS | GP --------------------------------------- GLONASS | GL --------------------------------------- Galileo | GA --------------------------------------- BDS | GB --------------------------------------- QZSS | GQ --------------------------------------- 组合星系 | GN --------------------------------------- */ TalkerID [3]byte Type [4]byte } // NMEA_RMC 结构体定义RMC语句信息 type NMEA_RMC struct { Nmea NMEA /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte Status [2]byte // A有效,V导航接收警告 /* 含义: 纬度。 格式: ddmm.mmmmmm dd:度(00~90) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lat [12]byte N_S [2]byte // 纬度方向。N=北 S=南。数据无效时,为空 /* 含义: 经度。 格式: dddmm.mmmmmm ddd:度(00~180) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lon [13]byte E_W [2]byte // 经度方向。E=东 W=西。数据无效时,为空 SOG [11]byte // 对地速度。可变长度。最大长度待定 COG [7]byte // 对地真航向。可变长度,最大值:359.99。 /* 含义: 日期。 格式: ddmmyy dd:日 mm:月 yy:年,真实年份需要+2000*/ Date [7]byte MagVar [1]byte // MagVar 磁偏角暂不支持 MagVarDir [1]byte // MagVarDir 磁偏角方向暂不支持 ModeInd [2]byte /* 模式:A = 自主式模式,卫星系统处于非差分定位模式。 D = 差分模式,卫星系统处于差分定位模式;基于地面站或SBAS的修正。 N = 无定位,卫星系统没有用于位置定位,或定位无效。 */ NavStatus [2]byte /* 导航状态:S = 安全 C = 警告 U = 不安全 V = 导航状态无效,设备不提供导航状态指示 */ } // NMEA_GGA 结构体定义GGA语句信息 type NMEA_GGA struct { Nmea NMEA /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte /* 含义: 纬度。 格式: ddmm.mmmmmm dd:度(00~90) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lat [12]byte N_S [2]byte // 纬度方向。N=北 S=南。数据无效时,为空 /* 含义: 经度。 格式: dddmm.mmmmmm ddd:度(00~180) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lon [13]byte E_W [2]byte // 经度方向。E=东 W=西。数据无效时,为空 Quality [2]byte /* GPS 定位模式/状态指示: 0 = 定位不可用或无效 1 = GPS SPS 模式,定位有效 2 = 差分 GPS,SPS 模式,或SBAS,定位有效 */ NumSatUsed [3]byte // 使用的卫星数 HDOP [6]byte // 水平精度因子。数据无效时,此字段为 99.99。 Alt [10]byte // 平均海平面以上海拔(大地水准面)。数据无效时,此字段为空。 AltM [2]byte // Alt的单位。M=米。 Sep [10]byte // 大地水准面差距(WGS84 基准面与平均海平面之间的差值)。数据无效时,此字段为空。 SepM [2]byte // Sep的单位。M=米。 DiffAge [1]byte // 差分卫星导航系统数据龄期。暂不支持。 DiffStation [1]byte // 差分基准站标识号。暂不支持。 } // SAT_STATUS 结构体定义卫星状态信息 type SAT_STATUS struct { /* ---------------------------------------------------------- 卫星系统 | 系统标识符 | 卫星标识号 | 信号标识号/信号通道 ---------------------------------------------------------- GPS | 1 | 1~32:GPS, 33~64:SBAS| 1=L1 C/A ---------------------------------------------------------- GLONASS | 2 | 65~96 | 1=L1 ---------------------------------------------------------- Galileo | 3 | 1~36 | 7=E1 ---------------------------------------------------------- BDS | 4 | 1~63 | 1=B1I, 3=B1C ---------------------------------------------------------- QZSS | 5 | 1~10 | 1=L1 C/A ---------------------------------------------------------- */ SatID [3]byte // 卫星标识号。 SatElev [3]byte // 仰角。范围:00~90。数据无效时,此字段为空。 SatAz [4]byte // 真方位角。范围:000~359。数据无效时,此字段为空。 SatCN0 [3]byte // 载噪比(C/N0)。范围:00~99。未跟踪时为空。 } // NMEA_GSV 结构体定义GSV语句信息 type NMEA_GSV struct { Nmea NMEA TotalNumSen [2]byte // 语句总数。范围:1~9。 SenNum [2]byte // 语句号。范围:1~。 TotalNumSat [3]byte // 可视的卫星总数。 SatStatus [4]SAT_STATUS // 卫星信息 SignalID [2]byte // 信号标识符 } // NMEA_GSA 结构体定义GSA语句信息 type NMEA_GSA struct { Nmea NMEA Mode [2]byte // A = 自动,允许2D/3D定位模式自动变换。 FixMode [2]byte // 定位模式: 1=定位不可用 2=2D定位模式 3=3D定位模式 SatID [12][3]byte // 解算中用到的卫星标识号。数据无效时,此字段为空。 PDOP [6]byte // 位置精度因子,最大值为99.99。数据无效时,此字段为99.99。 HDOP [6]byte // 水平精度因子,最大值为99.99。数据无效时,此字段为99.99。 VDOP [6]byte // 垂直精度因子,最大值为99.99。数据无效时,此字段为99.99。 SystemID [2]byte // GNSS系统标识符。 } // NMEA_VTG 结构体定义VTG语句信息 type NMEA_VTG struct { Nmea NMEA COGT [7]byte // 对地航向(真北)。数据无效时,此字段为空。 COGTT [2]byte // 固定字段:真。 COGM [7]byte // 对地航向(磁北)。 暂不支持。 COGMM [2]byte // 固定字段:磁场。 SOGN [5]byte // 对地速度,以节为单位。数据无效时,此字段为空。 SOGNN [2]byte // 固定字段:节。 SOGK [5]byte // 对地速度,以千米每小时为单位。数据无效时,此字段为空。 SOGKK [2]byte // 固定字段:千米每小时。 ModeInd [2]byte /* 模式指示: A = 自主式模式,卫星系统处于非差分定位模式 D = 差分模式,卫星系统处于差分定位模式;基 于地面站或SBAS的修正 N = 数据无效 */ } // NMEA_GLL 结构体定义GLL语句信息 type NMEA_GLL struct { Nmea NMEA /* 含义: 纬度。 格式: ddmm.mmmmmm dd:度(00~90) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lat [12]byte N_S [2]byte // 纬度方向。N=北 S=南。数据无效时,为空 /* 含义: 经度。 格式: dddmm.mmmmmm ddd:度(00~180) mm:分(00~59) mmmmmm:分的十进制小数 数据无效时,为空。*/ Lon [13]byte E_W [2]byte // 经度方向。E=东 W=西。数据无效时,为空 /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte Status [2]byte // A有效,V导航接收警告 ModeInd [2]byte /* 模式:A = 自主式模式,卫星系统处于非差分定位模式。 D = 差分模式,卫星系统处于差分定位模式;基于地面站或SBAS的修正。 N = 无定位,卫星系统没有用于位置定位,或定位无效。 */ } // NMEA_ZDA 结构体定义ZDA语句信息 type NMEA_ZDA struct { Nmea NMEA /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte Day [3]byte // 日。范围:01~3 Month [3]byte // 月。范围:01~12。 Year [5]byte // 年。 LocalHour [3]byte // 本时区小时。暂不支持。 LocalMin [3]byte // 本时区分钟。暂不支持。 } // NMEA_GRS 结构体定义GRS语句信息 type NMEA_GRS struct { Nmea NMEA /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte Mode [2]byte // 使用的计算方法。0=残差用于计算匹配GGA句子中给出的位置。1=在计算GGA位置后重新计算残差。 Resi [12][6]byte // 导航中使用的可视卫星的距离残差。范围:-999到999。数据无效时,此字段为空。 SystemID [2]byte // GNSS系统标识符。 SignalID [2]byte // 卫星标识号。 } // NMEA_GST 结构体定义GST语句信息 type NMEA_GST struct { Nmea NMEA /* 含义: 定位的UTC时间。 格式: hhmmss.sss hh:时(00~23) mm:分(00~59) ss:秒((00~59) sss:秒的十进制小数 */ UTC [11]byte RMS_D [10]byte // 导航过程中输入的标准偏差范围的RMS值。数据无效时,此字段为空。 MajorD [10]byte // 误差椭圆半长轴的标准偏差。数据无效时,此字段为空。 MinorD [10]byte // 误差椭圆半短轴的标准偏差。数据无效时,此字段为空。 Orient [10]byte // 误差椭圆半长轴方向。数据无效时,此字段为空。 LatD [10]byte // 纬度误差的标准差。数据无效时,此字段为空。 LonD [10]byte // 经度误差的标准偏差。数据无效时,此字段为空。 AltD [10]byte // 高度误差的标准偏差。数据无效时,此字段为空。 } // QlCheckXOR 计算校验和 func QlCheckXOR(pData []byte, length uint) byte { var checksum byte = 0 for i := uint(0); i < length; i++ { checksum ^= pData[i] } return checksum } // Strnstr 在haystack中查找needle,限制搜索长度为len func Strnstr(haystack string, needle string, length int) string { if len(needle) == 0 { return haystack } if len(haystack) == 0 || length <= 0 { return "" } if length > len(haystack) { length = len(haystack) } for i := 0; i <= length-len(needle); i++ { if haystack[i:i+len(needle)] == needle { return haystack[i:] } } return "" } // ValidateNMEAChecksum 验证NMEA语句的校验和 func ValidateNMEAChecksum(sentence string, length int) bool { if length < 4 { return false } // 查找*号位置 asteriskPos := strings.LastIndex(sentence, "*") if asteriskPos == -1 || asteriskPos >= length-2 { return false } // 提取校验和 checksumStr := sentence[asteriskPos+1:] if len(checksumStr) < 2 { return false } expectedChecksum, err := strconv.ParseUint(checksumStr[:2], 16, 8) if err != nil { return false } // 计算实际校验和(从$后到*前的所有字符) var actualChecksum byte = 0 for i := 1; i < asteriskPos; i++ { actualChecksum ^= sentence[i] } return actualChecksum == byte(expectedChecksum) } // ParsNMEAType 解析NMEA语句类型 func ParsNMEAType(strNMEA string, length int) NMEA_TYPE { if length < 6 { return NMEA_UNKONW_TYPE } // 检查是否以$开头 if strNMEA[0] != '$' { return NMEA_UNKONW_TYPE } // 提取语句类型(跳过$和前两个字符的TalkerID) if length >= 6 { msgType := strNMEA[3:6] switch msgType { case "GGA": return NMEA_GGA_TYPE case "RMC": return NMEA_RMC_TYPE case "GLL": return NMEA_GLL_TYPE case "GSA": return NMEA_GSA_TYPE case "GSV": return NMEA_GSV_TYPE case "VTG": return NMEA_VTG_TYPE case "GST": return NMEA_GST_TYPE case "GRS": return NMEA_GRS_TYPE case "ZDA": return NMEA_ZDA_TYPE } } return NMEA_UNKONW_TYPE } // ParsNMEAGST 解析GST语句 func ParsNMEAGST(strGST string, length int) *NMEA_GST { // 实现解析GST语句的逻辑 return nil } // ParsNMEAGRS 解析GRS语句 func ParsNMEAGRS(strGRS string, length int) *NMEA_GRS { // 实现解析GRS语句的逻辑 return nil } // ParsNMEAZDA 解析ZDA语句 func ParsNMEAZDA(strZDA string, length int) *NMEA_ZDA { // 实现解析ZDA语句的逻辑 return nil } // ParsNMEAGLL 解析GLL语句 func ParsNMEAGLL(strGLL string, length int) *NMEA_GLL { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strGLL, length) { return nil } // 分割字段 fields := strings.Split(strGLL, ",") if len(fields) < 7 { return nil } gll := &NMEA_GLL{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(gll.Nmea.TalkerID[:], fields[0][1:3]) copy(gll.Nmea.Type[:], fields[0][3:6]) } // 解析纬度 if len(fields[1]) > 0 && len(fields[1]) <= 11 { copy(gll.Lat[:], fields[1]) } // 解析纬度方向 if len(fields[2]) > 0 { copy(gll.N_S[:], fields[2]) } // 解析经度 if len(fields[3]) > 0 && len(fields[3]) <= 12 { copy(gll.Lon[:], fields[3]) } // 解析经度方向 if len(fields[4]) > 0 { copy(gll.E_W[:], fields[4]) } // 解析UTC时间 if len(fields[5]) > 0 && len(fields[5]) <= 10 { copy(gll.UTC[:], fields[5]) } // 解析状态 if len(fields[6]) > 0 { copy(gll.Status[:], fields[6]) } // 解析模式指示(如果存在) if len(fields) > 7 && len(fields[7]) > 0 { copy(gll.ModeInd[:], fields[7]) } return gll } // ParsNMEAVTG 解析VTG语句 func ParsNMEAVTG(strVTG string, length int) *NMEA_VTG { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strVTG, length) { return nil } // 分割字段 fields := strings.Split(strVTG, ",") if len(fields) < 9 { return nil } vtg := &NMEA_VTG{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(vtg.Nmea.TalkerID[:], fields[0][1:3]) copy(vtg.Nmea.Type[:], fields[0][3:6]) } // 解析对地航向(真北) if len(fields[1]) > 0 && len(fields[1]) <= 6 { copy(vtg.COGT[:], fields[1]) } // 解析固定字段:真 if len(fields[2]) > 0 { copy(vtg.COGTT[:], fields[2]) } // 解析对地航向(磁北) if len(fields[3]) > 0 && len(fields[3]) <= 6 { copy(vtg.COGM[:], fields[3]) } // 解析固定字段:磁场 if len(fields[4]) > 0 { copy(vtg.COGMM[:], fields[4]) } // 解析对地速度(节) if len(fields[5]) > 0 && len(fields[5]) <= 4 { copy(vtg.SOGN[:], fields[5]) } // 解析固定字段:节 if len(fields[6]) > 0 { copy(vtg.SOGNN[:], fields[6]) } // 解析对地速度(km/h) if len(fields[7]) > 0 && len(fields[7]) <= 4 { copy(vtg.SOGK[:], fields[7]) } // 解析固定字段:千米每小时 if len(fields[8]) > 0 { copy(vtg.SOGKK[:], fields[8]) } // 解析模式指示(如果存在) if len(fields) > 9 && len(fields[9]) > 0 { copy(vtg.ModeInd[:], fields[9]) } return vtg } // ParsNMEAGSA 解析GSA语句 func ParsNMEAGSA(strGSA string, length int) *NMEA_GSA { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strGSA, length) { return nil } // 分割字段 fields := strings.Split(strGSA, ",") if len(fields) < 18 { return nil } gsa := &NMEA_GSA{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(gsa.Nmea.TalkerID[:], fields[0][1:3]) copy(gsa.Nmea.Type[:], fields[0][3:6]) } // 解析模式 if len(fields[1]) > 0 { copy(gsa.Mode[:], fields[1]) } // 解析定位模式 if len(fields[2]) > 0 { copy(gsa.FixMode[:], fields[2]) } // 解析卫星标识号(字段3-14) for i := 0; i < 12 && i+3 < len(fields); i++ { if len(fields[i+3]) > 0 && len(fields[i+3]) <= 2 { copy(gsa.SatID[i][:], fields[i+3]) } } // 解析PDOP if len(fields[15]) > 0 && len(fields[15]) <= 5 { copy(gsa.PDOP[:], fields[15]) } // 解析HDOP if len(fields[16]) > 0 && len(fields[16]) <= 5 { copy(gsa.HDOP[:], fields[16]) } // 解析VDOP if len(fields[17]) > 0 && len(fields[17]) <= 5 { copy(gsa.VDOP[:], fields[17]) } // 解析系统标识符(如果存在) if len(fields) > 18 && len(fields[18]) > 0 { copy(gsa.SystemID[:], fields[18]) } return gsa } // ParsNMEAGSV 解析GSV语句 func ParsNMEAGSV(strGSV string, length int) *NMEA_GSV { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strGSV, length) { return nil } // 分割字段 fields := strings.Split(strGSV, ",") if len(fields) < 4 { return nil } gsv := &NMEA_GSV{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(gsv.Nmea.TalkerID[:], fields[0][1:3]) copy(gsv.Nmea.Type[:], fields[0][3:6]) } // 解析语句总数 if len(fields[1]) > 0 { copy(gsv.TotalNumSen[:], fields[1]) } // 解析语句号 if len(fields[2]) > 0 { copy(gsv.SenNum[:], fields[2]) } // 解析可视卫星总数 if len(fields[3]) > 0 && len(fields[3]) <= 2 { copy(gsv.TotalNumSat[:], fields[3]) } // 解析卫星信息(每个卫星4个字段:ID、仰角、方位角、载噪比) satIndex := 0 for i := 4; i < len(fields) && satIndex < 4; { if i+3 < len(fields) { // 卫星标识号 if len(fields[i]) > 0 && len(fields[i]) <= 2 { copy(gsv.SatStatus[satIndex].SatID[:], fields[i]) } // 仰角 if len(fields[i+1]) > 0 && len(fields[i+1]) <= 2 { copy(gsv.SatStatus[satIndex].SatElev[:], fields[i+1]) } // 方位角 if len(fields[i+2]) > 0 && len(fields[i+2]) <= 3 { copy(gsv.SatStatus[satIndex].SatAz[:], fields[i+2]) } // 载噪比 if len(fields[i+3]) > 0 && len(fields[i+3]) <= 2 { copy(gsv.SatStatus[satIndex].SatCN0[:], fields[i+3]) } satIndex++ i += 4 } else { break } } // 解析信号标识符(如果存在) lastFieldIndex := len(fields) - 1 if lastFieldIndex > 4 { // 检查最后一个字段是否包含*(校验和标记) lastField := fields[lastFieldIndex] if strings.Contains(lastField, "*") { // 提取*之前的部分作为信号标识符 parts := strings.Split(lastField, "*") if len(parts[0]) > 0 && len(parts[0]) <= 1 { copy(gsv.SignalID[:], parts[0]) } } } return gsv } // ParsNMEAGGA 解析GGA语句 func ParsNMEAGGA(strGGA string, length int) *NMEA_GGA { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strGGA, length) { return nil } // 分割字段 fields := strings.Split(strGGA, ",") if len(fields) < 15 { return nil } gga := &NMEA_GGA{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(gga.Nmea.TalkerID[:], fields[0][1:3]) copy(gga.Nmea.Type[:], fields[0][3:6]) } // 解析UTC时间 if len(fields[1]) > 0 && len(fields[1]) <= 10 { copy(gga.UTC[:], fields[1]) } // 解析纬度 if len(fields[2]) > 0 && len(fields[2]) <= 11 { copy(gga.Lat[:], fields[2]) } // 解析纬度方向 if len(fields[3]) > 0 { copy(gga.N_S[:], fields[3]) } // 解析经度 if len(fields[4]) > 0 && len(fields[4]) <= 12 { copy(gga.Lon[:], fields[4]) } // 解析经度方向 if len(fields[5]) > 0 { copy(gga.E_W[:], fields[5]) } // 解析定位质量 if len(fields[6]) > 0 { copy(gga.Quality[:], fields[6]) } // 解析使用的卫星数 if len(fields[7]) > 0 && len(fields[7]) <= 2 { copy(gga.NumSatUsed[:], fields[7]) } // 解析水平精度因子 if len(fields[8]) > 0 && len(fields[8]) <= 5 { copy(gga.HDOP[:], fields[8]) } // 解析海拔高度 if len(fields[9]) > 0 && len(fields[9]) <= 9 { copy(gga.Alt[:], fields[9]) } // 解析海拔高度单位 if len(fields[10]) > 0 { copy(gga.AltM[:], fields[10]) } // 解析大地水准面差距 if len(fields[11]) > 0 && len(fields[11]) <= 9 { copy(gga.Sep[:], fields[11]) } // 解析大地水准面差距单位 if len(fields[12]) > 0 { copy(gga.SepM[:], fields[12]) } return gga } // ParsNMEARMC 解析RMC语句 func ParsNMEARMC(strRMC string, length int) *NMEA_RMC { if length < 10 { return nil } // 验证校验和 if !ValidateNMEAChecksum(strRMC, length) { return nil } // 分割字段 fields := strings.Split(strRMC, ",") if len(fields) < 12 { return nil } rmc := &NMEA_RMC{} // 解析TalkerID和Type if len(fields[0]) >= 6 { copy(rmc.Nmea.TalkerID[:], fields[0][1:3]) copy(rmc.Nmea.Type[:], fields[0][3:6]) } // 解析UTC时间 if len(fields[1]) > 0 && len(fields[1]) <= 10 { copy(rmc.UTC[:], fields[1]) } // 解析状态 if len(fields[2]) > 0 { copy(rmc.Status[:], fields[2]) } // 解析纬度 if len(fields[3]) > 0 && len(fields[3]) <= 11 { copy(rmc.Lat[:], fields[3]) } // 解析纬度方向 if len(fields[4]) > 0 { copy(rmc.N_S[:], fields[4]) } // 解析经度 if len(fields[5]) > 0 && len(fields[5]) <= 12 { copy(rmc.Lon[:], fields[5]) } // 解析经度方向 if len(fields[6]) > 0 { copy(rmc.E_W[:], fields[6]) } // 解析对地速度 if len(fields[7]) > 0 && len(fields[7]) <= 10 { copy(rmc.SOG[:], fields[7]) } // 解析对地真航向 if len(fields[8]) > 0 && len(fields[8]) <= 6 { copy(rmc.COG[:], fields[8]) } // 解析日期 if len(fields[9]) > 0 && len(fields[9]) <= 6 { copy(rmc.Date[:], fields[9]) } return rmc }