美文网首页
Android解析GPS卫星的NMEA数据

Android解析GPS卫星的NMEA数据

作者: 浪里_个郎 | 来源:发表于2020-03-12 21:08 被阅读0次

    关于NMEA

    GPS模块会上报NMEA协议的字符串,NMEA也分很多类数据,里面包含时间、卫星信息、经纬度等。

    Android framework如何使用NMEA

    可以参考我的上一篇文章: “android GPS框架” 

    总的来说,正常情况下,framework层不参与NMEA数据解析,解析的工作由HAL层完成

    但如果你想要外挂GPS模块,或者伪造一些NMEA数据,都可以让在framework自己进行解析,供原生Location相关API使用。具体要在GnssLocationProvider类中拿到NMEA或NMEA解析后的数据,然后调用该类的reportSvStatus注入卫星信息,调用reportLocation注入定位信息。

    使用哪类NMEA数据

    我自己使用ZDA数据获取时间;使用GSV数据获取卫星信息;使用GGA数据获取定位经纬度信息

    解析NMEA数据的坑

    1,导航信息回调流程中(GnssLocationProvider.reportLocation)封装成Location类,成员变量mProvider对应了定位监听注册函数LocationManager.requestLocationUpdates中第一个入参String provider,一般都是“gps”,所以构建用于定位的Location类时mProvider也要赋值“gps”

    2,GPS状态回调流程中(GnssLocationProvider.reportSvStatus)需要解析得到mSvidWithFlags,这个值在后面会有大量的位移和标志位校验操作,必须设置正确。详见GpsStatus.setStatus中的代码。

    我们从NMEA中拿到的svid一般是从1开始的数值,首先我们要将svid左移:

    svid << GnssStatus.SVID_SHIFT_WIDTH

    然后我们根据GSV所属的发送器标识符来确定卫星类型,如BDGSV就是北斗,我们要根据卫星类型,加上掩码:

    //判断卫星类型

                if(sField[0].equalsIgnoreCase("$GLGSV")) {

                    mSvidWithFlags[i] += GnssStatus.CONSTELLATION_GLONASS << GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH;

                }else if(sField[0].equalsIgnoreCase("$BDGSV")) {

                    mSvidWithFlags[i] += GnssStatus.CONSTELLATION_BEIDOU << GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH;

                }else if(sField[0].equalsIgnoreCase("$GPGSV")) {

                    mSvidWithFlags[i] += GnssStatus.CONSTELLATION_GPS << GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH;

                }else {

                    mSvidWithFlags[i] += GnssStatus.CONSTELLATION_QZSS << GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH;

                }

    然后再根据实际情况,加上是否携带某类数据的标识:

    mSvidWithFlags[i] += GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA + GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA +   GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX;   

    3,NMEA数据是带校验位的,如“$GPGGA,235316.000,2959.9925,S,12000.0090,E,1,06,1.21,62.77,M,0.00,M,,*7B” 其中“*”后面的7B就是$和"*"之间所有字符的异或结果,因为有时候以",12*5A"结尾,有时候又是",*7B",要小心split后最后一位是空数据拿不到,导致数组下标少一位,取数据时容易出错,我们预处理时可以这么搞:

    data.replaceAll(",\\*\\w\\w",",0").replaceAll("\\*\\w\\w","").replace("\n","").replace("\r","").split(",");

    这样如果是",*7B"会被转成“0”,而",12*5A"转换为“12”

    4,要注意经纬度换算!

    GGA中的经纬度是ddmm.mmmm和dddmm.mmmm(d代表度,m代表分),实际传给高德等导航软件,需要将分转化为度。比如1234.5678,转换算法为:12+34.5678/60

    5,要注意时区

    ZDA里的时间字符转成Date是UTC时间,而系统校时所需的Date是要加上时区的。我的做法如下:

    //处理ZDA数据

            if (sField[0].contains("ZDA") && sField.length >= 5) {

                //拼接时间格式"yyyy-MM-dd HH:mm:ss"

                StringBuffer sb = new StringBuffer();

                sb.append(sField[4]).append("-").append(sField[3]).append("-").append(sField[2]).append(" ").append(sField[1].substring(0,2)).

                    append(":").append(sField[1].substring(2,4)).append(":").append(sField[1].substring(4,6));

                Log.d(TAG, "ZDA : HIK receive time = " + sb.toString());

                SimpleDateFormat sdfUTC = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));  // 设置UTC时区。如果不设置,默认为当前时区时间

                try {

                    mDate = sdfUTC.parse(sb.toString());//生成与时区无关的Date。但SimpleDateFormat 必须设置正确时区,因为生成Date时如果是别的时区,会转成UTC时间

                    Log.d(TAG, "ZDA : HIK receive long time = " + mDate.getTime());

                } catch (ParseException e) {

                    e.printStackTrace();

                }

            }

    相关文章

      网友评论

          本文标题:Android解析GPS卫星的NMEA数据

          本文链接:https://www.haomeiwen.com/subject/yrgwjhtx.html