Android 安卓:如何获得准确的高度?

Android 安卓:如何获得准确的高度?,android,gps,location,altitude,Android,Gps,Location,Altitude,我只需要用GPS精确测量高度 我尝试了Location.getAltitude(),但这非常不准确。 有什么建议吗?使用智能手机/平板电脑GPS的高度有两个问题: 高度是WGS84参考椭球体上方的高度。它不是高于地面或海平面的高度。以下是关于这方面的更多详细信息:。这个错误是可以纠正的;以下是如何手动执行此操作的说明:。这篇网络文章链接到一个计算器,以获得要校正的大地水准面高度;我不知道是否还有一个web服务可用于此计算 相对便宜的GPS接收机的GPS高度非常不准确。这里有一篇关于这方面的文章:

我只需要用GPS精确测量高度

我尝试了
Location.getAltitude()
,但这非常不准确。
有什么建议吗?

使用智能手机/平板电脑GPS的高度有两个问题:

  • 高度是WGS84参考椭球体上方的高度。它不是高于地面或海平面的高度。以下是关于这方面的更多详细信息:。这个错误是可以纠正的;以下是如何手动执行此操作的说明:。这篇网络文章链接到一个计算器,以获得要校正的大地水准面高度;我不知道是否还有一个web服务可用于此计算
  • 相对便宜的GPS接收机的GPS高度非常不准确。这里有一篇关于这方面的文章:。处理这种不精确性的一种方法是过滤高度数据。我使用了一个圆形阵列数据结构来记住最近几次(我使用了4次)的高度读数并计算平均值。这足以为我的应用程序获得相对准确的垂直速度读数

  • 除了通过全球定位系统,还有其他方法获得海拔高度。你可以使用气压计,但是现在还没有那么多带有气压传感器的设备(只有新的)。我建议使用web服务来获取所需的数据


    这里有一个问题可以帮助您解决:

    另一种方法是解析NMEA字符串。已包含海平面以上的校正高度数据

    因此,只需为LocationManager创建NMEA字符串的侦听器并解析消息:

    private GpsStatus.NmeaListener mNmeaListener = new GpsStatus.NmeaListener() {
        @Override
        public void onNmeaReceived(long timestamp, String nmea) {
            parseNmeaString(nmea);
        }
    };
    
    public void registerLocationManager(Context context) {
            mLocationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
            mLocationManager.addNmeaListener(mNmeaListener);
    }
    
    private void parseNmeaString(String line) {
            if (line.startsWith("$")) {
                String[] tokens = line.split(",");
                String type = tokens[0];
    
                // Parse altitude above sea level, Detailed description of NMEA string here http://aprs.gids.nl/nmea/#gga
                if (type.startsWith("$GPGGA")) {
                    if (!tokens[9].isEmpty()) {
                        mLastMslAltitude = Double.parseDouble(tokens[9]);
                    }
                }
            }
        }
    

    您可以替换通过位置侦听器接收的最后一个位置对象的高度,也可以通过NMEA解析整个新位置

    对于新来者,我制作了一个库,将LocationManager封装到rxjava可观测数据中,并添加一些可观测辅助程序,以从Nmea/GPGGA mre info获取海平面高度

    另一种方法是从气压计测量高度

    通过使用压力,您可以计算用户的高度。我不确定这个问题的准确度,也不确定它是否比另一个答案的方法更准确

    通过使用,您可以计算高度:

    变量定义:

    P0: Sea-level pressure 
    P: Atmospheric pressure 
    T: Temperature 
    
    你可以在android中从


    有一些库,比如开源的CS-Map,它提供了一个API,可以在大型表中进行这些查找。指定坐标,它将告诉您需要应用于该位置的椭球体高度的高度偏移,以获得“真实世界”正射高度

    注意:以前使用过CS Map,插入它并不是一件简单的5分钟工作。提前警告您,这比使用一组lat-long坐标调用单个API并获取高度要复杂得多我不再在我们从事此类工作的公司工作,因此很遗憾,我无法查找代码来准确说明要调用哪个API。


    现在(2019年)在谷歌上搜索CS地图,似乎CS地图已经被合并到OSGeo的MetaCRS中,即“”项目。同样的搜索也让我找到了,PROJ似乎与CS-Map类似。

    我建议使用nAlistener,作为sladstaetter 在中建议。然而,根据你的说法,“$GPGGA”并不是你能得到的唯一一句话。您应该查找任何“GGA”语句($--GGA)

    正则表达式在这方面非常有用,例如:

    @Override
    public void onNmeaReceived(final long timestamp, final String nmea) {
        final Pattern pattern = Pattern.compile("\\$..GGA,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([+-]?\\d+(.\\d+)?),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$");
        final Matcher matcher = pattern.matcher(nmea);
        if (matcher.find()) {
            final float altitude = Float.parseFloat(matcher.group(1));
            // ...enjoy the altitude!
        }
    }
    

    如果设备有气压计,则可以使用它来提高相对精度。我的意思不是用气压计来计算与海平面相关的高度,这可以在几个公式中找到,因为这在很大程度上取决于天气条件

    你要做的是,当你知道该设备有一个很好的定位,具有高精度值时,获得GPS高度。在这一点上,你获取大气压力,并将该压力设置为参考

    当压力增加约12百帕时,你会知道你的高度降低了约100米(“在海平面以上的低海拔地区,压力每100米降低约1.2千帕(12百帕)。”

    不要将该值视为精确值,但由于树木覆盖视线和其他因素,GPS确定的高度变化很大,而气压计在这些条件下将保持非常精确

    下图是一次约1小时20分钟的自行车骑行。起点和终点在海拔477米左右相同,通过GPS确定。测得的压力为1015.223 hPA

    最低点为377 m,实测压力为1025.119 hPa。所以在这种情况下,100米产生10百帕的差异

    最高点为550 m,测量压力为1007.765 hPa

    终点的高度和压力与起始条件完全相同(压力可能会因天气条件而变化,但事实并非如此)。温度下降了1摄氏度左右,所以一切都很稳定

    包含许多变化的黑线是通过GPS测量的高度,镜像但干净的线是大气压力。它的变化很小,只是因为压力的变化不像GPS质量那样剧烈。这是一条非常平滑、非常精确的曲线。这不是因为滞后。这是用博世BME280传感器测量的,该传感器能够检测门的关闭、楼层检测的变化、电梯方向、无人机,噪音为0.2 Pa,相当于1.7 cm,高度变化400 m时误差为1 m。这些传感器集成在一些智能手机中。例如,像素3包含博世BMP380

    如果镜像压力图,如黑色虚线所示,您将看到它与GPS高度非常接近。它比GPS精确得多,但唯一的问题是,你不能把它当作
    @Override
    public void onNmeaReceived(final long timestamp, final String nmea) {
        final Pattern pattern = Pattern.compile("\\$..GGA,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([+-]?\\d+(.\\d+)?),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$");
        final Matcher matcher = pattern.matcher(nmea);
        if (matcher.find()) {
            final float altitude = Float.parseFloat(matcher.group(1));
            // ...enjoy the altitude!
        }
    }