北斗衛(wèi)星導(dǎo)航拓荒記

最近接觸了一個項目,主要的使用場景是沒有互聯(lián)網(wǎng)的系奉,所以需要App與北斗衛(wèi)星進(jìn)行通信檬贰,包括獲取地理信息,上報信息喜最,解析后臺通過衛(wèi)星下發(fā)的信息偎蘸。北斗海聊官方只提供了PC版的測試軟件,我不知使用了什么方法去查看了他們的源碼瞬内,沒有發(fā)現(xiàn)對底層通信協(xié)議單獨做的封裝迷雪。網(wǎng)上能查到的都沒法用,所以虫蝶,只能自己從0開始了章咧。

硬件設(shè)備

image.png

如圖,就是這樣一個燈罩狀的設(shè)備能真,里面插了一張北斗SIM卡赁严。

PC端測試軟件

image.png

通過USB口連接設(shè)備,重新選擇端口和波特率粉铐,點擊端口號旁邊的連接疼约,正常情況下就ok了:


image.png

此時就可以發(fā)一些指令來測試軟件和硬件是否正常工作了。


image.png

我發(fā)送了兩條指令蝙泼,分別是IC讀取和定位申請程剥,即上圖中的紅框1,2。
紅框3是發(fā)送的指令汤踏,紅框4是收到的指令织鲸,具體的協(xié)議我們先跳過,后面再研究溪胶。

USB轉(zhuǎn)串口通信

手機(jī)可以用無線和有線兩種方式與設(shè)備進(jìn)行通信搂擦,我們選擇的有線的方式,所以使用USB轉(zhuǎn)串口通信哗脖。這部分推薦Android usb及串口通信瀑踢,我也是使用博主的工具進(jìn)行調(diào)試的扳还,調(diào)通之后再開始接入自己的項目,進(jìn)行后續(xù)開發(fā)橱夭。我針對北斗海聊做的一些特殊的優(yōu)化在這個項目中普办。

北斗協(xié)議

image.png

開發(fā)文檔我先后拿到過三份,都不盡相同徘钥。推薦新手從開發(fā)快速入門手冊開始看,能夠比較快的上手肢娘。我最開始拿到的是頁數(shù)最多的那份呈础,當(dāng)時心里真是...ε=(′ο`*)))。
image.png

標(biāo)準(zhǔn):
$IDsss,d1橱健,d2而钞,……,dn*hh<CR><LF>
一些典型的指令:
$CCICA,0,00*7B\r\n
$CCRMO,GGA,2,60*09\r\n
$BDFKI,DWA,Y,Y,0,0000*0C\n

  • $
    一句指令的開始拘荡。
  • ID
    這里只是一個標(biāo)識符臼节,不要給后面出現(xiàn)的用戶ID混淆。發(fā)送給設(shè)備的指令為CC珊皿,收到設(shè)備返回的指令為BD网缝。
  • sss
    這是具體指令的名字。
  • ...
    一直到*號之前蟋定,這就是具體的指令內(nèi)容了嫩海。
  • *號
    分割符庇楞,前面是具體指令內(nèi)容,后面兩位就異或校驗。
  • <CR><LF>
    回車換行符官册,不同平臺有所區(qū)別,Android平臺是\r\n刻剥。這是一條指令的終止符馋记,很重要!K磷省矗愧!

Talk is cheap,show me the code

  • IC讀取
    /**
     * 讀取卡號
     *
     * @return 讀取卡號命令
     */
    public static String getICCmd() {
        return "$CCICA,0,00*7B\r\n";
    }
  • 獲取地理信息
     /**
     * 獲取位置信息,北斗一代
     *
     * @return 獲取位置信息命令
     */
    public static String getLocationCmdV1() {
        return "$CCDWA,0000000,V,1,L,,0,,,0*65\r\n";
    }

一代的定位精度低一些迅耘,現(xiàn)在一般都不用了贱枣。

     /**
     * 獲取位置信息,北斗二代颤专,更加精確纽哥,頻度60s
     *
     * @return 獲取位置信息命令
     */
    public static String getLocationCmd() {
        return "$CCRMO,GGA,2,60*09\r\n";
    }

    public static String getLocationCmd(int freq) {
        String s = "CCRMO,GGA,2," + freq;
        String check = SerialPortUtil.getBCC(s.getBytes());
        return "$" + s + "*" + check + "\r\n";
    }

北斗二代獲取地理信息有頻度限制,最高60s/次栖秕。

  • 停止輸出
     /**
     * 停止輸出所有指令
     *
     * @return
     */
    public static String stopOutputCmd() {
        return "$CCRMO,,3,*4F" + "\r\n";
    }

比如60s/次開始定位后春塌,想通過不斷電的方式讓設(shè)備停止定位,則可以發(fā)送此指令。

  • 發(fā)送短報文
    /**
     * 發(fā)送短報文
     *
     * @param id      收信方用戶id只壳,必須為7位俏拱,eg:0967760
     * @param content 短報文內(nèi)容
     * @return 發(fā)送短報文命令,eg:
     * $CCTXA,0967760,1,2,A43132335F414243BABAD7D6*77吼句,其內(nèi)容為”123_ABC漢字“
     */
    public static String getMsgCmd(String id, String content) {
        String contentFlag = "A4";
        String start = "CCTXA";
        //分別表示通信類別和傳輸方式锅必,這里選擇了普通通信、混合傳輸
        String middle = "1,2";
        String result = null;
        try {
            String charsetName = "gb2312";
            byte[] contentBytes = content.getBytes(charsetName);
            StringBuilder sb = new StringBuilder(start)
                    .append(",").append(id)
                    .append(",").append(middle)
                    .append(",").append(contentFlag);
            String hexString = SerialPortUtil.encodeHexString(contentBytes);
            sb.append(hexString);
            String s = sb.toString();
            String check = SerialPortUtil.getBCC(s.getBytes(charsetName));
            result = "$" + s + "*" + check + "\r\n";
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
  • 解析反饋信息
    /**
     * 解析反饋信息
     *
     * @param response 反饋字符串惕艳,eg:$BDFKI,TXA,Y,Y,0,0000*13搞隐,
     *                 $BDFKI,DWA,N,Y,0,0058*16
     * @return
     */
    public static BeidouBean.Response parseResponse(String response) {
        String[] split = response.split(",");
        BeidouBean.Response res = new BeidouBean.Response();
        res.cmdName = split[1];
        res.success = "Y".equals(split[2]);
        res.freqSetting = "Y".equals(split[3]);
        res.limitStatus = Integer.parseInt(split[4]);
        String hourSecond = split[5];
        String hour = hourSecond.substring(0, 2);
        String second = hourSecond.substring(2, 4);
        res.waitSecond = Integer.parseInt(hour) * 60 +
                Integer.parseInt(second);
        return res;
    }
    //發(fā)出指令后的反饋信息
    public static class Response{
        public String cmdName;
        //指令是否執(zhí)行成功
        public boolean success;
        public boolean freqSetting;
        //0-發(fā)射抑制解除,大于0則不正常
        public int limitStatus;
        //當(dāng)用戶設(shè)備發(fā)送入站申請時远搪,若距離上一次入站申請
        //的時間間隔小于服務(wù)頻度時劣纲,給出等待時間提示,格式為hhss
        public int waitSecond;
    }
  • 解析地理信息
    /**
     * 解析位置信息谁鳍,用于北斗一代
     *
     * @param response 回傳字符串癞季,eg:
     *                 $BDDWR,1,0242407,084936.50,2302.2434,N,11323.6667,E,14,M,-6,M,1,V,V,L*1F
     * @return
     */
    public static BeidouBean.Location parseLocationV1(String response) {
        String[] split = response.split(",");
        BeidouBean.Location location = new BeidouBean.Location();
        location.customStr = response;
        location.userId = split[2];
        location.time = split[3];
        location.lat = split[4];
        location.latDirection = split[5];
        location.lon = split[6];
        location.lonDirection = split[7];
        location.altitude = split[8];
        return location;
    }

    /**
     * 解析位置信息
     *
     * @param response 回傳字符串,eg:
     *                 $GNGGA,063846.00,2914.96875,N,10444.57129,E,1,12,1.07,316.47,M,0,M,,,2.58*6A
     * @return
     */
    public static BeidouBean.Location parseLocation(String response) {
        String[] split = response.split(",");
        BeidouBean.Location location = new BeidouBean.Location();
        location.customStr = response;
        location.time = split[1];
        location.lat = split[2];
        location.latDirection = split[3];

        location.lon = split[4];
        location.lonDirection = split[5];
        location.altitude = split[9];
        return location;
    }

我封裝的數(shù)據(jù)模型里并沒有把所有信息都加進(jìn)去倘潜,大家使用的使用可以自己拓展绷柒。另外,以上方法中沒有對回傳的指令進(jìn)行異或校驗窍荧,上生產(chǎn)時應(yīng)該加上辉巡。

  • 接收指令
    這里有一個坑,一條指令可能會分為2次甚至3次傳送回來蕊退,所以必須自己做處理郊楣。我的解決方案是創(chuàng)建一個buf數(shù)組,每次接收到的指令都往里面放瓤荔,直到讀到終止符\r\n净蚤。
DeviceMeasureController.INSTANCE.measure(usbSerialPort,
                new UsbMeasureParameter(UsbPortDeviceType.USB_OTHERS,
                        19200, 8, 1, 0), new UsbMeasureListener() {

                    private byte[] buf = new byte[256];
                    private int index = 0;

                    @Override
                    public void measuring(@NotNull UsbSerialPort usbSerialPort, @NotNull byte[] data) {
                        XLog.d(Arrays.toString(data));
                        System.arraycopy(data, 0, buf, index, data.length);             
                            // 換行符
                            if (data[data.length - 1] == (byte) 10) {
                                String response = new String(buf, 0, index + data.length);
                                XLog.d(response);
                                XLog.d(response.length());

                                String info;
                                if (response.startsWith("$BDFKI")) {
                                    BeidouBean.Response bResponse = BeidouUtil.parseResponse(response);

                                    if ("DWA".equals(bResponse.cmdName)) {
                                        if (!bResponse.success) {
                                            //todo
                                        }
                                    } else if ("TXA".equals(bResponse.cmdName)) {
                                        if (bResponse.success) {
                                            //todo
                                        } else {
                                            if (bResponse.waitSecond == 0) {
                                               //todo
                                            } else {
                                               //todo
                                                } 
                                                XLog.w(String.format("還需等待%ss", bResponse.waitSecond));
                                            }
                                        }
                                    }

                                    info = bResponse.toString();
                                } else if (response.startsWith("$GNGGA")) {
                                    String location = BeidouUtil.customLocation(response);
                                    //todo
                                    info = location;
                                }else if(response.startsWith("$BDTXR")){
                                    //下發(fā)
                                    BeidouBean.pushMsg pushMsg = BeidouUtil.parsePushMsg(response);
                                    receiveMsg(pushMsg);
                                    info = pushMsg.toString();
                                } else {
                                    info = response;
                                }
                                XLog.d(info);
                                XLog.d(Arrays.toString(buf));
                                buf = new byte[256];
                                index = 0;
                            } else {
                                index += data.length;
                            }

                        });

                    }

                    @Override
                    public void write(@NotNull UsbSerialPort usbSerialPort) {
                        //允許持續(xù)性寫入數(shù)據(jù)
                        try {
                            usbSerialPort.write(new byte[]{(byte) 0xff, (byte) 0xff}, 1000);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void measureError(@NotNull String message) {
                        XLog.e(message);
                    }
                });

       

以上代碼看起來很長,因為我把解析不同指令的代碼放里面了输硝,簡化版本的核心邏輯就這樣:

DeviceMeasureController.INSTANCE.measure(usbSerialPort,
                new UsbMeasureParameter(UsbPortDeviceType.USB_OTHERS,
                        19200, 8, 1, 0), new UsbMeasureListener() {

                    private byte[] buf = new byte[256];
                    private int index = 0;

                    @Override
                    public void measuring(@NotNull UsbSerialPort usbSerialPort, @NotNull byte[] data) {
                        XLog.d(Arrays.toString(data));
                        System.arraycopy(data, 0, buf, index, data.length);             
                            // 換行符
                            if (data[data.length - 1] == (byte) 10) {
                                String response = new String(buf, 0, index + data.length);
                                //todo
                                buf = new byte[256];
                                index = 0;
                            } else {
                                index += data.length;
                            }

                        });

                    }

                    @Override
                    public void write(@NotNull UsbSerialPort usbSerialPort) {
                        //允許持續(xù)性寫入數(shù)據(jù)
                        try {
                            usbSerialPort.write(new byte[]{(byte) 0xff, (byte) 0xff}, 1000);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void measureError(@NotNull String message) {
                        XLog.e(message);
                    }
                });

       

image.png

以上是我這次使用北斗短報文實現(xiàn)的一個偽IM今瀑。
代碼在此

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市点把,隨后出現(xiàn)的幾起案子橘荠,更是在濱河造成了極大的恐慌,老刑警劉巖郎逃,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哥童,死亡現(xiàn)場離奇詭異,居然都是意外死亡褒翰,警方通過查閱死者的電腦和手機(jī)贮懈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門匀泊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朵你,你說我怎么就攤上這事各聘。” “怎么了抡医?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵躲因,是天一觀的道長。 經(jīng)常有香客問我忌傻,道長毛仪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任芯勘,我火速辦了婚禮,結(jié)果婚禮上腺逛,老公的妹妹穿的比我還像新娘荷愕。我一直安慰自己,他們只是感情好棍矛,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布安疗。 她就那樣靜靜地躺著,像睡著了一般够委。 火紅的嫁衣襯著肌膚如雪荐类。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天茁帽,我揣著相機(jī)與錄音玉罐,去河邊找鬼。 笑死潘拨,一個胖子當(dāng)著我的面吹牛吊输,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铁追,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼季蚂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琅束?” 一聲冷哼從身側(cè)響起扭屁,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涩禀,沒想到半個月后料滥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡埋泵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年幔欧,在試婚紗的時候發(fā)現(xiàn)自己被綠了罪治。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡礁蔗,死狀恐怖觉义,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浴井,我是刑警寧澤晒骇,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站磺浙,受9級特大地震影響洪囤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撕氧,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一瘤缩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伦泥,春花似錦剥啤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至防楷,卻和暖如春牺丙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背复局。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工冲簿, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亿昏。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓民假,卻偏偏與公主長得像,于是被迫代替她去往敵國和親龙优。 傳聞我的和親對象是個殘疾皇子羊异,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350