Análisis del Protocolo Beidou (Beidou III)

Prólogo : La empresa pertenece a la industria de las comunicaciones de Beidou y, a menudo, está expuesta al protocolo Beidou durante el desarrollo diario. Dado que Beidou Communications es relativamente impopular entre los desarrolladores externos, y el protocolo Beidou de tercera generación solo se desarrolló para uso civil el año pasado, fue difícil encontrar información relevante durante el desarrollo inicial, por lo que tuve que leer el protocolo y explorarlo lentamente. Ahora, después de haber sido bautizado por una gran cantidad de proyectos relacionados, también sé un poco sobre el Acuerdo de Beisan, así que escribí una clase de herramienta de análisis.

1. Sin más preámbulos, simplemente pega el código y podrás usarlo directamente después de copiarlo.

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

}

2. Explique brevemente

1. Análisis

De hecho, el protocolo en sí es relativamente simple, pero requiere el uso de varios enlaces de comunicación como Bluetooth, puertos serie, etc. durante el proceso de desarrollo. La conexión del equipo Beidou hace que todo el proceso parezca engorroso. Hay dos métodos de análisis anteriores: Uno es
:
public static void parseData_fragment(String str)
Esto se utiliza para empalmar datos fragmentados. Es adecuado para conexión Bluetooth o puerto serie. Los datos capturados durante la conexión Bluetooth o puerto serie están relativamente fragmentados, por lo que es necesario unirlos. por tí mismo. Si los datos que recibe son una instrucción completa, puede utilizar directamente otro método: public static void parseData(String intactData)

2. Protocolo Beidou

El formato estándar del protocolo Beidou es: comenzando con el símbolo "$" y terminando con "*", "suma de comprobación" y "retorno de carro y avance de línea (/r/n)". Con estas características, es fácil de analizar. el proceso de desarrollo real
. Solo hay unas pocas instrucciones que deben usarse: $BDICP para consultar la información del dispositivo, $BDPWI para consultar el estado de la información y $BDFKI para consultar el estado de emisión del comando. Todos estos están escritos en el código. Simplemente agregue sus propias operaciones en las ubicaciones correspondientes. Si necesita analizar otras instrucciones, puede agregarlas usted mismo de acuerdo con el protocolo Beidou. También se adjunta una versión simplificada del protocolo Beidou:

Supongo que te gusta

Origin blog.csdn.net/lxt1292352578/article/details/132060876
Recomendado
Clasificación