Android: How to get accurate altitude?
Asked Answered
S

8

64

I need to get an accurate measurement of altitude using GPS only.

I tried Location.getAltitude(), but that is terribly inaccurate. Any advice?

Schaab answered 20/2, 2012 at 13:23 Comment(1)
Possible duplicate of Get altitude by longitude and latitude in AndroidStatue
R
65

There are two issues with using altitude of a smartphone / tablet GPS:

  1. The altitude is the altitude above the WGS84 reference ellipsoid. It is not the altitude above ground level or sea level. Here is more detail on that: http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915. This error can be corrected; here is a description how to do that by hand: http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html. The web article links to a calculator to get the Geoid height for correction; I do not know if there is also a web service available for this computation.
  2. The GPS altitude is terribly inaccurate for relatively cheap GPS receivers. Here is an article on that: http://gpsinformation.net/main/altitude.htm. One method to cope with this kind of inaccuracy is to filter the altitude data. I used a circular array data structure to remember the last few (I used 4) altitude readings and compute the average. This sufficed to get a relatively accurate reading of vertical speed for my application.
Recalescence answered 24/2, 2012 at 14:29 Comment(3)
Thanks. So I guess I can't get accurate altitude unless I have internet access in my app to correct for Geoid height.Schaab
You can do that offline. You just need to calculate the difference between the ellipsoid and sea level. Which is non-trivial. See this answer: https://mcmap.net/q/303221/-how-to-calculate-the-altitude-above-from-mean-sea-levelLuxe
You've said that from the last 4 altitude readings, you've computed the average but this is for a single location? I have a service, where I get constant updates of the user's location when the user is walking. My question is: If I take the average of all locations(the raw data without the difference between the ellipsoid and sea level) for a specific area, will the value be accurate or is just average inaccurate?Harmonia
R
10

Another way would be parsing NMEA strings. The $GPGGA sentence already contains the corrected altitude data above sea level.

So, simply create a listener to NMEA-strings for your LocationManager and parse the messages:

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]);
                }
            }
        }
    }

You can either replace the altitude of the last Location object received through a location listener, or parse the whole new location through NMEA.

Retriever answered 13/6, 2017 at 10:5 Comment(4)
This approach works well, however you should look for any "GGA" ($--GGA) sentence, not only $GPGGA. I do it using regular expression in my answer.Multiplicate
Call requires API level 24 ... android.location.LocationManager#addNmeaListenerMargarethe
NmeaListener is deprecated. Use OnNmeaMessageListener instead.Pyoid
@Berťák you can just check if it contains or ends with GGA type.endsWith("GGA"), no need to use long pattern as yoursRoss
O
6

Another approach is to measure the altitude from the barometer.

By using the pressure you can calculate the user's altitude. I'm uncertain of the precision level of this and whether it is more accurate than the other answer's approach.

By using the hypsometric formula you can calculate the altitude:

hypsometric formula

Variable definition:

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

You can get the pressure in android from the environment sensors

SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)
Ontologism answered 31/1, 2019 at 11:33 Comment(6)
This won't cut it as the weather will impact pressure, and therefore the reading on your barometer.Wommera
Yes the weather will impact the barometer. But depending on the situation you might not be weather impacted, or less. For example you can measure the altitude by the airplane you are sitting in with a barometer. Impossible with GPS. But okay downvote this answer @ben, when I am just giving alternatives. This is all dependent on the use case, and throwing 1 sensor out the door as useless is the wrong approach.Ontologism
this works in theory but if you do enough testing you will notice highly variable and inaccurate results. I know a lot of apps do it that way and developers stumble upon this type of threads that make them think that calculating altitude is as easy as that, and it contributes to the large ecosystem of apps that pretend you're at a certain altitude while you're not. I'm sorry I don't mean to downvote you for free but this just isn't the right answer to getting a valid altitude value in the large majority of cases. Most apps will need to look into WGS84 offsets.Wommera
btw to be clear we're talking about large inaccuracies, not small ones. Expect 100s of feet difference very frequently if using the barometer alone.Wommera
I guess i will use gps to measure the elevation of the airplane i am in.. Oh wait, thats not possible.Ontologism
@Wommera while you're right with what you say, taking the pressure as a helper, calibrating it every couple of minutes with the GPS measurement when you have a really good fix, is the best thing you can do. I just added an answer which points this out. I'll try to confirm the "100s of feet difference very frequently" during the next couple of months.Hauler
H
5

If the device has a barometer, then using it can improve the relative accuracy. I don't mean to use the barometer to compute the height in relation to the sea level, as can be found in several formulas, as this is highly dependent of the weather conditions.

What you want to do is to get the GPS-altitude when you know that the device has a good fix, with high accuracy values. At that point you fetch the barometric pressure and set that pressure as a reference.

When the pressure increases around 12 hPa, you will know that your altitude decreased by around 100 m ( https://en.wikipedia.org/wiki/Atmospheric_pressure "At low altitudes above sea level, the pressure decreases by about 1.2 kPa (12 hPa) for every 100 metres." ).

Don't take that value as an exact, but variations in the altitude determined by GPS vary a lot due to trees covering the line of sight and other factors, while the barometer will remain very precise under those conditions.

The following graph is a bike ride of around one hour and 20 minutes in duration. Starting point and end point are the same at around 477 m above sea level, determined via GPS. The measured pressure is 1015.223 hPA.

The lowest point is 377 m, with a measured pressure of 1025.119 hPa. So in that case, 100 m make a difference of 10 hPa.

The highest point is 550 m, with a measured pressure of 1007.765 hPa.

Ending point is the same height, and the exact same pressure as the starting conditions (the pressure could have varied due to the weather conditions, but it didn't). The temperature dropped around 1°C, so it was all pretty constant.

The black line containing the many variations is the altitude measured via GPS, the mirrored, but clean line, is the barometric pressure. It has very little variation in it simply because the pressure doesn't vary as wild as the GPS-quality. It is a very smooth, very precise curve. This is not due to lag. This is measured with a Bosch BME280 sensor, which is capable of detecting the closing of a door, change of floor detection, elevator direction, drones, with a noise of 0.2 Pa which equals 1.7 cm and an error of 1m at 400m of height change. These kind of sensors are integrated in some Smartphones. For example, the Pixel 3 contains a Bosch BMP380.

If you mirror the pressure graph, as is shown with the dotted black line, you will see that it very closely matches the GPS altitude. It is much more precise than GPS, but the only problem is that you can't take it as an absolute value.

The samples of GPS and pressure were both taken in 1 second intervals, so there is no curve smoothing from the d3 library causing some false impressions.

enter image description here

So maybe readjusting the pressure around every 10-30 minutes while you have a GPS good fix will give you a good base to perform your altitude measurements by pressure in between.

Hauler answered 30/6, 2020 at 12:42 Comment(2)
Hey, your approach seems interesting! How do you define a "good fix" when it comes to GPS?Worldbeater
@forstackoverflowizi I would base it on the horizontal accuracy and sample it a couple of times. During development I'd sample all the data and look when the best matches occur, how it relates to the horizontal accuracy, since that is the only accuracy data we get.Hauler
M
1

There are other ways to get the altitude than by GPS. You can use the barometer but as there isn't that many devices with a barometric sensors yet (only the new ones). I will recommend to use a web service to acquire the desired data.

Here is a question which should help you through: Get altitude by longitude and latitude in Android

Moffit answered 25/1, 2013 at 20:39 Comment(0)
C
1

For newcomers I made a library that wrap LocationManager into rxjava observables and add some observable helpers to get sea level altitutde from Nmea/GPGGA mre info here

Cardialgia answered 2/11, 2018 at 10:42 Comment(0)
P
0

There are libraries, such as the open-source CS-Map which provide an API that do these lookups in large tables. You specify the coordinates, and it will tell you the height offset that needs to be applied to the ellipsoidal height at that location to get the "real-world" orthometric height.

Note: Having used CS-Map before, it isn't exactly straight-forward 5-minute job to plug it in. Warning you in advance that it is more complicated than calling a single API with a set of lat-long coordinates and getting back a height. I no longer work at the company where we were doing this kind of work, so unfortunately cannot look up the code to say exactly which API to call.

Doing a google search for it right now (2019), it seems CS-Map has been merged into MetaCRS in OSGeo, the "Open Source Geospatial Foundation" project. That same search led me to this old CS-Map wiki as well as the PROJ GitHub page, where PROJ seems to be similar project to CS-Map.

Phonsa answered 24/6, 2019 at 18:12 Comment(0)
P
-1

I would recommend using NmeaListener, as sladstaetter suggested in his answer. However, according to NMEA documentation, "$GPGGA" is not the only sentence you can get. You should look for any "GGA" sentence ($--GGA).

Regular expressions are great for that, e.g.:

@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!
    }
}
Porcia answered 1/7, 2019 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.