diff options
author | Paolo Angelelli <paolo.angelelli@qt.io> | 2019-05-10 19:55:33 +0200 |
---|---|---|
committer | paolo <paolo.angelelli@qt.io> | 2019-08-15 10:08:13 +0200 |
commit | f613eb3a6031d6d4f3e5ee230fab3056314fa9a4 (patch) | |
tree | 804e4d47b2eed988c1af0847f023b81dae4afc96 /src/positioning | |
parent | 3334c6f6d00255f6fd5690e4b9cb05416950fd4d (diff) | |
download | qtlocation-f613eb3a6031d6d4f3e5ee230fab3056314fa9a4.tar.gz |
Add Satellite support to serialnmea plugin
This adds a new class, QNmeaSatelliteInfoSource, locally
to the plugin, that behaves similarly to
QNmeaPositionInfoSource in the way of handling the
IODevice and producing the updates.
Change-Id: Id594152dd70514974ac79c7757ce6f0da4631191
Fixes: QTBUG-59274
Reviewed-by: Paolo Angelelli <paolo.angelelli@qt.io>
Diffstat (limited to 'src/positioning')
-rw-r--r-- | src/positioning/qgeosatelliteinfo.cpp | 61 | ||||
-rw-r--r-- | src/positioning/qgeosatelliteinfo.h | 2 | ||||
-rw-r--r-- | src/positioning/qgeosatelliteinfo_p.h | 67 | ||||
-rw-r--r-- | src/positioning/qgeosatelliteinfosource.cpp | 108 | ||||
-rw-r--r-- | src/positioning/qgeosatelliteinfosource.h | 6 | ||||
-rw-r--r-- | src/positioning/qgeosatelliteinfosource_p.h | 70 | ||||
-rw-r--r-- | src/positioning/qlocationutils.cpp | 102 | ||||
-rw-r--r-- | src/positioning/qlocationutils_p.h | 27 |
8 files changed, 390 insertions, 53 deletions
diff --git a/src/positioning/qgeosatelliteinfo.cpp b/src/positioning/qgeosatelliteinfo.cpp index e62bd164..91ebfa85 100644 --- a/src/positioning/qgeosatelliteinfo.cpp +++ b/src/positioning/qgeosatelliteinfo.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ #include "qgeosatelliteinfo.h" +#include "qgeosatelliteinfo_p.h" #include <QHash> #include <QDebug> @@ -44,16 +45,6 @@ QT_BEGIN_NAMESPACE -class QGeoSatelliteInfoPrivate -{ -public: - int signal; - int satId; - QGeoSatelliteInfo::SatelliteSystem system; - QHash<int, qreal> doubleAttribs; -}; - - /*! \class QGeoSatelliteInfo \inmodule QtPositioning @@ -103,6 +94,10 @@ QGeoSatelliteInfo::QGeoSatelliteInfo(const QGeoSatelliteInfo &other) operator=(other); } +QGeoSatelliteInfo::QGeoSatelliteInfo(QGeoSatelliteInfoPrivate &dd) : d(&dd) +{ +} + /*! Destroys a satellite information object. */ @@ -119,10 +114,9 @@ QGeoSatelliteInfo &QGeoSatelliteInfo::operator=(const QGeoSatelliteInfo & other) if (this == &other) return *this; - d->signal = other.d->signal; - d->satId = other.d->satId; - d->system = other.d->system; - d->doubleAttribs = other.d->doubleAttribs; + delete d; + d = other.d->clone(); + return *this; } @@ -132,10 +126,7 @@ QGeoSatelliteInfo &QGeoSatelliteInfo::operator=(const QGeoSatelliteInfo & other) */ bool QGeoSatelliteInfo::operator==(const QGeoSatelliteInfo &other) const { - return d->signal == other.d->signal - && d->satId == other.d->satId - && d->system == other.d->system - && d->doubleAttribs == other.d->doubleAttribs; + return *d == *other.d; } /*! @@ -309,6 +300,40 @@ QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info) info.d->system = (QGeoSatelliteInfo::SatelliteSystem)system; return stream; } + +QGeoSatelliteInfoPrivate::QGeoSatelliteInfoPrivate() +{ + +} + +QGeoSatelliteInfoPrivate::QGeoSatelliteInfoPrivate(const QGeoSatelliteInfoPrivate &other) +{ + signal = other.signal; + satId = other.satId; + system = other.system; + doubleAttribs = other.doubleAttribs; +} + +QGeoSatelliteInfoPrivate::~QGeoSatelliteInfoPrivate() {} + +QGeoSatelliteInfoPrivate *QGeoSatelliteInfoPrivate::clone() const +{ + return new QGeoSatelliteInfoPrivate(*this); +} + +bool QGeoSatelliteInfoPrivate::operator==(const QGeoSatelliteInfoPrivate &other) const +{ + return signal == other.signal + && satId == other.satId + && system == other.system + && doubleAttribs == other.doubleAttribs; +} + +QGeoSatelliteInfoPrivate *QGeoSatelliteInfoPrivate::get(const QGeoSatelliteInfo &info) +{ + return info.d; +} + #endif QT_END_NAMESPACE diff --git a/src/positioning/qgeosatelliteinfo.h b/src/positioning/qgeosatelliteinfo.h index e68d8d9f..28766257 100644 --- a/src/positioning/qgeosatelliteinfo.h +++ b/src/positioning/qgeosatelliteinfo.h @@ -63,6 +63,7 @@ public: QGeoSatelliteInfo(); QGeoSatelliteInfo(const QGeoSatelliteInfo &other); + QGeoSatelliteInfo(QGeoSatelliteInfoPrivate &dd); ~QGeoSatelliteInfo(); QGeoSatelliteInfo &operator=(const QGeoSatelliteInfo &other); @@ -96,6 +97,7 @@ private: friend Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info); #endif QGeoSatelliteInfoPrivate *d; + friend class QGeoSatelliteInfoPrivate; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/positioning/qgeosatelliteinfo_p.h b/src/positioning/qgeosatelliteinfo_p.h new file mode 100644 index 00000000..6320bf2e --- /dev/null +++ b/src/positioning/qgeosatelliteinfo_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFO_P_H +#define QGEOSATELLITEINFO_P_H + +#include <QtPositioning/private/qpositioningglobal_p.h> +#include <QtPositioning/qgeosatelliteinfo.h> +#include <QHash> + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QGeoSatelliteInfoPrivate +{ +public: + QGeoSatelliteInfoPrivate(); + QGeoSatelliteInfoPrivate(const QGeoSatelliteInfoPrivate &other); + virtual ~QGeoSatelliteInfoPrivate(); + virtual QGeoSatelliteInfoPrivate *clone() const; + virtual bool operator==(const QGeoSatelliteInfoPrivate &other) const; + static QGeoSatelliteInfoPrivate *get(const QGeoSatelliteInfo &info); + + int signal; + int satId; + QGeoSatelliteInfo::SatelliteSystem system; + QHash<int, qreal> doubleAttribs; +}; + +QT_END_NAMESPACE + +#endif // QGEOSATELLITEINFO_P_H diff --git a/src/positioning/qgeosatelliteinfosource.cpp b/src/positioning/qgeosatelliteinfosource.cpp index c55c36d3..6796b921 100644 --- a/src/positioning/qgeosatelliteinfosource.cpp +++ b/src/positioning/qgeosatelliteinfosource.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ #include <qgeosatelliteinfosource.h> +#include <qgeosatelliteinfosource_p.h> #include "qgeopositioninfosourcefactory.h" #include "qgeopositioninfosource_p.h" #include <QPluginLoader> @@ -84,16 +85,20 @@ QT_BEGIN_NAMESPACE update intervals, as returned by minimumUpdateInterval(). */ -class QGeoSatelliteInfoSourcePrivate -{ -public: - int interval; - QString providerName; -}; - /*! Creates a satellite source with the specified \a parent. */ + +QGeoSatelliteInfoSourcePrivate::~QGeoSatelliteInfoSourcePrivate() +{ + +} + +QGeoSatelliteInfoSourcePrivate *QGeoSatelliteInfoSourcePrivate::get(QGeoSatelliteInfoSource &source) +{ + return source.d; +} + QGeoSatelliteInfoSource::QGeoSatelliteInfoSource(QObject *parent) : QObject(parent), d(new QGeoSatelliteInfoSourcePrivate) @@ -101,6 +106,13 @@ QGeoSatelliteInfoSource::QGeoSatelliteInfoSource(QObject *parent) d->interval = 0; } +QGeoSatelliteInfoSource::QGeoSatelliteInfoSource(QGeoSatelliteInfoSourcePrivate &dd, QObject *parent) +: QObject(parent), + d(&dd) +{ + +} + /*! Destroys the satellite source. */ @@ -153,7 +165,21 @@ int QGeoSatelliteInfoSource::updateInterval() const return d->interval; } - +static QGeoSatelliteInfoSource* createSource_real(const QJsonObject &meta, const QVariantMap ¶meters, QObject *parent) +{ + QGeoPositionInfoSourcePrivate d; + d.metaData = meta; + d.loadPlugin(); + QGeoSatelliteInfoSource *s = nullptr; + if (!parameters.isEmpty() && d.factoryV2) + s = d.factoryV2->satelliteInfoSourceWithParameters(parent, parameters); + else if (d.factory) + s = d.factory->satelliteInfoSource(parent); + if (s) + QGeoSatelliteInfoSourcePrivate::get(*s)->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); + + return s; +} /*! Creates and returns a source with the specified \a parent that reads @@ -165,6 +191,34 @@ int QGeoSatelliteInfoSource::updateInterval() const */ QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createDefaultSource(QObject *parent) { + return createDefaultSource(QVariantMap(), parent); +} + +/*! + Creates and returns a source with the given \a parent, + by loading the plugin named \a sourceName. + + Returns 0 if the plugin cannot be found. +*/ +QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createSource(const QString &sourceName, QObject *parent) +{ + return createSource(sourceName, QVariantMap(), parent); +} + +/*! + Creates and returns a satellite source with the given \a parent that + reads from the system's default sources of satellite data, or the plugin + with the highest available priority. + + Returns nullptr if the system has no default satellite source, no valid plugins + could be found or the user does not have the permission to access the satellite information. + + This method passes \a parameters to the factory to configure the source. + + \since Qt 5.14 +*/ +QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createDefaultSource(const QVariantMap ¶meters, QObject *parent) +{ QList<QJsonObject> plugins = QGeoPositionInfoSourcePrivate::pluginsSorted(); foreach (const QJsonObject &obj, plugins) { if (obj.value(QStringLiteral("Satellite")).isBool() @@ -176,43 +230,29 @@ QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createDefaultSource(QObject *p if (inTest) continue; } - QGeoPositionInfoSourcePrivate d; - d.metaData = obj; - d.loadPlugin(); - QGeoSatelliteInfoSource *s = 0; - if (d.factory) - s = d.factory->satelliteInfoSource(parent); - if (s) - s->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); - return s; + return createSource_real(obj, parameters, parent); } } - return 0; + return nullptr; } /*! - Creates and returns a source with the given \a parent, + Creates and returns a satellite source with the given \a parent, by loading the plugin named \a sourceName. - Returns 0 if the plugin cannot be found. + Returns nullptr if the plugin cannot be found. + + This method passes \a parameters to the factory to configure the source. + + \since Qt 5.14 */ -QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createSource(const QString &sourceName, QObject *parent) +QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createSource(const QString &sourceName, const QVariantMap ¶meters, QObject *parent) { QHash<QString, QJsonObject> plugins = QGeoPositionInfoSourcePrivate::plugins(); - if (plugins.contains(sourceName)) { - QGeoPositionInfoSourcePrivate d; - d.metaData = plugins.value(sourceName); - d.loadPlugin(); - QGeoSatelliteInfoSource *src = 0; - if (d.factory) - src = d.factory->satelliteInfoSource(parent); - if (src) - src->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); - return src; - } - - return 0; + if (plugins.contains(sourceName)) + return createSource_real(plugins.value(sourceName), parameters, parent); + return nullptr; } /*! diff --git a/src/positioning/qgeosatelliteinfosource.h b/src/positioning/qgeosatelliteinfosource.h index 391eefcf..4f073864 100644 --- a/src/positioning/qgeosatelliteinfosource.h +++ b/src/positioning/qgeosatelliteinfosource.h @@ -67,6 +67,8 @@ public: static QGeoSatelliteInfoSource *createDefaultSource(QObject *parent); static QGeoSatelliteInfoSource *createSource(const QString &sourceName, QObject *parent); + static QGeoSatelliteInfoSource *createDefaultSource(const QVariantMap ¶meters, QObject *parent); + static QGeoSatelliteInfoSource *createSource(const QString &sourceName, const QVariantMap ¶meters, QObject *parent); static QStringList availableSources(); QString sourceName() const; @@ -88,9 +90,13 @@ Q_SIGNALS: void requestTimeout(); void error(QGeoSatelliteInfoSource::Error); +protected: + QGeoSatelliteInfoSource(QGeoSatelliteInfoSourcePrivate &dd, QObject *parent); + private: Q_DISABLE_COPY(QGeoSatelliteInfoSource) QGeoSatelliteInfoSourcePrivate *d; + friend class QGeoSatelliteInfoSourcePrivate; }; QT_END_NAMESPACE diff --git a/src/positioning/qgeosatelliteinfosource_p.h b/src/positioning/qgeosatelliteinfosource_p.h new file mode 100644 index 00000000..58001b21 --- /dev/null +++ b/src/positioning/qgeosatelliteinfosource_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_P_H +#define QGEOSATELLITEINFOSOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtPositioning/private/qpositioningglobal_p.h> +#include <QString> + +QT_BEGIN_NAMESPACE +class QGeoSatelliteInfoSource; +class Q_POSITIONING_PRIVATE_EXPORT QGeoSatelliteInfoSourcePrivate +{ +public: + virtual ~QGeoSatelliteInfoSourcePrivate(); + static QGeoSatelliteInfoSourcePrivate *get(QGeoSatelliteInfoSource &source); + int interval; + QString providerName; +}; + +QT_END_NAMESPACE + +#endif // QGEOSATELLITEINFOSOURCE_P_H diff --git a/src/positioning/qlocationutils.cpp b/src/positioning/qlocationutils.cpp index f5062eb6..fec8ccba 100644 --- a/src/positioning/qlocationutils.cpp +++ b/src/positioning/qlocationutils.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qlocationutils_p.h" #include "qgeopositioninfo.h" +#include "qgeosatelliteinfo.h" #include <QTime> #include <QList> @@ -124,6 +125,25 @@ static void qlocationutils_readGsa(const char *data, int size, QGeoPositionInfo } } +static void qlocationutils_readGsa(const char *data, + int size, + QList<int> &pnrsInUse) +{ + QList<QByteArray> parts = QByteArray::fromRawData(data, size).split(','); + pnrsInUse.clear(); + if (parts.count() <= 2) + return; + bool ok; + for (int i = 3; i <= qMin(14, parts.size()); ++i) { + const QByteArray &pnrString = parts.at(i); + if (pnrString.isEmpty()) + continue; + int pnr = pnrString.toInt(&ok); + if (ok) + pnrsInUse.append(pnr); + } +} + static void qlocationutils_readGll(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) { QByteArray sentence(data, size); @@ -269,6 +289,9 @@ QLocationUtils::NmeaSentence QLocationUtils::getNmeaSentenceType(const char *dat if (data[3] == 'G' && data[4] == 'S' && data[5] == 'A') return NmeaSentenceGSA; + if (data[3] == 'G' && data[4] == 'S' && data[5] == 'V') + return NmeaSentenceGSV; + if (data[3] == 'G' && data[4] == 'L' && data[5] == 'L') return NmeaSentenceGLL; @@ -329,6 +352,85 @@ bool QLocationUtils::getPosInfoFromNmea(const char *data, int size, QGeoPosition } } +QLocationUtils::GSVParseStatus QLocationUtils::getSatInfoFromNmea(const char *data, int size, QList<QGeoSatelliteInfo> &infos) +{ + if (!data || !size) + return GSVNotParsed; + + NmeaSentence nmeaType = getNmeaSentenceType(data, size); + if (nmeaType != NmeaSentenceGSV) + return GSVNotParsed; + + QList<QByteArray> parts = QByteArray::fromRawData(data, size).split(','); + + if (parts.count() <= 3) { + infos.clear(); + return GSVFullyParsed; // Malformed sentence. + } + bool ok; + const int totalSentences = parts.at(1).toInt(&ok); + if (!ok) { + infos.clear(); + return GSVFullyParsed; // Malformed sentence. + } + + const int sentence = parts.at(2).toInt(&ok); + if (!ok) { + infos.clear(); + return GSVFullyParsed; // Malformed sentence. + } + + const int totalSats = parts.at(3).toInt(&ok); + if (!ok) { + infos.clear(); + return GSVFullyParsed; // Malformed sentence. + } + + if (sentence == 1) + infos.clear(); + + const int numSatInSentence = qMin(sentence * 4, totalSats) - (sentence - 1) * 4; + + int field = 4; + for (int i = 0; i < numSatInSentence; ++i) { + QGeoSatelliteInfo info; + const int prn = parts.at(field++).toInt(&ok); + info.setSatelliteIdentifier((ok) ? prn : 0); + const int elevation = parts.at(field++).toInt(&ok); + info.setAttribute(QGeoSatelliteInfo::Elevation, (ok) ? elevation : 0); + const int azimuth = parts.at(field++).toInt(&ok); + info.setAttribute(QGeoSatelliteInfo::Azimuth, (ok) ? azimuth : 0); + const int snr = parts.at(field++).toInt(&ok); + info.setSignalStrength((ok) ? snr : -1); + infos.append(info); + } + + if (sentence == totalSentences) + return GSVFullyParsed; + return GSVPartiallyParsed; +} + +bool QLocationUtils::getSatInUseFromNmea(const char *data, int size, QList<int> &pnrsInUse) +{ + pnrsInUse.clear(); + if (!data || !size) + return false; + + NmeaSentence nmeaType = getNmeaSentenceType(data, size); + if (nmeaType != NmeaSentenceGSA) + 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; + } + } + qlocationutils_readGsa(data, size, pnrsInUse); + return true; +} + bool QLocationUtils::hasValidNmeaChecksum(const char *data, int size) { int asteriskIndex = -1; diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h index e3881f6f..e2d739e7 100644 --- a/src/positioning/qlocationutils_p.h +++ b/src/positioning/qlocationutils_p.h @@ -65,6 +65,7 @@ class QTime; class QByteArray; class QGeoPositionInfo; +class QGeoSatelliteInfo; class Q_POSITIONING_PRIVATE_EXPORT QLocationUtils { public: @@ -94,7 +95,8 @@ public: NmeaSentenceGLL, // Lat/Lon data NmeaSentenceRMC, // Recommended minimum data for gps NmeaSentenceVTG, // Vector track an Speed over the Ground - NmeaSentenceZDA // Date and Time + NmeaSentenceZDA, // Date and Time + NmeaSentenceGSV // Per-Satellite Info }; inline static bool isValidLat(double lat) { @@ -282,6 +284,29 @@ public: bool *hasFix = nullptr); /* + Retruns a list of QGeoSatelliteInfo in the view. + + Note: this function has to be called repeatedly until it returns true. + Reason being that GSV sentences can be split into multiple samples, so getting the full data + requires parsing multiple sentences. + */ + enum GSVParseStatus { + GSVNotParsed, + GSVPartiallyParsed, + GSVFullyParsed + }; + static GSVParseStatus getSatInfoFromNmea(const char *data, + int size, + QList<QGeoSatelliteInfo> &infos); + + /* + Parses GSA for satellites in use. + */ + static bool getSatInUseFromNmea(const char *data, + int size, + QList<int> &pnrsInUse); + + /* Returns true if the given NMEA sentence has a valid checksum. */ static bool hasValidNmeaChecksum(const char *data, int size); |