summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/positioning/qlocationutils.cpp54
-rw-r--r--src/positioning/qlocationutils_p.h4
-rw-r--r--src/positioning/qnmeapositioninfosource.cpp65
-rw-r--r--src/positioning/qnmeapositioninfosource.h3
-rw-r--r--src/positioning/qnmeapositioninfosource_p.h3
-rw-r--r--tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp61
-rw-r--r--tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h2
-rw-r--r--tests/auto/utils/qlocationtestutils.cpp5
-rw-r--r--tests/auto/utils/qlocationtestutils_p.h1
9 files changed, 186 insertions, 12 deletions
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 <aaron.mccarthy@jollamobile.com>
** 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<QByteArray> 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<QByteArray> 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 <aaron.mccarthy@jollamobile.com>
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
@@ -45,6 +47,7 @@
#include <QBasicTimer>
#include <QTimerEvent>
#include <QTimer>
+#include <QtCore/QtNumeric>
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().
*/
@@ -482,6 +505,41 @@ QNmeaPositionInfoSource::~QNmeaPositionInfoSource()
}
/*!
+ 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.
The default implementation will parse standard NMEA sentences.
@@ -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;
diff --git a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp
index 4b5f4ff6..c0a57535 100644
--- a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp
+++ b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp
@@ -1,5 +1,7 @@
/****************************************************************************
**
+** Copyright (C) 2013 Jolla Ltd.
+** Contact: Aaron McCarthy <aaron.mccarthy@jollamobile.com>
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
@@ -43,6 +45,8 @@
#include "tst_qnmeapositioninfosource.h"
+#include <QtCore/QtNumeric>
+
#ifdef Q_OS_WIN
// Windows seems to require longer timeouts and step length
@@ -103,6 +107,14 @@ void tst_QNmeaPositionInfoSource::minimumUpdateInterval()
QCOMPARE(source.minimumUpdateInterval(), 100);
}
+void tst_QNmeaPositionInfoSource::userEquivalentRangeError()
+{
+ QNmeaPositionInfoSource source(m_mode);
+ QVERIFY(qIsNaN(source.userEquivalentRangeError()));
+ source.setUserEquivalentRangeError(5.1);
+ QVERIFY(qFuzzyCompare(source.userEquivalentRangeError(), 5.1));
+}
+
void tst_QNmeaPositionInfoSource::setUpdateInterval_delayedUpdate()
{
// If an update interval is set, and an update is not available at a
@@ -348,8 +360,11 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime()
QFETCH(QByteArray, bytes);
QFETCH(QList<QDateTime>, dateTimes);
+ QFETCH(QList<bool>, expectHorizontalAccuracy);
+ QFETCH(QList<bool>, expectVerticalAccuracy);
QNmeaPositionInfoSource source(m_mode);
+ source.setUserEquivalentRangeError(5.1);
QNmeaPositionInfoSourceProxyFactory factory;
QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
@@ -359,14 +374,33 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime()
proxy->feedBytes(bytes);
QTRY_COMPARE(spy.count(), dateTimes.count());
- for (int i=0; i<spy.count(); i++)
- QCOMPARE(spy[i][0].value<QGeoPositionInfo>().timestamp(), dateTimes[i]);
+ for (int i=0; i<spy.count(); i++) {
+ QGeoPositionInfo pInfo = spy[i][0].value<QGeoPositionInfo>();
+
+ QCOMPARE(pInfo.timestamp(), dateTimes[i]);
+
+ // Generated GGA/GSA sentences have hard coded HDOP of 3.5, which corrisponds to a
+ // horizontal accuracy of 35.7, for the user equivalent range error of 5.1 set above.
+ QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy),
+ expectHorizontalAccuracy[i]);
+ if (pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
+ QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::HorizontalAccuracy), 35.7));
+
+ // Generate GSA sentences have hard coded VDOP of 4.0, which corrisponds to a vertical
+ // accuracy of 40.8, for the user equivalent range error of 5.1 set above.
+ QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy),
+ expectVerticalAccuracy[i]);
+ if (pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
+ QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::VerticalAccuracy), 40.8));
+ }
}
void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data()
{
QTest::addColumn<QByteArray>("bytes");
QTest::addColumn<QList<QDateTime> >("dateTimes");
+ QTest::addColumn<QList<bool> >("expectHorizontalAccuracy");
+ QTest::addColumn<QList<bool> >("expectVerticalAccuracy");
QDateTime dt = QDateTime::currentDateTime().toUTC();
QByteArray bytes;
@@ -376,7 +410,9 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data()
bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1();
bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1();
QTest::newRow("Feed GGA,RMC,GGA; expect RMC, second GGA only")
- << bytes << (QList<QDateTime>() << dt.addSecs(2) << dt.addSecs(3));
+ << bytes << (QList<QDateTime>() << dt.addSecs(2) << dt.addSecs(3))
+ << (QList<bool>() << true << true)
+ << (QList<bool>() << false << false);
// should not receive ZDA (has no coordinates) but should get the GGA
// sentence after it since it got the date/time from ZDA
@@ -385,7 +421,20 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data()
bytes += QLocationTestUtils::createZdaSentence(dt.addSecs(2)).toLatin1();
bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1();
QTest::newRow("Feed GGA,ZDA,GGA; expect second GGA only")
- << bytes << (QList<QDateTime>() << dt.addSecs(3));
+ << bytes << (QList<QDateTime>() << dt.addSecs(3))
+ << (QList<bool>() << true)
+ << (QList<bool>() << false);
+
+ // Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA.
+ bytes.clear();
+ bytes += QLocationTestUtils::createZdaSentence(dt.addSecs(1)).toLatin1();
+ bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(2).time()).toLatin1();
+ bytes += QLocationTestUtils::createGsaSentence().toLatin1();
+ bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1();
+ QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA")
+ << bytes << (QList<QDateTime>() << dt.addSecs(2) << dt.addSecs(3))
+ << (QList<bool>() << true << true)
+ << (QList<bool>() << false << true);
if (m_mode == QNmeaPositionInfoSource::SimulationMode) {
// In sim m_mode, should ignore sentence with a date/time before the known date/time
@@ -395,7 +444,9 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data()
bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(-2)).toLatin1();
bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1();
QTest::newRow("Feed good RMC, RMC with bad date/time, good RMC; expect first and third RMC only")
- << bytes << (QList<QDateTime>() << dt.addSecs(1) << dt.addSecs(2));
+ << bytes << (QList<QDateTime>() << dt.addSecs(1) << dt.addSecs(2))
+ << (QList<bool>() << false << false)
+ << (QList<bool>() << false << false);
}
}
diff --git a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h
index d541465d..9d837933 100644
--- a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h
+++ b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h
@@ -96,6 +96,8 @@ private slots:
void minimumUpdateInterval();
+ void userEquivalentRangeError();
+
void setUpdateInterval_delayedUpdate();
void lastKnownPosition();
diff --git a/tests/auto/utils/qlocationtestutils.cpp b/tests/auto/utils/qlocationtestutils.cpp
index 97e4f8d9..d5eed344 100644
--- a/tests/auto/utils/qlocationtestutils.cpp
+++ b/tests/auto/utils/qlocationtestutils.cpp
@@ -93,3 +93,8 @@ QString QLocationTestUtils::createZdaSentence(const QDateTime &dt)
.arg(time).arg(dt.toString("dd")).arg(dt.toString("MM")).arg(dt.toString("yyyy"));
return addNmeaChecksumAndBreaks(nmea);
}
+
+QString QLocationTestUtils::createGsaSentence()
+{
+ return addNmeaChecksumAndBreaks(QStringLiteral("$GPGSA,A,3,,,,,,,,,,,,,3.0,3.5,4.0*"));
+}
diff --git a/tests/auto/utils/qlocationtestutils_p.h b/tests/auto/utils/qlocationtestutils_p.h
index 2c827d41..6ed5de38 100644
--- a/tests/auto/utils/qlocationtestutils_p.h
+++ b/tests/auto/utils/qlocationtestutils_p.h
@@ -57,6 +57,7 @@ namespace QLocationTestUtils
QString createGgaSentence(const QTime &time);
QString createGgaSentence(int lat, int lng, const QTime &time);
QString createZdaSentence(const QDateTime &dt);
+ QString createGsaSentence();
//The purpose of compareEquality() is to test equality
//operators where it is expected that A == B.