如何在Flutter中将经纬度转换为GT06协议格式?
GT06协议经纬度转换修复(Flutter)
我是Flutter新手,正尝试将经纬度转换为GT06协议格式,协议结构如下:
| 字段 | 长度(字节) |
|---|---|
| 起始位 | 2 |
| 数据包长度 | 1 |
| 协议编号 | 1 |
| 信息内容 | N |
| 信息序列号 | 2 |
| 校验码 | 2 |
| 停止位 | 2 |
我编写了以下转换函数,但无法正常工作:
String convertCoordinatesToFormat(double latitude, double longitude) { // 起始位(2字节) String startBit = (0x78.toString()) ; // 协议编号(1字节) int protocolNumber = 0x22; // 替换为合适的协议编号 // 信息内容(N字节) // 将经纬度转换为所需格式,例如字符串形式 String latitudeString = latitude.toStringAsFixed(6); // 替换为所需的纬度精度 String longitudeString = longitude.toStringAsFixed(6); // 替换为所需的经度精度 String informationContent = "$latitudeString$longitudeString"; // 计算字节长度 int length = 5 + informationContent.length ~/ 2; // 除以2是因为每个字节在十六进制中用2个字符表示 // 长度(1字节) String lengthString = length.toRadixString(16).padLeft(2, "0"); // 数据包长度(1字节) // int packetLength = lengthString; // 剩余字节的长度(协议编号 + 信息内容 + 信息序列号 + 校验码 + 停止位) // 信息序列号(2字节) int informationSerialNumber = 1; // 替换为合适的序列号 // 校验码(2字节) int errorCheck = calculateErrorCheck(informationContent); String errorCheckString = errorCheck.toRadixString(16).padLeft(4, "0"); // 停止位(2字节) String stopBit = (0x0D.toString()) + (0x0A.toString()); // 将所有字段拼接在一起 String result = "$startBit$lengthString$protocolNumber$informationContent$informationSerialNumber$errorCheckString$stopBit"; List<int> bytes = utf8.encode(result); String answer = ""; for (int a in bytes){ answer += a.toString(); } // 将格式化后的坐标转换为大写 return answer; } int calculateErrorCheck(String content) { int sum = 0; for (int i = 0; i < content.length; i++) { sum += content.codeUnitAt(i); } return sum % 65536; }
问题分析
原代码存在多个核心错误,完全不符合GT06协议的二进制编码规则:
- 字段编码逻辑错误:所有协议字段需以十六进制字节形式表示,而非十进制字符串或普通文本
- 经纬度编码错误:GT06不使用字符串存储经纬度,需将经纬度值×10^6后转为4字节大端整数
- 数据包长度计算错误:数据包长度是「协议编号+信息内容+信息序列号」的总字节数
- 校验码计算范围错误:校验码需计算从数据包长度到信息序列号的所有字节累加和,取低16位
- 起始/停止位错误:起始位应为
0x7878、停止位应为0x0D0A,需转成十六进制字符串而非十进制
修复后的代码
String convertCoordinatesToGT06(double latitude, double longitude) { // 1. 起始位:0x7878(2字节) final startBit = "7878"; // 2. 协议编号:0x22是位置信息包(1字节) final protocolNumber = 0x22; final protocolHex = protocolNumber.toRadixString(16).padLeft(2, "0"); // 3. 经纬度编码:转成4字节大端整数(值×10^6) final latInt = (latitude * 1000000).toInt(); final lonInt = (longitude * 1000000).toInt(); // 转大端十六进制,每个占8位(4字节) final latHex = latInt.toRadixString(16).padLeft(8, "0"); final lonHex = lonInt.toRadixString(16).padLeft(8, "0"); // 信息内容:这里简化处理,实际GT06位置包还有状态、时间等字段,需根据协议补全 // 示例仅包含经纬度,实际需参考GT06协议规范添加其他必填字段 final informationContent = latHex + lonHex; final infoContentLen = informationContent.length ~/ 2; // 字节数 // 4. 数据包长度:协议编号(1) + 信息内容(N) + 信息序列号(2) 的总字节数 final packetLength = 1 + infoContentLen + 2; final packetLengthHex = packetLength.toRadixString(16).padLeft(2, "0"); // 5. 信息序列号:2字节,自增即可 final serialNumber = 1; final serialHex = serialNumber.toRadixString(16).padLeft(4, "0"); // 6. 校验码:计算从packetLength到serialNumber的所有字节累加和,取低16位 final checkData = packetLengthHex + protocolHex + informationContent + serialHex; int checkSum = 0; for (int i = 0; i < checkData.length; i += 2) { final byteStr = checkData.substring(i, i + 2); checkSum += int.parse(byteStr, radix: 16); } final checkCode = checkSum % 65536; final checkCodeHex = checkCode.toRadixString(16).padLeft(4, "0"); // 7. 停止位:0x0D0A(2字节) final stopBit = "0D0A"; // 拼接所有十六进制字段 final hexResult = startBit + packetLengthHex + protocolHex + informationContent + serialHex + checkCodeHex + stopBit; // 如果需要转为字节数组(发送给设备用),可以用下面的代码: // List<int> bytes = []; // for (int i = 0; i < hexResult.length; i += 2) { // bytes.add(int.parse(hexResult.substring(i, i + 2), radix: 16)); // } return hexResult.toUpperCase(); }
注意事项
- GT06的位置信息包(0x22协议)的信息内容包含更多字段(如定位状态、速度、方向、时间等),上述代码仅做经纬度核心编码,实际使用需参考完整的GT06协议规范补全所有必填字段
- 经纬度的正负需注意:GT06中通常用最高位或单独的字节表示南北纬/东西经,上述代码默认处理正负整数的十六进制转换,需根据协议确认是否需要调整
- 信息序列号需每次发送自增,避免重复
内容的提问来源于stack exchange,提问作者Vedant Kakade
相关产品推荐
相关产品推荐

