QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSReceiver.cc
Go to the documentation of this file.
1#include "GPSReceiver.h"
2
4#include "GPSReceiverConfig.h"
10#include "GPSSourceHealth.h"
11#include "PositionManager.h"
12#include "QGCLoggingCategory.h"
13#include "RuntimeScheduler.h"
14#include "TCPGPSTransport.h"
15#include "UDPGPSTransport.h"
16
17#ifndef QGC_NO_SERIAL_LINK
18#include "SerialGPSTransport.h"
19#include "SerialPortManager.h"
20#endif
21
22#include <algorithm>
23#include <type_traits>
24#include <utility>
25#include <variant>
26
27#include <QtCore/QDeadlineTimer>
28#include <QtCore/QSet>
29
30QGC_LOGGING_CATEGORY(GPSReceiverLog, "GPS.Receiver.GPSReceiver")
31
32namespace {
33
34void logConnectionError(GPSConnectionError error, const QString& detail)
35{
36 switch (error) {
37 case GPSConnectionError::OpenFailed:
38 qCWarning(GPSReceiverLog) << "Failed to open GPS receiver transport:" << detail;
39 break;
40 case GPSConnectionError::ConfigFailed:
41 case GPSConnectionError::ConsentRequired:
42 qCWarning(GPSReceiverLog) << "GPS receiver did not accept configuration";
43 break;
44 case GPSConnectionError::ProtocolError:
45 qCWarning(GPSReceiverLog) << "GPS receiver protocol error, connection lost:" << detail;
46 break;
47 case GPSConnectionError::DeviceError:
48 qCWarning(GPSReceiverLog) << "GPS device error, connection lost:" << detail;
49 break;
50 case GPSConnectionError::None:
51 break;
52 }
53}
54
55} // namespace
56
57GPSReceiver::GPSReceiver(QObject* parent, RuntimeScheduler* scheduler, const Dependencies& dependencies)
58 : QObject(parent)
59 , _correctionManager(dependencies.corrections)
60 , _positionManager(dependencies.positions)
61 , _scheduler(RuntimeScheduler::orDefault(scheduler, this))
62 , _positionHealth(new GPSSourceHealth(this, _scheduler))
63 , _outputOverflowTask(_scheduler, this)
64 , _pollTask(_scheduler, this)
65#ifndef QGC_NO_SERIAL_LINK
66 , _serialPorts(dependencies.serialPorts)
67#endif
68{
69 qCDebug(GPSReceiverLog) << this;
70 // A silent receiver stays connected, so its last solution must not remain on display.
71 connect(_positionHealth, &GPSSourceHealth::positionChanged, this, [this]() {
72 if (_positionHealth->state() == GPSSourceHealth::State::Stale) {
73 _clearStaleSolution();
74 }
75 });
76 _connection = std::make_unique<GPSReceiverConnectionPolicy>(static_cast<GPSReceiverConnector&>(*this),
77 _configuration, _scheduler);
78 _facts = new GPSReceiverFactGroup(this);
79 for (Fact* fact : {_facts->fixType(), _facts->active()}) {
80 connect(fact, &Fact::rawValueChanged, this, &GPSReceiver::_updateLabels);
81 }
82 connect(_facts, &FactGroup::telemetryAvailableChanged, this, &GPSReceiver::_updateLabels);
83 connect(this, &GPSReceiver::receiverChanged, this, &GPSReceiver::_updateLabels);
84 _updateLabels();
85 _notified.receiver = _receiverState();
86#ifndef QGC_NO_SERIAL_LINK
87 _serialTransportFactory = [](const QString& device, GPSCancelToken cancelToken) {
88 return std::make_unique<SerialGPSTransport>(device, std::move(cancelToken));
89 };
90 if (_serialPorts) {
91 connect(_serialPorts, &SerialPortManager::portsEnumerated, this, &GPSReceiver::_onSerialPortsEnumerated);
92 }
93#endif
94}
95
97{
98 if (_configuration == configuration) {
99 return;
100 }
101 if (!_configuration.sameConnection(configuration)) {
102 _setPersistentChangesAllowed(false);
103 }
104 const Configuration previous = std::exchange(_configuration, configuration);
105 _connection->configurationChanged(previous);
106 ++_configurationRevision;
107 _emitChanges();
108}
109
111{
112 _setPersistentChangesAllowed(allowed);
113 _emitChanges();
114}
115
116void GPSReceiver::_setPersistentChangesAllowed(bool allowed)
117{
118 if (!_destroying) {
119 _persistentChangesAllowed = allowed;
120 }
121}
122
127
129{
130 const GPSReceiverPresentation presentation =
131 capabilitiesFor(_configuration.receiverRole, _configuration.baseReceiverManufacturer);
132 if (presentation.passive) {
133 return true;
134 }
135 return std::visit(
136 [&presentation]<typename Mode>(const Mode&) {
137 if constexpr (std::is_same_v<Mode, GPSBaseStationConfig::Fixed>) {
138 return presentation.rtkBase;
139 } else if constexpr (std::is_same_v<Mode, GPSBaseStationConfig::SurveyIn>) {
140 return presentation.surveyIn;
141 } else {
142 return presentation.receiverAveraging;
143 }
144 },
145 _configuration.base.mode);
146}
147
149{
150 const GPSReceiverPresentation presentation =
151 capabilitiesFor(_configuration.receiverRole, _configuration.baseReceiverManufacturer);
152 return (presentation.specificReceiver || presentation.automatic) && baseModeSupported() && connectionSupported();
153}
154
156{
157 if (_destroying) {
158 return;
159 }
160 if (hasReceiver()) {
161 qCWarning(GPSReceiverLog) << "Inject the worker factory before connecting a receiver";
162 return;
163 }
164 _workerFactory = std::move(factory);
165}
166
168{
169 _destroying = true;
170 // The policy must not run against a partially destroyed receiver.
171 _connection.reset();
172 _retireSession();
173
174 qCDebug(GPSReceiverLog) << this;
175}
176
177GPSReceiver::ReceiverState GPSReceiver::_receiverState() const
178{
179 return {.hasReceiver = hasReceiver(),
180 .baseMode = activeBaseMode(),
181 .endpoint = activeEndpoint(),
182 .identity = receiverIdentity(),
183 .detected = detectedReceiver(),
184 .reconnecting = reconnecting(),
185 .presentation = activePresentation(),
186 .role = activeRole(),
187 .forwardingCorrections = forwardingCorrections()};
188}
189
190void GPSReceiver::_emitChanges()
191{
192 if (_destroying) {
193 return;
194 }
195 // Starting or stopping to reconnect the saved receiver is a session change.
196 if (reconnecting() != _notified.receiver.reconnecting) {
197 _setPersistentChangesAllowed(false);
198 }
199 if (std::exchange(_notified.configurationRevision, _configurationRevision) != _configurationRevision) {
201 }
202 if (std::exchange(_notified.persistentChangesAllowed, _persistentChangesAllowed) != _persistentChangesAllowed) {
204 }
205 const QString error = errorMessage();
206 if (std::exchange(_notified.errorMessage, error) != error) {
207 emit errorMessageChanged();
208 }
209 const ReceiverState receiver = _receiverState();
210 if (std::exchange(_notified.receiver, receiver) != receiver) {
211 emit receiverChanged();
212 }
213}
214
215void GPSReceiver::_resetStatus()
216{
217 _facts->_reset();
218 _outputOverflowUs = 0;
219 _outputOverflowTask.cancel();
220 _outputOverflowWarning.clear();
221}
222
223void GPSReceiver::_clearStaleSolution()
224{
225 _facts->_clearSolution();
226 _emitChanges();
227}
228
230{
231 return _errorMessage.isEmpty() ? _outputOverflowWarning : _errorMessage;
232}
233
234void GPSReceiver::_updateOutputOverflow(uint64_t receiptUs)
235{
236 if (receiptUs == 0 || std::exchange(_outputOverflowUs, receiptUs) == receiptUs) {
237 return;
238 }
239 _outputOverflowWarning =
241 _outputOverflowTask.schedule(OUTPUT_OVERFLOW_WARNING_DURATION, [this]() {
242 _outputOverflowWarning.clear();
243 _emitChanges();
244 });
245}
246
247void GPSReceiver::_onGPSConnect()
248{
249 _setError();
250 _connection->receiverReady();
251 _facts->_setConfigured(true);
252 _setPersistentChangesAllowed(false);
253 if (_session) {
254 _session->registerPosition(_positionManager, _positionHealth);
255 }
256 _emitChanges();
257}
258
259void GPSReceiver::_onReceiverDetected()
260{
261 _setPersistentChangesAllowed(false);
262 _emitChanges();
263}
264
265void GPSReceiver::_onInputProblem(GPSInputProblem problem)
266{
267 _setError(GPSReceiverMessages::inputProblem(problem, _session ? _session->detectedType() : std::nullopt,
269 _emitChanges();
270}
271
272void GPSReceiver::_endSession(GPSConnectionError error, const QString& detail, bool portRemoved)
273{
274 // The family an Automatic session detected, else the selected one.
275 const auto* family = _session ? gpsReceiverDescriptorForManufacturer(_session->manufacturer()) : nullptr;
276 _retireSession();
277 _resetStatus();
278 _setPersistentChangesAllowed(false);
279 const GPSReceiverSessionOutcome outcome = _connection->sessionEnded(portRemoved);
280 if (portRemoved) {
281 qCDebug(GPSReceiverLog) << "Receiver serial device removed";
282 }
283 // Automatic connections never pass the flash-save consent this receiver asked for.
284 if (outcome == GPSReceiverSessionOutcome::AutoRetrying && error == GPSConnectionError::ConsentRequired &&
285 !detail.isEmpty() && family) {
286 qCWarning(GPSReceiverLog) << "GPS receiver did not accept configuration";
287 _setError(GPSReceiverMessages::consentRefused(detail, family->type));
288 return;
289 }
291 qCWarning(GPSReceiverLog) << "GPS receiver session ended:" << error << detail;
292 } else if (outcome == GPSReceiverSessionOutcome::None && !portRemoved) {
293 logConnectionError(error, detail);
294 }
295 _setError(GPSReceiverMessages::sessionEnded(outcome, error, detail, portRemoved));
296}
297
298void GPSReceiver::_onGPSSurveyReport(const GPSSurveyReport& status)
299{
300 _facts->_setSurvey(status);
301 _emitChanges();
302}
303
304#ifndef QGC_NO_SERIAL_LINK
305void GPSReceiver::_onSerialPortsEnumerated(const QStringList& devices)
306{
307 if (_destroying) {
308 return;
309 }
310 if (_session && !_session->serialDevice().isEmpty() && !devices.contains(_session->serialDevice())) {
311 _endSession(GPSConnectionError::DeviceError, {}, true);
312 _emitChanges();
313 }
314 _updateSerialPortEntries();
315}
316
317bool GPSReceiver::serialPortAvailable(const QString& device) const
318{
319 if (device.isEmpty() || !_serialPorts) {
320 return false;
321 }
322 const QList<SerialPortManager::Port> ports = _serialPorts->availablePorts();
323 return std::any_of(ports.cbegin(), ports.cend(),
324 [&device](const auto& port) { return port.systemLocation == device && !port.bootloader; });
325}
326
327QStringList GPSReceiver::rtkReceiverPorts() const
328{
329 QStringList receivers;
330 if (!_serialPorts) {
331 return receivers;
332 }
333 QSet<QString> seenDevices;
334 for (const auto& port : _serialPorts->availablePorts()) {
335 if (port.boardType != QGCSerialPortInfo::BoardTypeRTKGPS || port.bootloader) {
336 continue;
337 }
338 // A labelled NMEA interface remains a receiver candidate on composite GPS devices.
339 const bool primary = SerialPortManager::isPrimaryInterface(port, seenDevices);
340 if ((primary || port.description.contains(QStringLiteral("NMEA"))) &&
341 _serialPorts->canReservePort(port.systemLocation)) {
342 receivers.append(port.systemLocation);
343 }
344 }
345 return receivers;
346}
347
348GPSSerialClaim GPSReceiver::claimSerialPort(const QString& device)
349{
350 GPSSerialClaim claim = _serialPorts ? _serialPorts->reservePort(device) : nullptr;
351 if (claim) {
352 _serialClaims.insert(device, claim);
353 }
354 return claim;
355}
356
357void GPSReceiver::_updateSerialPortEntries()
358{
359 // Just after a scan the manager returns that inventory without scanning again.
360 const QList<SerialPortManager::Port> available = _serialPorts->availablePorts();
361 QList<GPSSerialPortEntry> entries;
362 entries.reserve(available.size());
363 for (const auto& port : available) {
364 // The USB product string, else the name of a known board.
365 const QString name = port.description.trimmed().isEmpty() ? port.boardName : port.description.trimmed();
366 //: %1 is a serial device's name, such as u-blox GNSS receiver; %2 its path, such as /dev/ttyACM0 or COM3
367 QString label = name.isEmpty() ? port.systemLocation : tr("%1 – %2").arg(name, port.systemLocation);
368 if (_serialPorts->isPortReserved(port.systemLocation) && _serialClaims.value(port.systemLocation).expired()) {
369 //: %1 is a serial device that another QGroundControl connection, such as a vehicle link, uses
370 label = tr("%1 (in use)").arg(label);
371 }
372 entries.append({.value = port.systemLocation, .label = label});
373 }
374 if (entries != _serialPortEntries) {
375 _serialPortEntries = std::move(entries);
377 }
378}
379
380bool GPSReceiver::connectSerial(const QString& device, GPSType type, uint32_t baudRate, bool allowPersistentChanges,
381 GPSSerialClaim claim)
382{
383 const QString endpoint = device.trimmed();
384 return _connectReceiver(
385 type,
386 [endpoint, claim = std::move(claim), factory = _serialTransportFactory](GPSCancelToken cancelToken) {
387 return factory(endpoint, std::move(cancelToken));
388 },
389 QStringLiteral("serial:%1").arg(endpoint), baudRate, allowPersistentChanges, endpoint, endpoint);
390}
391#endif
392
393bool GPSReceiver::connectTcp(const QString& host, quint16 port, GPSType type, bool allowPersistentChanges)
394{
395 const QString endpoint = QStringLiteral("%1:%2").arg(host).arg(port);
396 return _connectReceiver(
397 type,
398 [host, port](GPSCancelToken cancelToken) {
399 return std::make_unique<TCPGPSTransport>(host, port, std::move(cancelToken));
400 },
401 QStringLiteral("tcp:%1").arg(endpoint), GPSTransport::BRIDGE_BAUDRATE, allowPersistentChanges, {}, endpoint);
402}
403
404bool GPSReceiver::connectUdp(quint16 port, GPSType type)
405{
406 //: %1 is the local UDP port the receiver's data arrives on
407 const QString endpoint = tr("UDP port %1").arg(port);
408 return _connectReceiver(
409 type,
410 [port](GPSCancelToken cancelToken) { return std::make_unique<UDPGPSTransport>(port, std::move(cancelToken)); },
411 QStringLiteral("udp:%1").arg(port), GPSTransport::BRIDGE_BAUDRATE, false, {}, endpoint);
412}
413
415{
416 QList<int> rates;
417#ifndef QGC_NO_SERIAL_LINK
418 for (const QString& text : SerialPortManager::supportedBaudRates()) {
419 const uint32_t rate = text.toUInt();
420 if (gpsValidBaudRate(rate, true) && !rates.contains(static_cast<int>(rate))) {
421 rates.append(static_cast<int>(rate));
422 }
423 }
424#endif
425 return rates;
426}
427
429{
431 role == RTKSettings::ConfiguredBase ? manufacturer : gpsReceiverManufacturerForType(GPSType::passive));
432}
433
435{
436 return _session && _session->hasWorker();
437}
438
439int GPSReceiver::_activeManufacturer() const
440{
441 return _session ? _session->manufacturer() : 0;
442}
443
445{
446 return _session ? _session->baseMode() : -1;
447}
448
450{
451 return _session ? _session->endpoint() : QString();
452}
453
455{
456 return _session ? _session->identity() : QString();
457}
458
460{
461 const std::optional<GPSType> detected = _session ? _session->detectedType() : std::nullopt;
462 if (!detected) {
463 return {};
464 }
465 return activeRole() == RTKSettings::Passive ? gpsInputProtocolName(*detected) : gpsReceiverName(*detected);
466}
467
469{
470 return _session ? _session->role() : RTKSettings::ConfiguredBase;
471}
472
474{
475 return _session && _session->forwardsCorrections();
476}
477
479{
480 return gpsReceiverPresentation(_activeManufacturer());
481}
482
484{
485 if (_destroying) {
486 return false;
487 }
488 const bool allowed =
489 _persistentChangesAllowed &&
491 _setPersistentChangesAllowed(false);
492 const bool connected = _connection->connectConfigured(allowed);
493 _emitChanges();
494 return connected;
495}
496
498{
499 if (_destroying) {
500 return;
501 }
502 _setPersistentChangesAllowed(false);
503 _connection->disconnectConfigured();
504 _emitChanges();
505}
506
507void GPSReceiver::startConnectionPolling(std::function<bool()> suspended)
508{
509 if (_destroying) {
510 return;
511 }
512 (void) _pollTask.scheduleRepeating(POLL_INTERVAL, [this, suspended = std::move(suspended)]() {
513 if (!suspended || !suspended()) {
514 tick();
515 }
516 });
517}
518
520{
521 if (_destroying) {
522 return;
523 }
524 _connection->update();
525 _emitChanges();
526}
527
529{
530 return _connection && _connection->reconnecting();
531}
532
534{
535 return _positionHealth->acceptedObservation(use);
536}
537
538bool GPSReceiver::_connectReceiver(GPSType type, GPSReceiverWorker::TransportFactory transportFactory,
539 const QString& sourceInstance, uint32_t baudRate, bool allowPersistentChanges,
540 const QString& serialDevice, const QString& endpoint)
541{
542 QString configError;
544 gpsReceiverConfigFor(_configuration.base, type, baudRate, allowPersistentChanges, &configError);
545 if (!configError.isEmpty()) {
546 _setError(configError);
547 return false;
548 }
549 _retireSession();
550 _resetStatus();
551 _setError();
552 const RTKSettings::ReceiverRole role =
553 type == GPSType::passive ? RTKSettings::Passive : RTKSettings::ConfiguredBase;
554 auto* const session = new GPSReceiverSession(
555 ++_sessionCount,
556 {.type = type,
557 .role = role,
558 .forwardsCorrections = role == RTKSettings::ConfiguredBase || _configuration.forwardReceiverRtcm,
559 .config = std::move(config),
560 .serialDevice = serialDevice,
561 .endpoint = endpoint},
562 this);
563 session->registerCorrections(_correctionManager, sourceInstance);
564 _session = session;
565 connect(session, &GPSReceiverSession::satelliteInfoUpdated, this, &GPSReceiver::_onSatelliteInfoUpdated);
566 connect(session, &GPSReceiverSession::positionUpdated, this, &GPSReceiver::_onPositionUpdated);
567 connect(session, &GPSReceiverSession::surveyInStatusUpdated, this, &GPSReceiver::_onGPSSurveyReport);
568 connect(session, &GPSReceiverSession::receiverReady, this, &GPSReceiver::_onGPSConnect);
569 connect(session, &GPSReceiverSession::receiverDetected, this, &GPSReceiver::_onReceiverDetected);
570 connect(session, &GPSReceiverSession::inputProblem, this, &GPSReceiver::_onInputProblem);
571 connect(session, &GPSReceiverSession::ended, this, [this](GPSConnectionError error, const QString& detail) {
572 _endSession(error, detail, false);
573 _emitChanges();
574 });
575 if (!session->start(_workerFactory, std::move(transportFactory))) {
576 _retireSession();
577 _setError(tr("Failed to create the receiver session."));
578 return false;
579 }
580 _setPersistentChangesAllowed(false);
581 return true;
582}
583
584void GPSReceiver::_retireSession()
585{
586 if (auto* const retired = std::exchange(_session, nullptr)) {
587 _retiredWorkers.removeIf([](const QPointer<GPSReceiverWorker>& worker) { return worker.isNull(); });
588 if (QPointer<GPSReceiverWorker> worker = retired->worker()) {
589 _retiredWorkers.append(std::move(worker));
590 }
591 retired->deleteLater();
592 retired->retire();
593 }
594 _positionHealth->reset();
595}
596
598{
599 if (_destroying) {
600 return;
601 }
602 _pollTask.cancel();
603 _connection->reset();
604 endConnection(false);
605 _joinRetiredWorkers();
606 _emitChanges();
607}
608
609void GPSReceiver::_joinRetiredWorkers()
610{
611 // At application exit the event loop that deletes finished workers has stopped, so they are joined here.
612 const QDeadlineTimer deadline(SHUTDOWN_TIMEOUT);
613 for (const QPointer<GPSReceiverWorker>& worker : std::exchange(_retiredWorkers, {})) {
614 if (!worker) {
615 continue;
616 }
617 if (worker->wait(deadline)) {
618 delete worker.data();
619 } else {
620 qCWarning(GPSReceiverLog) << "GPS receiver worker did not stop within" << SHUTDOWN_TIMEOUT.count() << "s";
621 }
622 }
623}
624
625void GPSReceiver::endConnection(bool clearError)
626{
627 _retireSession();
628 _resetStatus();
629 _setPersistentChangesAllowed(false);
630 if (clearError) {
631 _setError();
632 }
633}
634
635void GPSReceiver::_onSatelliteInfoUpdated(const GPSSatelliteReport& msg)
636{
637 const int inView = msg.inView.value_or(-1);
638 const int used = msg.used.value_or(-1);
639 qCDebug(GPSReceiverLog) << QStringLiteral("%1 in view, %2 used")
640 .arg(inView)
641 .arg(msg.used ? QString::number(used) : QStringLiteral("unknown"));
642 _facts->_setSatellites(inView, used);
643 _emitChanges();
644}
645
646void GPSReceiver::_onPositionUpdated(const GPSPositionReport& report)
647{
648 if (gpsFixQualityFromValue(_facts->fixType()->rawValue().toInt()) != report.navigation.fixType) {
649 qCDebug(GPSReceiverLog) << "Receiver fix changed:" << report.navigation.fixType;
650 }
651 // Drivers project integrity to Unknown once it is older than its freshness window.
652 _facts->_setSolution(report.navigation.fixType, report.integrity);
653 _updateOutputOverflow(report.integrity.outputOverflowUs);
654 if (_session) {
655 _positionHealth->updateObservation(_session->observation(report, _scheduler->nowUs()));
656 }
657 _emitChanges();
658}
659
660void GPSReceiver::_updateLabels()
661{
662 const QString status = GPSReceiverMessages::receiverStatus({.configured = _facts->telemetryAvailable(),
664 .identifying = activePresentation().automatic,
666 .role = activeRole(),
668 .baseMode = activeBaseMode(),
669 .surveyActive = _facts->active()->rawValue().toBool()});
670 if (std::exchange(_statusText, status) != status) {
671 emit statusTextChanged();
672 }
673
674 QString label;
676 label = _facts->active()->rawValue().toBool() ? tr("Survey", "Base survey-in in progress") : tr("Base");
677 } else {
679 }
680 if (std::exchange(_summaryLabel, label) != label) {
681 emit summaryLabelChanged();
682 }
683}
constexpr bool gpsValidBaudRate(uint32_t baudRate, bool passive)
GPSReceiverSessionOutcome
What the policy does after a receiver session ends; the receiver words the message for it.
@ None
Nothing reconnects; the receiver's own message for the error applies.
@ AutoRetrying
A discovered receiver failed or was lost; discovery retries it.
@ Retrying
The saved receiver's connection was lost; a retry is scheduled.
SerialPortManager::ReservationPtr GPSSerialClaim
Holds a serial device for one receiver session until the last copy is released.
const GPSReceiverDescriptor * gpsReceiverDescriptorForManufacturer(int manufacturer)
QString gpsInputProtocolName(GPSType type)
int gpsReceiverManufacturerForType(GPSType type)
The persisted manufacturer of type, or GPS_AUTOMATIC_MANUFACTURER when it has none.
QString gpsReceiverName(GPSType type)
The name of type's receiver family as shown to users; empty when no descriptor exists.
const GPSReceiverPresentation & gpsReceiverPresentation(int manufacturer)
constexpr GPSFixQuality gpsFixQualityFromValue(int value)
GPSReceiverConfig gpsReceiverConfigFor(const GPSBaseStationConfig &base, GPSType type, uint32_t baudRate, bool allowPersistentChanges, QString *error)
Config config
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void telemetryAvailableChanged(bool telemetryAvailable)
bool telemetryAvailable() const
Definition FactGroup.h:45
A Fact is used to hold a single value within the system.
Definition Fact.h:15
void rawValueChanged(const QVariant &value)
QVariant rawValue() const
Definition Fact.h:87
void registerPosition(PositionManager *positionManager, GPSSourceHealth *producer)
Publishes producer as the receiver position source. Registration observers may retire this session.
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.
int baseMode() const
BaseModeDefinition::Mode of a configured base, or -1 for a passive receiver.
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.
bool compactObservations() const
A configured base sends compact (MSM4) observations.
const QString & serialDevice() const
RTKSettings::ReceiverRole role() const
void positionUpdated(const GPSPositionReport &report)
const QString & identity() const
std::optional< GPSType > detectedType() const
const QString & endpoint() const
void receiverDetected(GPSType type)
The family an Automatic session detected, or the protocol a passive receiver's output carries.
int manufacturer() const
The detected family's once an Automatic session detected it; a passive receiver's stays passive.
void inputProblem(GPSInputProblem problem)
Why a passive receiver delivers no position; None once positions arrive.
void satelliteInfoUpdated(const GPSSatelliteReport &report)
bool forwardsCorrections() const
std::function< std::unique_ptr< GPSTransport >(GPSCancelToken)> TransportFactory
Consumed on the worker thread, so transport construction, I/O and destruction share that thread.
void summaryLabelChanged()
static constexpr std::chrono::seconds POLL_INTERVAL
How often connection polling ticks the receiver.
void startConnectionPolling(std::function< bool()> suspended={})
Runs tick() every POLL_INTERVAL until shutdown(), skipping ticks while suspended returns true.
RTKSettings::ConnectionType effectiveConnectionType() const
QString detectedReceiver() const
static QList< int > serialBaudRates()
The application's serial rates that a receiver accepts as an explicit rate; empty without serial link...
GPSReceiver(QObject *parent=nullptr, RuntimeScheduler *scheduler=nullptr, const Dependencies &dependencies={})
const Configuration & configuration() const
void receiverChanged()
RTKSettings::ReceiverRole activeRole() const
bool connectionSupported() const
UDP only receives, so a receiver QGroundControl configures needs serial or TCP.
QString activeEndpoint() const
Serial device or TCP host:port of the active receiver.
void configurationChanged()
Q_INVOKABLE GPSReceiverPresentation capabilitiesFor(int role, int manufacturer) const
Editable settings for a role; configured bases also depend on the manufacturer.
bool hasReceiver() const override
void persistentChangesAllowedChanged()
void serialPortEntriesChanged()
bool forwardingCorrections() const
GPSReceiverWorkerFactory WorkerFactory
void shutdown()
static constexpr std::chrono::seconds SHUTDOWN_TIMEOUT
How long shutdown() waits for the workers of retired sessions.
int activeBaseMode() const
bool reconnecting() const
The saved receiver is being connected automatically, at startup or after its connection was lost.
void setWorkerFactory(WorkerFactory factory)
Inject before connecting; an empty factory restores the real worker.
GPSReceiverPresentation activePresentation() const
Presentation capabilities of the connected receiver family.
void statusTextChanged()
void errorMessageChanged()
static constexpr std::chrono::seconds OUTPUT_OVERFLOW_WARNING_DURATION
How long the warning about output the receiver dropped stays after the last report of it.
std::optional< GPSObservation > acceptedPositionObservation(GPSObservation::PositionUse use) const
Latest receiver solution that passes the consumer's gates; empty without a fresh fix.
QString errorMessage() const
The connection or input problem, else a recent report that the receiver dropped output.
void setConfiguration(const Configuration &configuration)
Q_INVOKABLE bool connectReceiver()
void tick()
One step of automatic connection: connections due, reconnections, and USB discovery.
bool baseModeSupported() const
void setPersistentChangesAllowed(bool allowed)
Granting permission only lasts until the configuration changes, a session starts or ends,...
bool canConnect() const
Q_INVOKABLE void disconnectReceiver()
Disconnects and pauses automatic connection until the user connects again.
QString receiverIdentity() const
Session health is independent of transport readiness and RTK survey-in validity.
void positionChanged()
void updateObservation(const GPSObservation &observation)
std::optional< GPSObservation > acceptedObservation(GPSObservation::PositionUse use=GPSObservation::PositionUse::GroundStation, std::optional< std::chrono::milliseconds > maximumAge=std::nullopt) const
State state() const
static constexpr unsigned BRIDGE_BAUDRATE
ReceiverRole
Persisted values of receiverRole. The storage type keeps any saved value a valid enum value.
Definition RTKSettings.h:36
virtual quint64 nowUs() const =0
bool scheduleRepeating(std::chrono::microseconds interval, RuntimeScheduler::Callback callback)
bool schedule(std::chrono::microseconds delay, RuntimeScheduler::Callback callback)
static QStringList supportedBaudRates()
static bool isPrimaryInterface(const Port &port, QSet< QString > &seenDevices)
void portsEnumerated(const QStringList &availablePorts)
Emitted only after a fresh scan, never for a retained Android snapshot. Includes native names and pat...
GPSInputProblem
Why a receiver that stays connected while silent reports no position.
QString consentRefused(const QString &detail, GPSType family)
The error of an automatic connection that the family receiver refused for lack of flash-save consent.
QString outputOverflow(RTKSettings::ReceiverRole role, bool compactObservations)
The warning that the receiver produces more output than its connection carries.
QString fixLabel(GPSFixQuality fixType)
The short label of a fix type; empty for an unknown one.
QString receiverStatus(const ReceiverStatus &status)
The receiver status sentence; empty when no receiver is connected or being connected.
QString sessionEnded(GPSReceiverSessionOutcome outcome, GPSConnectionError error, const QString &detail, bool portRemoved)
QString inputProblem(GPSInputProblem problem, std::optional< GPSType > detected, const QString &endpoint, bool forwardingCorrections)
GPSCommandSequence port(QByteArrayView port, unsigned baud)
Clears the SBF output of port, keeps a serial port at the link's baud, and selects SBF and the WGS84 ...
Definition SBFPlan.cc:34
GPSIntegrityReport integrity
GPSNavigationValues navigation
The receiver configuration the application's settings binding supplies as values.
RTKSettings::ReceiverRole receiverRole
Only a configured base is written to by QGroundControl.
bool sameConnection(const GPSReceiverConfiguration &other) const
int baseReceiverManufacturer
A GPSReceiverDescriptor manufacturer, or GPS_AUTOMATIC_MANUFACTURER to detect the family on every con...
bool forwardReceiverRtcm
A passive receiver's RTCM output becomes a correction source for vehicles.
Services a GPSReceiver uses, which must outlive it.
Definition GPSReceiver.h:32
bool automatic
The family is detected when connecting.
std::optional< int > used
std::optional< int > inView