关于GPS的NMEA0831协议的解析

来源:互联网 发布:2015 网络事件 编辑:程序博客网 时间:2024/05/17 04:08

一、NMEA-0183概述

NMEA 0183是美国国家海洋电子协会(National Marine Electronics Association )为海用电子设备制定的标准格式。目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。
序号
命令
说明
最大帧长
1
$GPGGA
全球定位数据
72
2
$GPGSA
卫星PRN数据
65
3
$GPGSV
卫星状态信息
210
4
$GPRMC
运输定位数据
70
5
$GPVTG
地面速度信息
34
6
$GPGLL
大地坐标信息
7
$GPZDA
UTC时间和日期
注:发送次序$GPZDA、$GPGGA、$GPGLL、$GPVTG、$GPGSA、$GPGSV*3、$GPRMC
协议帧总说明:
该协议采用ASCII码,其串行通信默认参数为:波特率=4800bps,数据位=8bit,开始位=1bit,停止位=1bit,无奇偶校验
帧格式形如:$aaccc,ddd,ddd,…,ddd*hh<CR><LF>
1、“$”——帧命令起始位
2、aaccc——地址域,前两位为识别符,后三位为语句名
3、ddd…ddd——数据
4、“*”——校验和前缀
5、hh——校验和(check sum),$与*之间所有字符ASCII码的校验和(各字节做异或运算,得到校验和后,再转换16进制格式的ASCII字符。)
6、<CR><LF>——CR(Carriage Return) + LF(Line Feed)帧结束,回车和换行
GPGGA
GPS固定数据输出语句,这是一帧GPS定位的主要数据,也是使用最广的数据。
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,<14>*<15><CR><LF>
<1> UTC时间,格式为hhmmss.sss。
<2> 纬度,格式为ddmm.mmmm(前导位数不足则补0)。
<3> 纬度半球,N或S(北纬或南纬)。
<4> 经度,格式为dddmm.mmmm(前导位数不足则补0)。
<5> 经度半球,E或W(东经或西经)。
<6> 定位质量指示,0=定位无效,1=定位有效。
<7> 使用卫星数量,从00到12(前导位数不足则补0)。
<8> 水平精确度,0.5到99.9。
<9> 天线离海平面的高度,-9999.9到9999.9米
<10> 高度单位,M表示单位米。
<11> 大地椭球面相对海平面的高度(-999.9到9999.9)。
<12> 高度单位,M表示单位米。
<13> 差分GPS数据期限(RTCM SC-104),最后设立RTCM传送的秒数量。
<14> 差分参考基站标号,从0000到1023(前导位数不足则补0)。
<15> 校验和。
二、Java代码解析
     其实在外国的网站上面你是可以搜到冠以GPS所有信息的解析的,但是我在实际应用中只是解析GPGGA就可以了,所
     以这里只是对GPGGA进行解析
    注解:newstr是包含ConstVal.HEX_YHZL_GPSQ_INDEX的字符串,而String HEX_YHZL_GPSQ_INDEX = "24 47 50   
    47 47 41";

int startIndex = newstr
        .indexOf(ConstVal.HEX_YHZL_GPSQ_INDEX);
      int endIndex = startIndex + 14;
      // 截取标志字符串“24 47 50 47 47 41”以后的字符串
      String strnew = newstr.substring(startIndex + 3);
      // 1、得到所有的十六进制
      String[] messageArray = strnew.split(" ");
      StringBuilder sb = new StringBuilder();
      int a;
      int flagChar = 0x00;
      int j = 0;
      String f = "";
      String[] b = new String[messageArray.length];
      for (int i = 0; i < messageArray.length; i++) {
       a = Integer.parseInt(messageArray[i], 16);
       sb.append(((char) a));
       // 当遇到“2a”的时候,就
       if (!"2a".equalsIgnoreCase(messageArray[i])
         && (flagChar == 0x00 || flagChar == 0x01 || flagChar == 0x02)) {

        if (flagChar == 0x02) {
         f += ((char) a + "");
         b[j++] = f;
         b = Arrays.copyOf(b, j);
         break;
        }
        if (flagChar == 0x01) {
         f += ((char) a + "");
         flagChar = 0x02;
        }
        if (flagChar == 0x00) {
         b[j++] = messageArray[i];
        }
       }
       if ("2a".equalsIgnoreCase(messageArray[i])) {
        flagChar = 0x01;
       }

      }
      // $GPGGA,100252.000,4000.0137,N,11622.6537,E,1,8,1.50,84.0,M,-6.3,M,,*70
      // $GPGGA,111723.000,3959.9903,N,11622.6575,E,2,9,0.98,90.9,M,-6.3,M,0000,0000*7A\r\n
      // 开始校验
      String c = sb.toString();
      // 1、如果没有*,校验失败
      int index1 = c.indexOf("*");
      if (index1 == -1) {
       return;
      }
      // 2、没有校验和的位数,校验失败
      try {
       c.substring(0, index1 + 3);
      } catch (IndexOutOfBoundsException e1) {
       return;
      }
      // 3、进行校验和校验
      if (!BDUtils.checkSum(b)) {
       send_OTHER_CMD(ConstVal.HEX_YHZL_GPSQ);
       return;
      }
      // 如果有,就截取之
      String g = c.substring(0, index1 + 1);
      String[] strArray = g.split(",");
      if ("0".equals(strArray[6]) || "3".equals(strArray[6])) {
       // 当得到的信息无效的时候,重新发送GPS定位信息
       send_OTHER_CMD(ConstVal.HEX_YHZL_GPSQ);
       return;
      }

      if ("N".equals(strArray[3])) {
       // 如果是北纬的话,在解析的时候,不用加负号
       double temp0 = Double.parseDouble(strArray[2]) / 100;
       String[] temp1 = (temp0 + "").split("\\.");
       double temp2 = Double.parseDouble("0." + temp1[1]) * 100;
       DecimalFormat format = new DecimalFormat();
       format.applyPattern("0.0000");
       String temp3 = format.format(temp2);
       double temp4 = Double.parseDouble(temp3) / 60;
       latitude = Double.parseDouble(temp1[0]) + temp4;

      } else if ("S".equals(strArray[3])) {
       // 如果是南纬的话,在解析纬度的时候,要加上负号
       double temp0 = Double.parseDouble(strArray[2]) / 100;
       String[] temp1 = (temp0 + "").split("\\.");
       double temp2 = Double.parseDouble("0." + temp1[1]) * 100 / 60;
       latitude = (-1)
         * (Double.parseDouble(temp1[0]) + temp2);
      }
      if ("E".equals(strArray[5])) {
       // 如果是东经的话,就不用加负号
       double temp0 = Double.parseDouble(strArray[4]) / 100;
       String[] temp1 = (temp0 + "").split("\\.");
       double temp2 = Double.parseDouble("0." + temp1[1]) * 100;
       DecimalFormat format = new DecimalFormat();
       format.applyPattern("0.0000");
       String temp3 = format.format(temp2);
       double temp4 = Double.parseDouble(temp3) / 60;
       longitude = Double.parseDouble(temp1[0]) + temp4;

      } else if ("W".equals(strArray[5])) {
       // 如果是西经的话,就需要加上负号
       double temp0 = Double.parseDouble(strArray[4]) / 100;
       String[] temp1 = (temp0 + "").split("\\.");
       double temp2 = Double.parseDouble("0." + temp1[1]) * 100 / 60;
       longitude = (-1)
         * (Double.parseDouble(temp1[0]) + temp2);
       // longitude = (-1)
       // * (Double.parseDouble(strArray[4]) / 100);
      }

    下面是校验的时候需要的方法:
  

public static boolean checkSum(String[] strArray) {
  boolean result = false;
  String content = "";
  String CRC = strArray[(strArray.length - 1)];
  // 校验和数据
  for (int i = 0; i < strArray.length - 1; i++) {
   content += strArray[i];
  }
  String strSum = doCheckSum(content);
  if ((strSum.toUpperCase()).equals(CRC.toUpperCase()))
   result = true;
  return result;
 }

 /**
  * 计算校验和
  */
 public static String doCheckSum(String msg) {
  // String msg = "2458545A4A000D0679DC0005";
  byte cmdBytes[] = null;
  int i;
  byte bt;
  try {
   cmdBytes = hexToByte(msg);
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  for (bt = cmdBytes[0], i = 1; i < cmdBytes.length; i++) {
   bt ^= cmdBytes[i];
  }

  String hex = Integer.toHexString(bt & 0xFF);
  if (hex.length() == 1) {
   hex = '0' + hex;
  }

  return hex;
 }

 

public static byte[] hexToByte(String s) throws Exception {
  if ("0x".equals(s.substring(0, 2))) {
   s = s.substring(2);
  }
  Log.d("BDUtils==hexToByte==", s);
  byte[] baKeyword = new byte[s.length() / 2];
  for (int i = 0; i < baKeyword.length; i++) {
   try {
    baKeyword[i] = (byte) (0xff & Integer.parseInt(
      s.substring(i * 2, i * 2 + 2), 16));
   } catch (Exception e) {
    e.printStackTrace();
    throw e;
   }
  }

  return baKeyword;
 }