QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
TerrainProtocolHandler.cc
Go to the documentation of this file.
2#include "TerrainFactGroup.h"
3#include "TerrainQuery.h"
4#include "Vehicle.h"
6#include "MAVLinkProtocol.h"
8
9#include <QtCore/QTimer>
10
11QGC_LOGGING_CATEGORY(TerrainProtocolHandlerLog, "Vehicle.TerrainProtocolHandler")
12
13TerrainProtocolHandler::TerrainProtocolHandler(Vehicle *vehicle, TerrainFactGroup *terrainFactGroup, QObject *parent)
14 : QObject(parent)
15 , _vehicle(vehicle)
16 , _terrainFactGroup(terrainFactGroup)
17 , _terrainDataSendTimer(new QTimer(this))
18{
19 // qCDebug(TerrainProtocolHandlerLog) << Q_FUNC_INFO << this;
20
21 _terrainDataSendTimer->setSingleShot(false);
22 _terrainDataSendTimer->setInterval(1000.0/12.0);
23 (void) connect(_terrainDataSendTimer, &QTimer::timeout, this, &TerrainProtocolHandler::_sendNextTerrainData);
24}
25
27{
28 // qCDebug(TerrainProtocolHandlerLog) << Q_FUNC_INFO << this;
29}
30
32{
33 switch (message.msgid) {
34 case MAVLINK_MSG_ID_TERRAIN_REQUEST:
35 _handleTerrainRequest(message);
36 return false;
37 case MAVLINK_MSG_ID_TERRAIN_REPORT:
38 _handleTerrainReport(message);
39 return false;
40 default:
41 return true;
42 }
43}
44
45void TerrainProtocolHandler::_handleTerrainRequest(const mavlink_message_t &message)
46{
47 mavlink_terrain_request_t terrainRequest;
48 mavlink_msg_terrain_request_decode(&message, &terrainRequest);
49
50 // Defensive drop: autopilots can emit TERRAIN_REQUEST with uninitialized (0, 0)
51 // before GPS lock; honoring it just spams the elevation server with tile-(0,0) fetches.
52 if (terrainRequest.lat == 0 && terrainRequest.lon == 0) {
53 qCDebug(TerrainProtocolHandlerLog) << "Ignoring TERRAIN_REQUEST with lat=0, lon=0";
54 return;
55 }
56
57 _currentTerrainRequest = terrainRequest;
58 _terrainRequestActive = true;
59 _sendNextTerrainData();
60}
61
62void TerrainProtocolHandler::_handleTerrainReport(const mavlink_message_t &message)
63{
64 mavlink_terrain_report_t terrainReport;
65 mavlink_msg_terrain_report_decode(&message, &terrainReport);
66
67 _terrainFactGroup->blocksPending()->setRawValue(terrainReport.pending);
68 _terrainFactGroup->blocksLoaded()->setRawValue(terrainReport.loaded);
69
70 if (TerrainProtocolHandlerLog().isDebugEnabled()) {
71 bool error;
72 QList<double> altitudes;
73 QList<QGeoCoordinate> coordinates;
74 QGeoCoordinate coord(static_cast<double>(terrainReport.lat) / 1e7, static_cast<double>(terrainReport.lon) / 1e7);
75 (void) coordinates.append(coord);
76 const bool altAvailable = TerrainAtCoordinateQuery::getAltitudesForCoordinates(coordinates, altitudes, error);
77 const QString vehicleAlt = terrainReport.spacing ? QStringLiteral("%1").arg(terrainReport.terrain_height) : QStringLiteral("n/a");
78 QString qgcAlt;
79 if (error) {
80 qgcAlt = QStringLiteral("Error");
81 } else if (altAvailable && !altitudes.isEmpty()) {
82 qgcAlt = QStringLiteral("%1").arg(altitudes[0]);
83 } else {
84 qgcAlt = QStringLiteral("N/A");
85 }
86 qCDebug(TerrainProtocolHandlerLog) << "TERRAIN_REPORT" << coord << QStringLiteral("Vehicle(%1) QGC(%2)").arg(vehicleAlt).arg(qgcAlt);
87 }
88}
89
90void TerrainProtocolHandler::_sendNextTerrainData()
91{
92 if (!_terrainRequestActive) {
93 return;
94 }
95
96 QGeoCoordinate terrainRequestCoordSWCorner(static_cast<double>(_currentTerrainRequest.lat) / 1e7, static_cast<double>(_currentTerrainRequest.lon) / 1e7);
97 const int spacingBetweenGrids = _currentTerrainRequest.grid_spacing * 4;
98
99 // Each TERRAIN_DATA sent to vehicle contains a 4x4 grid of heights
100 // TERRAIN_REQUEST.mask has a bit for each entry in an 8x7 grid
101 // gridBit = 0 refers to the the sw corner of the 8x7 grid
102
103 bool bitFound = false;
104 for (int rowIndex=0; rowIndex<7; rowIndex++) {
105 for (int colIndex=0; colIndex<8; colIndex++) {
106 const uint8_t gridBit = (rowIndex * 8) + colIndex;
107 const uint64_t checkBit = 1ull << gridBit;
108 if (_currentTerrainRequest.mask & checkBit) {
109 // Move east and then north to generate the coordinate for sw corner of the specific gridBit
110 QGeoCoordinate swCorner = terrainRequestCoordSWCorner.atDistanceAndAzimuth(spacingBetweenGrids * colIndex, 90);
111 swCorner = swCorner.atDistanceAndAzimuth(spacingBetweenGrids * rowIndex, 0);
112 _sendTerrainData(swCorner, gridBit);
113 bitFound = true;
114 break;
115 }
116 }
117
118 if (bitFound) {
119 break;
120 }
121 }
122
123 if (bitFound) {
124 // Kick timer to send next possible TERRAIN_DATA to vehicle
125 _terrainDataSendTimer->start();
126 } else {
127 _terrainRequestActive = false;
128 }
129}
130
131void TerrainProtocolHandler::_sendTerrainData(const QGeoCoordinate &swCorner, uint8_t gridBit)
132{
133 QList<QGeoCoordinate> coordinates;
134 for (int rowIndex=0; rowIndex<4; rowIndex++) {
135 for (int colIndex=0; colIndex<4; colIndex++) {
136 // Move east and then north to generate the coordinate for grid point
137 QGeoCoordinate coord = swCorner.atDistanceAndAzimuth(_currentTerrainRequest.grid_spacing * colIndex, 90);
138 coord = coord.atDistanceAndAzimuth(_currentTerrainRequest.grid_spacing * rowIndex, 0);
139 coordinates.append(coord);
140 }
141 }
142
143 // Query terrain system for altitudes. If it has them available it will return them. If not they will be queued for download.
144 bool error = false;
145 QList<double> altitudes;
146 if (!TerrainAtCoordinateQuery::getAltitudesForCoordinates(coordinates, altitudes, error)) {
147 return;
148 }
149
150 // Clear the bit on error or success. On error (e.g. tile fetch failed at the server),
151 // looping on the bit would just hammer the same failed tile every timer tick. The vehicle
152 // will re-issue TERRAIN_REQUEST if it still needs the data, allowing a fresh attempt after
153 // TerrainTileManager's failed-tile backoff expires.
154 const uint64_t removeBit = ~(1ull << gridBit);
155 _currentTerrainRequest.mask &= removeBit;
156
157 if (error) {
158 qCDebug(TerrainProtocolHandlerLog) << Q_FUNC_INFO << "terrain data unavailable for gridBit" << gridBit;
159 return;
160 }
161 int altIndex = 0;
162 int16_t terrainData[16];
163 for (const double& altitude : altitudes) {
164 terrainData[altIndex++] = static_cast<int16_t>(altitude);
165 }
166
167 SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
168 if (sharedLink) {
170 (void) mavlink_msg_terrain_data_pack_chan(
173 sharedLink->mavlinkChannel(),
174 &msg,
175 _currentTerrainRequest.lat,
176 _currentTerrainRequest.lon,
177 _currentTerrainRequest.grid_spacing,
178 gridBit,
179 terrainData
180 );
181
182 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
183 }
184}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
Error error
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void setRawValue(const QVariant &value)
Definition Fact.cc:134
static int getComponentId()
static MAVLinkProtocol * instance()
int getSystemId() const
static bool getAltitudesForCoordinates(const QList< QGeoCoordinate > &coordinates, QList< double > &altitudes, bool &error)
bool mavlinkMessageReceived(const mavlink_message_t &message)
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:579
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1390