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でペアリングされたデバイス を作成する. Ntripプロトコルは差分データを取得する主な業務論理コードは以下の である.
NetWorkServiceのソースコードは次のとおりです.
UtilNtripソースコードは次のとおりです.
トラックを描画して保存し、データフォーマットを再カプセル化して再送
NMEA 0831フォーマットのデータを取得した後、APPのこの機能ブロックは主に2つの仕事をした.差分データはBluetoothSocketに書き込まれているため、高精度NMEA 0831形式のデータを自動的に計算して取得し、BluetoothSocketを介してデータを自動的に転送するため、BluetoothSocketのデータを読み取るだけで高精度情報を取得することができる.
Bluetoothデータのプライマリコードを読み込む
データを取得した後、緯度情報を抽出してArcgis for Androidのインタフェースを呼び出してモーショントラックを描画し、トラックを保存するときにインタフェースを呼び出してトラックパターンをJson形式に変換してローカルまたはデータベースに保存します.
drowRouteのプライマリコード
保存パスは簡単にJson形式に変換してローカルに保存しました
データの再カプセル化と再送
この部分では主にNMEA 0831を受信したフォーマットデータを分割して統合し,TCP/IPを用いて再送する.この機能ブロックはGGAとGSTプロトコル形式のデータのみをフィルタリングし,主にStringBuilderを用いて文字列のいくつかの操作を行う.再送データはSocketChannelを用いて通信チャネルを確立し、接続の確立、再接続メカニズム、サーバが閉じているかどうかを判断し、情報を送信したり、データを閉じたりするなど、よく使われるネットワークプログラミング技術を用いる.
まとめ
同機能ブロックは、携帯電話やタブレットに搭載されたGPSを使って軌跡を描いたもので、確かに差が大きいことがわかり、精度が高く2センチ程度の誤差しかない.この機能ブロックは主にネットワークプログラミング技術を用いており、通信機能が多く、スレッドも多く、現在通信のライブラリが多いため、自身の基礎的な知識点も完全に把握されていないため、Arcgis for Androidは絶えず学ぶ必要がある.
Ntripプロトコル差分サーバから差分データを取得
Ntripプロトコル(HTTPベースのアプリケーション層RTCMネットワーク伝送プロトコル)は、実際にはTCP/IPプロトコル上でカプセル化されており、依然としてSocketを使用してデータ通信が行われており、プロジェクトでは取得した差分データを直接デバイスのBluetoothSocketにカプセル化しているので、データを取得する前にBluetooth接続デバイスを開始し、BluetoothSocketを確立する必要がある.次に、プロシージャと一部のコードについて簡単に説明します.
BluetoothAdapter.getDefaultAdapter().getBondedDevices();
を検索して接続デバイスをクリックし、BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
btSocket.connect();
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は絶えず学ぶ必要がある.