QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PX4FirmwarePlugin.cc
Go to the documentation of this file.
1#include "PX4FirmwarePlugin.h"
2
3#include <QtCore/QString>
4
5#include "AppMessages.h"
6#include "MAVLinkLib.h"
9#include "ParameterManager.h"
10#include "ParameterMetaData.h"
11#include "PlanViewSettings.h"
12#include "QGCLoggingCategory.h"
13#include "SettingsManager.h"
14#include "Vehicle.h"
15#include "px4_custom_mode.h"
16
17QGC_LOGGING_CATEGORY(PX4FirmwarePluginLog, "FirmwarePlugin.PX4FirmwarePlugin")
18
21 , versionNotified(false)
22{
23
24}
25
27{
28 const QString manualFlightModeName = tr("Manual");
29 const QString acroFlightModeName = tr("Acro");
30 const QString stabilizedFlightModeName = tr("Stabilized");
31 const QString rattitudeFlightModeName = tr("Rattitude");
32 const QString altCtlFlightModeName = tr("Altitude");
33 const QString posCtlFlightModeName = tr("Position");
34 const QString offboardFlightModeName = tr("Offboard");
35 const QString readyFlightModeName = tr("Ready");
36 const QString takeoffFlightModeName = tr("Takeoff");
37 const QString holdFlightModeName = tr("Hold");
38 const QString missionFlightModeName = tr("Mission");
39 const QString rtlFlightModeName = tr("Return");
40 const QString landingFlightModeName = tr("Land");
41 const QString preclandFlightModeName = tr("Precision Land");
42 const QString followMeFlightModeName = tr("Follow Me");
43 const QString simpleFlightModeName = tr("Simple");
44 const QString orbitFlightModeName = tr("Orbit");
45 const QString slowFlightModeName = tr("Position Slow");
46 const QString altitudeCruiseFlightModeName = tr("Altitude Cruise");
47 const QString terminationFlightModeName = tr("Termination");
48 const QString vtolTakeoffFlightModeName = tr("VTOL Takeoff");
49 const QString guidedCourseFlightModeName = tr("Guided Course");
50
52 { PX4CustomMode::MANUAL, manualFlightModeName },
53 { PX4CustomMode::STABILIZED, stabilizedFlightModeName },
54 { PX4CustomMode::ACRO, acroFlightModeName },
55 { PX4CustomMode::RATTITUDE, rattitudeFlightModeName },
56 { PX4CustomMode::ALTCTL, altCtlFlightModeName },
57 { PX4CustomMode::OFFBOARD, offboardFlightModeName },
58 { PX4CustomMode::SIMPLE, simpleFlightModeName },
59 { PX4CustomMode::POSCTL_POSCTL, posCtlFlightModeName },
60 { PX4CustomMode::POSCTL_ORBIT, orbitFlightModeName },
61 { PX4CustomMode::POSCTL_SLOW, slowFlightModeName },
62 { PX4CustomMode::ALTITUDE_CRUISE, altitudeCruiseFlightModeName },
63 { PX4CustomMode::TERMINATION, terminationFlightModeName },
64 { PX4CustomMode::AUTO_LOITER, holdFlightModeName },
65 { PX4CustomMode::AUTO_MISSION, missionFlightModeName },
66 { PX4CustomMode::AUTO_RTL, rtlFlightModeName },
67 { PX4CustomMode::AUTO_FOLLOW_TARGET, followMeFlightModeName },
68 { PX4CustomMode::AUTO_LAND, landingFlightModeName },
69 { PX4CustomMode::AUTO_PRECLAND, preclandFlightModeName },
70 { PX4CustomMode::AUTO_READY, readyFlightModeName },
71 { PX4CustomMode::AUTO_TAKEOFF, takeoffFlightModeName },
72 { PX4CustomMode::AUTO_VTOL_TAKEOFF, vtolTakeoffFlightModeName },
73 { PX4CustomMode::AUTO_GUIDED_COURSE, guidedCourseFlightModeName },
74 });
75
76 static FlightModeList availableFlightModes = {
77 // Mode Name Custom Mode CanBeSet adv
78 { manualFlightModeName, PX4CustomMode::MANUAL, true, true },
79 { stabilizedFlightModeName, PX4CustomMode::STABILIZED, true, true },
80 { acroFlightModeName, PX4CustomMode::ACRO, true, true },
81 { rattitudeFlightModeName, PX4CustomMode::RATTITUDE, true, false},
82 { altCtlFlightModeName, PX4CustomMode::ALTCTL, true, false},
83 { offboardFlightModeName, PX4CustomMode::OFFBOARD, true, true },
84 { simpleFlightModeName, PX4CustomMode::SIMPLE, false, false},
85 { posCtlFlightModeName, PX4CustomMode::POSCTL_POSCTL, true, false},
86 { slowFlightModeName, PX4CustomMode::POSCTL_SLOW, true, true },
87 { altitudeCruiseFlightModeName, PX4CustomMode::ALTITUDE_CRUISE, true, true },
88 { orbitFlightModeName, PX4CustomMode::POSCTL_ORBIT, false, true },
89 { holdFlightModeName, PX4CustomMode::AUTO_LOITER, true, true },
90 { missionFlightModeName, PX4CustomMode::AUTO_MISSION, true, true },
91 { rtlFlightModeName, PX4CustomMode::AUTO_RTL, true, true },
92 { followMeFlightModeName, PX4CustomMode::AUTO_FOLLOW_TARGET, false, true },
93 { landingFlightModeName, PX4CustomMode::AUTO_LAND, false, true },
94 { preclandFlightModeName, PX4CustomMode::AUTO_PRECLAND, true, true },
95 { readyFlightModeName, PX4CustomMode::AUTO_READY, false, false},
96 { takeoffFlightModeName, PX4CustomMode::AUTO_TAKEOFF, false, false},
97 { vtolTakeoffFlightModeName, PX4CustomMode::AUTO_VTOL_TAKEOFF, false, true },
98 { terminationFlightModeName, PX4CustomMode::TERMINATION, false, true },
99 { guidedCourseFlightModeName, PX4CustomMode::AUTO_GUIDED_COURSE, false, true },
100 };
101
102 updateAvailableFlightModes(availableFlightModes);
103}
104
108
110{
111 return new PX4AutoPilotPlugin(vehicle, vehicle);
112}
113
114QStringList PX4FirmwarePlugin::flightModes(Vehicle* vehicle) const
115{
116 QStringList flightModesList;
117
118 for (auto &mode : _flightModeList) {
119 if (mode.canBeSet){
120 bool fw = (vehicle->fixedWing() && mode.fixedWing);
121 bool mc = (vehicle->multiRotor() && mode.multiRotor);
122
123 // show all modes for generic, vtol, etc
124 bool other = !vehicle->fixedWing() && !vehicle->multiRotor();
125 if (fw || mc || other) {
126 flightModesList += mode.mode_name;
127 }
128 }
129 }
130
131 return flightModesList;
132}
133
134QString PX4FirmwarePlugin::flightMode(uint8_t base_mode, uint32_t custom_mode) const
135{
136 QString flightMode = "Unknown";
137
138 if (base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) {
139 return _modeEnumToString.value(custom_mode, tr("Unknown %1:%2").arg(base_mode).arg(custom_mode));
140 }
141
142 return flightMode;
143}
144
145bool PX4FirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) const
146{
147 *base_mode = 0;
148 *custom_mode = 0;
149
150 bool found = false;
151
152 for (auto &mode: _flightModeList){
153 if(flightMode.compare(mode.mode_name, Qt::CaseInsensitive) == 0){
154 *base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
155 *custom_mode = mode.custom_mode;
156 found = true;
157 break;
158 }
159 }
160
161 if (!found) {
162 qCWarning(PX4FirmwarePluginLog) << "Unknown flight Mode" << flightMode;
163 }
164
165 return found;
166}
167
168bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) const
169{
171 //-- This is arbitrary until I find how to really tell if ROI is avaiable
172 if (vehicle->multiRotor()) {
174 }
175 if (vehicle->multiRotor() || vehicle->vtol()) {
177 }
178 if (vehicle->vtol()) {
180 }
181 if (vehicle->fixedWing()) {
183 }
184 return (capabilities & available) == capabilities;
185}
186
191
193{
194 // PX4 stack does not want home position sent in the first position.
195 // Subsequent sequence numbers must be adjusted.
196 return false;
197}
198
200{
201 QList<MAV_CMD> supportedCommands = {
202 MAV_CMD_NAV_WAYPOINT,
203 MAV_CMD_NAV_LOITER_UNLIM, MAV_CMD_NAV_LOITER_TIME,
204 MAV_CMD_NAV_RETURN_TO_LAUNCH,
205 MAV_CMD_DO_JUMP,
206 MAV_CMD_DO_DIGICAM_CONTROL,
207 MAV_CMD_DO_SET_CAM_TRIGG_DIST,
208 MAV_CMD_DO_SET_SERVO,
209 MAV_CMD_DO_SET_ACTUATOR,
210 MAV_CMD_DO_CHANGE_SPEED,
211 MAV_CMD_DO_SET_HOME,
212 MAV_CMD_DO_LAND_START,
213 MAV_CMD_DO_SET_ROI_LOCATION, MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET, MAV_CMD_DO_SET_ROI_NONE,
214 MAV_CMD_DO_MOUNT_CONFIGURE,
215 MAV_CMD_DO_MOUNT_CONTROL,
216 MAV_CMD_SET_CAMERA_MODE,
217 MAV_CMD_IMAGE_START_CAPTURE, MAV_CMD_IMAGE_STOP_CAPTURE, MAV_CMD_VIDEO_START_CAPTURE, MAV_CMD_VIDEO_STOP_CAPTURE,
218 MAV_CMD_NAV_DELAY,
219 MAV_CMD_CONDITION_YAW,
220 MAV_CMD_NAV_LOITER_TO_ALT,
221 MAV_CMD_DO_GRIPPER
222 };
223
224 QList<MAV_CMD> vtolCommands = {
225 MAV_CMD_DO_VTOL_TRANSITION, MAV_CMD_NAV_VTOL_TAKEOFF, MAV_CMD_NAV_VTOL_LAND,
226 };
227
228 QList<MAV_CMD> flightCommands = {
229 MAV_CMD_NAV_LAND, MAV_CMD_NAV_TAKEOFF,
230 };
231
232 if (vehicleClass == QGCMAVLink::VehicleClassGeneric) {
233 supportedCommands += vtolCommands;
234 supportedCommands += flightCommands;
235 }
236 if (vehicleClass == QGCMAVLink::VehicleClassVTOL) {
237 supportedCommands += vtolCommands;
238 supportedCommands += flightCommands;
239 } else if (vehicleClass == QGCMAVLink::VehicleClassFixedWing || vehicleClass == QGCMAVLink::VehicleClassMultiRotor) {
240 supportedCommands += flightCommands;
241 }
242
243 if (SettingsManager::instance()->planViewSettings()->useConditionGate()->rawValue().toBool()) {
244 supportedCommands.append(MAV_CMD_CONDITION_GATE);
245 }
246
247 return supportedCommands;
248}
249
251{
252 switch (vehicleClass) {
254 return QStringLiteral(":/json/PX4-MavCmdInfoCommon.json");
256 return QStringLiteral(":/json/PX4-MavCmdInfoFixedWing.json");
258 return QStringLiteral(":/json/PX4-MavCmdInfoMultiRotor.json");
260 return QStringLiteral(":/json/PX4-MavCmdInfoVTOL.json");
262 return QStringLiteral(":/json/PX4-MavCmdInfoSub.json");
264 return QStringLiteral(":/json/PX4-MavCmdInfoRover.json");
265 default:
266 qCWarning(PX4FirmwarePluginLog) << "PX4FirmwarePlugin::missionCommandOverrides called with bad VehicleClass_t:" << vehicleClass;
267 return QString();
268 }
269}
270
275
277{
278 vehicle->sendMavCommand(vehicle->defaultComponentId(),
279 MAV_CMD_DO_REPOSITION,
280 true, // show error if failed
281 -1.0f,
282 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
283 0.0f,
284 NAN,
285 NAN,
286 NAN,
287 NAN);
288}
289
290void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL) const
291{
292 Q_UNUSED(smartRTL);
294}
295
300
301void PX4FirmwarePlugin::_mavCommandResult(int vehicleId, int component, int command, int result, int failureCode)
302{
303 Q_UNUSED(vehicleId); Q_UNUSED(component); Q_UNUSED(failureCode);
304
305 auto* vehicle = qobject_cast<Vehicle*>(sender());
306 if (!vehicle) {
307 qCWarning(PX4FirmwarePluginLog) << "Dynamic cast failed!";
308 return;
309 }
310
311 if (command == MAV_CMD_NAV_TAKEOFF && result == MAV_RESULT_ACCEPTED) {
312 // Now that we are in takeoff mode we can arm the vehicle which will cause it to takeoff.
313 // We specifically don't retry arming if it fails. This way we don't fight with the user if
314 // They are trying to disarm.
315 disconnect(vehicle, &Vehicle::mavCommandResult, this, &PX4FirmwarePlugin::_mavCommandResult);
316 if (!vehicle->armed()) {
317 vehicle->setArmedShowError(true);
318 }
319 }
320}
321
322void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double takeoffAltRel) const
323{
324 double vehicleAltitudeAMSL = vehicle->altitudeAMSL()->rawValue().toDouble();
325 if (qIsNaN(vehicleAltitudeAMSL)) {
326 QGC::showAppMessage(tr("Unable to takeoff, vehicle position not known."));
327 return;
328 }
329
330 double takeoffAltAMSL = takeoffAltRel + vehicleAltitudeAMSL;
331
332 connect(vehicle, &Vehicle::mavCommandResult, this, &PX4FirmwarePlugin::_mavCommandResult);
333 vehicle->sendMavCommand(
334 vehicle->defaultComponentId(),
335 MAV_CMD_NAV_TAKEOFF,
336 true, // show error is fails
337 NAN, // No pitch requested
338 NAN, // param 2 unused
339 0, // param 3 takeoff flags
340 NAN, NAN, NAN, // No yaw, lat, lon
341 static_cast<float>(takeoffAltAMSL)); // AMSL altitude
342}
343
345{
346 QString speedParam("MPC_XY_VEL_MAX");
347
349 const Fact* speedFact =
351 if (speedFact) {
352 return speedFact->rawValue().toDouble();
353 }
354 }
355
357}
358
360{
361 QString airspeedMax("FW_AIRSPD_MAX");
362
364 const Fact* airspeedFact =
366 if (airspeedFact) {
367 return airspeedFact->rawValue().toDouble();
368 }
369 }
370
372}
373
375{
376 QString airspeedMin("FW_AIRSPD_MIN");
377
379 const Fact* airspeedFact =
381 if (airspeedFact) {
382 return airspeedFact->rawValue().toDouble();
383 }
384 }
385
387}
388
393
399
400bool PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord, double forwardFlightLoiterRadius) const
401{
402 // PX4 doesn't support setting the forward flight loiter radius of
403 // MAV_CMD_DO_REPOSITION
404 Q_UNUSED(forwardFlightLoiterRadius)
405
406 if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) {
407 QGC::showAppMessage(tr("Unable to go to location, vehicle position not known."));
408 return false;
409 }
410
411 if (vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
412 vehicle->sendMavCommandInt(vehicle->defaultComponentId(),
413 MAV_CMD_DO_REPOSITION,
414 MAV_FRAME_GLOBAL,
415 true, // show error is fails
416 -1.0f,
417 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
418 0.0f,
419 NAN,
420 gotoCoord.latitude(),
421 gotoCoord.longitude(),
422 vehicle->altitudeAMSL()->rawValue().toFloat());
423 } else {
424 vehicle->sendMavCommand(vehicle->defaultComponentId(),
425 MAV_CMD_DO_REPOSITION,
426 true, // show error is fails
427 -1.0f,
428 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
429 0.0f,
430 NAN,
431 static_cast<float>(gotoCoord.latitude()),
432 static_cast<float>(gotoCoord.longitude()),
433 vehicle->altitudeAMSL()->rawValue().toFloat());
434 }
435
436 return true;
437}
438
444
445static void _pauseVehicleThenChangeAltResultHandler(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, Vehicle::MavCmdResultFailureCode_t failureCode)
446{
447 if (ack.result != MAV_RESULT_ACCEPTED) {
448 switch (failureCode) {
450 qCDebug(PX4FirmwarePluginLog) << QStringLiteral("MAV_CMD_DO_REPOSITION error(%1)").arg(ack.result);
451 break;
453 qCDebug(PX4FirmwarePluginLog) << "MAV_CMD_DO_REPOSITION no response from vehicle";
454 break;
456 qCDebug(PX4FirmwarePluginLog) << "Internal Error: MAV_CMD_DO_REPOSITION could not be sent due to duplicate command";
457 break;
458 }
459 }
460
461 PauseVehicleThenChangeAltData_t* pData = static_cast<PauseVehicleThenChangeAltData_t*>(resultHandlerData);
462 pData->plugin->_changeAltAfterPause(resultHandlerData, ack.result == MAV_RESULT_ACCEPTED /* pauseSucceeded */);
463}
464
465void PX4FirmwarePlugin::_changeAltAfterPause(void* resultHandlerData, bool pauseSucceeded)
466{
467 PauseVehicleThenChangeAltData_t* pData = static_cast<PauseVehicleThenChangeAltData_t*>(resultHandlerData);
468
469 if (pauseSucceeded) {
470 pData->vehicle->sendMavCommand(
471 pData->vehicle->defaultComponentId(),
472 MAV_CMD_DO_REPOSITION,
473 true, // show error is fails
474 -1.0f, // Don't change groundspeed
475 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
476 0.0f, // Reserved
477 qQNaN(), qQNaN(), qQNaN(), // No change to yaw, lat, lon
478 static_cast<float>(pData->newAMSLAlt));
479 } else {
480 QGC::showAppMessage(tr("Unable to pause vehicle."));
481 }
482
483 delete pData;
484}
485
486void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeChange, bool pauseVehicle)
487{
488 if (!vehicle->homePosition().isValid()) {
489 QGC::showAppMessage(tr("Unable to change altitude, home position unknown."));
490 return;
491 }
492 if (qIsNaN(vehicle->homePosition().altitude())) {
493 QGC::showAppMessage(tr("Unable to change altitude, home position altitude unknown."));
494 return;
495 }
496
497 double currentAltRel = vehicle->altitudeRelative()->rawValue().toDouble();
498 double newAltRel = currentAltRel + altitudeChange;
499
501 resultData->plugin = this;
502 resultData->vehicle = vehicle;
503 resultData->newAMSLAlt = vehicle->homePosition().altitude() + newAltRel;
504
505 if (pauseVehicle) {
506 Vehicle::MavCmdAckHandlerInfo_t handlerInfo = {};
508 handlerInfo.resultHandlerData = resultData;
509
511 &handlerInfo,
512 vehicle->defaultComponentId(),
513 MAV_CMD_DO_REPOSITION,
514 -1.0f, // Don't change groundspeed
515 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
516 0.0f, // Reserved
517 qQNaN(), qQNaN(), qQNaN(), qQNaN()); // No change to yaw, lat, lon, alt
518 } else {
519 _changeAltAfterPause(resultData, true /* pauseSucceeded */);
520 }
521}
522
524{
525
526 vehicle->sendMavCommand(
527 vehicle->defaultComponentId(),
528 MAV_CMD_DO_CHANGE_SPEED,
529 true, // show error is fails
530 1, // 0: airspeed, 1: groundspeed
531 static_cast<float>(groundspeed), // groundspeed setpoint
532 -1, // throttle
533 0, // 0: absolute speed, 1: relative to current
534 NAN, NAN,NAN); // param 5-7 unused
535}
536
538{
539
540 vehicle->sendMavCommand(
541 vehicle->defaultComponentId(),
542 MAV_CMD_DO_CHANGE_SPEED,
543 true, // show error is fails
544 0, // 0: airspeed, 1: groundspeed
545 static_cast<float>(airspeed_equiv), // groundspeed setpoint
546 -1, // throttle
547 0, // 0: absolute speed, 1: relative to current
548 NAN, NAN,NAN); // param 5-7 unused
549}
550
551void PX4FirmwarePlugin::guidedModeChangeHeading(Vehicle* vehicle, const QGeoCoordinate &headingCoord) const
552{
554 QGC::showAppMessage(tr("Vehicle does not support guided rotate"));
555 return;
556 }
557
558 const float radians = qDegreesToRadians(vehicle->coordinate().azimuthTo(headingCoord));
559
560 vehicle->sendMavCommand(
561 vehicle->defaultComponentId(),
562 MAV_CMD_DO_REPOSITION,
563 true,
564 -1.0f, // no change in ground speed
565 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE, // switch to guided mode
566 0.0f, // reserved
567 radians, // change heading
568 NAN, NAN, NAN // no change lat, lon, alt
569 );
570}
571
572bool PX4FirmwarePlugin::guidedModeROI(Vehicle* vehicle, const QGeoCoordinate& roiCenterCoord, double relativeAltitudeMeters) const
573{
574 // PX4 ignores the frame on MAV_CMD_DO_SET_ROI_LOCATION and treats the altitude as AMSL
575 // (PX4-Autopilot#28257), so convert above-home to AMSL before sending.
576 const QGeoCoordinate homePosition = vehicle->homePosition();
577 if (!homePosition.isValid() || qIsNaN(homePosition.altitude())) {
578 QGC::showAppMessage(tr("Unable to set ROI, home position altitude unknown."));
579 return false;
580 }
581
582 _sendROICommand(vehicle, roiCenterCoord, MAV_FRAME_GLOBAL, static_cast<float>(homePosition.altitude() + relativeAltitudeMeters));
583 return true;
584}
585
587{
589 if (!_armVehicleAndValidate(vehicle)) {
590 QGC::showAppMessage(tr("Unable to start takeoff: Vehicle rejected arming."));
591 return;
592 }
593 } else {
594 QGC::showAppMessage(tr("Unable to start takeoff: Vehicle not changing to %1 flight mode.").arg(takeOffFlightMode()));
595 }
596}
597
599{
601 if (!_armVehicleAndValidate(vehicle)) {
602 QGC::showAppMessage(tr("Unable to start mission: Vehicle rejected arming."));
603 return;
604 }
605 } else {
606 QGC::showAppMessage(tr("Unable to start mission: Vehicle not changing to %1 flight mode.").arg(missionFlightMode()));
607 }
608}
609
610void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) const
611{
612 if (guidedMode) {
614 } else {
615 pauseVehicle(vehicle);
616 }
617}
618
623
628
633
638
643
648
653
658
663
664bool PX4FirmwarePlugin::isGuidedMode(const Vehicle* vehicle) const
665{
666 // Not supported by generic vehicle
667 return (vehicle->flightMode() == pauseFlightMode() || vehicle->flightMode() == takeOffFlightMode() || vehicle->flightMode() == landFlightMode());
668}
669
671{
672 //-- Don't process messages to/from UDP Bridge. It doesn't suffer from these issues
673 if (message->compid == MAV_COMP_ID_UDP_BRIDGE) {
674 return true;
675 }
676
677 switch (message->msgid) {
678 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
679 _handleAutopilotVersion(vehicle, message);
680 break;
681 }
682
683 return true;
684}
685
686void PX4FirmwarePlugin::_handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message)
687{
688 Q_UNUSED(vehicle);
689
690 auto* instanceData = qobject_cast<PX4FirmwarePluginInstanceData*>(vehicle->firmwarePluginInstanceData());
691 if (!instanceData->versionNotified) {
692 bool notifyUser = false;
693 int supportedMajorVersion = 1;
694 int supportedMinorVersion = 4;
695 int supportedPatchVersion = 1;
696
697 mavlink_autopilot_version_t version;
698 mavlink_msg_autopilot_version_decode(message, &version);
699
700 if (version.flight_sw_version != 0) {
701 int majorVersion, minorVersion, patchVersion;
702
703 majorVersion = (version.flight_sw_version >> (8*3)) & 0xFF;
704 minorVersion = (version.flight_sw_version >> (8*2)) & 0xFF;
705 patchVersion = (version.flight_sw_version >> (8*1)) & 0xFF;
706
707 if (majorVersion < supportedMajorVersion) {
708 notifyUser = true;
709 } else if (majorVersion == supportedMajorVersion) {
710 if (minorVersion < supportedMinorVersion) {
711 notifyUser = true;
712 } else if (minorVersion == supportedMinorVersion) {
713 notifyUser = patchVersion < supportedPatchVersion;
714 }
715 }
716 } else {
717 notifyUser = true;
718 }
719
720 if (notifyUser) {
721 instanceData->versionNotified = true;
722 QGC::showAppMessage(tr("QGroundControl supports PX4 Pro firmware Version %1.%2.%3 and above. You are using a version prior to that which will lead to unpredictable results. Please upgrade your firmware.").arg(supportedMajorVersion).arg(supportedMinorVersion).arg(supportedPatchVersion));
723 }
724 }
725}
726
727uint32_t PX4FirmwarePlugin::highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) const
728{
729 union px4_custom_mode px4_cm;
730 px4_cm.data = 0;
731 px4_cm.custom_mode_hl = hlCustomMode;
732
733 return px4_cm.data;
734}
735
736QString PX4FirmwarePlugin::_getLatestVersionFileUrl(Vehicle* vehicle) const
737{
738 Q_UNUSED(vehicle);
739 return QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases");
740}
741
742QString PX4FirmwarePlugin::_versionRegex() const
743{
744 return QStringLiteral("v([0-9,\\.]*) Stable");
745}
746
748{
749 return ((vehicle->vehicleType() == MAV_TYPE_GROUND_ROVER) || (vehicle->vehicleType() == MAV_TYPE_SUBMARINE));
750}
751
753{
754 static const char* HOOBS_HI = "LND_FLIGHT_T_HI";
755 static const char* HOOBS_LO = "LND_FLIGHT_T_LO";
756 uint64_t hobbsTimeSeconds = 0;
757
762 hobbsTimeSeconds = ((uint64_t)factHi->rawValue().toUInt() << 32 | (uint64_t)factLo->rawValue().toUInt()) / 1000000;
763 qCDebug(PX4FirmwarePluginLog) << "Hobbs Meter raw PX4:" << "(" << factHi->rawValue().toUInt() << factLo->rawValue().toUInt() << ")";
764 }
765
766 int hours = hobbsTimeSeconds / 3600;
767 int minutes = (hobbsTimeSeconds % 3600) / 60;
768 int seconds = hobbsTimeSeconds % 60;
769 QString timeStr = QString::asprintf("%04d:%02d:%02d", hours, minutes, seconds);
770 qCDebug(PX4FirmwarePluginLog) << "Hobbs Meter string:" << timeStr;
771 return timeStr;
772}
773
774bool PX4FirmwarePlugin::hasGripper(const Vehicle* vehicle) const
775{
776 ParameterManager* paramMgr = vehicle->parameterManager();
777
778 // PX4 versions prior to v1.17 use the PD_GRIPPER_EN boolean
779 const QString gripperEnableParam = QStringLiteral("PD_GRIPPER_EN");
780 if (paramMgr->parameterExists(ParameterManager::defaultComponentId, gripperEnableParam)) {
781 const Fact* gripperFact = paramMgr->getParameter(ParameterManager::defaultComponentId, gripperEnableParam);
782 return gripperFact && gripperFact->rawValue().toInt() != 0;
783 }
784
785 // PX4 v1.17+ removed PD_GRIPPER_EN; PD_GRIPPER_TYPE >= 0 means enabled (-1 = Undefined)
786 const QString gripperTypeParam = QStringLiteral("PD_GRIPPER_TYPE");
787 if (paramMgr->parameterExists(ParameterManager::defaultComponentId, gripperTypeParam)) {
788 const Fact* gripperFact = paramMgr->getParameter(ParameterManager::defaultComponentId, gripperTypeParam);
789 return gripperFact && gripperFact->rawValue().toInt() >= 0;
790 }
791
792 return false;
793}
794
796{
797 for(auto &mode: modeList){
798 PX4CustomMode::Mode cMode = static_cast<PX4CustomMode::Mode>(mode.custom_mode);
799
800 // Update Multi Rotor
801 switch (cMode) {
820 mode.multiRotor = true;
821 break;
826 mode.multiRotor = false;
827 break;
828 }
829
830 // Update Fixed Wing
831 switch (cMode){
837 mode.fixedWing = false;
838 break;
856 mode.fixedWing = true;
857 break;
858 }
859 }
860 _updateFlightModeList(modeList);
861}
862
863QVariant PX4FirmwarePlugin::expandedToolbarIndicatorSource(const Vehicle* /*vehicle*/, const QString& indicatorName) const
864{
865 if (indicatorName == "Battery") {
866 return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4BatteryIndicator.qml"));
867 } else if (indicatorName == "FlightMode") {
868 return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml"));
869 }
870
871 return QVariant();
872}
QList< FirmwareFlightMode > FlightModeList
static void _pauseVehicleThenChangeAltResultHandler(void *resultHandlerData, int, const mavlink_command_ack_t &ack, Vehicle::MavCmdResultFailureCode_t failureCode)
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
The AutoPilotPlugin class is an abstract base class which represents the methods and objects which ar...
A Fact is used to hold a single value within the system.
Definition Fact.h:15
QVariant rawValue() const
Definition Fact.h:87
void _setModeEnumToModeStringMapping(FlightModeCustomModeMap enumToString)
FlightModeList _flightModeList
virtual double minimumEquivalentAirspeed(Vehicle *) const
FirmwareCapabilities
Set of optional capabilites which firmware may support.
@ ChangeHeadingCapability
Vehicle supports changing heading at current location.
@ GuidedTakeoffCapability
Vehicle supports guided takeoff.
@ TakeoffVehicleCapability
Vehicle supports taking off.
@ GuidedModeCapability
Vehicle supports guided mode commands.
@ OrbitModeCapability
Vehicle supports orbit mode.
@ SetFlightModeCapability
FirmwarePlugin::setFlightMode method is supported.
@ VTOLMulticopterTakeoffCapability
VTOL supports MAV_CMD_NAV_TAKEOFF while remaining in multicopter mode.
@ ROIModeCapability
Vehicle supports ROI (both in Fly guided mode and from Plan creation)
@ PauseVehicleCapability
Vehicle supports pausing at current location.
void _sendROICommand(Vehicle *vehicle, const QGeoCoordinate &coord, MAV_FRAME frame, float altitude) const
Build + send MAV_CMD_DO_SET_ROI_LOCATION (COMMAND_INT when the vehicle supports it).
virtual double maximumEquivalentAirspeed(Vehicle *) const
bool _setFlightModeAndValidate(Vehicle *vehicle, const QString &flightMode) const
void _updateFlightModeList(FlightModeList &flightModeList)
bool _armVehicleAndValidate(Vehicle *vehicle) const
FlightModeCustomModeMap _modeEnumToString
virtual double maximumHorizontalSpeedMultirotorMetersSecond(Vehicle *) const
void initializeVehicle(Vehicle *vehicle) override
Called when Vehicle is first created to perform any firmware specific setup.
bool isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) const override
bool adjustIncomingMavlinkMessage(Vehicle *vehicle, mavlink_message_t *message) override
void setGuidedMode(Vehicle *vehicle, bool guidedMode) const override
Set guided flight mode.
QString gotoFlightMode(void) const override
Returns the flight mode which the vehicle will be in if it is performing a goto location.
QList< MAV_CMD > supportedMissionCommands(QGCMAVLink::VehicleClass_t vehicleClass) const override
List of supported mission commands. Empty list for all commands supported.
bool guidedModeGotoLocation(Vehicle *vehicle, const QGeoCoordinate &gotoCoord, double forwardFlightLoiterRadius) const override
QString flightMode(uint8_t base_mode, uint32_t custom_mode) const override
QString takeOffFlightMode(void) const override
Returns the flight mode for TakeOff.
QString pauseFlightMode(void) const override
Returns The flight mode which indicates the vehicle is paused.
bool sendHomePositionToVehicle(void) const override
bool setFlightMode(const QString &flightMode, uint8_t *base_mode, uint32_t *custom_mode) const override
void startMission(Vehicle *vehicle) const override
Command the vehicle to start the mission.
void updateAvailableFlightModes(FlightModeList &modeList) override
Update Available flight modes received from vehicle.
ParameterMetaData * _createParameterMetaData() final
QString takeControlFlightMode(void) const override
Returns the flight mode to use when the operator wants to take back control from autonomouse flight.
bool fixedWingAirSpeedLimitsAvailable(Vehicle *vehicle) const override
QString missionFlightMode(void) const override
Returns the flight mode for running missions.
void pauseVehicle(Vehicle *vehicle) const override
QString landFlightMode(void) const override
Returns the flight mode for Land.
QString rtlFlightMode(void) const override
Returns the flight mode for RTL.
void guidedModeRTL(Vehicle *vehicle, bool smartRTL) const override
Command vehicle to return to launch.
void guidedModeChangeHeading(Vehicle *vehicle, const QGeoCoordinate &headingCoord) const override
Command vehicle to rotate towards specified location.
bool guidedModeROI(Vehicle *vehicle, const QGeoCoordinate &roiCenterCoord, double relativeAltitudeMeters) const override
uint32_t highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) const override
Convert from HIGH_LATENCY2.custom_mode value to correct 32 bit value.
void startTakeoff(Vehicle *vehicle) const override
Command the vehicle to start a takeoff.
double maximumHorizontalSpeedMultirotorMetersSecond(Vehicle *vehicle) const override
QStringList flightModes(Vehicle *vehicle) const override
QString getHobbsMeter(Vehicle *vehicle) const override
gets hobbs meter from autopilot. This should be reimplmeented for each firmware
bool isGuidedMode(const Vehicle *vehicle) const override
Returns whether the vehicle is in guided mode or not.
void guidedModeTakeoff(Vehicle *vehicle, double takeoffAltRel) const override
Command vehicle to takeoff from current location to the specified height.
QVariant expandedToolbarIndicatorSource(const Vehicle *vehicle, const QString &indicatorName) const override
double minimumEquivalentAirspeed(Vehicle *vehicle) const override
QString followFlightMode(void) const override
Returns the flight mode which the vehicle will be for follow me.
void guidedModeChangeEquivalentAirspeedMetersSecond(Vehicle *vehicle, double airspeed_equiv) const override
bool supportsNegativeThrust(Vehicle *vehicle) const override
bool mulirotorSpeedLimitsAvailable(Vehicle *vehicle) const override
QString missionCommandOverrides(QGCMAVLink::VehicleClass_t vehicleClass) const override
double maximumEquivalentAirspeed(Vehicle *vehicle) const override
void guidedModeChangeAltitude(Vehicle *vehicle, double altitudeRel, bool pauseVehicle) override
QString stabilizedFlightMode(void) const override
Returns the flight mode for Stabilized.
void _changeAltAfterPause(void *resultHandlerData, bool pauseSucceeded)
AutoPilotPlugin * autopilotPlugin(Vehicle *vehicle) const override
bool hasGripper(const Vehicle *vehicle) const override
void guidedModeChangeGroundSpeedMetersSecond(Vehicle *vehicle, double groundspeed) const override
void guidedModeLand(Vehicle *vehicle) const override
Command vehicle to land at current location.
bool parameterExists(int componentId, const QString &paramName) const
Fact * getParameter(int componentId, const QString &paramName)
static constexpr int defaultComponentId
static SettingsManager * instance()
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:2173
QString flightMode() const
Definition Vehicle.cc:1485
bool vtol() const
Definition Vehicle.cc:1780
QGeoCoordinate homePosition()
Definition Vehicle.cc:1451
bool multiRotor() const
Definition Vehicle.cc:1775
void setFirmwarePluginInstanceData(FirmwarePluginInstanceData *firmwarePluginInstanceData)
Definition Vehicle.cc:2594
uint64_t capabilityBits() const
Definition Vehicle.h:727
MAV_TYPE vehicleType() const
Definition Vehicle.h:446
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:2202
int defaultComponentId() const
Definition Vehicle.h:700
ParameterManager * parameterManager()
Definition Vehicle.h:595
bool fixedWing() const
Definition Vehicle.cc:1755
QGeoCoordinate coordinate()
Definition Vehicle.h:427
void mavCommandResult(int vehicleId, int targetComponent, int command, int ackResult, int failureCode)
class FirmwarePluginInstanceData * firmwarePluginInstanceData()
Definition Vehicle.h:715
void sendMavCommandWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, 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)
Sends the command and calls the callback with the result.
Definition Vehicle.cc:2197
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9
static constexpr VehicleClass_t VehicleClassGeneric
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultHandler resultHandler
nullptr for no handler
@ MavCmdResultFailureDuplicateCommand
Unable to send command since duplicate is already being waited on for response.
@ MavCmdResultCommandResultOnly
commandResult specifies full success/fail info
@ MavCmdResultFailureNoResponseToCommand
No response from vehicle to command.
uint16_t custom_mode_hl