Android:如何获得准确的海拔高度?

时间:2012-02-20 13:23:56

标签: android gps location altitude

我只需要使用GPS就可以准确测量海拔高度。

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

8 个答案:

答案 0 :(得分:52)

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

  1. 海拔高度是WGS84参考椭球上方的海拔高度。它不是高于地面或海平面的高度。以下是有关详细信息:http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915。这个错误可以纠正;以下是如何手动执行此操作的说明:http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html。网络文章链接到计算器以获得大地水准面高度进行校正;我不知道是否还有可用于此计算的Web服务。
  2. 对于相对便宜的GPS接收器,GPS高度非常不准确。这是一篇关于它的文章:http://gpsinformation.net/main/altitude.htm。处理这种不准确性的一种方法是过滤高度数据。我使用圆形阵列数据结构来记住最后几个(我使用4个)高度读数并计算平均值。这足以让我的应用程序获得相对准确的垂直速度读数。

答案 1 :(得分:4)

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

因此,只需为您的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]);
                }
            }
        }
    }

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

答案 2 :(得分:1)

除了GPS之外,还有其他方法可以获得高度。您可以使用气压计,但因为没有那么多具有气压传感器的设备(只有新的气压计)。我建议使用Web服务来获取所需的数据。

以下是一个可以帮助您完成问题的问题:Get altitude by longitude and latitude in Android

答案 3 :(得分:1)

如果设备具有气压计,则使用该气压计可以提高相对精度。我并不是要使用气压计来计算相对于海平面的高度,正如可以在几个公式中找到的那样,因为高度高度依赖于天气条件。

您要做的是在知道设备具有良好的定位以及高精度值的情况下获取GPS高度。此时,您将获取大气压力并将该压力作为参考。

当压力增加约12 hPa时,您会知道您的海拔降低了约100 m(https://en.wikipedia.org/wiki/Atmospheric_pressure在海拔高度较低的低海拔处,压力降低了约1.2 kPa(12 hPa)每100米。“)。

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

下图是持续时间约1小时20分钟的自行车骑行时间。通过GPS确定,起点和终点在海拔477 m处相同。测得的压力为1015.223 hPA。

最低点是377 m,测得的压力是1025.119 hPa。因此,在这种情况下,100 m相差10 hPa。

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

终点是相同的高度,并且压力与开始条件完全相同(压力可能会因天气条件而变化,但没有变化)。温度下降了大约1°C,因此温度一直保持恒定。

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

如果镜像压力图(如黑色虚线所示),您将看到它与GPS高度非常匹配。它比GPS精确得多,但是唯一的问题是您不能将其作为绝对值。

GPS和压力的采样都是在1秒的间隔内进行的,因此d3库中没有进行曲线平滑处理,从而造成了一些错误的印象。

enter image description here

因此,在GPS定位良好的情况下,大约每10-30分钟重新调整一次压力将为您提供一个良好的基础,以便您可以通过两者之间的压力进行海拔高度测量。

答案 4 :(得分:0)

对于新来者,我制作了一个库,将LocationManager包装到rxjava可观察对象中,并添加了一些可观察的帮助程序,以从Nmea / GPGGA mre info here

获取海平面高度

答案 5 :(得分:0)

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

通过使用压力,您可以计算用户的高度。我不确定此方法的精确度以及它是否比其他答案的方法更准确。

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

hypsometric formula

变量定义:

P0: Sea-level pressure 
P: Atmospheric pressure 
T: Temperature 

您可以从environment sensors

获得android系统的压力
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)

答案 6 :(得分:0)

有些库(例如开源CS-Map)提供了可以在大型表中进行这些查找的API。您指定坐标,它会告诉您需要将该高度应用于该位置处的椭球高度的高度偏移量。

注意:以前使用过CS-Map,插入它并不是5分钟的直接工作。提前警告您,它比调用一组带有lat-long的API更复杂。坐标并返回高度。 我不再在我们从事此类工作的公司工作,因此很遗憾,无法查找代码以确切说明要调用的API。

现在(2019年)在Google上进行搜索,看来CS-Map已被合并到OSGeo中的“ Open Source Geospatial Foundation”项目中的MetaCRS中。相同的搜索使我进入了this old CS-Map wikithe PROJ GitHub page,其中PROJ似乎与CS-Map类似。

答案 7 :(得分:0)

我建议使用 NmeaListener 作为标准 在his answer中建议。但是,根据NMEA documentation,“ $ 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!
    }
}