https://www.jianshu.com/p/7b93952febc0

项目背景

  • 最近在做一个Android的APP项目中有个功能,需要用到Ntrip协议从差分服务器获取差分数据,并将差分数据通过蓝牙传送至高精度手持设备(华信TR502接收机)之后返回固定解的高精度定位数据(NMEA0813协议数据),解出位置信息后在APP地图上显示并描绘运动轨迹并将运动轨迹保存至手机,最后将获取的数据(GGA和GST格式的数据)重新封装添加自己的信息后实时回传至服务器。
  • 现将过程中一些代码技术做出总结,方便之后记忆和查阅

Ntrip协议从差分服务器获取差分数据

Ntrip协议(基于HTTP的应用层RTCM网络传输的协议)实际是在TCP/IP协议上进行封装的,依然使用Socket进行数据通信,项目中我们直接将获取到的差分数据封装进了设备的BluetoothSocket因此在获取数据前需要先开始蓝牙连接设备并建立BluetoothSocket。接下来简述过程和部分代码。

  1. 连接设备蓝牙并建立蓝牙数据通道
    目前使用手机或平板本身的功能与设备蓝牙初次配对,因此在App里先进行搜索已配对的设备
    BluetoothAdapter.getDefaultAdapter().getBondedDevices();
    点击连接设备并创建BluetoothSocket
btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
btSocket.connect();
  1. 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\n";
//差分服务器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<String> mountedPoints = null;
private String feedBackState = null;
//获取连接状态
public String getFeedBackState() { return this.feedBackState; }
public ArrayList<String> 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\n".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\n");                    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\n";        msg = msg + "User-Agent: NTRIP GNSSInternetRadio/1.4.11\r\n";        msg = msg + "Accept: */*\r\n";        msg = msg + "Connection: close\r\n";        String tempString = userId + ":" + password;        byte[] buf = tempString.getBytes();        String code = Base64.encodeToString(buf, 2);        msg = msg + "Authorization: Basic " + code + "\r\n";        msg = msg + "\r\n";        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();    }
}

绘制并保存轨迹、重新封装数据格式后回传

获取到NMEA0831格式数据后,APP的该功能块主要做了两个工作一是将轨迹绘制在Shape底图上,二是将数据拆分并添加上自己的字段信息后重新封装后将数据回传至远程服务器端,该功能块采用TCP/IP协议进行回传,绘制运动轨迹我们采用Arcgis for Android的引擎。由于差分数据已经被写入了BluetoothSocket中传入手持高精度设备中(华信TR502接收机),该设备会自动计算并获取高精度NMEA0831格式数据,设备自动通过BluetoothSocket传输数据,因此只需要通过读取BluetoothSocket中的数据就可获取高精度信息。

读取蓝牙数据主要代码
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 + "\n" + "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);
重新封装并回传数据

这一部分主要是对收到NMEA0831格式数据进行拆分合并,使用TCP/IP进行回传。该功能块只筛选出了GGA和GST协议格式的数据,主要就是使用的是StringBuilder进行字符串的一些操作。
回传数据使用SocketChannel建立通信信道,连接的建立、重连机制、判断服务器是否关闭、发送信息或者数据、关闭连接等使用常用的网络编程技术。

总结

该功能块还使用了手机或平板自带的GPS进行轨迹绘制,发现确实差距比较大,这种方式精度很高只有2厘米左右误差,底图也是专业的高精度测绘仪器测量出来并做成Shape地图,作为Android开发者第一次接触这种设备,学到了很多。该功能块主要使用网络编程技术,通信功能比较多,线程也比较多,现在通信的库比较多导致自身的一些基础的知识点也没有完全掌握,Arcgis for Android 还需要不断学习。

作者:一袋小乞丐
链接:https://www.jianshu.com/p/7b93952febc0
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android通过NTRIP协议获取差分数据实现高精度定位相关推荐

  1. 使用Ntrip协议连接CORS服务器获取差分数据-Java

    文中提供的NtripClient类,支持CORS服务器的连接.断开.获取挂载点列表. 调用方法 连接服务器 NtripClient ntripClient = new NtripClient(); n ...

  2. 一个简单的Android客户端从服务器端获取json数据并解析的实现代码

    今天总结一下android客户端从服务器端获取json数据的实现代码,需要的朋友可以参考下 首先客户端从服务器端获取json数据 1.利用HttpUrlConnection 复制代码代码如下: /** ...

  3. android客户端从服务器端获取json数据并解析的实现代码

    2019独角兽企业重金招聘Python工程师标准>>> 首先客户端从服务器端获取json数据 1.利用HttpUrlConnection 代码如下: /** * 从指定的URL中获取 ...

  4. android自定义Dcloud插件,调用android原生界面并获取返回数据

    由于工作的需要,所以我接触到了Dcloud这个我不是很愿意接触的东西.Dcloud也是Webapp的一个工具,同样也可以用来做原生android的插件.比较一下Dcloud和Cordova的优缺点: ...

  5. Android 使用Get请求获取网络数据(极速数据)

    1.环境配置 android studio 2.3.3 java 16.0.1 1.1.配置Json库 需要准备的jar包:json-lib-2.4-kdj15.jar 第一步:拷贝需要用到的jar包 ...

  6. Android HTTP网络请求获取JSON数据

  7. Ntrip协议访问千寻位置服务

    Ntrip协议获取千寻位置差分数据步骤: 1.Ntrip Client使用TCP方式连接Ntrip Caster(Ntrip Caster当前即指千寻位置服务) ①地址 域名:rtk.ntrip.qx ...

  8. 什么是Ntrip?Ntrip协议简介

    文章目录 Ntrip通讯协议1.0 Ntrip是什么? Ntrip系统组成 NtripServer NtripClient 4.1 获取源列表 4.2 获取差分数据 其他资料 Ntrip通讯协议1.0 ...

  9. androidclient和站点数据交互的实现(基于Http协议获取数据方法)

    androidclient一般不直接訪问站点数据库,而是像浏览器一样发送get或者post请求.然后站点返回client能理解的数据格式,client解析这些数据.显示在界面上.经常使用的数据格式是x ...

最新文章

  1. ​哪些开发问题最让程序员“头秃”?我们分析了Stack Overflow的11000个问题
  2. 【c语言】输入输出格式练习
  3. 线性时不变系统以及响应的分类
  4. [译] 在 iOS 上使用 Carthage 建立依赖
  5. 机器学习者应知的五大深度学习框架
  6. 如何使用纯 CSS 创建翻牌动画
  7. 老笔记整理四:字符串的完美度
  8. 领诵员冯琳最新消息!保研复旦、主持卫视跨年晚会
  9. springboot跨域解决方案
  10. 为什么vacuum后表还是继续膨胀?
  11. 毫秒间完成行为分析,飞猪端智能技术实践!
  12. 未来的程序员都将在浏览器中编码!
  13. NSString 和 NSMutableString
  14. 机器学习代码实战——保存和加载模型(Save and Load Model)
  15. ZOJ 3992 2017CCPC秦皇岛 L:One-Dimensional Maze
  16. SpringBoot 下 Mybatis 的缓存
  17. tomcat闪退没有报错_越狱后直接换sileo商店附Sileo的部分报错解决办法
  18. Golang go mod 使用
  19. 音频信号处理——基音周期
  20. Ceph Calamari Server RPM编译

热门文章

  1. 因子分析 factor analysis (七) :因子分析法与主成分分析的异同
  2. pk+uk+fk+index
  3. Python自动打码,DdddOcr通用验证码自动识别库
  4. Spring源码分析之TDDL
  5. WORDPRESS QQ扫码登录插件
  6. Flutter PopupMenuButton
  7. 时间都去哪了--你统计过你每天花在手机上的时间么?
  8. 可汗学院”(Khan Academy)提出的考验智商的谜题
  9. 软件需求工程与uml建模——高速公路智能服务区
  10. hexo 修改index.html,hexo--新建页面、修改主页