Android: как получить точную высоту?

Мне нужно получить точное измерение высоты, используя только GPS.

Я старался Location.getAltitude(), но это ужасно неточно. Любой совет?

4 ответа

Решение

Есть две проблемы с использованием высоты GPS смартфона / планшета:

  1. Высота - это высота над эталонным эллипсоидом WGS84. Это не высота над уровнем земли или над уровнем моря. Вот более подробно об этом: http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915. Эта ошибка может быть исправлена; Вот описание того, как сделать это вручную: http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html. Веб-статья ссылается на калькулятор, чтобы получить высоту геоида для коррекции; Я не знаю, есть ли веб-сервис для этого вычисления.
  2. Высота GPS ужасно неточна для относительно дешевых приемников GPS. Вот статья об этом: http://gpsinformation.net/main/altitude.htm. Один из способов справиться с такой неточностью - это отфильтровать данные о высоте. Я использовал структуру данных кругового массива, чтобы запомнить последние несколько (я использовал 4) показаний высоты и вычислить среднее значение. Этого было достаточно, чтобы получить относительно точное показание вертикальной скорости для моего приложения.

Другим способом будет разбор строк NMEA. Предложение $GPGGA уже содержит исправленные данные о высоте над уровнем моря.

Итак, просто создайте прослушиватель NMEA-строк для вашего LocationManager и проанализируйте сообщения:

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.

Другой подход заключается в измерении высоты от барометра.

С помощью давления вы можете рассчитать высоту пользователя. Я не уверен в точности уровня этого и является ли он более точным, чем подход другого ответа.

С помощью гипсометрической формулы вы можете рассчитать высоту:

Определение переменной:

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

Вы можете получить давление в Android от датчиков окружающей среды

SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)

Если в приборе есть барометр, используйте его для повышения относительной точности. Я не собираюсь использовать барометр для вычисления высоты относительно уровня моря, как это можно найти в нескольких формулах, поскольку это сильно зависит от погодных условий.

Что вы хотите сделать, так это получить GPS-высоту, когда вы знаете, что устройство имеет хорошее исправление с высокими значениями точности. В этот момент вы получаете атмосферное давление и устанавливаете это давление в качестве ориентира.

Когда давление увеличивается примерно на 12 гПа, вы будете знать, что ваша высота уменьшилась примерно на 100 м ( https://en.wikipedia.org/wiki/Atmospheric_pressure " На малых высотах над уровнем моря давление снижается примерно на 1,2 кПа (12 гПа) на каждые 100 метров ").

Не принимайте это значение за точное, но изменения высоты, определяемые GPS, сильно различаются из-за деревьев, закрывающих линию обзора, и других факторов, в то время как барометр будет оставаться очень точным в этих условиях.

На следующем графике показана поездка на велосипеде продолжительностью около одного часа 20 минут. Начальная и конечная точки совпадают на высоте около 477 м над уровнем моря, что определяется с помощью GPS. Измеренное давление составляет 1015,223 гПа.

Самая низкая точка - 377 м с измеренным давлением 1025,119 гПа. Таким образом, в этом случае разница в 100 м составляет 10 гПа.

Наивысшая точка - 550 м, измеренное давление - 1007,765 гПа.

Конечная точка имеет ту же высоту и такое же давление, что и начальные условия (давление могло меняться из-за погодных условий, но это не так). Температура упала примерно на 1°C, так что все было довольно стабильно.

Черная линия, содержащая множество вариаций, - это высота, измеренная с помощью GPS, зеркальная, но чистая линия - это атмосферное давление. У него очень мало вариаций просто потому, что давление меняется не так сильно, как качество GPS. Это очень плавная и очень точная кривая. Это не из-за отставания. Это измеряется датчиком Bosch BME280, который способен обнаруживать закрытие двери, изменение пола, направление лифта, дроны с шумом 0,2 Па, который равен 1,7 см, и погрешностью 1 м при изменении высоты 400 м.. Такие датчики встроены в некоторые смартфоны. Например, Pixel 3 содержит Bosch BMP380.

Если вы отразите график давления, как показано пунктирной черной линией, вы увидите, что он очень точно соответствует высоте GPS. Он намного точнее, чем GPS, но проблема только в том, что вы не можете принять его за абсолютное значение.

Образцы GPS и давления были взяты с интервалом в 1 секунду, поэтому нет сглаживания кривой из библиотеки d3, вызывающего некоторые ложные впечатления.

Так что, возможно, корректировка давления примерно каждые 10-30 минут, пока у вас есть хороший GPS-навигатор, даст вам хорошую основу для измерения высоты с помощью промежуточного давления.

Есть и другие способы получить высоту, кроме GPS. Вы можете использовать барометр, но пока не так много устройств с барометрическими датчиками (только новые). Я рекомендую использовать веб-сервис для получения нужных данных.

Вот вопрос, который должен вам помочь: получить высоту по долготе и широте в Android

Для новичков я сделал библиотеку, которая оборачивает LocationManager в наблюдаемые rxjava и добавляет несколько наблюдаемых помощников, чтобы получить высоту уровня моря из информации Nmea/GPGGA здесь.

Существуют библиотеки, такие как CS-Map с открытым исходным кодом, которые предоставляют API, который выполняет поиск в больших таблицах. Вы указываете координаты, и он сообщит вам смещение высоты, которое необходимо применить к эллипсоидальной высоте в этом месте, чтобы получить ортометрическую высоту "реального мира".

Примечание. Используя CS-Map ранее, подключить его не так просто, как 5-минутная работа. Заранее предупреждаю, что это сложнее, чем вызов одного API с набором координат долготы и получение вернуться на высоту. Я больше не работаю в компании, где мы выполняли такую ​​работу, поэтому, к сожалению, не могу найти код, чтобы точно сказать, какой API вызывать.

Сейчас, выполняя поиск в Google (2019), кажется, что CS-Map была объединена с MetaCRS в OSGeo, проекте " Open Source Geospatial Foundation". Тот же поиск привел меня на эту старую вики CS-Map, а также на страницу 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!
    }
}
Другие вопросы по тегам