Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qohosgeopositioninfosource.h"
#include "qohosgeosatelliteinfosource.h"
#include "qohospositioncommon.h"
#include "qohospositioningenums.h"
#include <QtCore/private/qcore_ohos_p.h>
#include <QtCore/private/qnapi_p.h>
#include <QtCore/private/qohoscommon_p.h>
#include <QtCore/private/qohosjstools_p.h>
#include <QtCore/private/qohoslogger_p.h>
#include <QtCore/qset.h>
#include <QtCore/qmath.h>
#include <QtPositioning/qgeosatelliteinfo.h>
#include <algorithm>
#include <chrono>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
QT_BEGIN_NAMESPACE
namespace {
constexpr int updateAsOftenAsNecessaryInterval = 0;
using SatelliteConstellationCategory =
QtOhosPositioning::enums::ohos::geoLocationManager::SatelliteConstellationCategory;
struct SatelliteInfo
{
int id;
double carrierToNoiseDensity;
double altitude;
double azimuth;
std::optional<SatelliteConstellationCategory> optConstellationCategory;
bool usedInFix;
};
std::optional<std::vector<SatelliteConstellationCategory>>
tryGetSatelliteConstelationCategoriesFromSatelliteStatusInfoObject(
QOhosJsState &jsState, QNapi::Object satelliteStatusInfoObject)
{
auto satelliteConstellationArray = QNapi::getOptionalPropOrEmpty<QNapi::Array>(
satelliteStatusInfoObject, "satelliteConstellation");
return !satelliteConstellationArray.IsEmpty()
? std::make_optional(
QNapi::getArrayElements<std::vector<SatelliteConstellationCategory>, QNapi::Number>(
satelliteConstellationArray,
[&](auto satelliteConstellation) {
return jsState.mapOhosEnumFromJs<SatelliteConstellationCategory>(satelliteConstellation);
}))
: std::nullopt;
}
std::optional<std::vector<int>> tryGetSatelliteAdditionalInfosFromSatelliteStatusInfoObject(
QOhosJsState &, QNapi::Object satelliteStatusInfoObject)
{
auto satelliteAdditionalInfoArray = QNapi::getOptionalPropOrEmpty<QNapi::Array>(
satelliteStatusInfoObject, "satelliteAdditionalInfo");
return !satelliteAdditionalInfoArray.IsEmpty()
? std::make_optional(
QNapi::getArrayElements<std::vector<int>, QNapi::Number>(satelliteAdditionalInfoArray))
: std::nullopt;
}
bool isSatelliteUsedInFix(QOhosJsState &jsState, int satelliteAdditionalInfo)
{
int satelliteUsedInValueFlag = jsState.eval<QNapi::Number>(
"@ohos.geoLocationManager.SatelliteAdditionalInfo.SATELLITES_ADDITIONAL_INFO_USED_IN_FIX");
return (satelliteAdditionalInfo & satelliteUsedInValueFlag) != 0;
}
std::vector<SatelliteInfo> convertJsSatelliteStatusInfoObjectToQGeoSatelliteInfos(
QOhosJsState &jsState, QNapi::Object satelliteStatusInfoObject)
{
int satellitesNumber = satelliteStatusInfoObject.get<QNapi::Number>("satellitesNumber");
auto satelliteIds = QNapi::getArrayElements<std::vector<int>, QNapi::Number>(
satelliteStatusInfoObject.get<QNapi::Array>("satelliteIds"));
auto carrierToNoiseDensities = QNapi::getArrayElements<std::vector<double>, QNapi::Number>(
satelliteStatusInfoObject.get<QNapi::Array>("carrierToNoiseDensitys"));
auto altitudes = QNapi::getArrayElements<std::vector<double>, QNapi::Number>(
satelliteStatusInfoObject.get<QNapi::Array>("altitudes"));
auto azimuths = QNapi::getArrayElements<std::vector<double>, QNapi::Number>(
satelliteStatusInfoObject.get<QNapi::Array>("azimuths"));
auto satelliteConstellations = tryGetSatelliteConstelationCategoriesFromSatelliteStatusInfoObject(
jsState, satelliteStatusInfoObject);
auto satelliteAdditionalInfos = tryGetSatelliteAdditionalInfosFromSatelliteStatusInfoObject(
jsState, satelliteStatusInfoObject);
std::vector<SatelliteInfo> satelliteInfos;
satelliteInfos.reserve(satellitesNumber);
for (int i = 0; i < satellitesNumber; i++) {
satelliteInfos.push_back(SatelliteInfo{
.id = satelliteIds[i],
.carrierToNoiseDensity = carrierToNoiseDensities[i],
.altitude = altitudes[i],
.azimuth = azimuths[i],
.optConstellationCategory = satelliteConstellations.has_value()
? std::make_optional(satelliteConstellations.value()[i])
: std::nullopt,
.usedInFix = satelliteAdditionalInfos.has_value()
? isSatelliteUsedInFix(jsState, satelliteAdditionalInfos.value()[i])
: false
});
}
return satelliteInfos;
}
QGeoSatelliteInfo::SatelliteSystem convertSatelliteConstellationCategoryToQt(
SatelliteConstellationCategory constellationCategory)
{
switch (constellationCategory) {
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_UNKNOWN:
return QGeoSatelliteInfo::Undefined;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_GPS:
return QGeoSatelliteInfo::GPS;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_GLONASS:
return QGeoSatelliteInfo::GLONASS;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_SBAS:
return QGeoSatelliteInfo::SBAS;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_QZSS:
return QGeoSatelliteInfo::QZSS;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_BEIDOU:
return QGeoSatelliteInfo::BEIDOU;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_GALILEO:
return QGeoSatelliteInfo::GALILEO;
case SatelliteConstellationCategory::CONSTELLATION_CATEGORY_IRNSS:
return QGeoSatelliteInfo::IRNSS;
}
qOhosReportFatalErrorAndAbort(
"%s Received an unknown SatelliteConstellationCategory: %d",
Q_FUNC_INFO, constellationCategory);
}
std::shared_ptr<void> registerSatelliteStatusInfoProducerConsumer(
QObject *contextObject, QOhosConsumer<std::vector<SatelliteInfo>> satelliteStatusInfoConsumer)
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject;
auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
auto sharedSatelliteStatusInfoConsumer =
QtOhos::moveToSharedPtr(std::move(satelliteStatusInfoConsumer));
auto weakSatelliteStatusInfoConsumer = QtOhos::makeWeakPtr(sharedSatelliteStatusInfoConsumer);
auto registrationHandle = QOhosJsThreadGateway::eval(
[&](QOhosJsState &jsState) {
return QtOhos::makeProxyWithJsThreadDeleter(
registerQOhosOnOffMethodsBasedEventHandler(
getGeoLocationManagerObject(jsState), "satelliteStatusChange",
[contextObjectRef, weakSatelliteStatusInfoConsumer](const QOhosCallbackInfo &cbInfo) {
auto satelliteStatusInfo = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
auto satelliteInfos = convertJsSatelliteStatusInfoObjectToQGeoSatelliteInfos(
cbInfo.jsState(), satelliteStatusInfo);
contextObjectRef.visitInQtThreadIfAlive(
[weakSatelliteStatusInfoConsumer, satelliteInfos](auto &) {
auto sharedSatelliteStatusInfoConsumer = weakSatelliteStatusInfoConsumer.lock();
if (sharedSatelliteStatusInfoConsumer)
(*sharedSatelliteStatusInfoConsumer)(satelliteInfos);
});
}));
});
return QtOhos::moveToSharedPtr(
std::make_pair(registrationHandle, sharedSatelliteStatusInfoConsumer));
}
class QOhosGeoSatelliteInfoSource : public QGeoSatelliteInfoSource
{
public:
QOhosGeoSatelliteInfoSource(QObject *parent);
~QOhosGeoSatelliteInfoSource() override;
void setUpdateInterval(int msec) override;
int minimumUpdateInterval() const override;
Error error() const override;
public:
void startUpdates() override;
void stopUpdates() override;
void requestUpdate(int timeout) override;
private:
enum UpdateType
{
SingleUpdate,
ContinuousUpdate,
};
void startUpdatesHelper();
void setErrorHelper(Error error);
void handleSatelliteStatusInfoUpdate(std::vector<SatelliteInfo> updatedSatelliteStatusInfo);
void handleSingleUpdateFinished();
void handleUpdateFinished();
QSet<UpdateType> m_updateTypes;
std::shared_ptr<void> m_updateSatelliteStatusInfoProducerHandle;
std::unique_ptr<QGeoPositionInfoSource> m_positionInfoSource;
std::unique_ptr<QTimer> m_singleUpdateTimeoutTimer;
std::optional<QList<QGeoSatelliteInfo>> m_lastUpdatedSatellitesInView;
std::optional<QList<QGeoSatelliteInfo>> m_lastUpdatedSatellitesInUse;
QGeoSatelliteInfoSource::Error m_error = QGeoSatelliteInfoSource::NoError;
};
QOhosGeoSatelliteInfoSource::QOhosGeoSatelliteInfoSource(QObject *parent)
: QGeoSatelliteInfoSource(parent)
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
}
QOhosGeoSatelliteInfoSource::~QOhosGeoSatelliteInfoSource() = default;
void QOhosGeoSatelliteInfoSource::setUpdateInterval(int msec)
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO << msec;
if (msec != updateAsOftenAsNecessaryInterval)
qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Specific update intervals are not supported";
}
int QOhosGeoSatelliteInfoSource::minimumUpdateInterval() const
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
return updateAsOftenAsNecessaryInterval;
}
QGeoSatelliteInfoSource::Error QOhosGeoSatelliteInfoSource::error() const
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
return m_error;
}
void QOhosGeoSatelliteInfoSource::startUpdates()
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
if (!checkLocationOrApproximatelyLocationPermissionGranted()) {
qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Location permission not granted. Can't start updates";
setErrorHelper(QGeoSatelliteInfoSource::AccessError);
return;
}
if (m_updateTypes.contains(UpdateType::ContinuousUpdate))
return;
m_updateTypes.insert(UpdateType::ContinuousUpdate);
if (!m_updateSatelliteStatusInfoProducerHandle && !m_positionInfoSource)
startUpdatesHelper();
}
void QOhosGeoSatelliteInfoSource::stopUpdates()
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
m_updateTypes.remove(UpdateType::ContinuousUpdate);
handleUpdateFinished();
}
void QOhosGeoSatelliteInfoSource::requestUpdate(int timeout)
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO << timeout;
if (!checkLocationOrApproximatelyLocationPermissionGranted()) {
qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Location permission not granted. Can't request update";
setErrorHelper(QGeoSatelliteInfoSource::AccessError);
return;
}
if (m_updateTypes.contains(UpdateType::SingleUpdate))
return;
m_updateTypes.insert(UpdateType::SingleUpdate);
if (!m_updateSatelliteStatusInfoProducerHandle && !m_positionInfoSource)
startUpdatesHelper();
m_singleUpdateTimeoutTimer = makeSingleShotUpdateTimeoutTimer(
timeout, [this]() {
if (m_lastUpdatedSatellitesInUse.has_value() && m_lastUpdatedSatellitesInView.has_value()) {
Q_EMIT satellitesInUseUpdated(m_lastUpdatedSatellitesInUse.value());
Q_EMIT satellitesInViewUpdated(m_lastUpdatedSatellitesInView.value());
} else {
Q_EMIT errorOccurred(QGeoSatelliteInfoSource::UpdateTimeoutError);
}
handleSingleUpdateFinished();
});
}
void QOhosGeoSatelliteInfoSource::startUpdatesHelper()
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO;
m_updateSatelliteStatusInfoProducerHandle =
registerSatelliteStatusInfoProducerConsumer(
this, [this](auto updatedSatelliteInfos) {
handleSatelliteStatusInfoUpdate(updatedSatelliteInfos);
});
m_positionInfoSource = std::unique_ptr<QGeoPositionInfoSource>(tryMakeQOhosGeoPositionInfoSource());
if (!m_positionInfoSource)
qOhosReportFatalErrorAndAbort("%s positionInfoSource is invalid", Q_FUNC_INFO);
m_positionInfoSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
m_positionInfoSource->startUpdates();
}
void QOhosGeoSatelliteInfoSource::setErrorHelper(Error error)
{
qOhosWarning(QtForOhos) << Q_FUNC_INFO << error;
if (m_error == error)
return;
m_error = error;
if (error != QGeoSatelliteInfoSource::NoError)
Q_EMIT QGeoSatelliteInfoSource::errorOccurred(m_error);
}
void QOhosGeoSatelliteInfoSource::handleSatelliteStatusInfoUpdate(
std::vector<SatelliteInfo> updatedSatellites)
{
qOhosDebug(QtForOhos) << Q_FUNC_INFO << "found" << updatedSatellites.size() << "satellites";
QList<QGeoSatelliteInfo> satellitesInView;
QList<QGeoSatelliteInfo> satellitesInUse;
QSet<std::pair<int, int>> seenIds;
for (const auto &updatedSatellite : updatedSatellites) {
if (qFuzzyIsNull(updatedSatellite.carrierToNoiseDensity))
continue;
QGeoSatelliteInfo satelliteInfo;
satelliteInfo.setSatelliteIdentifier(updatedSatellite.id);
satelliteInfo.setSatelliteSystem(
updatedSatellite.optConstellationCategory.has_value()
? convertSatelliteConstellationCategoryToQt(updatedSatellite.optConstellationCategory.value())
: QGeoSatelliteInfo::SatelliteSystem::Undefined);
satelliteInfo.setSignalStrength(updatedSatellite.carrierToNoiseDensity);
satelliteInfo.setAttribute(QGeoSatelliteInfo::Azimuth, updatedSatellite.azimuth);
satelliteInfo.setAttribute(QGeoSatelliteInfo::Elevation, updatedSatellite.altitude);
const auto uid = std::make_pair(static_cast<int>(satelliteInfo.satelliteSystem()),
satelliteInfo.satelliteIdentifier());
if (seenIds.contains(uid))
continue;
seenIds.insert(uid);
satellitesInView.append(satelliteInfo);
if (updatedSatellite.usedInFix)
satellitesInUse.append(satelliteInfo);
}
Q_EMIT satellitesInUseUpdated(satellitesInUse);
Q_EMIT satellitesInViewUpdated(satellitesInView);
m_lastUpdatedSatellitesInView = satellitesInView;
m_lastUpdatedSatellitesInUse = satellitesInUse;
if (m_updateTypes.contains(UpdateType::SingleUpdate))
handleSingleUpdateFinished();
}
void QOhosGeoSatelliteInfoSource::handleSingleUpdateFinished()
{
m_updateTypes.remove(UpdateType::SingleUpdate);
m_singleUpdateTimeoutTimer.reset();
handleUpdateFinished();
}
void QOhosGeoSatelliteInfoSource::handleUpdateFinished()
{
if (m_updateTypes.empty()) {
m_positionInfoSource.reset();
m_updateSatelliteStatusInfoProducerHandle.reset();
m_lastUpdatedSatellitesInView.reset();
m_lastUpdatedSatellitesInUse.reset();
}
}
}
QGeoSatelliteInfoSource *makeQOhosGeoSatelliteInfoSource(QObject *parent)
{
return new QOhosGeoSatelliteInfoSource(parent);
}
QT_END_NAMESPACE