AndroidはNTRIPプロトコルで差分データを取得し高精度な位置決めを実現

19517 ワード

プロジェクトの背景
  • 最近Androidを作るアプリプロジェクトには、Ntripプロトコルで差分サーバから差分データを取得し、差分データをBluetoothで高精度ハンドヘルドデバイス(華信TR 502受信機)に転送した後、固定解の高精度測位データ(NMEA 0813プロトコルデータ)に戻り、位置情報を解いた後、アプリマップに動き軌跡を表示して描き、動き軌跡を携帯電話に保存する機能があります.最後に取得したデータ(GGAとGST形式のデータ)をカプセル化して自分の情報を追加し、リアルタイムでサーバに返信します.
  • 現在、プロセスのいくつかのコード技術をまとめ、
  • を記憶し、参照するのに便利である.
    Ntripプロトコル差分サーバから差分データを取得
    Ntripプロトコル(HTTPベースのアプリケーション層RTCMネットワーク伝送プロトコル)は、実際にはTCP/IPプロトコル上でカプセル化されており、依然としてSocketを使用してデータ通信が行われており、プロジェクトでは取得した差分データを直接デバイスのBluetoothSocketにカプセル化しているので、データを取得する前にBluetooth接続デバイスを開始し、BluetoothSocketを確立する必要がある.次に、プロシージャと一部のコードについて簡単に説明します.
  • デバイスBluetoothを接続し、Bluetoothデータチャネルを確立する現在、携帯電話またはタブレット自体の機能を使用してデバイスBluetoothと初めてペアリングされているため、AppでペアリングされたデバイスBluetoothAdapter.getDefaultAdapter().getBondedDevices();を検索して接続デバイスをクリックし、BluetoothSocket
  • を作成する.
    btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
    btSocket.connect();
    
  • Ntripプロトコルは差分データを取得する主な業務論理コードは以下の
  • である.
    NetWorkServiceNtrip netWorkService=new NetWorkServiceNtrip (
    this,
    ip,
    port,
    account,
    pwd,
    mountedId,
    btSocket
    );
    netWorkService.getDifferentialData();
    

    NetWorkServiceのソースコードは次のとおりです.
    public class NetWorkServiceNtrip {    
    private static final String CMD_HEAD = "$FCMDB,";    
    private static final String CMD_END = ",*FF\r
    "; // IP private String mIP; // private String mPort; // private String mUserID; // private String mPwd; // private String mMountedpoint; // Socket private Socket mSocket; //Android Socket private BluetoothSocket bluetoothSocket; // Socket private DataOutputStream dos; // private OutputStream btDos; // Socket private DataInputStream dis; // private NetWorkServiceNtrip.UpdateSourceTableThread mUpdateSourceTableThread; private NetWorkServiceNtrip.ReportGGA2Service mReportGGA2Service = null; // private NetWorkServiceNtrip.AcquireDataThread mAcquireDataThread; // private ArrayList mountedPoints = null; private String feedBackState = null; // public String getFeedBackState() { return this.feedBackState; } public ArrayList getMountedPoints() { return this.mountedPoints; } private Context mContext; // public NetWorkServiceNtrip(Context context,String ipAddress, String port, String userID, String password, String mountedPoint, BluetoothSocket bluetoothSocket) { this.mContext=context; this.mIP = ipAddress; this.mPort = port; this.mUserID = userID; this.mPwd = password; this.mMountedpoint = mountedPoint; this.bluetoothSocket=bluetoothSocket; } // public synchronized void connect2Server() { if(this.mUpdateSourceTableThread != null) { this.mUpdateSourceTableThread.release(); this.mUpdateSourceTableThread = null; } this.mUpdateSourceTableThread = new NetWorkServiceNtrip.UpdateSourceTableThread((NetWorkServiceNtrip.UpdateSourceTableThread)null); this.mUpdateSourceTableThread.start(); } // public synchronized void getDifferentialData() { if(this.mAcquireDataThread != null) { this.mAcquireDataThread.cancle(); this.mAcquireDataThread = null; } this.mAcquireDataThread = new NetWorkServiceNtrip.AcquireDataThread((NetWorkServiceNtrip.AcquireDataThread)null); this.mAcquireDataThread.start(); } // private void getCorsServiceSocket(String ip, String port) { try { if(this.mSocket == null) { InetAddress e = Inet4Address.getByName(ip); this.mSocket = new Socket(e, Integer.parseInt(port)); } if(this.dos == null) { this.dos = new DataOutputStream(this.mSocket.getOutputStream()); } if(this.dis == null) { this.dis = new DataInputStream(this.mSocket.getInputStream()); } if(this.bluetoothSocket != null) { this.btDos = this.bluetoothSocket.getOutputStream(); } Log.d("getCorsServiceSocket","Successful"); } catch (UnknownHostException var4) { var4.printStackTrace(); } catch (NumberFormatException var5) { var5.printStackTrace(); } catch (IOException var6) { var6.printStackTrace(); } } // private class AcquireDataThread extends Thread { private boolean _run; private byte[] buffer; private AcquireDataThread(AcquireDataThread acquireDataThread) { this._run = true; this.buffer = new byte[256]; } public void run() { if(NetWorkServiceNtrip.this.mSocket != null) { NetWorkServiceNtrip.this.mSocket = null; } try { NetWorkServiceNtrip.this.getCorsServiceSocket(NetWorkServiceNtrip.this.mIP, NetWorkServiceNtrip.this.mPort); if(NetWorkServiceNtrip.this.dos!=null){ // Ntrip NetWorkServiceNtrip.this.dos.write(UtilNtrip.CreateHttpRequsets(NetWorkServiceNtrip.this.mMountedpoint,NetWorkServiceNtrip.this.mUserID,NetWorkServiceNtrip.this.mPwd).getBytes()); } boolean e = true; while(this._run) { if(NetUtils.isConnected(mContext)){ int e1 = NetWorkServiceNtrip.this.dis.read(this.buffer, 0, this.buffer.length); // SharePreference UserPreferences.getInstance(mContext).setChaFenDataSize(e1); if(e1 >= 1) { String e1x = new String(this.buffer); if(e1x.startsWith("ICY 200 OK")) { if(NetWorkServiceNtrip.this.mReportGGA2Service == null) { NetWorkServiceNtrip.this.mReportGGA2Service = NetWorkServiceNtrip.this.new ReportGGA2Service(NetWorkServiceNtrip.this.dos, (NetWorkServiceNtrip.ReportGGA2Service)null); NetWorkServiceNtrip.this.mReportGGA2Service.start(); } NetWorkServiceNtrip.this.feedBackState = "ICY 200 OK"; } else if(e1x.contains("401 Unauthorized")) { NetWorkServiceNtrip.this.feedBackState = "401 UNAUTHORIZED"; } else { NetWorkServiceNtrip.this.feedBackState = "SUCCESSFUL"; if(NetWorkServiceNtrip.this.btDos != null) { // BluetoothSocket String head = "$FCMDB," + String.valueOf(e1 + 17) + ","; NetWorkServiceNtrip.this.btDos.write(head.getBytes()); NetWorkServiceNtrip.this.btDos.write(this.buffer, 0, e1); Log.d("buffer",UtilNtrip.bytesToHexString(this.buffer)); NetWorkServiceNtrip.this.btDos.write(",*FF\r
    ".getBytes()); } } } } } } catch (UnknownHostException var5) { var5.printStackTrace(); } catch (IOException var6) { var6.printStackTrace(); try { NetWorkServiceNtrip.this.dos.close(); NetWorkServiceNtrip.this.dis.close(); } catch (IOException var4) { var4.printStackTrace(); } } } public void cancle() { try { this._run = false; NetWorkServiceNtrip.this.mSocket.close(); } catch (IOException var2) { var2.printStackTrace(); } } } private class ReportGGA2Service extends Thread { private DataOutputStream dos; private boolean _run; private ReportGGA2Service(DataOutputStream dos, ReportGGA2Service reportGGA2Service) { this.dos = null; this._run = false; this.dos = dos; } public void run() { while(!this._run) { try { this.dos.write(Praser.getGGAMsg().getBytes()); Thread.sleep(180000L); } catch (Exception var2) { this.Cancle(); } } } public void Cancle() { try { this._run = true; } catch (Exception var2) { } } } private class UpdateSourceTableThread extends Thread { private UpdateSourceTableThread(UpdateSourceTableThread updateSourceTableThread) { } public void run() { try { NetWorkServiceNtrip.this.getCorsServiceSocket(NetWorkServiceNtrip.this.mIP, NetWorkServiceNtrip.this.mPort); if(NetWorkServiceNtrip.this.dos == null) { NetWorkServiceNtrip.this.mSocket.setSoTimeout(5000); NetWorkServiceNtrip.this.dos = (DataOutputStream)NetWorkServiceNtrip.this.mSocket.getOutputStream(); } NetWorkServiceNtrip.this.dos.write(Util.Request2NtripServer().getBytes()); byte[] e = new byte[1024]; StringBuilder sb = new StringBuilder(); boolean len = true; String sourceString; int var14; while((var14 = NetWorkServiceNtrip.this.dis.read(e, 0, e.length)) != -1) { sourceString = new String(e, 0, var14); sb.append(sourceString); } sourceString = sb.toString(); if(sourceString.startsWith("SOURCETABLE 200 OK")) { ArrayList mountPoints = new ArrayList(); String[] linStrings = sourceString.split("\r
    "); String[] var10 = linStrings; int var9 = linStrings.length; for(int var8 = 0; var8 < var9; ++var8) { String line = var10[var8]; if(line.startsWith("STR")) { String[] dataStrings = line.trim().split(";"); mountPoints.add(dataStrings[1]); } } NetWorkServiceNtrip.this.mountedPoints = mountPoints; } this.release(); NetWorkServiceNtrip.this.mUpdateSourceTableThread = null; } catch (UnknownHostException var12) { var12.printStackTrace(); } catch (IOException var13) { var13.printStackTrace(); } } private void release() { try { if(NetWorkServiceNtrip.this.dos != null) { NetWorkServiceNtrip.this.dos.close(); } if(NetWorkServiceNtrip.this.dis != null) { NetWorkServiceNtrip.this.dis.close(); } if(NetWorkServiceNtrip.this.mSocket != null) { NetWorkServiceNtrip.this.mSocket.close(); } } catch (IOException var2) { var2.printStackTrace(); } } } }

    UtilNtripソースコードは次のとおりです.
    public class UtilNtrip {    
        public static String CreateHttpRequsets(String mountPoint, String userId, String password) {        
            String msg = "GET /" + mountPoint + " HTTP/1.0\r
    "; msg = msg + "User-Agent: NTRIP GNSSInternetRadio/1.4.11\r
    "; msg = msg + "Accept: */*\r
    "; msg = msg + "Connection: close\r
    "; String tempString = userId + ":" + password; byte[] buf = tempString.getBytes(); String code = Base64.encodeToString(buf, 2); msg = msg + "Authorization: Basic " + code + "\r
    "; msg = msg + "\r
    "; return msg; } public static final String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); } return sb.toString(); } }

    トラックを描画して保存し、データフォーマットを再カプセル化して再送
    NMEA 0831フォーマットのデータを取得した後、APPのこの機能ブロックは主に2つの仕事をした.差分データはBluetoothSocketに書き込まれているため、高精度NMEA 0831形式のデータを自動的に計算して取得し、BluetoothSocketを介してデータを自動的に転送するため、BluetoothSocketのデータを読み取るだけで高精度情報を取得することができる.
    Bluetoothデータのプライマリコードを読み込む
    try{
        inputStream = btSocket.getInputStream();
        if (inputStream != null) {
            BufferedReader reader = new BufferedReader(new   InputStreamReader(inputStream, "ASCII"));
            while ((line = reader.readLine()) != null) {
                ...
                ...
                //    
                drowRoute(double lng, double lat);
                ...
                //       
                sendTCPData(GGAUtils.cutString(msg, GROUP, DEVICE));
                ...
                ...
            }
    }catch(IOException e){
        e.printStackTrace();
    }
    

    データを取得した後、緯度情報を抽出してArcgis for Androidのインタフェースを呼び出してモーショントラックを描画し、トラックを保存するときにインタフェースを呼び出してトラックパターンをJson形式に変換してローカルまたはデータベースに保存します.
    drowRouteのプライマリコード
    private void drawRoute(double lng, double lat) {  
        //       ,              
        locationLayer.removeAll();    
        //        
        if (isFristLoaction && lat != 0) {        
            isFristLoaction = !isFristLoaction;    
            //  Arcgis  
            lastPoint = new Point(lng, lat);    
            //  Arcgis    
            poly = new Polyline();        
            //  Aicgis  
            polyGraphic = new Graphic(poly, sls);  
            //                 
            poly.startPath(lastPoint);    
        } else {        
            Point wsgpoint = new Point(lng, lat);  
            //            
            Point mapPoint = (Point) GeometryEngine.project(wsgpoint, SpatialReference.create(4326), mapView.getSpatialReference());        
            if (MDistance(lastPoint.getX(), lastPoint.getY(), mapPoint.getX(), mapPoint.getY()) >= 0.05 && MDistance(lastPoint.getX(), lastPoint.getY(), mapPoint.getX(), mapPoint.getY()) <= 20) {            
                pathGraphlayer.removeAll();  
                //                
                poly.lineTo(mapPoint);            
                lastPoint = mapPoint; 
                //                      
                pathGraphlayer.addGraphic(polyGraphic);    
                //                    
                if (polyGraphic != null) {                
                    pathBtn.post(new Runnable() {                    
                    @Override                    
                    public void run() {                        
                        pathBtn.setVisibility(View.VISIBLE);                    
                    }                
                });            
            } 
            //          BUG,GraphicsLayer addGraphic      ,                       
            if (pathGraphlayer.getGraphicIDs().length > 8800) {               
                pathGraphlayer.removeAll();            
            }        
        }        
        locagraphic = new Graphic(mapPoint, locationMS);            
        locationTS = new TextSymbol(15, "latitude:" + lat + "
    " + "longitude:" + lng, Color.BLACK); Graphic locaTSgra = new Graphic(mapPoint, locationTS); locationLayer.addGraphic(locaTSgra); locationLayer.addGraphic(locagraphic); } }

    保存パスは簡単にJson形式に変換してローカルに保存しました
    FileUtils.writeStrToFile(
    new Date().getTime() + "",
    GeometryEngine.geometryToJson(mapView.getSpatialReference(), polyGraphic.getGeometry()),
    fileName);
    

    データの再カプセル化と再送
    この部分では主にNMEA 0831を受信したフォーマットデータを分割して統合し,TCP/IPを用いて再送する.この機能ブロックはGGAとGSTプロトコル形式のデータのみをフィルタリングし,主にStringBuilderを用いて文字列のいくつかの操作を行う.再送データはSocketChannelを用いて通信チャネルを確立し、接続の確立、再接続メカニズム、サーバが閉じているかどうかを判断し、情報を送信したり、データを閉じたりするなど、よく使われるネットワークプログラミング技術を用いる.
    まとめ
    同機能ブロックは、携帯電話やタブレットに搭載されたGPSを使って軌跡を描いたもので、確かに差が大きいことがわかり、精度が高く2センチ程度の誤差しかない.この機能ブロックは主にネットワークプログラミング技術を用いており、通信機能が多く、スレッドも多く、現在通信のライブラリが多いため、自身の基礎的な知識点も完全に把握されていないため、Arcgis for Androidは絶えず学ぶ必要がある.