北斗协议解析(北三)

前言:公司属于北斗通信行业,平时开发时经常要接触到北斗协议。由于北斗通信相对于外界开发人员来说比较冷门,北斗三代协议又是去年才开发民用的,所以在早期开发时想要找到相关资料比较难,因此只好自己看协议慢慢摸索。现在经过了大量相关项目的洗礼之后对北三协议也略知一二就写了个解析工具类。
2024-3-25更新:新增北斗协议解析SDK
协议解析SDK说明:https://blog.csdn.net/lxt1292352578/article/details/137019548
协议解析SDK资源:https://download.csdn.net/download/lxt1292352578/89030621

一、废话不多说,直接贴代码,复制就能直接使用

package com.example.SecondProject.Utils;

import android.util.Log;
import java.util.Arrays;

// 协议解析/封装工具
public class ProtocolUtil {
    public static String TAG = "ProtocolUtil";

    private static String data = "";
    // 北斗指令拼接:每次接收的都是碎片数据,需要自行拼接为完整的指令,例如:$CCIC     A,1595   0044,0,1,33    3211*99
    public static void parseData_fragment(String str){
        // 拼接数据
        data +=  str;
        // 找到 $ 符,如果没有的话,这条数据就丢弃,重置数据,如果有的话就从 $ 开始截取到后面的所有字符
        int startIndex = data.indexOf("$");
        if (startIndex < 0){
            data = "";
            return;
        }
        if (startIndex > 0) {
            data = data.substring(startIndex);
        }
        // 找到 * 符,如果没有代表接收的是中间的数据,退出接收下一串,如果有代表这是这条指令的最后一段 *+校验
        int endIndex = data.indexOf("*");
        if( endIndex < 1 ) return;

        String intactData;
        // 先判断一下长度,不然的话长度不足会断掉
        if(data.length() < endIndex+3){
            intactData = data;
        }else {
            intactData = data.substring(0,endIndex+3);  // 截出 $---*66
            data = data.substring(endIndex+3);  // 多出来的是下一条指令的开头部分,保留到下一次用
        }
        // 这里偷懒了,没有对最后两位校验符做验证,有强迫症的话可以自行添加
        String XOR_str = intactData.substring(intactData.length() - 2);  // 异或校验符
        parseData(intactData);  // 解析协议
    }

    // 解析数据
    public static void parseData(String intactData){
        // 拿到 * 的位置
        int xorIndex = intactData.indexOf("*");
        if(xorIndex == -1){return;}
        String data_str = intactData.substring(0, xorIndex);  // 截取到 * 之前,例:$CCICR,0,00
        if(!data_str.contains(",")){return;}
        String[] values = data_str.split(",", -1);  // , 分割,例:["$CCICR","0","00"]
//        Log.e(TAG, "正在解析的数据:"+ Arrays.toString(values));
        if (data_str.contains("FKI")) {  // 反馈信息
            BDFKI(values);
        }else if (data_str.contains("ICP")) {  // IC 信息
            BDICP(values);
        } else if (data_str.contains("PWI")) {  // 波束信息
            BDPWI(values);
        } else if (data_str.contains("TCI")){  // 北斗三代通信信息
            BDTCI(values);
        }
        // 其余类型的应该都是 RNSS 定位数据
        else {
            parseRNSS(values);
        }

    }


    public  static void BDICP(String[] value){
        try {
            String cardID = value[1];
            String cardFrequency = value[14];
            String cardLevel = value[15];
            Log.e(TAG, "收到IC信息: 卡号-" + cardID + " 频度-" + cardFrequency + " 等级-" + cardLevel);
        }catch (Exception e){
            Log.e(TAG, "BDICP: 解析错误" + e.toString());
            e.printStackTrace();
            return;
        }
    }


    public static void BDPWI(String[] values){
        // 尽量用 try 避免线程中断
        try {
            int rdss2Count1 = Integer.parseInt(values[2]);
            int index = 2 + (rdss2Count1*3) + 1;
            int rdss3Count = Integer.parseInt(values[index]);
            index++;
            int s21[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
            for (int i = 0 ;i < rdss3Count; i++) {
                if(values.length < index+2){
                    return;  // 越界检测
                }
                int id = Integer.parseInt(values[index]);
                if (id > 21 || id <= 0) continue;
                int number = Integer.parseInt(values[index+1]);
                s21[id-1] = number;
                index += 4;
            }
            Log.e(TAG, "收到信号数据:"+Arrays.toString(s21));

        }catch (Exception e){
            Log.e(TAG, "BDPWI: 解析错误" + e.toString());
            e.printStackTrace();
            return;
        }
    }

    // 通信申请后的反馈信息
    public static void BDFKI(String[] values){
        try {
            String type = values[2];  // 反馈的指令类型 :只用 TCQ
            String result = values[3];  // 反馈结果 : Y / N
            String reason = values[4];  // 失败原因

            if(result.equals("Y")){
                Log.e(TAG, "收到反馈信息: " + type + "指令发送成功");
            } else {
                switch ( reason ){
                    case "1":
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败:频度未到,发射被抑制");
                        break;
                    case "2":
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败:接收到系统的抑制指令,发射被抑制");
                        break;
                    case "3":
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败:当前设置为无线电静默状态,发射被抑制");
                        break;
                    case "4":
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败:功率未锁定");
                        break;
                    case "5":
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败:未检测到IC模块信息");
                        break;
                    default:
                        Log.e(TAG, "收到反馈信息: " + type + "指令发送失败,原因码是" + reason);
                        break;
                }
            }

        }catch (Exception e){
            Log.e(TAG, "BDFKI: 解析错误" + e.toString());
            e.printStackTrace();
            return;
        }

    }


    // 收到了 TCI 通信信息
    public static void BDTCI(String[] values){
        try {
            String fromNumber = values[1];  // 发送发地址
            String toNumber = values[2];  // 收信方地址
            String messageType = values[5];  // 消息类型:汉字/代码/混合
            String timeHour = values[4];  // 消息时间
            String content = values[7];  // 消息内容
            Log.e(TAG, "收到通信信息: 发送号码-" + fromNumber + " 接收号码-" + toNumber + " 消息类型-" + messageType + " 消息时间-" + timeHour + " 消息内容-" + content );
        }catch (Exception  e){
            Log.e(TAG, "BDTCI: 解析错误" + e.toString());
            e.printStackTrace();
            return;
        }
    }

    // 解析 RNSS 位置数据,只解析了 GGA 和 GLL ,其余的参考文档自行解析
    public static void parseRNSS(String[] values){
        try {
            // 拆分数据
            String head = values[0];
            if (head.contains("GGA")){
                if (values[6].equals("0")) return;  // 0 就是无效定位,不要
                String latitude = analysisLonlat(values[2]) + "";  // 纬度
                String longitude = analysisLonlat(values[4]) + "";  // 经度
                String altitude;  // 海拔
                if(values[9] != null || !values[9].equals("")){
                    altitude = values[9];
                }else {
                    altitude = "0";
                }
                Log.e(TAG, "解析RNSS定位数据(GGA): 纬度-" + latitude + " 经度-" + longitude + " 高度-" + altitude );
            }else if (head.contains("GLL")){
                if (values[6].equals("V")) return;  // V - 无效  A - 有效
                String latitude = analysisLonlat(values[1]) + "";  // 纬度
                String longitude = analysisLonlat(values[3]) + "";  // 经度
                Log.e(TAG, "解析RNSS定位数据(GLL): 纬度-" + latitude + " 经度-" + longitude );
            }
            else {
                return;
            }
        }catch (Exception e){
            Log.e(TAG, "parseRNSS: 解析错误" + e.toString());
            e.printStackTrace();
            return;
        }
    }

    public static double analysisLonlat(String value){
        if(value.contains("N") || value.contains("E")){
            return 0.0;
        }
        if (value.equals("")|| value == null) return 0.0;
        double lonlat = Double.valueOf(value);
        int dd = (int)lonlat / 100;
        int mm = (int)lonlat % 100;
        double ms = lonlat - (int)lonlat;
        return dd+((mm+ms)/60.0);
    }

// 封装 ---------------------------------------------------------------------------------------
    // 查询 IC 信息:type - 0检测本机IC信息,1检测本机编组信息,2检测下属用户,3检测IC模块工作模式
    public static String CCICR(int type, String info) {
        String command = "CCICR," + type + "," + info;
        return packaging(command);
    }


    // 打包,加上 $ 和 * 和 校验和,输出 hex_str
    public static String packaging(String tmp){
        String hexCommand = DataUtil.string2Hex(tmp);
        String hh = getCheckCode0007(hexCommand).toUpperCase();  // 检验和
        return "24"+hexCommand+"2A"+DataUtil.string2Hex(hh)+"0D0A";
    }

    // 计算异或校验和
    public static String getCheckCode0007(String strProtocol) {
        strProtocol.replace(" ",  "");
        byte chrCheckCode = 0;
        for (int i = 0; i < strProtocol.length(); i += 2) {
            char chrTmp ;
            chrTmp = strProtocol.charAt(i);
            if (chrTmp == ' ') continue;
            byte chTmp1 = (byte) (DataUtil.char2HexByte(chrTmp) << 4);
            chrTmp = strProtocol.charAt(i + 1);
            byte chTmp2 = (byte) (chTmp1 + (DataUtil.char2HexByte(chrTmp) & 15));
            chrCheckCode = i == 0 ? chTmp2 : (byte) (chrCheckCode ^ chTmp2);
        }
        String strHexCheckCode = String.format("%x", Byte.valueOf(chrCheckCode));
        if ((strHexCheckCode = strHexCheckCode.toUpperCase()).length() != 2) {
            if (strHexCheckCode.length() > 2) {
                strHexCheckCode = strHexCheckCode.substring(strHexCheckCode.length() - 2);
            } else if (strHexCheckCode.length() < 2 && strHexCheckCode.length() > 0) {
                strHexCheckCode = "0" + strHexCheckCode;
            }
        }
        return strHexCheckCode;
    }

}

二、简单说明一下

1. 解析

其实协议本身也比较简单,只是开发过程中需要用到各种不同的通信链路例如:蓝牙、串口等,连接北斗设备导致这整个流程显得比较繁琐
上面的解析方法有两个:
一个是:public static void parseData_fragment(String str)
这是拼接碎块化数据用的,适用于蓝牙或串口连接时捕获到的数据比较碎所以需要自行拼接。如果你收到的数据是完整的一条指令就可以直接用另一个方法:public static void parseData(String intactData)

2. 北斗协议

北斗协议的标准格式是:以 “$” 符号开头以 “*” 和 “校验和” “回车换行(/r/n)”  结尾,有了这些特征以后就很好解析了
其实开发中实际需要用到的指令无非就那几条:$BDICP 查询设备信息、$BDPWI 查询信息状况、$BDFKI 查询指令下发状况,这些在代码里面都写好了,在对应的位置增加自己的操作即可,如果需要解析其他指令的话再根据北斗协议自行添加,另附一份精简版北三协议:

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值