17#ifndef QGC_NO_SERIAL_LINK
27#include <QtCore/QDeadlineTimer>
37 case GPSConnectionError::OpenFailed:
38 qCWarning(GPSReceiverLog) <<
"Failed to open GPS receiver transport:" << detail;
40 case GPSConnectionError::ConfigFailed:
41 case GPSConnectionError::ConsentRequired:
42 qCWarning(GPSReceiverLog) <<
"GPS receiver did not accept configuration";
44 case GPSConnectionError::ProtocolError:
45 qCWarning(GPSReceiverLog) <<
"GPS receiver protocol error, connection lost:" << detail;
47 case GPSConnectionError::DeviceError:
48 qCWarning(GPSReceiverLog) <<
"GPS device error, connection lost:" << detail;
50 case GPSConnectionError::None:
59 , _correctionManager(dependencies.corrections)
60 , _positionManager(dependencies.positions)
63 , _outputOverflowTask(_scheduler, this)
64 , _pollTask(_scheduler, this)
65#ifndef QGC_NO_SERIAL_LINK
66 , _serialPorts(dependencies.serialPorts)
69 qCDebug(GPSReceiverLog) <<
this;
73 _clearStaleSolution();
76 _connection = std::make_unique<GPSReceiverConnectionPolicy>(
static_cast<GPSReceiverConnector&
>(*
this),
77 _configuration, _scheduler);
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));
102 _setPersistentChangesAllowed(
false);
105 _connection->configurationChanged(previous);
106 ++_configurationRevision;
112 _setPersistentChangesAllowed(allowed);
116void GPSReceiver::_setPersistentChangesAllowed(
bool allowed)
119 _persistentChangesAllowed = allowed;
136 [&presentation]<
typename Mode>(
const Mode&) {
137 if constexpr (std::is_same_v<Mode, GPSBaseStationConfig::Fixed>) {
139 }
else if constexpr (std::is_same_v<Mode, GPSBaseStationConfig::SurveyIn>) {
161 qCWarning(GPSReceiverLog) <<
"Inject the worker factory before connecting a receiver";
164 _workerFactory = std::move(factory);
174 qCDebug(GPSReceiverLog) <<
this;
177GPSReceiver::ReceiverState GPSReceiver::_receiverState()
const
190void GPSReceiver::_emitChanges()
196 if (
reconnecting() != _notified.receiver.reconnecting) {
197 _setPersistentChangesAllowed(
false);
199 if (std::exchange(_notified.configurationRevision, _configurationRevision) != _configurationRevision) {
202 if (std::exchange(_notified.persistentChangesAllowed, _persistentChangesAllowed) != _persistentChangesAllowed) {
206 if (std::exchange(_notified.errorMessage,
error) !=
error) {
209 const ReceiverState receiver = _receiverState();
210 if (std::exchange(_notified.receiver, receiver) != receiver) {
215void GPSReceiver::_resetStatus()
218 _outputOverflowUs = 0;
219 _outputOverflowTask.
cancel();
220 _outputOverflowWarning.clear();
223void GPSReceiver::_clearStaleSolution()
225 _facts->_clearSolution();
231 return _errorMessage.isEmpty() ? _outputOverflowWarning : _errorMessage;
234void GPSReceiver::_updateOutputOverflow(uint64_t receiptUs)
236 if (receiptUs == 0 || std::exchange(_outputOverflowUs, receiptUs) == receiptUs) {
239 _outputOverflowWarning =
242 _outputOverflowWarning.clear();
247void GPSReceiver::_onGPSConnect()
250 _connection->receiverReady();
251 _facts->_setConfigured(
true);
252 _setPersistentChangesAllowed(
false);
259void GPSReceiver::_onReceiverDetected()
261 _setPersistentChangesAllowed(
false);
278 _setPersistentChangesAllowed(
false);
281 qCDebug(GPSReceiverLog) <<
"Receiver serial device removed";
285 !detail.isEmpty() && family) {
286 qCWarning(GPSReceiverLog) <<
"GPS receiver did not accept configuration";
291 qCWarning(GPSReceiverLog) <<
"GPS receiver session ended:" <<
error << detail;
293 logConnectionError(
error, detail);
300 _facts->_setSurvey(status);
304#ifndef QGC_NO_SERIAL_LINK
305void GPSReceiver::_onSerialPortsEnumerated(
const QStringList& devices)
311 _endSession(GPSConnectionError::DeviceError, {},
true);
314 _updateSerialPortEntries();
317bool GPSReceiver::serialPortAvailable(
const QString& device)
const
319 if (device.isEmpty() || !_serialPorts) {
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; });
327QStringList GPSReceiver::rtkReceiverPorts()
const
329 QStringList receivers;
333 QSet<QString> seenDevices;
334 for (
const auto& port : _serialPorts->availablePorts()) {
340 if ((primary ||
port.description.contains(QStringLiteral(
"NMEA"))) &&
341 _serialPorts->canReservePort(
port.systemLocation)) {
342 receivers.append(
port.systemLocation);
350 GPSSerialClaim claim = _serialPorts ? _serialPorts->reservePort(device) :
nullptr;
352 _serialClaims.insert(device, claim);
357void GPSReceiver::_updateSerialPortEntries()
360 const QList<SerialPortManager::Port> available = _serialPorts->availablePorts();
361 QList<GPSSerialPortEntry> entries;
362 entries.reserve(available.size());
363 for (
const auto& port : available) {
365 const QString name =
port.description.trimmed().isEmpty() ?
port.boardName :
port.description.trimmed();
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()) {
370 label = tr(
"%1 (in use)").arg(label);
372 entries.append({.value =
port.systemLocation, .label = label});
374 if (entries != _serialPortEntries) {
375 _serialPortEntries = std::move(entries);
380bool GPSReceiver::connectSerial(
const QString& device,
GPSType type, uint32_t baudRate,
bool allowPersistentChanges,
383 const QString endpoint = device.trimmed();
384 return _connectReceiver(
386 [endpoint, claim = std::move(claim), factory = _serialTransportFactory](
GPSCancelToken cancelToken) {
387 return factory(endpoint, std::move(cancelToken));
389 QStringLiteral(
"serial:%1").arg(endpoint), baudRate, allowPersistentChanges, endpoint, endpoint);
393bool GPSReceiver::connectTcp(
const QString& host, quint16 port,
GPSType type,
bool allowPersistentChanges)
395 const QString endpoint = QStringLiteral(
"%1:%2").arg(host).arg(port);
396 return _connectReceiver(
399 return std::make_unique<TCPGPSTransport>(host, port, std::move(cancelToken));
404bool GPSReceiver::connectUdp(quint16 port,
GPSType type)
407 const QString endpoint = tr(
"UDP port %1").arg(port);
408 return _connectReceiver(
410 [port](
GPSCancelToken cancelToken) {
return std::make_unique<UDPGPSTransport>(port, std::move(cancelToken)); },
417#ifndef QGC_NO_SERIAL_LINK
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));
436 return _session && _session->
hasWorker();
439int GPSReceiver::_activeManufacturer()
const
446 return _session ? _session->
baseMode() : -1;
451 return _session ? _session->
endpoint() : QString();
456 return _session ? _session->
identity() : QString();
461 const std::optional<GPSType> detected = _session ? _session->
detectedType() : std::nullopt;
489 _persistentChangesAllowed &&
491 _setPersistentChangesAllowed(
false);
492 const bool connected = _connection->connectConfigured(allowed);
502 _setPersistentChangesAllowed(
false);
503 _connection->disconnectConfigured();
513 if (!suspended || !suspended()) {
524 _connection->update();
530 return _connection && _connection->reconnecting();
539 const QString& sourceInstance, uint32_t baudRate,
bool allowPersistentChanges,
540 const QString& serialDevice,
const QString& endpoint)
545 if (!configError.isEmpty()) {
546 _setError(configError);
559 .config = std::move(
config),
560 .serialDevice = serialDevice,
561 .endpoint = endpoint},
572 _endSession(
error, detail,
false);
575 if (!session->start(_workerFactory, std::move(transportFactory))) {
577 _setError(tr(
"Failed to create the receiver session."));
580 _setPersistentChangesAllowed(
false);
584void GPSReceiver::_retireSession()
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));
591 retired->deleteLater();
594 _positionHealth->
reset();
603 _connection->reset();
604 endConnection(
false);
605 _joinRetiredWorkers();
609void GPSReceiver::_joinRetiredWorkers()
613 for (
const QPointer<GPSReceiverWorker>& worker : std::exchange(_retiredWorkers, {})) {
617 if (worker->wait(deadline)) {
618 delete worker.data();
620 qCWarning(GPSReceiverLog) <<
"GPS receiver worker did not stop within" <<
SHUTDOWN_TIMEOUT.count() <<
"s";
625void GPSReceiver::endConnection(
bool clearError)
629 _setPersistentChangesAllowed(
false);
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")
641 .arg(msg.
used ? QString::number(used) : QStringLiteral(
"unknown"));
642 _facts->_setSatellites(inView, used);
660void GPSReceiver::_updateLabels()
670 if (std::exchange(_statusText, status) != status) {
676 label = _facts->
active()->
rawValue().toBool() ? tr(
"Survey",
"Base survey-in in progress") : tr(
"Base");
680 if (std::exchange(_summaryLabel, label) != label) {
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)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void telemetryAvailableChanged(bool telemetryAvailable)
bool telemetryAvailable() const
A Fact is used to hold a single value within the system.
void rawValueChanged(const QVariant &value)
QVariant rawValue() const
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
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
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 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,...
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 updateObservation(const GPSObservation &observation)
std::optional< GPSObservation > acceptedObservation(GPSObservation::PositionUse use=GPSObservation::PositionUse::GroundStation, std::optional< std::chrono::milliseconds > maximumAge=std::nullopt) const
static constexpr unsigned BRIDGE_BAUDRATE
ReceiverRole
Persisted values of receiverRole. The storage type keeps any saved value a valid enum value.
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 ...
uint64_t outputOverflowUs
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
GPSBaseStationConfig base
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.
bool automatic
The family is detected when connecting.
bool persistentConfiguration
std::optional< int > used
std::optional< int > inView