QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PositionManager.cpp
Go to the documentation of this file.
1#include "PositionManager.h"
2#include "AppMessages.h"
3#include "QGCCorePlugin.h"
4#include "SimulatedPosition.h"
5// #include "QGCSensors.h"
7
8#include <QtCore/QApplicationStatic>
9#include <QtCore/QPermissions>
10#include <QtPositioning/QNmeaPositionInfoSource>
11
12QGC_LOGGING_CATEGORY(QGCPositionManagerLog, "PositionManager.QGCPositionManager")
13
15
17 : QObject(parent)
18{
19 qCDebug(QGCPositionManagerLog) << this;
20}
21
23{
24 qCDebug(QGCPositionManagerLog) << this;
25}
26
28{
29 return _positionManager();
30}
31
33{
35 _simulatedSource = new SimulatedPosition(this);
36 _setPositionSource(QGCPositionSource::Simulated);
37 } else {
38 _checkPermission();
39 }
40}
41
42void QGCPositionManager::_setupPositionSources()
43{
44 _defaultSource = QGCCorePlugin::instance()->createPositionSource(this);
45 if (_defaultSource) {
46 _usingPluginSource = true;
47 } else {
48 qCDebug(QGCPositionManagerLog) << Q_FUNC_INFO << QGeoPositionInfoSource::availableSources();
49
50 _defaultSource = QGeoPositionInfoSource::createDefaultSource(this);
51 if (!_defaultSource) {
52 qCWarning(QGCPositionManagerLog) << Q_FUNC_INFO << "No default source available";
53 return;
54 }
55 }
56
57 _setPositionSource(QGCPositionSource::InternalGPS);
58}
59
60void QGCPositionManager::_handlePermissionStatus(Qt::PermissionStatus permissionStatus)
61{
62 if (permissionStatus == Qt::PermissionStatus::Granted) {
63 _setupPositionSources();
64 } else {
65 qCWarning(QGCPositionManagerLog) << Q_FUNC_INFO << "Location Permission Denied";
66 }
67}
68
69void QGCPositionManager::_checkPermission()
70{
71 QLocationPermission locationPermission;
72 locationPermission.setAccuracy(QLocationPermission::Precise);
73
74 const Qt::PermissionStatus permissionStatus = QCoreApplication::instance()->checkPermission(locationPermission);
75 if (permissionStatus == Qt::PermissionStatus::Undetermined) {
76 QCoreApplication::instance()->requestPermission(locationPermission, this, [this](const QPermission &permission) {
77 _handlePermissionStatus(permission.status());
78 });
79 } else {
80 _handlePermissionStatus(permissionStatus);
81 }
82}
83
85{
86 if (_nmeaSource) {
87 _nmeaSource->stopUpdates();
88 (void) _nmeaSource->disconnect(this);
89
90 if (_currentSource == _nmeaSource) {
91 _currentSource = nullptr;
92 }
93
94 delete _nmeaSource;
95 _nmeaSource = nullptr;
96 }
97
98 _nmeaSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode, this);
99 _nmeaSource->setDevice(device);
100 _nmeaSource->setUserEquivalentRangeError(5.1);
101 _setPositionSource(QGCPositionManager::NmeaGPS);
102}
103
105{
106 if (!_nmeaSource) {
107 return;
108 }
109
110 if (_currentSource == _nmeaSource) {
111 // Switch away while the NMEA source is still valid so _setPositionSource() can run its
112 // usual cleanup (stop updates, disconnect, and reset the stale GCS position/accuracy)
113 // before we delete it. Falls back to the platform's default source (e.g. integrated GPS).
114 _setPositionSource(QGCPositionManager::InternalGPS);
115 } else {
116 _nmeaSource->stopUpdates();
117 (void) _nmeaSource->disconnect(this);
118 }
119
120 delete _nmeaSource;
121 _nmeaSource = nullptr;
122}
123
124void QGCPositionManager::_positionUpdated(const QGeoPositionInfo &update)
125{
126 _geoPositionInfo = update;
127 _gcsPositioningError = QGeoPositionInfoSource::NoError;
128
129 QGeoCoordinate newGCSPosition(_gcsPosition);
130
131 if (update.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) {
132 if ((qAbs(update.coordinate().latitude()) > 0.001) && (qAbs(update.coordinate().longitude()) > 0.001)) {
133 _gcsPositionHorizontalAccuracy = update.attribute(QGeoPositionInfo::HorizontalAccuracy);
134 if (_gcsPositionHorizontalAccuracy <= kMinHorizonalAccuracyMeters) {
135 newGCSPosition.setLatitude(update.coordinate().latitude());
136 newGCSPosition.setLongitude(update.coordinate().longitude());
137 // Stamp the local arrival time so consumers can tell how fresh gcsPosition is.
138 // Updates rejected by the accuracy gate leave the stamp alone, since they leave
139 // the previous coordinate in place as well.
140 _gcsPositionTimestamp = QDateTime::currentDateTimeUtc();
141 }
142 emit gcsPositionHorizontalAccuracyChanged(_gcsPositionHorizontalAccuracy);
143 }
144 }
145
146 if (update.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
147 _gcsPositionVerticalAccuracy = update.attribute(QGeoPositionInfo::VerticalAccuracy);
148 if (_gcsPositionVerticalAccuracy <= kMinVerticalAccuracyMeters) {
149 newGCSPosition.setAltitude(update.coordinate().altitude());
150 }
151 }
152
153 _gcsPositionAccuracy = sqrt(pow(_gcsPositionHorizontalAccuracy, 2) + pow(_gcsPositionVerticalAccuracy, 2));
154
155 _setGCSPosition(newGCSPosition);
156
157 if (update.hasAttribute(QGeoPositionInfo::DirectionAccuracy)) {
158 _gcsDirectionAccuracy = update.attribute(QGeoPositionInfo::DirectionAccuracy);
159 if (_gcsDirectionAccuracy <= kMinDirectionAccuracyDegrees) {
160 _setGCSHeading(update.attribute(QGeoPositionInfo::Direction));
161 }
162 } else if (_usingPluginSource) {
163 _setGCSHeading(update.attribute(QGeoPositionInfo::Direction));
164 }
165
166 emit positionInfoUpdated(update);
167}
168
169void QGCPositionManager::_positionError(QGeoPositionInfoSource::Error gcsPositioningError)
170{
171 qCWarning(QGCPositionManagerLog) << Q_FUNC_INFO << "Positioning error:" << gcsPositioningError;
172 _gcsPositioningError = gcsPositioningError;
173}
174
175void QGCPositionManager::_setGCSHeading(qreal newGCSHeading)
176{
177 if (newGCSHeading != _gcsHeading) {
178 _gcsHeading = newGCSHeading;
179 emit gcsHeadingChanged(_gcsHeading);
180 }
181}
182
183void QGCPositionManager::_setGCSPosition(const QGeoCoordinate& newGCSPosition)
184{
185 if (newGCSPosition != _gcsPosition) {
186 _gcsPosition = newGCSPosition;
187 emit gcsPositionChanged(_gcsPosition);
188 }
189}
190
191void QGCPositionManager::_setPositionSource(QGCPositionSource source)
192{
193 if (_currentSource != nullptr) {
194 _currentSource->stopUpdates();
195 // Note the receiver-side overload: disconnect(_currentSource) would drop our own signals
196 // to the source (of which there are none), leaving source->this connected and duplicating
197 // it every time a source is re-selected.
198 (void) _currentSource->disconnect(this);
199
200 _geoPositionInfo = QGeoPositionInfo();
201 emit positionInfoUpdated(_geoPositionInfo);
202
203 _setGCSPosition(QGeoCoordinate());
204 _gcsPositionTimestamp = QDateTime();
205
206 _setGCSHeading(qQNaN());
207
208 _gcsPositionHorizontalAccuracy = std::numeric_limits<qreal>::infinity();
209 emit gcsPositionHorizontalAccuracyChanged(_gcsPositionHorizontalAccuracy);
210 }
211
212 switch (source) {
213 case QGCPositionManager::Log:
214 break;
215 case QGCPositionManager::Simulated:
216 _currentSource = _simulatedSource;
217 break;
218 case QGCPositionManager::NmeaGPS:
219 _currentSource = _nmeaSource;
220 break;
221 case QGCPositionManager::InternalGPS:
222 _currentSource = _defaultSource;
223 break;
224 case QGCPositionManager::ExternalGPS:
225 break;
226 default:
227 _currentSource = _defaultSource;
228 break;
229 }
230
231 if (_currentSource != nullptr) {
232 _currentSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
233 _updateInterval = _currentSource->minimumUpdateInterval();
234 #if !defined(Q_OS_DARWIN) && !defined(Q_OS_IOS)
235 _currentSource->setUpdateInterval(_updateInterval);
236 #endif
237
238 (void) connect(_currentSource, &QGeoPositionInfoSource::positionUpdated, this, &QGCPositionManager::_positionUpdated);
239 (void) connect(_currentSource, &QGeoPositionInfoSource::errorOccurred, this, &QGCPositionManager::_positionError);
240
241 // (void) connect(QGCCompass::instance(), &QGCCompass::positionUpdated, this, &QGCPositionManager::_positionUpdated);
242
243 _currentSource->startUpdates();
244 }
245}
Q_APPLICATION_STATIC(QGCPositionManager, _positionManager)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
virtual QGeoPositionInfoSource * createPositionSource(QObject *parent)
static QGCCorePlugin * instance()
static QGCPositionManager * instance()
void gcsPositionChanged(QGeoCoordinate gcsPosition)
QGeoPositionInfoSource::Error gcsPositioningError() const
void setNmeaSourceDevice(QIODevice *device)
void gcsPositionHorizontalAccuracyChanged(qreal gcsPositionHorizontalAccuracy)
void positionInfoUpdated(QGeoPositionInfo update)
void gcsHeadingChanged(qreal gcsHeading)
bool runningUnitTests()