From 2e75e8c18617250c864d2305a1ad14391e0cf4d7 Mon Sep 17 00:00:00 2001 From: Aaron McCarthy Date: Wed, 4 Sep 2013 17:28:27 +1000 Subject: Support position accuracy in QNmeaPositionInfoSource. Extract the HDOP and VDOP from the NMEA stream and calculate the twice the distance root mean square (2DRMS) error based on the user provided User equivalent range error value. [ChangeLog][QtPositioning][QNmeaPositionInfoSource] Added support for reporting position accuracy. Change-Id: I59e5f8c32070fa96ae4d0bd02a18f38663920e05 Reviewed-by: Alex Blasche --- src/positioning/qlocationutils.cpp | 54 ++++++++++++++++++++++-- src/positioning/qlocationutils_p.h | 4 +- src/positioning/qnmeapositioninfosource.cpp | 65 +++++++++++++++++++++++++++-- src/positioning/qnmeapositioninfosource.h | 3 ++ src/positioning/qnmeapositioninfosource_p.h | 3 ++ 5 files changed, 122 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/positioning/qlocationutils.cpp b/src/positioning/qlocationutils.cpp index 5d6cfbb2..830ac64a 100644 --- a/src/positioning/qlocationutils.cpp +++ b/src/positioning/qlocationutils.cpp @@ -1,5 +1,7 @@ /**************************************************************************** ** +** Copyright (C) 2013 Jolla Ltd. +** Contact: Aaron McCarthy ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** @@ -58,7 +60,8 @@ static double qlocationutils_nmeaDegreesToDecimal(double nmeaDegrees) return deg + (min / 60.0); } -static void qlocationutils_readGga(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +static void qlocationutils_readGga(const char *data, int size, QGeoPositionInfo *info, double uere, + bool *hasFix) { QByteArray sentence(data, size); QList parts = sentence.split(','); @@ -82,6 +85,13 @@ static void qlocationutils_readGga(const char *data, int size, QGeoPositionInfo } } + if (parts.count() > 8 && !parts[8].isEmpty()) { + bool hasHdop = false; + double hdop = parts[8].toDouble(&hasHdop); + if (hasHdop) + info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere); + } + if (parts.count() > 9 && parts[9].count() > 0) { bool hasAlt = false; double alt = parts[9].toDouble(&hasAlt); @@ -93,6 +103,29 @@ static void qlocationutils_readGga(const char *data, int size, QGeoPositionInfo info->setCoordinate(coord); } +static void qlocationutils_readGsa(const char *data, int size, QGeoPositionInfo *info, double uere, + bool *hasFix) +{ + QList parts = QByteArray::fromRawData(data, size).split(','); + + if (hasFix && parts.count() > 2 && !parts[2].isEmpty()) + *hasFix = parts[2].toInt() > 0; + + if (parts.count() > 16 && !parts[16].isEmpty()) { + bool hasHdop = false; + double hdop = parts[16].toDouble(&hasHdop); + if (hasHdop) + info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere); + } + + if (parts.count() > 17 && !parts[17].isEmpty()) { + bool hasVdop = false; + double vdop = parts[17].toDouble(&hasVdop); + if (hasVdop) + info->setAttribute(QGeoPositionInfo::VerticalAccuracy, 2 * vdop * uere); + } +} + static void qlocationutils_readGll(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) { QByteArray sentence(data, size); @@ -227,7 +260,8 @@ static void qlocationutils_readZda(const char *data, int size, QGeoPositionInfo info->setTimestamp(QDateTime(date, time, Qt::UTC)); } -bool QLocationUtils::getPosInfoFromNmea(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +bool QLocationUtils::getPosInfoFromNmea(const char *data, int size, QGeoPositionInfo *info, + double uere, bool *hasFix) { if (!info) return false; @@ -237,9 +271,23 @@ bool QLocationUtils::getPosInfoFromNmea(const char *data, int size, QGeoPosition if (size < 6 || data[0] != '$' || !hasValidNmeaChecksum(data, size)) return false; + // Adjust size so that * and following characters are not parsed by the following functions. + for (int i = 0; i < size; ++i) { + if (data[i] == '*') { + size = i; + break; + } + } + if (data[3] == 'G' && data[4] == 'G' && data[5] == 'A') { // "$--GGA" sentence. - qlocationutils_readGga(data, size, info, hasFix); + qlocationutils_readGga(data, size, info, uere, hasFix); + return true; + } + + if (data[3] == 'G' && data[4] == 'S' && data[5] == 'A') { + // "$--GSA" sentence. + qlocationutils_readGsa(data, size, info, uere, hasFix); return true; } diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h index 4b42695c..10dd61d4 100644 --- a/src/positioning/qlocationutils_p.h +++ b/src/positioning/qlocationutils_p.h @@ -94,7 +94,9 @@ public: - RMC reports date with a two-digit year so in this case the year is assumed to be after the year 2000. */ - Q_AUTOTEST_EXPORT static bool getPosInfoFromNmea(const char *data, int size, QGeoPositionInfo *info, bool *hasFix = 0); + Q_AUTOTEST_EXPORT static bool getPosInfoFromNmea(const char *data, int size, + QGeoPositionInfo *info, double uere, + bool *hasFix = 0); /* Returns true if the given NMEA sentence has a valid checksum. diff --git a/src/positioning/qnmeapositioninfosource.cpp b/src/positioning/qnmeapositioninfosource.cpp index 9ac928b2..23706a4f 100644 --- a/src/positioning/qnmeapositioninfosource.cpp +++ b/src/positioning/qnmeapositioninfosource.cpp @@ -1,5 +1,7 @@ /**************************************************************************** ** +** Copyright (C) 2013 Jolla Ltd. +** Contact: Aaron McCarthy ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** @@ -45,6 +47,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -133,8 +136,7 @@ void QNmeaSimulatedReader::simulatePendingUpdate() if (m_pendingUpdates.size() > 0) { // will be dequeued in processNextSentence() QPendingGeoPositionInfo &pending = m_pendingUpdates.head(); - if (pending.info.coordinate().type() != QGeoCoordinate::InvalidCoordinate) - m_proxy->notifyNewUpdate(&pending.info, pending.hasFix); + m_proxy->notifyNewUpdate(&pending.info, pending.hasFix); } processNextSentence(); @@ -173,6 +175,9 @@ void QNmeaSimulatedReader::processNextSentence() timeToNextUpdate = prevTime.msecsTo(time); if (timeToNextUpdate >= 0) break; + } else { + timeToNextUpdate = 0; + break; } } } @@ -199,10 +204,13 @@ QNmeaPositionInfoSourcePrivate::QNmeaPositionInfoSourcePrivate(QNmeaPositionInfo m_device(0), m_invokedStart(false), m_positionError(QGeoPositionInfoSource::UnknownSourceError), + m_userEquivalentRangeError(qQNaN()), m_source(parent), m_nmeaReader(0), m_updateTimer(0), m_requestTimer(0), + m_horizontalAccuracy(qQNaN()), + m_verticalAccuracy(qQNaN()), m_noUpdateLastInterval(false), m_updateTimeoutSent(false), m_connectedReadyRead(false) @@ -375,6 +383,17 @@ void QNmeaPositionInfoSourcePrivate::notifyNewUpdate(QGeoPositionInfo *update, b update->setTimestamp(QDateTime(m_currentDate, time, Qt::UTC)); } + // Some attributes are sent in separate NMEA sentences. Save and restore the accuracy + // measurements. + if (update->hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + m_horizontalAccuracy = update->attribute(QGeoPositionInfo::HorizontalAccuracy); + else if (!qIsNaN(m_horizontalAccuracy)) + update->setAttribute(QGeoPositionInfo::HorizontalAccuracy, m_horizontalAccuracy); + if (update->hasAttribute(QGeoPositionInfo::VerticalAccuracy)) + m_verticalAccuracy = update->attribute(QGeoPositionInfo::VerticalAccuracy); + else if (!qIsNaN(m_verticalAccuracy)) + update->setAttribute(QGeoPositionInfo::VerticalAccuracy, m_verticalAccuracy); + if (hasFix && update->isValid()) { if (m_requestTimer && m_requestTimer->isActive()) { m_requestTimer->stop(); @@ -451,6 +470,10 @@ void QNmeaPositionInfoSourcePrivate::emitUpdated(const QGeoPositionInfo &update) In both cases the position information is received via the positionUpdated() signal and the last known position can be accessed with lastKnownPosition(). + + QNmeaPositionInfoSource supports reporting the accuracy of the horizontal and vertical position. + To enable position accuracy reporting an estimate of the User Equivalent Range Error associated + with the NMEA source must be set with setUserEquivalentRangeError(). */ @@ -481,6 +504,41 @@ QNmeaPositionInfoSource::~QNmeaPositionInfoSource() delete d; } +/*! + Sets the User Equivalent Range Error (UERE) to \a uere. The UERE is used in calculating an + estimate of the accuracy of the position information reported by the position info source. The + UERE should be set to a value appropriate for the GPS device which generated the NMEA stream. + + The true UERE value is calculated from multiple error sources including errors introduced by + the satellites and signal propogation delays through the atmosphere as well as errors + introduced by the receiving GPS equipment. For details on GPS accuracy see + \l {http://edu-observatory.org/gps/gps_accuracy.html}. + + A typical value for UERE is approximately 5.1. + + \since 5.3 + + \sa userEquivalentRangeError() +*/ +void QNmeaPositionInfoSource::setUserEquivalentRangeError(double uere) +{ + d->m_userEquivalentRangeError = uere; +} + +/*! + Returns the current User Equivalent Range Error (UERE). The UERE is used in calculating an + estimate of the accuracy of the position information reported by the position info source. The + default value is NaN which means no accuracy information will be provided. + + \since 5.3 + + \sa setUserEquivalentRangeError() +*/ +double QNmeaPositionInfoSource::userEquivalentRangeError() const +{ + return d->m_userEquivalentRangeError; +} + /*! Parses an NMEA sentence string into a QGeoPositionInfo. @@ -498,7 +556,8 @@ QNmeaPositionInfoSource::~QNmeaPositionInfoSource() bool QNmeaPositionInfoSource::parsePosInfoFromNmeaData(const char *data, int size, QGeoPositionInfo *posInfo, bool *hasFix) { - return QLocationUtils::getPosInfoFromNmea(data, size, posInfo, hasFix); + return QLocationUtils::getPosInfoFromNmea(data, size, posInfo, d->m_userEquivalentRangeError, + hasFix); } /*! diff --git a/src/positioning/qnmeapositioninfosource.h b/src/positioning/qnmeapositioninfosource.h index 605a5696..ae19309f 100644 --- a/src/positioning/qnmeapositioninfosource.h +++ b/src/positioning/qnmeapositioninfosource.h @@ -60,6 +60,9 @@ public: explicit QNmeaPositionInfoSource(UpdateMode updateMode, QObject *parent = 0); ~QNmeaPositionInfoSource(); + void setUserEquivalentRangeError(double uere); + double userEquivalentRangeError() const; + UpdateMode updateMode() const; void setDevice(QIODevice *source); diff --git a/src/positioning/qnmeapositioninfosource_p.h b/src/positioning/qnmeapositioninfosource_p.h index ec9ebeaf..afe0b3aa 100644 --- a/src/positioning/qnmeapositioninfosource_p.h +++ b/src/positioning/qnmeapositioninfosource_p.h @@ -96,6 +96,7 @@ public: QGeoPositionInfo m_lastUpdate; bool m_invokedStart; QGeoPositionInfoSource::Error m_positionError; + double m_userEquivalentRangeError; public Q_SLOTS: void readyRead(); @@ -120,6 +121,8 @@ private: QGeoPositionInfo m_pendingUpdate; QDate m_currentDate; QTimer *m_requestTimer; + qreal m_horizontalAccuracy; + qreal m_verticalAccuracy; bool m_noUpdateLastInterval; bool m_updateTimeoutSent; bool m_connectedReadyRead; -- cgit v1.2.1