QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSReceiverSession.cc
Go to the documentation of this file.
2
3#include <cmath>
4#include <limits>
5#include <utility>
6
9#include "PositionManager.h"
10#include "QGCLoggingCategory.h"
11
12QGC_LOGGING_CATEGORY(GPSReceiverSessionLog, "GPS.Receiver.GPSReceiverSession")
13
14void GPSBaseStationState::applySurvey(const GPSSurveyReport& report)
15{
16 if (!mode || *mode == BaseModeDefinition::Mode::BaseFixed) {
17 return;
18 }
19 const bool located =
20 std::isfinite(report.position.latitudeDegrees) && std::isfinite(report.position.longitudeDegrees);
21 const double accuracy = report.meanAccuracyMeters.value_or(
22 surveyAccuracyLimitMeters.value_or(std::numeric_limits<double>::quiet_NaN()));
23 if (report.valid && located && std::isfinite(accuracy)) {
24 position = std::pair(report.position, accuracy);
25 } else {
26 position.reset();
27 }
28}
29
31 bool allowPersistentChanges, QString* error)
32{
33 GPSReceiverConfig config{.baudRate = baudRate, .allowPersistentChanges = allowPersistentChanges};
34 if (type != GPSType::passive) {
35 config.base = base;
36 // The option is hidden for receivers that cannot send MSM4, so it never blocks their connection.
37 config.base.compactObservations = base.compactObservations && gpsReceiverCapabilities(type).compactObservations;
38 }
39 QString diagnostic = gpsReceiverConfigError(type, config);
40 if (error) {
41 *error = std::move(diagnostic);
42 }
43 return config;
44}
45
47{
49 if (type == GPSType::passive) {
50 return state;
51 }
52 if (const auto* fixed = std::get_if<GPSBaseStationConfig::Fixed>(&config.base.mode)) {
54 state.position = std::pair(fixed->position, static_cast<double>(fixed->accuracyMeters));
55 } else if (const auto* survey = std::get_if<GPSBaseStationConfig::SurveyIn>(&config.base.mode)) {
57 state.surveyAccuracyLimitMeters = survey->accuracyMeters;
58 } else {
60 }
61 return state;
62}
63
64GPSReceiverSession::GPSReceiverSession(quint64 id, Connection connection, QObject* parent)
65 : QObject(parent)
66 , _sessionId(id)
67 , _connection(std::move(connection))
68 , _base(gpsBaseStationStateFor(_connection.type, _connection.config))
69{
70 qCDebug(GPSReceiverSessionLog) << this;
71}
72
74{
75 retire();
76 qCDebug(GPSReceiverSessionLog) << this;
77}
78
79void GPSReceiverSession::registerCorrections(GPSCorrectionManager* manager, const QString& sourceInstance)
80{
81 if (_retired || !_connection.forwardsCorrections || !manager) {
82 return;
83 }
84 _correction = manager->openSource(GPSCorrectionSettings::LocalReceiver, sourceInstance);
85}
86
89{
90 if (_retired || _worker) {
91 return false;
92 }
93 const GPSType type = _connection.type;
94 _worker = factory ? factory(std::move(transportFactory), type, _connection.config, this)
95 : new GPSReceiverWorker(std::move(transportFactory), type, _connection.config, this);
96 if (!_worker) {
97 return false;
98 }
99 GPSReceiverWorker* const worker = _worker;
100 worker->setEndsWhenIdle(_connection.role == RTKSettings::ConfiguredBase);
101 (void) connect(
103 [this]() {
104 if (!_retired) {
105 emit ended(GPSConnectionError::DeviceError, {});
106 }
107 },
108 Qt::QueuedConnection);
109 (void) connect(worker, &GPSReceiverWorker::finished, worker, &QObject::deleteLater);
110 const auto relay = [this, worker]<typename... Args>(void (GPSReceiverWorker::*signal)(Args...),
111 void (GPSReceiverSession::*handler)(Args...)) {
112 (void) connect(
113 worker, signal, this,
114 [this, handler](Args... args) {
115 if (!_retired) {
116 (this->*handler)(args...);
117 }
118 },
119 Qt::QueuedConnection);
120 };
121 relay(&GPSReceiverWorker::rtcmDataReceived, &GPSReceiverSession::_forwardCorrection);
124 relay(&GPSReceiverWorker::surveyInStatusUpdated, &GPSReceiverSession::_onSurvey);
126 relay(&GPSReceiverWorker::receiverReady, &GPSReceiverSession::_onReady);
127 relay(&GPSReceiverWorker::receiverDetected, &GPSReceiverSession::_onDetected);
129 worker->start();
130 return true;
131}
132
134{
135 if (_retired || !positionManager || _position) {
136 return;
137 }
138 auto registration = positionManager->registerReceiver(producer, _sessionId);
139 // An observer that retired this session during registration leaves the registration to end here.
140 if (!_retired) {
141 _position = std::move(registration);
142 }
143}
144
146{
147 if (std::exchange(_retired, true)) {
148 return;
149 }
150 const QPointer<GPSReceiverWorker> worker = _worker;
151 if (worker) {
152 (void) disconnect(worker, nullptr, this, nullptr);
153 // Registration observers may delete the owner; the worker must already be independent.
154 worker->setParent(nullptr);
155 worker->stop();
156 }
157 _position.reset();
158 _correction.reset();
159}
160
161GPSObservation GPSReceiverSession::observation(const GPSPositionReport& report, quint64 receivedAtUs) const
162{
164 _base.position->second, receivedAtUs)
165 : GPSObservation::fromNavigation(report.navigation, receivedAtUs);
166 observation.sessionId = _sessionId;
167 return observation;
168}
169
170void GPSReceiverSession::_onSurvey(const GPSSurveyReport& report)
171{
172 if (_connection.role != RTKSettings::ConfiguredBase) {
173 return;
174 }
175 _base.applySurvey(report);
176 emit surveyInStatusUpdated(report);
177}
178
179void GPSReceiverSession::_onReady(const QString& identity)
180{
181 if (std::exchange(_identity, identity) != identity) {
182 qCDebug(GPSReceiverSessionLog) << "Receiver identity:" << identity;
183 }
185}
186
187void GPSReceiverSession::_onDetected(GPSType type)
188{
189 if (_connection.type != GPSType::automatic && _connection.type != GPSType::passive) {
190 return;
191 }
192 _detectedType = type;
193 qCDebug(GPSReceiverSessionLog) << "Detected receiver family:" << type;
194 emit receiverDetected(type);
195}
196
197void GPSReceiverSession::_forwardCorrection(const QByteArray& data, qint64 receivedAtMs)
198{
199 if (_correction) {
200 _correction->submit(data, receivedAtMs);
201 }
202}
QString gpsReceiverConfigError(GPSType type, const GPSReceiverConfig &config)
An empty diagnostic means the receiver supports the role and all supplied settings.
std::function< GPSReceiverWorker *(GPSReceiverWorker::TransportFactory, GPSType, const GPSReceiverConfig &, QObject *)> GPSReceiverWorkerFactory
Creates a receiver session's worker; tests inject scripted workers.
GPSReceiverCapabilities gpsReceiverCapabilities(GPSType type)
What type supports, for either role; unknown receiver families support nothing.
GPSBaseStationState gpsBaseStationStateFor(GPSType type, const GPSReceiverConfig &config)
The state a session starts from; a fixed base knows its position before the receiver reports.
GPSReceiverConfig gpsReceiverConfigFor(const GPSBaseStationConfig &base, GPSType type, uint32_t baudRate, bool allowPersistentChanges, QString *error)
Config config
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
std::unique_ptr< GPSCorrectionSourceHandle > openSource(GPSCorrectionSettings::CorrectionSource source, const QString &instance)
void registerPosition(PositionManager *positionManager, GPSSourceHealth *producer)
Publishes producer as the receiver position source. Registration observers may retire this session.
GPSReceiverSession(quint64 id, Connection connection, QObject *parent)
void registerCorrections(GPSCorrectionManager *manager, const QString &sourceInstance)
Forwards the receiver's RTCM as the local correction source when the connection asks for it.
void receiverReady(const QString &identity)
GPSObservation observation(const GPSPositionReport &report, quint64 receivedAtUs) const
A base with a known antenna position reports that position instead of its navigation solution.
void ended(GPSConnectionError error, const QString &detail)
The worker failed or exited; the owner ends the session.
void surveyInStatusUpdated(const GPSSurveyReport &report)
Survey progress of a configured base.
QPointer< GPSReceiverWorker > worker() const
The worker, which outlives the session once retired until its thread exits.
bool start(const GPSReceiverWorkerFactory &factory, GPSReceiverWorker::TransportFactory transportFactory)
Creates the worker, with a real worker for an empty factory, and starts it. False when none is create...
void positionUpdated(const GPSPositionReport &report)
const QString & identity() const
void receiverDetected(GPSType type)
The family an Automatic session detected, or the protocol a passive receiver's output carries.
void inputProblem(GPSInputProblem problem)
Why a passive receiver delivers no position; None once positions arrive.
void satelliteInfoUpdated(const GPSSatelliteReport &report)
void inputProblem(GPSInputProblem problem)
void connectionError(GPSConnectionError error, const QString &detail={})
detail is the transport's or the protocol's diagnostic, or empty when it gave none.
void finished()
Delivered on this object's thread once the session thread has finished.
void receiverReady(const QString &identity={})
identity is the receiver model and firmware, or empty when the family does not report them.
void satelliteInfoUpdated(const GPSSatelliteReport &message)
void surveyInStatusUpdated(const GPSSurveyReport &report)
void receiverDetected(GPSType type)
std::function< std::unique_ptr< GPSTransport >(GPSCancelToken)> TransportFactory
Consumed on the worker thread, so transport construction, I/O and destruction share that thread.
void rtcmDataReceived(const QByteArray &message, qint64 receivedAtMs)
RTCM output the receiver sent, stamped with the steady clock when it was read.
void positionUpdated(const GPSPositionReport &report)
Session health is independent of transport readiness and RTK survey-in validity.
GPSPositionSourceRegistration registerReceiver(GPSSourceHealth *producer, quint64 sessionId=0)
Configuration used only by the RTK base-station role.
A receiver session's base-station mode and the antenna position it reports.
void applySurvey(const GPSSurveyReport &report)
A surveying base adopts a valid, located result and otherwise has no position.
std::optional< std::pair< GPSEllipsoidPosition, double > > position
Antenna position and its accuracy in meters once known; the receiver then reports only a time fix.
std::optional< double > surveyAccuracyLimitMeters
Assumed accuracy of a survey-in result that reports none.
std::optional< BaseModeDefinition::Mode > mode
Base mode of a configured base; empty for a passive receiver.
Receiver-independent data. Unknown metadata remains absent, never a manufactured zero.
static GPSObservation fromSurveyedPosition(const GPSEllipsoidPosition &position, double accuracyMeters, quint64 receivedAtUs)
A known antenna position, such as a fixed or surveyed RTK base, which receivers report as a time-only...
static GPSObservation fromNavigation(const GPSNavigationValues &navigation, quint64 receivedAtUs)
Converts a native receiver solution that arrived at receivedAtUs on the monotonic clock.
GPSNavigationValues navigation
uint32_t baudRate
Zero lets a configurable receiver detect the rate; passive serial input requires an explicit rate.
RTKSettings::ReceiverRole role
bool forwardsCorrections
The receiver's RTCM output is a correction source.