从事android开发以来,解析过不少数据,都是根据所谓的协议来的。其实,这些协议都是双方约定或者一方约定的数据格式。
1,标准的gga坐标数据解析
例如:$GPGGA,033744,2446.5241,N,12100.1536,E,1,10,0.8,133.4,M,,,,*1F
看看geomap源码是怎么对坐标数据的解析的 NMEA 0183
/*
* OpenNMEA - A Java library for parsing NMEA 0183 data sentences
* Copyright (C)2006 Joey Gannon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.opennmea;
/**
* An object for storing and comparing geographic latitude and longitude
*
* @author Joey Gannon
* @version 1.00, 08/11/06
* @since OpenNMEA v0.1
*/
public class Geocoordinate implements Comparable<Geocoordinate>
{
private int latdeg,londeg,latmin,lonmin;
private double latsec,lonsec,lattot,lontot;
private char mode;
private boolean valid=true;
/**
* Constructs a Geocoordinate from latitude and longitude strings, with cardinal direction
* indicated by separate strings
* <p>
* This constructor defaults to the decimal minutes output mode.
* <p>
* If the strings passed to this constructor are malformed, the values will default to
* <code>{0.0, 0.0}</code>.
* @param lat Latitude, in the form DDmm[.mmmm]
* @param ns North/South ("N" or "S")
* @param lon Longitude, in the form DDDmm[.mmmm]
* @param ew East/West ("E" or "W")
*/
public Geocoordinate(String lat,String ns,String lon,String ew)
{
this(lat.equals("")?0.0:(Integer.parseInt(lat.substring(0,2))+Double.parseDouble(lat.substring(2))/60)*(ns.equals("N")?1:-1),lon.equals("")?0.0:(Integer.parseInt(lon.substring(0,3))+Double.parseDouble(lon.substring(3))/60)*(ew.equals("E")?1:-1));
if((lat.equals(""))||(lon.equals("")))
valid=false;
mode='M';
}
/**
* Constructs a Geocoordinate from floating-point latitude and longitude values, with
* cardinal direction indicated by numerical sign
* <p>
* This constructor defaults to the decimal degrees output mode.
* @param lat Latitude, in decimal degrees (south is negative)
* @param lon Longitude, in decimal degrees (west is negative)
*/
public Geocoordinate(double lat,double lon)
{
while(lat<-90)
lat+=180;
while(lat>90)
lat-=180;
while(lon<=-180)
lat+=360;
while(lon>180)
lat-=360;
lattot=lat;
lontot=lon;
latdeg=(int)lat;
londeg=(int)lon;
latmin=(int)(60*(lat-latdeg));
lonmin=(int)(60*(lon-londeg));
latsec=60*(60*(lat-latdeg)-latmin);
lonsec=60*(60*(lon-londeg)-lonmin);
mode='D';
}
/**
* Sets the output mode to decimal degrees
*/
public void toDegrees()
{
mode='D';
}
/**
* Sets the output mode to degrees and decimal minutes
*/
public void toMinutes()
{
mode='M';
}
/**
* Sets the output mode to degrees, minutes, and decimal seconds
*/
public void toSeconds()
{
mode='S';
}
/**
* Tells where the current mode applies the fractional part of the latitude
* and longitude values
* <p>
* Possible modes are degrees (<code>'D'</code>), minutes (<code>'M'</code>),
* or seconds (<code>'S'</code>).
* @return the current mode
*/
public char getMode()
{
return mode;
}
/**
* Returns the latitude, formatted according to the current mode
* <p>
* If the latitude stored by this Geocoordinate was 12.3456, then
* the array returned for each mode would be:<br>
* Degrees mode: <code>[12.3456]</code><br>
* Minutes mode: <code>[12.0, 20.736]</code><br>
* Seconds mode: <code>[12.0, 20.0, 44.16]</code>
* @return the latitude
* @see Geocoordinate#getLatitudeDegrees()
*/
public double[] getLatitude()
{
double[] array;
if(mode=='D')
{
array=new double[1];
array[0]=lattot;
}
else if(mode=='M')
{
array=new double[2];
array[0]=(double)latdeg;
array[1]=latmin+latsec/60;
}
else
{
array=new double[3];
array[0]=(double)latdeg;
array[1]=(double)latmin;
array[2]=latsec;
}
return array;
}
/**
* Returns the latitude in decimal degrees
* <p>
* This is equivalent to <code>getLatitude()[0]</code> in Degrees mode.
* @return the latitude
* @see Geocoordinate#getLatitude()
*/
public double getLatitudeDegrees()
{
return lattot;
}
/**
* Returns the longitude, formatted according to the current mode
* <p>
* If the longitude stored by this Geocoordinate was 12.3456, then
* the array returned for each mode would be:<br>
* Degrees mode: <code>[12.3456]</code><br>
* Minutes mode: <code>[12.0, 20.736]</code><br>
* Seconds mode: <code>[12.0, 20.0, 44.16]</code>
* @return the longitude
* @see Geocoordinate#getLongitudeDegrees()
*/
public double[] getLongitude()
{
double[] array;
if(mode=='D')
{
array=new double[1];
array[0]=lontot;
}
else if(mode=='M')
{
array=new double[2];
array[0]=(double)londeg;
array[1]=lonmin+lonsec/60;
}
else
{
array=new double[3];
array[0]=(double)londeg;
array[1]=(double)lonmin;
array[2]=lonsec;
}
return array;
}
/**
* Returns the longitude in decimal degrees
* <p>
* This is equivalent to <code>getLongitude()[0]</code> in Degrees mode.
* @return the longitude
* @see Geocoordinate#getLongitude()
*/
public double getLongitudeDegrees()
{
return lontot;
}
/**
* Determines if a coordinate is valid
* <p>
* An invalid coordinate could be generated by the constructor if
* passed invalid strings. This method is helpful in filtering out
* such coordinates.
* @return true if the coordinate is valid, false otherwise
*/
public boolean isValid()
{
return valid;
}
/**
* Returns the distance between two Geocoordinates on Earth, in meters
* <p>
* In order to fulfill the contract of <code>Comparable</code>, this method returns
* a negative distance if <code>this</code> is farther south than <code>g</code>. If
* <code>this</code> and <code>g</code> are at the same latitude, then this method
* returns a negative distance if <code>this</code> is farther west than <code>g</code>.
* @return the distance between two Geocoordinates
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Geocoordinate g)
{
return (int)(Math.toDegrees(Math.acos(Math.sin(Math.toRadians(lattot))*Math.sin(Math.toRadians(g.getLatitudeDegrees()))+Math.cos(Math.toRadians(lattot))*Math.cos(Math.toRadians(g.getLatitudeDegrees()))*Math.cos(Math.toRadians(lontot-g.getLongitudeDegrees()))))*111189.577)*(lattot==g.getLatitudeDegrees()?(lontot>g.getLongitudeDegrees()?1:-1):(lattot>g.getLatitudeDegrees()?1:-1));
}
/**
* Determines if this Geocoordinate is equal to another object
* <p>
* A comparison only makes sense if the object being compared is also a Geocoordinate,
* so this method returns false if the parameter is not an instance of Geocoordinate.
* @return true if the objects are equal, false otherwise
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o)
{
if(!(o instanceof Geocoordinate))
return false;
return ((lattot==((Geocoordinate)o).getLatitudeDegrees())&&(lontot==((Geocoordinate)o).getLongitudeDegrees()));
}
/**
* Returns a string representation of the coordinate
* @return a string representation of the coordinate
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
if(mode=='D')
return (latdeg+latmin/60)+"бу"+' '+(londeg+lonmin/60)+"бу";
else if(mode=='M')
return latdeg+"бу"+Math.abs(latmin)+'\''+' '+londeg+"бу"+Math.abs(lonmin)+'\'';
else
return latdeg+"бу"+Math.abs(latmin)+'\''+Math.abs(latsec)+'\"'+' '+londeg+"бу"+Math.abs(lonmin)+'\''+Math.abs(lonsec)+'\"';
}
}
其中经纬度都是有正负,以及南北半球,东西半球之分的
上面的代码主要就是
纬度解析:lat.equals("")?0.0:(Integer.parseInt(lat.substring(0,2))+Double.parseDouble(lat.substring(2))/60)*(ns.equals("N")?1:-1)
经度解析:lon.equals("")?0.0:(Integer.parseInt(lon.substring(0,3))+Double.parseDouble(lon.substring(3))/60)*(ew.equals("E")?1:-1)
关于其他的mnea 0183经纬度解析可参照:GPS NMEA-0183标准详解(常用的精度以及经纬度坐标)
2,数据解析
数据解析,收到的数据如下
数据分析,前面的006是没什么用的。Ed开始,后面跟着一个空格,或者是回车换行。
1.811后面也是回车换行。
006Ed 0010,0,0.000,0.0,-99998.446,-99998.102,1.811
解出来需要的数据如下:-99998.446
-99998.102
1.811
代码解析:
private void totalStationData(int nLength, byte[] data){
try {
String dyString1 = new String(data, 0, nLength);
if (dyString1.contains("006\r\n")) {
return;
}
strData = strData + dyString1;
if (strData.contains("Ed" )&& strData.substring(strData.length()-2,strData.length()).contains("\r\n")&& strData.length()>15) {
String[] srcData = strData.split("Ed");
if (srcData[1] != null && srcData[1].length() >=20) {
Log.i("Show", strData);
String[] srcStrings = srcData[1].split(",");
if (srcStrings != null && srcStrings.length >= 5) {
if (srcStrings[4].substring(0, 1).equals("-")) {
mdTotalStationCoord[0] = -Double.valueOf(srcStrings[4].substring(1,srcStrings[4].length()));
}else {
mdTotalStationCoord[0] = Double.valueOf(srcStrings[4]);
}
Log.i("Show",String.valueOf(mdTotalStationCoord[0]));
if (srcStrings[5].substring(0, 1).equals("-")) {
mdTotalStationCoord[1] = -Double.valueOf(srcStrings[5].substring(1,srcStrings[5].length()));
}else {
mdTotalStationCoord[1] = Double.valueOf(srcStrings[5]);
}
Log.i("Show", String.valueOf(mdTotalStationCoord[1]));
if (srcStrings[6].substring(0, 1).equals("-")) {
mdTotalStationCoord[2] = -Double.valueOf(srcStrings[6].substring(1,6));
}else {
mdTotalStationCoord[2] = Double.valueOf(srcStrings[6].substring(0, 5));
}
Log.i("Show", String.valueOf(mdTotalStationCoord[2]));
strData = "";
}
}
}
} catch (Exception e) {
strData = "";
}
}
需要注意地方是,数据接收时,是断断续续从缓冲区取出来,不是一次性的。可能间隔就是几百毫秒。
这种时候,需要特别了解每种情况接收到最后的数据时什么样。找出每条完整数据的共性。
抓住主要特点,想到各种情况。
会遇到很多很坑的数据解析
这里只提供一种参考思路。
3,物联网一般数据解析
跟硬件那边传输数据,一般都是十六进制传输的数据,所以一般先解析出来十六进制的字符串后者十六进制的字符数组
收到原始数据:
byte[] buffer = new byte[] {104, 56, 56, 104, 0, 114, 120, 85, 52, 18, 67, 35, 1, 7, 0, 0, 0, 0, 12, 19, 120, 86, 52, 18, 12,
59, 120, 52, 18, 12, 38, 120, 86, 52, 18, 11, 89, 69, 35, 0, 2, -3, 23, 0, 0, 22};
/**
* byte数组转换成16进制字符数组
*
* @param src
* @return
*/
public static String[] bytesToHexStrings(byte[] src) {
if (src == null || src.length <= 0) {
return null;
}
String[] str = new String[src.length];
for (int i = 0; i < src.length; i++) {
str[i] = String.format("%02X", src[i]);
}
return str;
}
然后就是根据不同位数提取有用的数据
//总流量
if (strResult.length != 0) {
if (strResult[19].equalsIgnoreCase("0C") && strResult[20].equalsIgnoreCase("13")) {
String strWater = strResult[24] + strResult[23] + strResult[22] + strResult[21];
double dWater = Double.valueOf(strWater);
mtextViewShow1.setText(String.valueOf(dWater/1000) + "m³");
}
//流速
if (strResult[25].equalsIgnoreCase("0C") && strResult[26].equalsIgnoreCase("3B")) {
String strFlow = strResult[30]+strResult[29]+strResult[28]+strResult[27];
double dFlow = Double.valueOf(strFlow);
mtextViewShow2.setText(String.valueOf(dFlow/1000) + "m³/h");
}
//温度
if (strResult[37].equalsIgnoreCase("0B") && strResult[38].equalsIgnoreCase("59")) {
String strTemp = strResult[41]+strResult[40]+strResult[39];
double dTemp = Double.valueOf(strTemp);
mtextViewShow5.setText(String.format("%.2f", dTemp/100) + "℃");
}
if (strResult[42].equalsIgnoreCase("02")
&& strResult[43].equalsIgnoreCase("FD") && strResult[44].equalsIgnoreCase("17")) {
String hexResult = StringUtil.reverseString(StringUtil.hexStringToBinary(strResult[45]));
if(hexResult == null){
return;
}
String str = hexResult.substring(5, 6);
//0表示管道正常 倒数第三位
if (str.equalsIgnoreCase("0")) {
mtextViewShow3.setText("Normal");
}
//1表示空管;
else if (str.equalsIgnoreCase("1")) {
mtextViewShow3.setText("Empty");
}
// 最后两位
String str1 = hexResult.substring(6, 8);
//01 表示静止
if (str1.equalsIgnoreCase("01") || str1.equalsIgnoreCase("11")) {
mtextViewShow4.setText("Flowing");
}
//00 表示启动
if (str1.equalsIgnoreCase("00")) {
mtextViewShow4.setText("Start");
}
//10,11表示流动
if (str1.equalsIgnoreCase("10")) {
mtextViewShow4.setText("Stagnant");
}
}
}
都是根据约定提取数据啦
最后附上一个常用进制转换工具类
package com.aufw.util;
import java.io.ByteArrayOutputStream;
public class StringUtil {
// 十六进制的字符串转换成byte数组
public static byte[] HexCommandtoByte(byte[] data) {
if (data == null) {
return null;
}
int nLength = data.length;
String strTemString = new String(data, 0, nLength);
String[] strings = strTemString.split(" ");
nLength = strings.length;
data = new byte[nLength];
for (int i = 0; i < nLength; i++) {
if (strings[i].length() != 2) {
data[i] = 00;
continue;
}
try {
data[i] = (byte)Integer.parseInt(strings[i], 16);
} catch (Exception e) {
data[i] = 00;
continue;
}
}
return data;
}
/**
* byte数组转换成16进制字符数组
*
* @param src
* @return
*/
public static String[] bytesToHexStrings(byte[] src) {
if (src == null || src.length <= 0) {
return null;
}
String[] str = new String[src.length];
for (int i = 0; i < src.length; i++) {
str[i] = String.format("%02X", src[i]);
}
return str;
}
/**
* 十六进制字符串装十进制
*
* @param hex
* 十六进制字符串
* @return 十进制数值
*/
public static int hexStringToAlgorism(String hex) {
if (hex == null) {
return 0;
}
hex = hex.toUpperCase();
int max = hex.length();
int result = 0;
for (int i = max; i > 0; i--) {
char c = hex.charAt(i - 1);
int algorism = 0;
if (c >= '0' && c <= '9') {
algorism = c - '0';
} else {
algorism = c - 55;
}
result += Math.pow(16, max - i) * algorism;
}
return result;
}
// 十六进制的字符串转化为String
private static String hexString = "0123456789ABCDEF";
public static String decode(String bytes) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(
bytes.length() / 2);
// 将每2位16进制整数组装成一个字节
for (int i = 0; i < bytes.length(); i += 2)
baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
.indexOf(bytes.charAt(i + 1))));
return new String(baos.toByteArray());
}
/**
* 十六转二进制
*
* @param hex
* 十六进制字符串
* @return 二进制字符串
*/
public static String hexStringToBinary(String hex) {
if (hex == null) {
return null;
}
hex = hex.toUpperCase();
String result = "";
int max = hex.length();
for (int i = 0; i < max; i++) {
char c = hex.charAt(i);
switch (c) {
case '0':
result += "0000";
break;
case '1':
result += "0001";
break;
case '2':
result += "0010";
break;
case '3':
result += "0011";
break;
case '4':
result += "0100";
break;
case '5':
result += "0101";
break;
case '6':
result += "0110";
break;
case '7':
result += "0111";
break;
case '8':
result += "1000";
break;
case '9':
result += "1001";
break;
case 'A':
result += "1010";
break;
case 'B':
result += "1011";
break;
case 'C':
result += "1100";
break;
case 'D':
result += "1101";
break;
case 'E':
result += "1110";
break;
case 'F':
result += "1111";
break;
}
}
return result;
}
/**
* 倒置字符串
*
* @param str
* @return
*/
public static String reverseString(String str)
{
char[] arr=str.toCharArray();
int middle = arr.length>>1;//EQ length/2
int limit = arr.length-1;
for (int i = 0; i < middle; i++) {
char tmp = arr[i];
arr[i]=arr[limit-i];
arr[limit-i]=tmp;
}
return new String(arr);
}
}
关于物联网的解析数据可查看这个
android byte字节数组转换十六进制字符串(物联网开发总结)