QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MissionFlightStatusCalculator.cc
Go to the documentation of this file.
2#include "Vehicle.h"
3#include "FirmwarePlugin.h"
5#include "VisualMissionItem.h"
6#include "SimpleMissionItem.h"
10#include "AppSettings.h"
11#include "PlanViewSettings.h"
12#include "SettingsManager.h"
13
14#include <QtMath>
15
17 Vehicle* controllerVehicle, Vehicle* managerVehicle, bool missionStartsInVTOLMulticopterMode)
18{
19 _status.totalDistance = 0.0;
20 _status.plannedDistance = 0.0;
21 _status.maxTelemetryDistance = 0.0;
22 _status.totalTime = 0.0;
23 _status.hoverTime = 0.0;
24 _status.cruiseTime = 0.0;
25 _status.hoverDistance = 0.0;
26 _status.cruiseDistance = 0.0;
27 _status.cruiseSpeed = controllerVehicle->defaultCruiseSpeed();
28 _status.hoverSpeed = controllerVehicle->defaultHoverSpeed();
29 _status.vehicleSpeed = controllerVehicle->multiRotor() || managerVehicle->vtol() ? _status.hoverSpeed : _status.cruiseSpeed;
30 _status.vehicleYaw = qQNaN();
31 _status.gimbalYaw = qQNaN();
32 _status.gimbalPitch = qQNaN();
33 _status.mAhBattery = 0;
34 _status.hoverAmps = 0;
35 _status.cruiseAmps = 0;
36 _status.ampMinutesAvailable = 0;
37 _status.hoverAmpsTotal = 0;
38 _status.cruiseAmpsTotal = 0;
39 _status.batteryChangePoint = -1;
40 _status.batteriesRequired = -1;
41 _status.vtolMode = missionStartsInVTOLMulticopterMode
44
45 controllerVehicle->firmwarePlugin()->batteryConsumptionData(controllerVehicle, _status.mAhBattery, _status.hoverAmps, _status.cruiseAmps);
46 if (_status.mAhBattery != 0) {
47 double batteryPercentRemainingAnnounce = SettingsManager::instance()->appSettings()->batteryPercentRemainingAnnounce()->rawValue().toDouble();
48 _status.ampMinutesAvailable = static_cast<double>(_status.mAhBattery) / 1000.0 * 60.0 * ((100.0 - batteryPercentRemainingAnnounce) / 100.0);
49 }
50}
51
53 MissionSettingsItem* settingsItem,
54 Vehicle* controllerVehicle,
55 Vehicle* managerVehicle,
56 AppSettings* appSettings,
57 PlanViewSettings* planViewSettings,
58 bool missionStartsInVTOLMulticopterMode)
59{
60 bool firstCoordinateItem = true;
61 VisualMissionItem* lastFlyThroughVI = qobject_cast<VisualMissionItem*>(visualItems->get(0));
62
63 bool homePositionValid = settingsItem->coordinate().isValid();
64
65 // If home position is valid we can calculate distances between all waypoints.
66 // If home position is not valid we can only calculate distances between waypoints which are
67 // both relative altitude.
68
69 // No values for first item
70 lastFlyThroughVI->setAltDifference(0);
71 lastFlyThroughVI->setAzimuth(0);
72 lastFlyThroughVI->setDistance(0);
73 lastFlyThroughVI->setDistanceFromStart(0);
74
75 _minAMSLAltitude = _maxAMSLAltitude = qQNaN();
76
77 reset(controllerVehicle, managerVehicle, missionStartsInVTOLMulticopterMode);
78
79 bool linkStartToHome = false;
80 bool foundRTL = false;
81 bool pastLandCommand = false;
82 double totalHorizontalDistance = 0;
83
84 for (int i=0; i<visualItems->count(); i++) {
85 VisualMissionItem* item = qobject_cast<VisualMissionItem*>(visualItems->get(i));
86 SimpleMissionItem* simpleItem = qobject_cast<SimpleMissionItem*>(item);
87 ComplexMissionItem* complexItem = qobject_cast<ComplexMissionItem*>(item);
88
89 if (simpleItem && simpleItem->mavCommand() == MAV_CMD_NAV_RETURN_TO_LAUNCH) {
90 foundRTL = true;
91 }
92
93 // Assume the worst
94 item->setAzimuth(0);
95 item->setDistance(0);
96 item->setDistanceFromStart(0);
97
98 // Gimbal states reflect the state AFTER executing the item
99
100 // ROI commands cancel out previous gimbal yaw/pitch
101 if (simpleItem) {
102 switch (simpleItem->command()) {
103 case MAV_CMD_NAV_ROI:
104 case MAV_CMD_DO_SET_ROI_LOCATION:
105 case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
106 case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
107 _status.gimbalYaw = qQNaN();
108 _status.gimbalPitch = qQNaN();
109 break;
110 default:
111 break;
112 }
113 }
114
115 // Look for specific gimbal changes
116 double gimbalYaw = item->specifiedGimbalYaw();
117 if (!qIsNaN(gimbalYaw) || planViewSettings->showGimbalOnlyWhenSet()->rawValue().toBool()) {
118 _status.gimbalYaw = gimbalYaw;
119 }
120 double gimbalPitch = item->specifiedGimbalPitch();
121 if (!qIsNaN(gimbalPitch) || planViewSettings->showGimbalOnlyWhenSet()->rawValue().toBool()) {
122 _status.gimbalPitch = gimbalPitch;
123 }
124
125 // We don't need to do any more processing if:
126 // Mission Settings Item
127 // We are after an RTL command
128 if (i != 0 && !foundRTL) {
129 // We must set the mission flight status prior to querying for any values from the item. This is because things like
130 // current speed, gimbal, vtol state impact the values.
131 item->setMissionFlightStatus(_status);
132
133 // Link back to home if first item is takeoff and we have home position
134 if (firstCoordinateItem && simpleItem && (simpleItem->mavCommand() == MAV_CMD_NAV_TAKEOFF || simpleItem->mavCommand() == MAV_CMD_NAV_VTOL_TAKEOFF)) {
135 if (homePositionValid) {
136 linkStartToHome = true;
137 if (controllerVehicle->multiRotor() || controllerVehicle->vtol()) {
138 // We have to special case takeoff, assuming vehicle takes off straight up to specified altitude
139 double azimuth, distance, altDifference;
140 calcPrevWaypointValues(settingsItem, simpleItem, &azimuth, &distance, &altDifference);
141 double takeoffTime = qAbs(altDifference) / appSettings->offlineEditingAscentSpeed()->rawValue().toDouble();
142 _addHoverTime(takeoffTime, 0, -1);
143 }
144 }
145 }
146
147 if (!pastLandCommand)
148 _addTimeDistance(controllerVehicle, _status.vtolMode == QGCMAVLink::VehicleClassMultiRotor, 0, 0, item->additionalTimeDelay(), 0, -1);
149
150 if (item->specifiesCoordinate()) {
151
152 // Keep track of the min/max AMSL altitude for entire mission so we can calculate altitude percentages in terrain status display
153 if (simpleItem) {
154 double amslAltitude = item->amslEntryAlt();
155 _minAMSLAltitude = std::fmin(_minAMSLAltitude, amslAltitude);
156 _maxAMSLAltitude = std::fmax(_maxAMSLAltitude, amslAltitude);
157 } else {
158 // Complex item
159 double complexMinAMSLAltitude = complexItem->minAMSLAltitude();
160 double complexMaxAMSLAltitude = complexItem->maxAMSLAltitude();
161 _minAMSLAltitude = std::fmin(_minAMSLAltitude, complexMinAMSLAltitude);
162 _maxAMSLAltitude = std::fmax(_maxAMSLAltitude, complexMaxAMSLAltitude);
163 }
164
165 if (!item->isStandaloneCoordinate()) {
166 firstCoordinateItem = false;
167
168 // Update vehicle yaw assuming direction to next waypoint and/or mission item change
169 if (simpleItem) {
170 double newVehicleYaw = simpleItem->specifiedVehicleYaw();
171 if (qIsNaN(newVehicleYaw)) {
172 // No specific vehicle yaw set. Current vehicle yaw is determined from flight path segment direction.
173 if (simpleItem != lastFlyThroughVI) {
174 _status.vehicleYaw = lastFlyThroughVI->exitCoordinate().azimuthTo(simpleItem->entryCoordinate());
175 }
176 } else {
177 _status.vehicleYaw = newVehicleYaw;
178 }
179 simpleItem->setMissionVehicleYaw(_status.vehicleYaw);
180 }
181
182 if (lastFlyThroughVI != settingsItem || linkStartToHome) {
183 // This is a subsequent waypoint or we are forcing the first waypoint back to home
184 double azimuth, distance, altDifference;
185
186 calcPrevWaypointValues(item, lastFlyThroughVI, &azimuth, &distance, &altDifference);
187
188 // If the last waypoint was a land command, there's a discontinuity at this point
189 if (!lastFlyThroughVI->isLandCommand()) {
190 totalHorizontalDistance += distance;
191 item->setDistance(distance);
192
193 if (!pastLandCommand) {
194 // Calculate time/distance
195 double hoverTime = distance / _status.hoverSpeed;
196 double cruiseTime = distance / _status.cruiseSpeed;
197 _addTimeDistance(controllerVehicle, _status.vtolMode == QGCMAVLink::VehicleClassMultiRotor, hoverTime, cruiseTime, 0, distance, item->sequenceNumber());
198 }
199 }
200
201 item->setAltDifference(altDifference);
202 item->setAzimuth(azimuth);
203 item->setDistanceFromStart(totalHorizontalDistance);
204
205 _status.maxTelemetryDistance = qMax(_status.maxTelemetryDistance, calcDistanceToHome(item, settingsItem));
206 }
207
208 if (complexItem) {
209 // Add in distance/time inside complex items as well
210 double distance = complexItem->complexDistance();
211 _status.maxTelemetryDistance = qMax(_status.maxTelemetryDistance, complexItem->greatestDistanceTo(complexItem->exitCoordinate()));
212
213 if (!pastLandCommand) {
214 double hoverTime = distance / _status.hoverSpeed;
215 double cruiseTime = distance / _status.cruiseSpeed;
216 _addTimeDistance(controllerVehicle, _status.vtolMode == QGCMAVLink::VehicleClassMultiRotor, hoverTime, cruiseTime, 0, distance, item->sequenceNumber());
217 }
218
219 totalHorizontalDistance += distance;
220 }
221
222
223 lastFlyThroughVI = item;
224 }
225 }
226 }
227
228 // Speed, VTOL states changes are processed last since they take affect on the next item
229
230 double newSpeed = item->specifiedFlightSpeed();
231 if (!qIsNaN(newSpeed)) {
232 if (controllerVehicle->multiRotor()) {
233 _status.hoverSpeed = newSpeed;
234 } else if (controllerVehicle->vtol()) {
236 _status.hoverSpeed = newSpeed;
237 } else {
238 _status.cruiseSpeed = newSpeed;
239 }
240 } else {
241 _status.cruiseSpeed = newSpeed;
242 }
243 _status.vehicleSpeed = newSpeed;
244 }
245
246 // Update VTOL state
247 if (simpleItem && controllerVehicle->vtol()) {
248 switch (simpleItem->command()) {
249 case MAV_CMD_NAV_TAKEOFF:
251 controllerVehicle, simpleItem->mavCommand())
254 break;
255 case MAV_CMD_NAV_VTOL_TAKEOFF: // Vehicle goes straight up and then transitions to FW
256 case MAV_CMD_NAV_LAND:
258 break;
259 case MAV_CMD_NAV_VTOL_LAND:
261 break;
262 case MAV_CMD_DO_VTOL_TRANSITION:
263 {
264 int transitionState = simpleItem->missionItem().param1();
265 if (transitionState == MAV_VTOL_STATE_MC) {
267 } else if (transitionState == MAV_VTOL_STATE_FW) {
269 }
270 }
271 break;
272 default:
273 break;
274 }
275 }
276
277 if (item->isLandCommand()) {
278 pastLandCommand = true;
279 }
280 }
281 lastFlyThroughVI->setMissionVehicleYaw(_status.vehicleYaw);
282
283 // Add the information for the final segment back to home
284 if (foundRTL && lastFlyThroughVI != settingsItem && homePositionValid) {
285 double azimuth, distance, altDifference;
286 calcPrevWaypointValues(lastFlyThroughVI, settingsItem, &azimuth, &distance, &altDifference);
287
288 if (!pastLandCommand) {
289 // Calculate time/distance
290 double hoverTime = distance / _status.hoverSpeed;
291 double cruiseTime = distance / _status.cruiseSpeed;
292 double landTime = qAbs(altDifference) / appSettings->offlineEditingDescentSpeed()->rawValue().toDouble();
293 _addTimeDistance(controllerVehicle, _status.vtolMode == QGCMAVLink::VehicleClassMultiRotor, hoverTime, cruiseTime, landTime, distance, -1);
294 }
295 }
296
297 _status.totalDistance = totalHorizontalDistance;
298
299 if (_status.mAhBattery != 0 && _status.batteryChangePoint == -1) {
300 _status.batteryChangePoint = 0;
301 }
302
303 if (linkStartToHome) {
304 // Home position is taken into account for min/max values
305 _minAMSLAltitude = std::fmin(_minAMSLAltitude, settingsItem->plannedHomePositionAltitude()->rawValue().toDouble());
306 _maxAMSLAltitude = std::fmax(_maxAMSLAltitude, settingsItem->plannedHomePositionAltitude()->rawValue().toDouble());
307 }
308
309 // Walk the list calculating altitude percentages
310 double altRange = _maxAMSLAltitude - _minAMSLAltitude;
311 for (int i=0; i<visualItems->count(); i++) {
312 VisualMissionItem* item = qobject_cast<VisualMissionItem*>(visualItems->get(i));
313
314 if (item->specifiesCoordinate()) {
315 double amslAlt = item->amslEntryAlt();
316 if (altRange == 0.0) {
317 item->setAltPercent(0.0);
318 item->setTerrainPercent(qQNaN());
319 item->setTerrainCollision(false);
320 } else {
321 item->setAltPercent((amslAlt - _minAMSLAltitude) / altRange);
322 double terrainAltitude = item->terrainAltitude();
323 if (qIsNaN(terrainAltitude)) {
324 item->setTerrainPercent(qQNaN());
325 item->setTerrainCollision(false);
326 } else {
327 item->setTerrainPercent((terrainAltitude - _minAMSLAltitude) / altRange);
328 item->setTerrainCollision(amslAlt < terrainAltitude);
329 }
330 }
331 }
332 }
333}
334
335void MissionFlightStatusCalculator::calcPrevWaypointValues(VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference)
336{
337 QGeoCoordinate currentCoord = currentItem->entryCoordinate();
338 QGeoCoordinate prevCoord = prevItem->exitCoordinate();
339
340 *altDifference = currentItem->amslEntryAlt() - prevItem->amslExitAlt();
341 *distance = prevCoord.distanceTo(currentCoord);
342 *azimuth = prevCoord.azimuthTo(currentCoord);
343}
344
346{
347 QGeoCoordinate currentCoord = currentItem->entryCoordinate();
348 QGeoCoordinate homeCoord = homeItem->exitCoordinate();
349
350 return homeCoord.distanceTo(currentCoord);
351}
352
353void MissionFlightStatusCalculator::_updateBatteryInfo(int waypointIndex)
354{
355 if (_status.mAhBattery != 0) {
356 _status.hoverAmpsTotal = (_status.hoverTime / 60.0) * _status.hoverAmps;
357 _status.cruiseAmpsTotal = (_status.cruiseTime / 60.0) * _status.cruiseAmps;
358 _status.batteriesRequired = ceil((_status.hoverAmpsTotal + _status.cruiseAmpsTotal) / _status.ampMinutesAvailable);
359 if (waypointIndex != -1 && _status.batteriesRequired == 2 && _status.batteryChangePoint == -1) {
360 _status.batteryChangePoint = waypointIndex - 1;
361 }
362 }
363}
364
365void MissionFlightStatusCalculator::_addHoverTime(double hoverTime, double hoverDistance, int waypointIndex)
366{
367 _status.totalTime += hoverTime;
368 _status.hoverTime += hoverTime;
369 _status.hoverDistance += hoverDistance;
370 _status.plannedDistance += hoverDistance;
371 _updateBatteryInfo(waypointIndex);
372}
373
374void MissionFlightStatusCalculator::_addCruiseTime(double cruiseTime, double cruiseDistance, int waypointIndex)
375{
376 _status.totalTime += cruiseTime;
377 _status.cruiseTime += cruiseTime;
378 _status.cruiseDistance += cruiseDistance;
379 _status.plannedDistance += cruiseDistance;
380 _updateBatteryInfo(waypointIndex);
381}
382
383void MissionFlightStatusCalculator::_addTimeDistance(Vehicle* controllerVehicle, bool vtolInHover, double hoverTime, double cruiseTime, double extraTime, double distance, int seqNum)
384{
385 if (controllerVehicle->vtol()) {
386 if (vtolInHover) {
387 _addHoverTime(hoverTime, distance, seqNum);
388 _addHoverTime(extraTime, 0, -1);
389 } else {
390 _addCruiseTime(cruiseTime, distance, seqNum);
391 _addCruiseTime(extraTime, 0, -1);
392 }
393 } else {
394 if (controllerVehicle->multiRotor()) {
395 _addHoverTime(hoverTime, distance, seqNum);
396 _addHoverTime(extraTime, 0, -1);
397 } else {
398 _addCruiseTime(cruiseTime, distance, seqNum);
399 _addCruiseTime(extraTime, 0, -1);
400 }
401 }
402}
Application Settings.
Definition AppSettings.h:10
virtual double minAMSLAltitude(void) const =0
virtual double greatestDistanceTo(const QGeoCoordinate &other) const =0
virtual double complexDistance(void) const =0
virtual double maxAMSLAltitude(void) const =0
QVariant rawValue() const
Definition Fact.h:87
virtual void batteryConsumptionData(Vehicle *vehicle, int &mAhBattery, double &hoverAmps, double &cruiseAmps) const
static void calcPrevWaypointValues(VisualMissionItem *currentItem, VisualMissionItem *prevItem, double *azimuth, double *distance, double *altDifference)
void recalc(QmlObjectListModel *visualItems, MissionSettingsItem *settingsItem, Vehicle *controllerVehicle, Vehicle *managerVehicle, AppSettings *appSettings, PlanViewSettings *planViewSettings, bool missionStartsInVTOLMulticopterMode)
void reset(Vehicle *controllerVehicle, Vehicle *managerVehicle, bool missionStartsInVTOLMulticopterMode)
Resets the flight status fields to defaults based on vehicle properties.
static double calcDistanceToHome(VisualMissionItem *currentItem, VisualMissionItem *homeItem)
double param1(void) const
Definition MissionItem.h:54
QGeoCoordinate coordinate(void) const final
Fact * plannedHomePositionAltitude(void)
Q_INVOKABLE QObject * get(int index)
int count() const override final
static SettingsManager * instance()
AppSettings * appSettings() const
A SimpleMissionItem is used to represent a single MissionItem to the ui.
QGeoCoordinate entryCoordinate(void) const final
double specifiedVehicleYaw(void) override
MAV_CMD mavCommand(void) const
int command(void) const
MissionItem & missionItem(void)
static bool isVTOLMulticopterTakeoff(Vehicle *vehicle, MAV_CMD command)
bool vtol() const
Definition Vehicle.cc:1780
bool multiRotor() const
Definition Vehicle.cc:1775
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:462
double defaultHoverSpeed() const
Definition Vehicle.h:547
double defaultCruiseSpeed() const
Definition Vehicle.h:546
void setDistanceFromStart(double distanceFromStart)
virtual int sequenceNumber(void) const =0
void setTerrainCollision(bool terrainCollision)
void setAltDifference(double altDifference)
void setMissionVehicleYaw(double vehicleYaw)
virtual bool isStandaloneCoordinate(void) const =0
virtual double amslExitAlt(void) const =0
virtual double specifiedGimbalPitch(void)=0
void setDistance(double distance)
virtual double specifiedGimbalYaw(void)=0
virtual double specifiedFlightSpeed(void)=0
virtual double amslEntryAlt(void) const =0
void setAltPercent(double altPercent)
void setTerrainPercent(double terrainPercent)
virtual bool isLandCommand(void) const
virtual double additionalTimeDelay(void) const =0
virtual bool specifiesCoordinate(void) const =0
void setAzimuth(double azimuth)
virtual QGeoCoordinate exitCoordinate(void) const =0
virtual void setMissionFlightStatus(MissionFlightStatus_t &missionFlightStatus)
virtual QGeoCoordinate entryCoordinate(void) const =0
double terrainAltitude(void) const
int batteriesRequired
-1 for not supported
double gimbalPitch
NaN signals pitch was never changed.
int mAhBattery
0 for not available
double vehicleSpeed
Either cruise or hover speed based on vehicle type and vtol state.
double ampMinutesAvailable
Amp minutes available from single battery.
double hoverAmps
Amp consumption during hover.
double cruiseAmps
Amp consumption during cruise.
double hoverAmpsTotal
Total hover amps used.
double gimbalYaw
NaN signals yaw was never changed.
double cruiseAmpsTotal
Total cruise amps used.
QGCMAVLinkTypes::VehicleClass_t vtolMode
Either VehicleClassFixedWing, VehicleClassMultiRotor, VehicleClassGeneric (mode unknown)
int batteryChangePoint
-1 for not supported, 0 for not needed