我只需要使用 GPS 来准确测量高度。
我试过Location.getAltitude()
了,但那是非常不准确的。有什么建议吗?
使用智能手机/平板电脑 GPS 的高度存在两个问题:
另一种方法是解析 NMEA 字符串。$GPGGA 语句已经包含经过校正的海拔高度数据。
因此,只需为您的 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 解析整个新位置。
另一种方法是从气压计测量高度。
通过使用压力,您可以计算用户的高度。我不确定这个的精度水平以及它是否比其他答案的方法更准确。
通过使用hypsometric 公式,您可以计算高度:
变量定义:
P0: Sea-level pressure
P: Atmospheric pressure
T: Temperature
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)
如果设备有气压计,那么可以使用它来提高相对精度。我并不是要使用气压计来计算与海平面相关的高度,这可以在几个公式中找到,因为这高度依赖于天气条件。
当您知道该设备具有良好的定位和高精度值时,您想要做的是获取 GPS 高度。此时,您获取大气压力并将该压力设置为参考。
当压力增加约 12 hPa 时,您会知道您的海拔高度下降了约 100 m ( https://en.wikipedia.org/wiki/Atmospheric_pressure "在海平面以上的低海拔地区,压力下降约 1.2 kPa (12 hPa) 每 100 米。 ”)。
不要将此值视为精确值,但由于树木覆盖视线和其他因素,由 GPS 确定的高度变化很大,而在这些条件下,气压计将保持非常精确。
下图是持续时间约为 1 小时 20 分钟的自行车骑行。起点和终点在海拔约 477 m 处相同,通过 GPS 确定。测得的压力为 1015.223 hPA。
最低点为 377 m,实测压力为 1025.119 hPa。所以在这种情况下,100 m 会产生 10 hPa 的差异。
最高点550 m,实测压力1007.765 hPa。
终点高度相同,压力与起始条件完全相同(压力可能会因天气条件而变化,但事实并非如此)。温度下降了大约 1°C,所以一切都很稳定。
包含许多变化的黑线是通过 GPS 测量的高度,镜像但干净的线是气压。它几乎没有变化,这仅仅是因为压力不像 GPS 质量那样变化很大。这是一条非常平滑、非常精确的曲线。这不是由于滞后。这是用博世BME280传感器测量的,该传感器能够检测关门、楼层检测变化、电梯方向、无人机,噪声为0.2 Pa,等于1.7 cm,高度变化400m时误差为1m . 这种传感器集成在一些智能手机中。例如,Pixel 3 包含一个博世 BMP380。
如果您镜像压力图,如黑色虚线所示,您将看到它与 GPS 高度非常匹配。它比 GPS 精确得多,但唯一的问题是你不能把它当作绝对值。
GPS 和压力的样本都是以 1 秒的间隔采集的,因此 d3 库中没有曲线平滑,导致一些错误印象。
因此,在 GPS 定位良好的情况下,可能每 10-30 分钟左右重新调整一次压力,这将为您提供一个良好的基础,通过两者之间的压力进行高度测量。
除了 GPS 之外,还有其他方法可以获取高度。您可以使用气压计,但由于配备气压传感器的设备并不多(只有新设备)。我会建议使用网络服务来获取所需的数据。
这是一个可以帮助您解决的问题:在Android中按经度和纬度获取高度
对于新手,我制作了一个库,将 LocationManager 包装到 rxjava observables 中,并添加一些可观察的助手来从 Nmea/GPGGA mre info here获取海平面高度
有一些库,例如开源 CS-Map,它提供了在大型表中进行这些查找的 API。您指定坐标,它会告诉您需要应用于该位置的椭球高度以获得“真实世界”正高的高度偏移。
注意:之前使用过 CS-Map,插入它并不是很简单的 5 分钟工作。提前警告您,它比使用一组经纬度坐标调用单个 API 并获取更复杂回一个高度。 我不再在我们从事此类工作的公司工作,因此很遗憾无法查看代码以准确说明要调用哪个 API。
现在(2019 年)进行谷歌搜索,似乎 CS-Map 已合并到 OSGeo 中的 MetaCRS 中,即“开源地理空间基金会”项目。同样的搜索让我找到了这个旧的 CS-Map wiki以及PROJ GitHub 页面,其中 PROJ 似乎是与 CS-Map 类似的项目。
我会推荐使用NmeaListener,正如 sladstaetter 在他的回答中所建议的那样。然而,根据NMEA 文档,“$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!
}
}