diff options
Diffstat (limited to 'src/positioning/qnmeasatelliteinfosource.cpp')
-rw-r--r-- | src/positioning/qnmeasatelliteinfosource.cpp | 909 |
1 files changed, 0 insertions, 909 deletions
diff --git a/src/positioning/qnmeasatelliteinfosource.cpp b/src/positioning/qnmeasatelliteinfosource.cpp deleted file mode 100644 index a27ac2b0..00000000 --- a/src/positioning/qnmeasatelliteinfosource.cpp +++ /dev/null @@ -1,909 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qnmeasatelliteinfosource.h" -#include "private/qnmeasatelliteinfosource_p.h" -#include <QtPositioning/private/qgeosatelliteinfo_p.h> -#include <QtPositioning/private/qgeosatelliteinfosource_p.h> -#include <QtPositioning/private/qlocationutils_p.h> - -#include <QIODevice> -#include <QBasicTimer> -#include <QTimerEvent> -#include <QTimer> -#include <array> -#include <QDebug> -#include <QtCore/QtNumeric> - -QT_BEGIN_NAMESPACE - -#if USE_NMEA_PIMPL -class QGeoSatelliteInfoPrivateNmea : public QGeoSatelliteInfoPrivate -{ -public: - QGeoSatelliteInfoPrivateNmea(const QGeoSatelliteInfoPrivate &other); - QGeoSatelliteInfoPrivateNmea(const QGeoSatelliteInfoPrivateNmea &other); - virtual ~QGeoSatelliteInfoPrivateNmea(); - - QList<QByteArray> nmeaSentences; -}; - -QGeoSatelliteInfoPrivateNmea::QGeoSatelliteInfoPrivateNmea(const QGeoSatelliteInfoPrivate &other) - : QGeoSatelliteInfoPrivate(other) -{ -} - -QGeoSatelliteInfoPrivateNmea::QGeoSatelliteInfoPrivateNmea(const QGeoSatelliteInfoPrivateNmea &other) - : QGeoSatelliteInfoPrivate(other) -{ - nmeaSentences = other.nmeaSentences; -} - -QGeoSatelliteInfoPrivateNmea::~QGeoSatelliteInfoPrivateNmea() {} -#else -typedef QGeoSatelliteInfoPrivate QGeoSatelliteInfoPrivateNmea; -#endif - -QNmeaSatelliteInfoSourcePrivate::QNmeaSatelliteInfoSourcePrivate(QNmeaSatelliteInfoSource *parent, QNmeaSatelliteInfoSource::UpdateMode updateMode) - : m_source(parent), - m_updateMode(updateMode) -{ -} - -void QNmeaSatelliteInfoSourcePrivate::notifyNewUpdate() -{ - if (m_pendingUpdate.isValid() && m_pendingUpdate.isFresh()) { - if (m_requestTimer && m_requestTimer->isActive()) { // User called requestUpdate() - m_requestTimer->stop(); - emitUpdated(m_pendingUpdate, true); - } else if (m_invokedStart) { // user called startUpdates() - if (m_updateTimer && m_updateTimer->isActive()) { // update interval > 0 - // for periodic updates, only want the most recent update - if (m_noUpdateLastInterval) { - // if the update was invalid when timerEvent was last called, a valid update - // should be sent ASAP - emitPendingUpdate(); // m_noUpdateLastInterval handled in there. - } - } else { // update interval <= 0, send anything new ASAP - m_noUpdateLastInterval = !emitUpdated(m_pendingUpdate, false); - } - } - } -} - -void QNmeaSatelliteInfoSourcePrivate::processNmeaData(QNmeaSatelliteInfoUpdate &updateInfo) -{ - char buf[1024]; - qint64 size = m_device->readLine(buf, sizeof(buf)); - QList<int> satInUse; - const auto satSystemType = m_source->parseSatellitesInUseFromNmea(buf, size, satInUse); - if (satSystemType != QGeoSatelliteInfo::Undefined) { - const bool res = updateInfo.setSatellitesInUse(satSystemType, satInUse); -#if USE_NMEA_PIMPL - if (res) { - updateInfo.gsa = QByteArray(buf, size); - auto &info = updateInfo.m_satellites[satSystemType]; - if (info.satellitesInUse.size()) { - for (auto &s : info.satellitesInUse) { - static_cast<QGeoSatelliteInfoPrivateNmea *>(QGeoSatelliteInfoPrivate::get(s)) - ->nmeaSentences.append(updateInfo.gsa); - } - for (auto &s : info.satellitesInView) { - static_cast<QGeoSatelliteInfoPrivateNmea *>(QGeoSatelliteInfoPrivate::get(s)) - ->nmeaSentences.append(updateInfo.gsa); - } - } - } -#else - Q_UNUSED(res) -#endif // USE_NMEA_PIMPL - } else { - // Here we have the assumption that multiple parts of GSV sentence - // come one after another. At least this is how it should be. - auto systemType = QGeoSatelliteInfo::Undefined; - const auto parserStatus = m_source->parseSatelliteInfoFromNmea( - buf, size, updateInfo.m_satellitesInViewParsed, systemType); - if (parserStatus == QNmeaSatelliteInfoSource::PartiallyParsed) { - updateInfo.m_satellites[systemType].updatingGSV = true; -#if USE_NMEA_PIMPL - updateInfo.gsv.append(QByteArray(buf, size)); -#endif - } else if (parserStatus == QNmeaSatelliteInfoSource::FullyParsed) { -#if USE_NMEA_PIMPL - updateInfo.gsv.append(QByteArray(buf, size)); - for (int i = 0; i < updateInfo.m_satellitesInViewParsed.size(); i++) { - const QGeoSatelliteInfo &s = updateInfo.m_satellitesInViewParsed.at(i); - QGeoSatelliteInfoPrivateNmea *pimpl = - new QGeoSatelliteInfoPrivateNmea(*QGeoSatelliteInfoPrivate::get(s)); - pimpl->nmeaSentences.append(updateInfo.gsa); - pimpl->nmeaSentences.append(updateInfo.gsv); - updateInfo.m_satellitesInViewParsed.replace(i, QGeoSatelliteInfo(*pimpl)); - } - updateInfo.gsv.clear(); -#endif - updateInfo.setSatellitesInView(systemType, updateInfo.m_satellitesInViewParsed); - } - } -} - -QNmeaSatelliteInfoSourcePrivate::~QNmeaSatelliteInfoSourcePrivate() -{ - delete m_updateTimer; -} - -void QNmeaSatelliteInfoSourcePrivate::startUpdates() -{ - if (m_invokedStart) - return; - - m_satelliteError = QGeoSatelliteInfoSource::NoError; - - m_invokedStart = true; - m_pendingUpdate.clear(); - m_noUpdateLastInterval = false; - - bool initialized = initialize(); - if (!initialized) - return; - - if (m_updateMode == QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode) { - // skip over any buffered data - we only want the newest data. - // Don't do this in requestUpdate. In that case bufferedData is good to have/use. - if (m_device->bytesAvailable()) { - if (m_device->isSequential()) - m_device->readAll(); - else - m_device->seek(m_device->bytesAvailable()); - } - } - - if (m_updateTimer) - m_updateTimer->stop(); - - if (m_source->updateInterval() > 0) { - if (!m_updateTimer) - m_updateTimer = new QBasicTimer; - m_updateTimer->start(m_source->updateInterval(), this); - } - - if (initialized) - prepareSourceDevice(); -} - -void QNmeaSatelliteInfoSourcePrivate::stopUpdates() -{ - m_invokedStart = false; - if (m_updateTimer) - m_updateTimer->stop(); - m_pendingUpdate.clear(); - m_noUpdateLastInterval = false; -} - -void QNmeaSatelliteInfoSourcePrivate::requestUpdate(int msec) -{ - if (m_requestTimer && m_requestTimer->isActive()) - return; - - m_satelliteError = QGeoSatelliteInfoSource::NoError; - - if (msec <= 0 || msec < m_source->minimumUpdateInterval()) { - m_source->setError(QGeoSatelliteInfoSource::UpdateTimeoutError); - return; - } - - if (!m_requestTimer) { - m_requestTimer = new QTimer(this); - connect(m_requestTimer, SIGNAL(timeout()), SLOT(updateRequestTimeout())); - } - - bool initialized = initialize(); - if (!initialized) { - m_source->setError(QGeoSatelliteInfoSource::UpdateTimeoutError); - return; - } - - m_requestTimer->start(msec); - prepareSourceDevice(); -} - -void QNmeaSatelliteInfoSourcePrivate::readyRead() -{ - if (m_nmeaReader) - m_nmeaReader->readAvailableData(); -} - -void QNmeaSatelliteInfoSourcePrivate::emitPendingUpdate() -{ - if (m_pendingUpdate.isValid() && m_pendingUpdate.isFresh()) { - m_updateTimeoutSent = false; - m_noUpdateLastInterval = false; - if (!emitUpdated(m_pendingUpdate, false)) - m_noUpdateLastInterval = true; - // m_pendingUpdate.clear(); // Do not clear, it will be incrementally updated - } else { // invalid or not fresh update - if (m_noUpdateLastInterval && !m_updateTimeoutSent) { - m_updateTimeoutSent = true; - m_source->setError(QGeoSatelliteInfoSource::UpdateTimeoutError); - } - m_noUpdateLastInterval = true; - } -} - -void QNmeaSatelliteInfoSourcePrivate::sourceDataClosed() -{ - if (m_nmeaReader && m_device && m_device->bytesAvailable()) - m_nmeaReader->readAvailableData(); -} - -void QNmeaSatelliteInfoSourcePrivate::updateRequestTimeout() -{ - m_requestTimer->stop(); - m_source->setError(QGeoSatelliteInfoSource::UpdateTimeoutError); -} - -bool QNmeaSatelliteInfoSourcePrivate::openSourceDevice() -{ - if (!m_device) { - qWarning("QNmeaSatelliteInfoSource: no QIODevice data source, call setDevice() first"); - return false; - } - - if (!m_device->isOpen() && !m_device->open(QIODevice::ReadOnly)) { - qWarning("QNmeaSatelliteInfoSource: cannot open QIODevice data source"); - return false; - } - - connect(m_device, SIGNAL(aboutToClose()), SLOT(sourceDataClosed())); - connect(m_device, SIGNAL(readChannelFinished()), SLOT(sourceDataClosed())); - connect(m_device, SIGNAL(destroyed()), SLOT(sourceDataClosed())); - - return true; -} - -bool QNmeaSatelliteInfoSourcePrivate::initialize() -{ - if (m_nmeaReader) - return true; - - if (!openSourceDevice()) - return false; - - if (m_updateMode == QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode) - m_nmeaReader.reset(new QNmeaSatelliteRealTimeReader(this)); - else - m_nmeaReader.reset(new QNmeaSatelliteSimulationReader(this)); - - return true; -} - -void QNmeaSatelliteInfoSourcePrivate::prepareSourceDevice() -{ - // some data may already be available - if (m_updateMode == QNmeaSatelliteInfoSource::UpdateMode::SimulationMode) { - if (m_nmeaReader && m_device->bytesAvailable()) - m_nmeaReader->readAvailableData(); - } - - if (!m_connectedReadyRead) { - connect(m_device, SIGNAL(readyRead()), SLOT(readyRead())); - m_connectedReadyRead = true; - } -} - -bool QNmeaSatelliteInfoSourcePrivate::emitUpdated(QNmeaSatelliteInfoUpdate &update, - bool fromRequestUpdate) -{ - bool emitted = false; - if (!update.isFresh()) - return emitted; - - update.consume(); - bool inUseUpdated = false; - bool inViewUpdated = false; - if (!fromRequestUpdate) { - // we need to send update if information from at least one satellite - // systems has changed - for (auto it = update.m_satellites.cbegin(); it != update.m_satellites.cend(); ++it) { - inUseUpdated |= - it->satellitesInUse != m_lastUpdate.m_satellites[it.key()].satellitesInUse; - inViewUpdated |= - it->satellitesInView != m_lastUpdate.m_satellites[it.key()].satellitesInView; - } - } else { - // if we come here from requestUpdate(), we need to emit, even if the data - // didn't really change - inUseUpdated = true; - inViewUpdated = true; - } - - m_lastUpdate = update; - if (update.m_validInUse && inUseUpdated) { - emit m_source->satellitesInUseUpdated(update.allSatellitesInUse()); - emitted = true; - } - if (update.m_validInView && inViewUpdated) { - emit m_source->satellitesInViewUpdated(update.allSatellitesInView()); - emitted = true; - } - return emitted; -} - -void QNmeaSatelliteInfoSourcePrivate::timerEvent(QTimerEvent * /*event*/) -{ - emitPendingUpdate(); -} - -/*! - \class QNmeaSatelliteInfoSource - \inmodule QtPositioning - \ingroup QtPositioning-positioning - \since 6.2 - - \brief The \l QNmeaSatelliteInfoSource class provides satellite information - using an NMEA data source. - - NMEA is a commonly used protocol for the specification of one's global - position at a certain point in time. The \l QNmeaSatelliteInfoSource class - reads NMEA data and uses it to provide information about satellites in view - and satellites in use in form of lists of \l QGeoSatelliteInfo objects. - - A \l QNmeaSatelliteInfoSource instance operates in either \l {RealTimeMode} - or \l {SimulationMode}. These modes allow NMEA data to be read from either a - live source of data, or replayed for simulation purposes from previously - recorded NMEA data. - - The source of NMEA data is set via \l setDevice(). - - Use \l startUpdates() to start receiving regular satellite information - updates and \l stopUpdates() to stop these updates. If you only require - updates occasionally, you can call \l requestUpdate() to request a single - update of both satellites in view and satellites in use. - - The information about satellites in view is received via the - \l satellitesInViewUpdated() signal. - - The information about satellites in use is received via the - \l satellitesInUseUpdated() signal. -*/ - -/*! - \enum QNmeaSatelliteInfoSource::UpdateMode - Defines the available update modes. - - \value RealTimeMode Satellite information is read and distributed from the - data source as it becomes available. Use this mode if you are using - a live source of NMEA data (for example a GPS hardware device). - \value SimulationMode Satellite information is read and distributed from the - data source at the given rate. The rate is determined by the - \l {QNmeaSatelliteInfoSource::}{SimulationUpdateInterval} parameter. - Use this mode if the data source contains previously recorded NMEA - data and you want to replay the data for simulation purposes. -*/ - -/*! - \variable QNmeaSatelliteInfoSource::SimulationUpdateInterval - \brief The backend property name for data read rate in the - \l SimulationMode. The value for this property is the integer number - representing the amount of milliseconds between the subsequent reads. - Use this parameter in the \l {QNmeaSatelliteInfoSource::} - {setBackendProperty()} and \l {QNmeaSatelliteInfoSource::}{backendProperty()} - methods. - - \note This property is different from the interval that can be set via - \l {setUpdateInterval()}. The value set via \l {setUpdateInterval()} - denotes an interval for the user notification, while this parameter - specifies the internal frequency of reading the data from source file. It - means that one can have multiple (or none) reads during the - \l {updateInterval()} period. -*/ -QString QNmeaSatelliteInfoSource::SimulationUpdateInterval = - QStringLiteral("nmea.satellite_info_simulation_interval"); - -/*! - Constructs a \l QNmeaSatelliteInfoSource instance with the given \a parent - and \a mode. -*/ -QNmeaSatelliteInfoSource::QNmeaSatelliteInfoSource(UpdateMode mode, QObject *parent) - : QGeoSatelliteInfoSource(parent), - d(new QNmeaSatelliteInfoSourcePrivate(this, mode)) -{ -} - -/*! - Destroys the satellite information source. -*/ -QNmeaSatelliteInfoSource::~QNmeaSatelliteInfoSource() -{ - delete d; -} - -/*! - Returns the update mode. -*/ -QNmeaSatelliteInfoSource::UpdateMode QNmeaSatelliteInfoSource::updateMode() const -{ - return d->m_updateMode; -} - -/*! - Sets the NMEA data source to \a device. If the device is not open, it - will be opened in QIODevice::ReadOnly mode. - - The source device can only be set once and must be set before calling - \l startUpdates() or \l requestUpdate(). - - \note The \a device must emit \l {QIODevice::readyRead()} for the - source to be notified when data is available for reading. - \l QNmeaSatelliteInfoSource does not assume the ownership of the device, - and hence does not deallocate it upon destruction. -*/ -void QNmeaSatelliteInfoSource::setDevice(QIODevice *device) -{ - if (device != d->m_device) { - if (!d->m_device) - d->m_device = device; - else - qWarning("QNmeaSatelliteInfoSource: source device has already been set"); - } -} - -/*! - Returns the NMEA data source. -*/ -QIODevice *QNmeaSatelliteInfoSource::device() const -{ - return d->m_device; -} - -/*! - \reimp -*/ -void QNmeaSatelliteInfoSource::setUpdateInterval(int msec) -{ - int interval = msec; - if (interval != 0) - interval = qMax(msec, minimumUpdateInterval()); - QGeoSatelliteInfoSource::setUpdateInterval(interval); - if (d->m_invokedStart) { - d->stopUpdates(); - d->startUpdates(); - } -} - -/*! - \reimp -*/ -int QNmeaSatelliteInfoSource::minimumUpdateInterval() const -{ - return 2; // Some chips are capable of over 100 updates per seconds. -} - -/*! - \reimp -*/ -QGeoSatelliteInfoSource::Error QNmeaSatelliteInfoSource::error() const -{ - return d->m_satelliteError; -} - -/*! - \reimp -*/ -bool QNmeaSatelliteInfoSource::setBackendProperty(const QString &name, const QVariant &value) -{ - if (name == SimulationUpdateInterval && d->m_updateMode == UpdateMode::SimulationMode) { - bool ok = false; - const int interval = value.toInt(&ok); - if (ok) { - auto *reader = dynamic_cast<QNmeaSatelliteSimulationReader *>(d->m_nmeaReader.get()); - if (reader) { - reader->setUpdateInterval(interval); - } else { - // d->m_nmeaReader will use it in constructor - d->m_simulationUpdateInterval = interval; - } - return true; - } - } - return false; -} - -/*! - \reimp -*/ -QVariant QNmeaSatelliteInfoSource::backendProperty(const QString &name) const -{ - if (name == SimulationUpdateInterval && d->m_updateMode == UpdateMode::SimulationMode) { - auto *reader = dynamic_cast<QNmeaSatelliteSimulationReader *>(d->m_nmeaReader.get()); - if (reader) - return reader->updateInterval(); - else - return d->m_simulationUpdateInterval; - } - return QVariant(); -} - -/*! - \reimp -*/ -void QNmeaSatelliteInfoSource::startUpdates() -{ - d->startUpdates(); -} - -/*! - \reimp -*/ -void QNmeaSatelliteInfoSource::stopUpdates() -{ - d->stopUpdates(); -} - -/*! - \reimp -*/ -void QNmeaSatelliteInfoSource::requestUpdate(int msec) -{ - d->requestUpdate(msec == 0 ? 60000 * 5 : msec); // 5min default timeout -} - -/*! - Parses an NMEA sentence string to extract the IDs of satelites in use. - - The default implementation will parse standard NMEA $GPGSA sentences. - This method should be reimplemented in a subclass whenever the need to deal - with non-standard NMEA sentences arises. - - The parser reads \a size bytes from \a data and uses that information to - fill \a pnrsInUse list. - - Returns system type if the sentence was successfully parsed, otherwise - returns \l QGeoSatelliteInfo::Undefined and should not modifiy \a pnrsInUse. -*/ -QGeoSatelliteInfo::SatelliteSystem -QNmeaSatelliteInfoSource::parseSatellitesInUseFromNmea(const char *data, int size, - QList<int> &pnrsInUse) -{ - return QLocationUtils::getSatInUseFromNmea(data, size, pnrsInUse); -} - -/*! - \enum QNmeaSatelliteInfoSource::SatelliteInfoParseStatus - Defines the parse status of satellite information. The satellite information - can be split into multiple sentences, and we need to parse all of them. - \value NotParsed The data does not contain information about satellites. - \value PartiallyParsed A valid satellite information is received and parsed, - but it's not complete, so we need to wait for another NMEA sentence. - \value FullyParsed Satellite information was fully collected and parsed. -*/ - -/*! - Parses an NMEA sentence string to extract the information about satellites - in view. - - The default implementation will parse standard NMEA $GPGSV sentences. - This method should be reimplemented in a subclass whenever the need to deal - with non-standard NMEA sentences arises. - - The parser reads \a size bytes from \a data and uses that information to - fill \a infos list. - - Returns \l SatelliteInfoParseStatus with parse result. - Modifies \a infos list in case \l {QNmeaSatelliteInfoSource::} - {PartiallyParsed} or \l {QNmeaSatelliteInfoSource::}{FullyParsed} is - returned. - Also sets the \a system to correct satellite system type. This is required - to determine the system type in case there are no satellites in view. -*/ -QNmeaSatelliteInfoSource::SatelliteInfoParseStatus -QNmeaSatelliteInfoSource::parseSatelliteInfoFromNmea(const char *data, int size, - QList<QGeoSatelliteInfo> &infos, - QGeoSatelliteInfo::SatelliteSystem &system) -{ - return static_cast<SatelliteInfoParseStatus>( - QLocationUtils::getSatInfoFromNmea(data, size, infos, system)); -} - -void QNmeaSatelliteInfoSource::setError(QGeoSatelliteInfoSource::Error satelliteError) -{ - d->m_satelliteError = satelliteError; - if (d->m_satelliteError != QGeoSatelliteInfoSource::NoError) - emit QGeoSatelliteInfoSource::errorOccurred(satelliteError); -} - -QList<QGeoSatelliteInfo> QNmeaSatelliteInfoUpdate::allSatellitesInUse() const -{ - QList<QGeoSatelliteInfo> result; - for (const auto &s : m_satellites) - result.append(s.satellitesInUse); - return result; -} - -QList<QGeoSatelliteInfo> QNmeaSatelliteInfoUpdate::allSatellitesInView() const -{ - QList<QGeoSatelliteInfo> result; - for (const auto &s : m_satellites) - result.append(s.satellitesInView); - return result; -} - -void QNmeaSatelliteInfoUpdate::setSatellitesInView(QGeoSatelliteInfo::SatelliteSystem system, - const QList<QGeoSatelliteInfo> &inView) -{ - auto &info = m_satellites[system]; - info.updatingGSV = false; - - info.satellitesInView = inView; - info.validInView = true; - - if (!info.satellitesInUseReceived) { - // Normally a GSA message should come after a GSV message. If this flag - // is not set, we have received 2 consecutive GSV messages for this - // system without a GSA in between. - // This means that we could actually receive a $GNGSA empty message for - // this specific type. As it had no ids and GN talker id, we could not - // determine system type. This most probably means that we have no - // satellites in use for this system type. - // Clear satellites in use, if any. - info.satellitesInUse.clear(); - info.inUseIds.clear(); - info.validInUse = true; - } - info.satellitesInUseReceived = false; - - if (info.satellitesInView.isEmpty()) { - // If we received an empty list of satellites in view, then the list of - // satellites in use will also be empty, so we would not be able to - // match it with correct system type in case of $GNGSA message. Clear - // the list in advance. - info.satellitesInUse.clear(); - info.inUseIds.clear(); - info.validInUse = true; - } else if (!info.inUseIds.isEmpty()) { - // We have some satellites in use cached. Check if we have received the - // proper GSV for them. - info.satellitesInUse.clear(); - info.validInUse = false; - bool corrupt = false; - for (const auto id : info.inUseIds) { - bool found = false; - for (const auto &s : info.satellitesInView) { - if (s.satelliteIdentifier() == id) { - info.satellitesInUse.append(s); - found = true; - break; - } - } - if (!found) { - // The previoulsy received GSA is incorrect or not related to - // this GSV - info.satellitesInUse.clear(); - corrupt = true; - break; - } - } - info.validInUse = !corrupt; - info.inUseIds.clear(); - } - - m_validInUse = calculateValidInUse(); - m_validInView = calculateValidInView(); - m_fresh = true; -} - -bool QNmeaSatelliteInfoUpdate::setSatellitesInUse(QGeoSatelliteInfo::SatelliteSystem system, - const QList<int> &inUse) -{ - if (system == QGeoSatelliteInfo::Undefined || system == QGeoSatelliteInfo::Multiple) - return false; // No way to determine satellite system - - SatelliteInfo &info = m_satellites[system]; - info.satellitesInUse.clear(); - - info.satellitesInUseReceived = true; - info.inUseIds = inUse; - - if (info.updatingGSV) { - info.validInView = false; - m_validInView = false; - return false; - } - - for (const auto id : inUse) { - bool found = false; - for (const auto &s : info.satellitesInView) { - if (s.satelliteIdentifier() == id) { - info.satellitesInUse.append(s); - found = true; - break; - } - } - if (!found) { - // satellites in use are not in view -> related GSV is not yet received - info.satellitesInView.clear(); - info.validInView = false; - m_validInView = false; - return false; - } - } - - info.inUseIds.clear(); // make sure we remove all obsolete cache - - info.validInUse = true; - m_fresh = true; - m_validInUse = calculateValidInUse(); - - return true; -} - -void QNmeaSatelliteInfoUpdate::consume() -{ - m_fresh = false; -} - -bool QNmeaSatelliteInfoUpdate::isFresh() const -{ - return m_fresh; -} - -void QNmeaSatelliteInfoUpdate::clear() -{ - m_satellites.clear(); - m_satellitesInViewParsed.clear(); - m_validInView = false; - m_validInUse = false; - m_fresh = false; -#if USE_NMEA_PIMPL - gsa.clear(); - gsv.clear(); -#endif -} - -bool QNmeaSatelliteInfoUpdate::isValid() const -{ - // GSV without GSA is valid. GSA with outdated but still matching GSV also valid. - return m_validInView || m_validInUse; -} - -bool QNmeaSatelliteInfoUpdate::calculateValidInUse() const -{ - for (const auto &s : m_satellites) { - if (!s.validInUse) - return false; - } - return true; -} - -bool QNmeaSatelliteInfoUpdate::calculateValidInView() const -{ - for (const auto &s : m_satellites) { - if (!s.validInView) - return false; - } - return true; -} - -QNmeaSatelliteReader::QNmeaSatelliteReader(QNmeaSatelliteInfoSourcePrivate *sourcePrivate) - : m_proxy(sourcePrivate) -{ -} - -QNmeaSatelliteReader::~QNmeaSatelliteReader() -{ -} - -QNmeaSatelliteRealTimeReader::QNmeaSatelliteRealTimeReader(QNmeaSatelliteInfoSourcePrivate *sourcePrivate) - : QNmeaSatelliteReader(sourcePrivate) -{ -} - -void QNmeaSatelliteRealTimeReader::readAvailableData() -{ - while (m_proxy->m_device->canReadLine()) - m_proxy->processNmeaData(m_proxy->m_pendingUpdate); - m_proxy->notifyNewUpdate(); -} - -QNmeaSatelliteSimulationReader::QNmeaSatelliteSimulationReader(QNmeaSatelliteInfoSourcePrivate *sourcePrivate) - : QNmeaSatelliteReader(sourcePrivate) -{ - m_timer.reset(new QTimer); - QObject::connect(m_timer.get(), &QTimer::timeout, [this]() { - readAvailableData(); - }); - m_updateInterval = - qMax(m_proxy->m_simulationUpdateInterval, m_proxy->m_source->minimumUpdateInterval()); -} - -void QNmeaSatelliteSimulationReader::readAvailableData() -{ - if (!m_timer->isActive()) { - // At the very first start we just start a timer to simulate a short - // delay for overlapping requestUpdate() calls. - // See TestQGeoSatelliteInfoSource::requestUpdate_overlappingCalls and - // TestQGeoSatelliteInfoSource::requestUpdate_overlappingCallsWithTimeout - m_timer->start(m_updateInterval); - } else { - // Here we try to get both satellites in view and satellites in use. - // We behave like that because according to the QGeoSatelliteInfoSource - // tests each call to requestUpdate() should return both satellites in - // view and satellites in use. Same is expected on each interval for - // startUpdates(). - // However user-provided NMEA logs might not contain some of the - // messages, so we will try not to get stuck here infinitely. - int numSatInUseMsgs = 0; - int numSatInViewMsgs = 0; - while (!numSatInUseMsgs || !numSatInViewMsgs) { - m_proxy->processNmeaData(m_proxy->m_pendingUpdate); - if (m_proxy->m_pendingUpdate.m_validInUse) - numSatInUseMsgs++; - if (m_proxy->m_pendingUpdate.m_validInView) - numSatInViewMsgs++; - // if we got the second message for one of them, but still didn't - // receive any for the other - break. - // We use 2 in the comparison, because, as soon as the m_validIn* - // flag is set, it will stay true until we receive invalid message. - if (numSatInUseMsgs > 2 || numSatInViewMsgs > 2) { - const QString msgType = (numSatInUseMsgs > numSatInViewMsgs) - ? QStringLiteral("GSA") - : QStringLiteral("GSV"); - qWarning() << "nmea simulation reader: possibly incorrect message order. Got too " - "many consecutive" - << msgType << "messages"; - break; - } - } - m_proxy->notifyNewUpdate(); - } -} - -void QNmeaSatelliteSimulationReader::setUpdateInterval(int msec) -{ - // restart the timer with new interval - m_updateInterval = qMax(msec, m_proxy->m_source->minimumUpdateInterval()); - if (m_timer->isActive()) - m_timer->start(m_updateInterval); -} - -int QNmeaSatelliteSimulationReader::updateInterval() const -{ - return m_updateInterval; -} - -QT_END_NAMESPACE |