QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FollowMe.cc
Go to the documentation of this file.
1#include "FollowMe.h"
3#include "FirmwarePlugin.h"
4#include "Vehicle.h"
5#include "PositionManager.h"
6#include "SettingsManager.h"
7#include "AppSettings.h"
9
10#include <QtPositioning/QGeoPositionInfo>
11
12QGC_LOGGING_CATEGORY(FollowMeLog, "API.FollowMe")
13
14Q_APPLICATION_STATIC(FollowMe, _followMeInstance);
15
16FollowMe::FollowMe(QObject *parent)
17 : QObject(parent)
18 , _gcsMotionReportTimer(new QTimer(this))
19{
20 // qCDebug(FollowMeLog) << Q_FUNC_INFO << this;
21
22 _gcsMotionReportTimer->setSingleShot(false);
23 // We set the update interval to a fixed amount of time.
24 // Even if the device can't update this quickly we'll pick up a new value on the next time around.
25 // We can't trust the value for update interval which comes from the actual device.
26 _gcsMotionReportTimer->setInterval(kMotionUpdateInterval);
27}
28
30{
31 // qCDebug(FollowMeLog) << Q_FUNC_INFO << this;
32}
33
35{
36 return _followMeInstance();
37}
38
40{
41 static bool once = false;
42 if (!once) {
43 (void) connect(_gcsMotionReportTimer, &QTimer::timeout, this, &FollowMe::_sendGCSMotionReport);
44 (void) connect(SettingsManager::instance()->appSettings()->followTarget(), &Fact::rawValueChanged, this, &FollowMe::_settingsChanged);
45
46 _settingsChanged(SettingsManager::instance()->appSettings()->followTarget()->rawValue());
47 }
48 once = true;
49}
50
51void FollowMe::_settingsChanged(QVariant value)
52{
53 _currentMode = static_cast<FollowMode>(value.toUInt());
54
55 switch (_currentMode) {
56 case MODE_NEVER:
57 (void) disconnect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleAdded, this, &FollowMe::_vehicleAdded);
58 (void) disconnect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleRemoved, this, &FollowMe::_vehicleRemoved);
59 _disableFollowSend();
60 break;
61 case MODE_ALWAYS:
62 (void) connect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleAdded, this, &FollowMe::_vehicleAdded);
63 (void) connect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleRemoved, this, &FollowMe::_vehicleRemoved);
64 _enableFollowSend();
65 break;
66 case MODE_FOLLOWME:
67 (void) connect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleAdded, this, &FollowMe::_vehicleAdded);
68 (void) connect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleRemoved, this, &FollowMe::_vehicleRemoved);
69 _enableIfVehicleInFollow();
70 break;
71 }
72}
73
74void FollowMe::_enableFollowSend()
75{
76 if (!_gcsMotionReportTimer->isActive()) {
77 _gcsMotionReportTimer->start();
78 }
79}
80
81void FollowMe::_disableFollowSend()
82{
83 if (_gcsMotionReportTimer->isActive()) {
84 _gcsMotionReportTimer->stop();
85 }
86}
87
88void FollowMe::_sendGCSMotionReport()
89{
90 const QGeoPositionInfo geoPositionInfo = QGCPositionManager::instance()->geoPositionInfo();
91 const QGeoCoordinate gcsCoordinate = geoPositionInfo.coordinate();
92
93 if (!geoPositionInfo.isValid()) {
94 return;
95 }
96
97 // First check to see if any vehicles need follow me updates
98 bool needFollowMe = false;
99 if (_currentMode == MODE_ALWAYS) {
100 needFollowMe = true;
101 } else if (_currentMode == MODE_FOLLOWME) {
102 QmlObjectListModel* const vehicles = MultiVehicleManager::instance()->vehicles();
103 for (int i = 0; i < vehicles->count(); i++) {
104 const Vehicle* const vehicle = vehicles->value<const Vehicle*>(i);
105 if (_isFollowFlightMode(vehicle, vehicle->flightMode())) {
106 needFollowMe = true;
107 }
108 }
109 }
110 if (!needFollowMe) {
111 return;
112 }
113
114 GCSMotionReport motionReport{};
115 uint8_t estimationCapabilities = 0;
116
117 // Get the current location coordinates
118 // Important note: QGC only supports sending the constant GCS home position altitude for follow me.
119 motionReport.lat_int = static_cast<int>(gcsCoordinate.latitude() * 1e7);
120 motionReport.lon_int = static_cast<int>(gcsCoordinate.longitude() * 1e7);
121 motionReport.altMetersAMSL = gcsCoordinate.altitude();
122 estimationCapabilities |= (1 << POS);
123
124 if (geoPositionInfo.hasAttribute(QGeoPositionInfo::Direction)) {
125 estimationCapabilities |= (1 << HEADING);
126 motionReport.headingDegrees = geoPositionInfo.attribute(QGeoPositionInfo::Direction);
127 }
128
129 // get the current eph
130 if (geoPositionInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) {
131 motionReport.pos_std_dev[0] = motionReport.pos_std_dev[1] = geoPositionInfo.attribute(QGeoPositionInfo::HorizontalAccuracy);
132 }
133
134 // get the current epv
135 if (geoPositionInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
136 motionReport.pos_std_dev[2] = geoPositionInfo.attribute(QGeoPositionInfo::VerticalAccuracy);
137 }
138
139 // calculate z velocity if it's available
140 if (geoPositionInfo.hasAttribute(QGeoPositionInfo::VerticalSpeed)) {
141 motionReport.vzMetersPerSec = geoPositionInfo.attribute(QGeoPositionInfo::VerticalSpeed);
142 }
143
144 // calculate x,y velocity if it's available
145 if (geoPositionInfo.hasAttribute(QGeoPositionInfo::Direction) && geoPositionInfo.hasAttribute(QGeoPositionInfo::GroundSpeed)) {
146 estimationCapabilities |= (1 << VEL);
147
148 const qreal direction = qDegreesToRadians(geoPositionInfo.attribute(QGeoPositionInfo::Direction));
149 const qreal velocity = geoPositionInfo.attribute(QGeoPositionInfo::GroundSpeed);
150
151 motionReport.vxMetersPerSec = cos(direction) * velocity;
152 motionReport.vyMetersPerSec = sin(direction) * velocity;
153 } else {
154 motionReport.vxMetersPerSec = 0;
155 motionReport.vyMetersPerSec = 0;
156 }
157
158 QmlObjectListModel* const vehicles = MultiVehicleManager::instance()->vehicles();
159
160 for (int i = 0; i < vehicles->count(); i++) {
161 Vehicle* const vehicle = vehicles->value<Vehicle*>(i);
162 if ((_currentMode == MODE_ALWAYS) || (_isFollowFlightMode(vehicle, vehicle->flightMode()))) {
163 qCDebug(FollowMeLog) << "sendGCSMotionReport latInt:lonInt:altMetersAMSL" << motionReport.lat_int << motionReport.lon_int << motionReport.altMetersAMSL;
164 vehicle->firmwarePlugin()->sendGCSMotionReport(vehicle, motionReport, estimationCapabilities);
165 }
166 }
167}
168
169void FollowMe::_vehicleAdded(Vehicle *vehicle)
170{
171 (void) connect(vehicle, &Vehicle::flightModeChanged, this, &FollowMe::_enableIfVehicleInFollow);
172 _enableIfVehicleInFollow();
173}
174
175void FollowMe::_vehicleRemoved(Vehicle *vehicle)
176{
177 (void) disconnect(vehicle, &Vehicle::flightModeChanged, this, &FollowMe::_enableIfVehicleInFollow);
178 _enableIfVehicleInFollow();
179}
180
181void FollowMe::_enableIfVehicleInFollow()
182{
183 if (_currentMode == MODE_ALWAYS) {
184 return;
185 }
186
187 // Any vehicle in follow mode will enable the system
188 QmlObjectListModel* const vehicles = MultiVehicleManager::instance()->vehicles();
189
190 for (int i = 0; i < vehicles->count(); i++) {
191 const Vehicle* const vehicle = vehicles->value<const Vehicle*>(i);
192 if (_isFollowFlightMode(vehicle, vehicle->flightMode())) {
193 _enableFollowSend();
194 return;
195 }
196 }
197
198 _disableFollowSend();
199}
200
201bool FollowMe::_isFollowFlightMode(const Vehicle *vehicle, const QString &flightMode)
202{
203 return (flightMode.compare(vehicle->followFlightMode()) == 0);
204}
Q_APPLICATION_STATIC(FollowMe, _followMeInstance)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Fact *followTarget READ followTarget CONSTANT Fact * followTarget()
void rawValueChanged(const QVariant &value)
virtual void sendGCSMotionReport(Vehicle *vehicle, const FollowMe::GCSMotionReport &motionReport, uint8_t estimationCapabilities) const
Sends the appropriate mavlink message for follow me support.
static FollowMe * instance()
Definition FollowMe.cc:34
~FollowMe()
Definition FollowMe.cc:29
void init()
Definition FollowMe.cc:39
@ HEADING
Definition FollowMe.h:41
void vehicleAdded(Vehicle *vehicle)
void vehicleRemoved(Vehicle *vehicle)
T value(int index) const
int count() const override final
QString flightMode() const
Definition Vehicle.cc:1549
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:444
QString followFlightMode() const
Definition Vehicle.cc:3466
void flightModeChanged(const QString &flightMode)