QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
TerrainQueryCoordinator.cc
Go to the documentation of this file.
2
3#include <cmath>
4
5#include "AppMessages.h"
7#include "TerrainQuery.h"
8#include "Vehicle.h"
9
10QGC_LOGGING_CATEGORY(TerrainQueryCoordinatorLog, "Vehicle.TerrainQueryCoordinator")
11
12namespace {
13// DO_SET_HOME sanity bounds for resolved terrain altitude (AMSL, meters).
14constexpr double kSetHomeTerrainAltMax = 10000.0;
15constexpr double kSetHomeTerrainAltMin = -500.0;
16
17// Altitude-above-terrain polling throttle.
18constexpr int kAltQueryMinIntervalMs = 500;
19constexpr qreal kAltMinDistanceTraveled = 2.0; // meters
20constexpr float kAltMinAltitudeChanged = 0.5f; // meters
21} // namespace
22
24 : QObject(vehicle)
25 , _vehicle(vehicle)
26{
27 _altQueryTimer.restart();
28}
29
30void TerrainQueryCoordinator::doSetHomeWithTerrain(const QGeoCoordinate& coord)
31{
32 if (!coord.isValid()) {
33 return;
34 }
35
36 // If a prior query is still in-flight, disconnect it and start a new one. TerrainQuery
37 // auto-deletes the old query; we just let it go. This matters when two doSetHome
38 // commands arrive back-to-back: only the most recent should drive the outbound command.
39 if (_doSetHomeQuery) {
40 disconnect(_doSetHomeQuery, &TerrainAtCoordinateQuery::terrainDataReceived,
41 this, &TerrainQueryCoordinator::_doSetHomeTerrainReceived);
42 _doSetHomeQuery = nullptr;
43 }
44
45 _doSetHomeCoordinate = coord;
46
47 _doSetHomeQuery = new TerrainAtCoordinateQuery(true /* autoDelete */);
48 connect(_doSetHomeQuery, &TerrainAtCoordinateQuery::terrainDataReceived,
49 this, &TerrainQueryCoordinator::_doSetHomeTerrainReceived);
50
51 QList<QGeoCoordinate> rgCoord;
52 rgCoord.append(coord);
53 _doSetHomeQuery->requestData(rgCoord);
54}
55
56void TerrainQueryCoordinator::_doSetHomeTerrainReceived(bool success, QList<double> heights)
57{
58 if (success) {
59 const double terrainAltitude = heights[0];
60 if (_doSetHomeCoordinate.isValid()
61 && terrainAltitude <= kSetHomeTerrainAltMax
62 && terrainAltitude >= kSetHomeTerrainAltMin) {
63 _vehicle->sendMavCommand(
64 _vehicle->defaultComponentId(),
65 MAV_CMD_DO_SET_HOME,
66 true, // show error if fails
67 0,
68 0,
69 0,
70 static_cast<float>(qQNaN()),
71 _doSetHomeCoordinate.latitude(),
72 _doSetHomeCoordinate.longitude(),
73 terrainAltitude);
74 } else if (_doSetHomeCoordinate.isValid()) {
75 qCDebug(TerrainQueryCoordinatorLog) << "_doSetHomeTerrainReceived: elevation data out of limits";
76 } else {
77 qCDebug(TerrainQueryCoordinatorLog) << "_doSetHomeTerrainReceived: cached home coordinate not valid";
78 }
79 } else {
80 QGC::showAppMessage(tr("Set Home failed, terrain data not available for selected coordinate"));
81 }
82
83 _doSetHomeQuery = nullptr;
84 _doSetHomeCoordinate = QGeoCoordinate(); // Invalidate for extra safety
85}
86
87void TerrainQueryCoordinator::roiWithTerrain(const QGeoCoordinate& coord)
88{
89 if (!coord.isValid()) {
90 return;
91 }
92
93 if (_roiQuery) {
95 this, &TerrainQueryCoordinator::_roiTerrainReceived);
96 _roiQuery = nullptr;
97 }
98
99 _roiCoordinate = coord;
100
101 _roiQuery = new TerrainAtCoordinateQuery(true /* autoDelete */);
103 this, &TerrainQueryCoordinator::_roiTerrainReceived);
104
105 QList<QGeoCoordinate> rgCoord;
106 rgCoord.append(coord);
107 _roiQuery->requestData(rgCoord);
108}
109
110void TerrainQueryCoordinator::_roiTerrainReceived(bool success, QList<double> heights)
111{
112 _roiQuery = nullptr;
113
114 if (!_roiCoordinate.isValid()) {
115 return;
116 }
117
118 float roiAltitude;
119 if (success) {
120 roiAltitude = static_cast<float>(heights[0]);
121 } else {
122 qCDebug(TerrainQueryCoordinatorLog) << "_roiTerrainReceived: terrain query failed, falling back to home altitude";
123 roiAltitude = static_cast<float>(_vehicle->homePosition().altitude());
124 }
125
126 qCDebug(TerrainQueryCoordinatorLog) << "roiWithTerrain: lat" << _roiCoordinate.latitude()
127 << "lon" << _roiCoordinate.longitude()
128 << "terrainAltAMSL" << roiAltitude << "success" << success;
129
130 sendROICommand(_roiCoordinate, MAV_FRAME_GLOBAL, roiAltitude);
131
132 _roiCoordinate = QGeoCoordinate();
133}
134
135void TerrainQueryCoordinator::sendROICommand(const QGeoCoordinate& coord, MAV_FRAME frame, float altitude)
136{
137 if (_vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
138 _vehicle->sendMavCommandInt(
139 _vehicle->defaultComponentId(),
140 MAV_CMD_DO_SET_ROI_LOCATION,
141 frame,
142 true, // show error if fails
143 static_cast<float>(qQNaN()),
144 static_cast<float>(qQNaN()),
145 static_cast<float>(qQNaN()),
146 static_cast<float>(qQNaN()),
147 coord.latitude(),
148 coord.longitude(),
149 altitude);
150 } else {
151 _vehicle->sendMavCommand(
152 _vehicle->defaultComponentId(),
153 MAV_CMD_DO_SET_ROI_LOCATION,
154 true, // show error if fails
155 static_cast<float>(qQNaN()),
156 static_cast<float>(qQNaN()),
157 static_cast<float>(qQNaN()),
158 static_cast<float>(qQNaN()),
159 static_cast<float>(coord.latitude()),
160 static_cast<float>(coord.longitude()),
161 altitude);
162 }
163}
164
166{
167 // Throttle: no query sooner than 500 ms after the last one. Any requests that miss
168 // this window are fine — the next coordinateChanged will re-trigger us.
169 if (_altQueryTimer.elapsed() < kAltQueryMinIntervalMs) {
170 return;
171 }
172
173 const QGeoCoordinate currentCoord = _vehicle->coordinate();
174 if (!currentCoord.isValid()) {
175 return;
176 }
177
178 // Throttle: require minimum distance/altitude deltas to avoid pounding the
179 // terrain service with near-identical queries.
180 const float currentRelAlt = _vehicle->altitudeRelative()->rawValue().toFloat();
181 if (_altLastCoord.isValid() && !qIsNaN(_altLastRelAlt)) {
182 if (_altLastCoord.distanceTo(currentCoord) < kAltMinDistanceTraveled
183 && std::fabs(currentRelAlt - _altLastRelAlt) < kAltMinAltitudeChanged) {
184 return;
185 }
186 }
187
188 _altLastCoord = currentCoord;
189 _altLastRelAlt = currentRelAlt;
190
191 if (_altQuery) {
193 this, &TerrainQueryCoordinator::_altitudeAboveTerrainReceived);
194 _altQuery = nullptr;
195 }
196
197 _altQuery = new TerrainAtCoordinateQuery(true /* autoDelete */);
199 this, &TerrainQueryCoordinator::_altitudeAboveTerrainReceived);
200
201 QList<QGeoCoordinate> rgCoord;
202 rgCoord.append(currentCoord);
203 _altQuery->requestData(rgCoord);
204 _altQueryTimer.restart();
205}
206
207void TerrainQueryCoordinator::_altitudeAboveTerrainReceived(bool success, QList<double> heights)
208{
209 if (!success) {
210 qCDebug(TerrainQueryCoordinatorLog) << "_altitudeAboveTerrainReceived: terrain data not available";
211 } else {
212 const double terrainAltitude = heights[0];
213 const double altitudeAboveTerrain = _vehicle->altitudeAMSL()->rawValue().toDouble() - terrainAltitude;
214 _vehicle->altitudeAboveTerr()->setRawValue(altitudeAboveTerrain);
215 }
216
217 _altQuery = nullptr;
218}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void setRawValue(const QVariant &value)
Definition Fact.cc:128
QVariant rawValue() const
Value after translation.
Definition Fact.h:85
NOTE: TerrainAtCoordinateQuery is not thread safe. All instances/calls to ElevationProvider must be o...
void terrainDataReceived(bool success, const QList< double > &heights)
void roiWithTerrain(const QGeoCoordinate &coord)
void sendROICommand(const QGeoCoordinate &coord, MAV_FRAME frame, float altitude)
void doSetHomeWithTerrain(const QGeoCoordinate &coord)
TerrainQueryCoordinator(Vehicle *vehicle)
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2146
QGeoCoordinate homePosition()
Definition Vehicle.cc:1434
uint64_t capabilityBits() const
Definition Vehicle.h:697
void sendMavCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
Definition Vehicle.cc:2175
int defaultComponentId() const
Definition Vehicle.h:670
QGeoCoordinate coordinate()
Definition Vehicle.h:409
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9