从事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字节数组转换十六进制字符串(物联网开发总结)