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#include "ParameterMetaData.h"
4#include "AppMessages.h"
7#include "SettingsManager.h"
8#include "PlanViewSettings.h"
9#include "ParameterManager.h"
10#include "Vehicle.h"
11
12#include <QtCore/QString>
13
14#include "px4_custom_mode.h"
15
16QGC_LOGGING_CATEGORY(PX4FirmwarePluginLog, "FirmwarePlugin.PX4FirmwarePlugin")
17
20 , versionNotified(false)
21{
22
23}
24
26{
27 const QString manualFlightModeName = tr("Manual");
28 const QString acroFlightModeName = tr("Acro");
29 const QString stabilizedFlightModeName = tr("Stabilized");
30 const QString rattitudeFlightModeName = tr("Rattitude");
31 const QString altCtlFlightModeName = tr("Altitude");
32 const QString posCtlFlightModeName = tr("Position");
33 const QString offboardFlightModeName = tr("Offboard");
34 const QString readyFlightModeName = tr("Ready");
35 const QString takeoffFlightModeName = tr("Takeoff");
36 const QString holdFlightModeName = tr("Hold");
37 const QString missionFlightModeName = tr("Mission");
38 const QString rtlFlightModeName = tr("Return");
39 const QString landingFlightModeName = tr("Land");
40 const QString preclandFlightModeName = tr("Precision Land");
41 const QString followMeFlightModeName = tr("Follow Me");
42 const QString simpleFlightModeName = tr("Simple");
43 const QString orbitFlightModeName = tr("Orbit");
44 const QString slowFlightModeName = tr("Position Slow");
45 const QString altitudeCruiseFlightModeName = tr("Altitude Cruise");
46 const QString terminationFlightModeName = tr("Termination");
47 const QString vtolTakeoffFlightModeName = tr("VTOL Takeoff");
48 const QString guidedCourseFlightModeName = tr("Guided Course");
49
51 { PX4CustomMode::MANUAL, manualFlightModeName },
52 { PX4CustomMode::STABILIZED, stabilizedFlightModeName },
53 { PX4CustomMode::ACRO, acroFlightModeName },
54 { PX4CustomMode::RATTITUDE, rattitudeFlightModeName },
55 { PX4CustomMode::ALTCTL, altCtlFlightModeName },
56 { PX4CustomMode::OFFBOARD, offboardFlightModeName },
57 { PX4CustomMode::SIMPLE, simpleFlightModeName },
58 { PX4CustomMode::POSCTL_POSCTL, posCtlFlightModeName },
59 { PX4CustomMode::POSCTL_ORBIT, orbitFlightModeName },
60 { PX4CustomMode::POSCTL_SLOW, slowFlightModeName },
61 { PX4CustomMode::ALTITUDE_CRUISE, altitudeCruiseFlightModeName },
62 { PX4CustomMode::TERMINATION, terminationFlightModeName },
63 { PX4CustomMode::AUTO_LOITER, holdFlightModeName },
64 { PX4CustomMode::AUTO_MISSION, missionFlightModeName },
65 { PX4CustomMode::AUTO_RTL, rtlFlightModeName },
66 { PX4CustomMode::AUTO_FOLLOW_TARGET, followMeFlightModeName },
67 { PX4CustomMode::AUTO_LAND, landingFlightModeName },
68 { PX4CustomMode::AUTO_PRECLAND, preclandFlightModeName },
69 { PX4CustomMode::AUTO_READY, readyFlightModeName },
70 { PX4CustomMode::AUTO_TAKEOFF, takeoffFlightModeName },
71 { PX4CustomMode::AUTO_VTOL_TAKEOFF, vtolTakeoffFlightModeName },
72 { PX4CustomMode::AUTO_GUIDED_COURSE, guidedCourseFlightModeName },
73 });
74
75 static FlightModeList availableFlightModes = {
76 // Mode Name Custom Mode CanBeSet adv
77 { manualFlightModeName, PX4CustomMode::MANUAL, true, true },
78 { stabilizedFlightModeName, PX4CustomMode::STABILIZED, true, true },
79 { acroFlightModeName, PX4CustomMode::ACRO, true, true },
80 { rattitudeFlightModeName, PX4CustomMode::RATTITUDE, true, false},
81 { altCtlFlightModeName, PX4CustomMode::ALTCTL, true, false},
82 { offboardFlightModeName, PX4CustomMode::OFFBOARD, true, true },
83 { simpleFlightModeName, PX4CustomMode::SIMPLE, false, false},
84 { posCtlFlightModeName, PX4CustomMode::POSCTL_POSCTL, true, false},
85 { slowFlightModeName, PX4CustomMode::POSCTL_SLOW, true, true },
86 { altitudeCruiseFlightModeName, PX4CustomMode::ALTITUDE_CRUISE, true, true },
87 { orbitFlightModeName, PX4CustomMode::POSCTL_ORBIT, false, true },
88 { holdFlightModeName, PX4CustomMode::AUTO_LOITER, true, true },
89 { missionFlightModeName, PX4CustomMode::AUTO_MISSION, true, true },
90 { rtlFlightModeName, PX4CustomMode::AUTO_RTL, true, true },
91 { followMeFlightModeName, PX4CustomMode::AUTO_FOLLOW_TARGET, false, true },
92 { landingFlightModeName, PX4CustomMode::AUTO_LAND, false, true },
93 { preclandFlightModeName, PX4CustomMode::AUTO_PRECLAND, true, true },
94 { readyFlightModeName, PX4CustomMode::AUTO_READY, false, false},
95 { takeoffFlightModeName, PX4CustomMode::AUTO_TAKEOFF, false, false},
96 { vtolTakeoffFlightModeName, PX4CustomMode::AUTO_VTOL_TAKEOFF, false, true },
97 { terminationFlightModeName, PX4CustomMode::TERMINATION, false, true },
98 { guidedCourseFlightModeName, PX4CustomMode::AUTO_GUIDED_COURSE, false, true },
99 };
100
101 updateAvailableFlightModes(availableFlightModes);
102}
103
107
109{
110 return new PX4AutoPilotPlugin(vehicle, vehicle);
111}
112
113QStringList PX4FirmwarePlugin::flightModes(Vehicle* vehicle) const
114{
115 QStringList flightModesList;
116
117 for (auto &mode : _flightModeList) {
118 if (mode.canBeSet){
119 bool fw = (vehicle->fixedWing() && mode.fixedWing);
120 bool mc = (vehicle->multiRotor() && mode.multiRotor);
121
122 // show all modes for generic, vtol, etc
123 bool other = !vehicle->fixedWing() && !vehicle->multiRotor();
124 if (fw || mc || other) {
125 flightModesList += mode.mode_name;
126 }
127 }
128 }
129
130 return flightModesList;
131}
132
133QString PX4FirmwarePlugin::flightMode(uint8_t base_mode, uint32_t custom_mode) const
134{
135 QString flightMode = "Unknown";
136
137 if (base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) {
138 return _modeEnumToString.value(custom_mode, tr("Unknown %1:%2").arg(base_mode).arg(custom_mode));
139 }
140
141 return flightMode;
142}
143
144bool PX4FirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) const
145{
146 *base_mode = 0;
147 *custom_mode = 0;
148
149 bool found = false;
150
151 for (auto &mode: _flightModeList){
152 if(flightMode.compare(mode.mode_name, Qt::CaseInsensitive) == 0){
153 *base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
154 *custom_mode = mode.custom_mode;
155 found = true;
156 break;
157 }
158 }
159
160 if (!found) {
161 qCWarning(PX4FirmwarePluginLog) << "Unknown flight Mode" << flightMode;
162 }
163
164 return found;
165}
166
167bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) const
168{
170 //-- This is arbitrary until I find how to really tell if ROI is avaiable
171 if (vehicle->multiRotor()) {
173 }
174 if (vehicle->multiRotor() || vehicle->vtol()) {
176 }
177 if (vehicle->fixedWing()) {
179 }
180 return (capabilities & available) == capabilities;
181}
182
187
189{
190 // PX4 stack does not want home position sent in the first position.
191 // Subsequent sequence numbers must be adjusted.
192 return false;
193}
194
196{
197 QList<MAV_CMD> supportedCommands = {
198 MAV_CMD_NAV_WAYPOINT,
199 MAV_CMD_NAV_LOITER_UNLIM, MAV_CMD_NAV_LOITER_TIME,
200 MAV_CMD_NAV_RETURN_TO_LAUNCH,
201 MAV_CMD_DO_JUMP,
202 MAV_CMD_DO_DIGICAM_CONTROL,
203 MAV_CMD_DO_SET_CAM_TRIGG_DIST,
204 MAV_CMD_DO_SET_SERVO,
205 MAV_CMD_DO_SET_ACTUATOR,
206 MAV_CMD_DO_CHANGE_SPEED,
207 MAV_CMD_DO_SET_HOME,
208 MAV_CMD_DO_LAND_START,
209 MAV_CMD_DO_SET_ROI_LOCATION, MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET, MAV_CMD_DO_SET_ROI_NONE,
210 MAV_CMD_DO_MOUNT_CONFIGURE,
211 MAV_CMD_DO_MOUNT_CONTROL,
212 MAV_CMD_SET_CAMERA_MODE,
213 MAV_CMD_IMAGE_START_CAPTURE, MAV_CMD_IMAGE_STOP_CAPTURE, MAV_CMD_VIDEO_START_CAPTURE, MAV_CMD_VIDEO_STOP_CAPTURE,
214 MAV_CMD_NAV_DELAY,
215 MAV_CMD_CONDITION_YAW,
216 MAV_CMD_NAV_LOITER_TO_ALT,
217 MAV_CMD_DO_GRIPPER
218 };
219
220 QList<MAV_CMD> vtolCommands = {
221 MAV_CMD_DO_VTOL_TRANSITION, MAV_CMD_NAV_VTOL_TAKEOFF, MAV_CMD_NAV_VTOL_LAND,
222 };
223
224 QList<MAV_CMD> flightCommands = {
225 MAV_CMD_NAV_LAND, MAV_CMD_NAV_TAKEOFF,
226 };
227
228 if (vehicleClass == QGCMAVLink::VehicleClassGeneric) {
229 supportedCommands += vtolCommands;
230 supportedCommands += flightCommands;
231 }
232 if (vehicleClass == QGCMAVLink::VehicleClassVTOL) {
233 supportedCommands += vtolCommands;
234 supportedCommands += flightCommands;
235 } else if (vehicleClass == QGCMAVLink::VehicleClassFixedWing || vehicleClass == QGCMAVLink::VehicleClassMultiRotor) {
236 supportedCommands += flightCommands;
237 }
238
239 if (SettingsManager::instance()->planViewSettings()->useConditionGate()->rawValue().toBool()) {
240 supportedCommands.append(MAV_CMD_CONDITION_GATE);
241 }
242
243 return supportedCommands;
244}
245
247{
248 switch (vehicleClass) {
250 return QStringLiteral(":/json/PX4-MavCmdInfoCommon.json");
252 return QStringLiteral(":/json/PX4-MavCmdInfoFixedWing.json");
254 return QStringLiteral(":/json/PX4-MavCmdInfoMultiRotor.json");
256 return QStringLiteral(":/json/PX4-MavCmdInfoVTOL.json");
258 return QStringLiteral(":/json/PX4-MavCmdInfoSub.json");
260 return QStringLiteral(":/json/PX4-MavCmdInfoRover.json");
261 default:
262 qCWarning(PX4FirmwarePluginLog) << "PX4FirmwarePlugin::missionCommandOverrides called with bad VehicleClass_t:" << vehicleClass;
263 return QString();
264 }
265}
266
271
273{
274 vehicle->sendMavCommand(vehicle->defaultComponentId(),
275 MAV_CMD_DO_REPOSITION,
276 true, // show error if failed
277 -1.0f,
278 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
279 0.0f,
280 NAN,
281 NAN,
282 NAN,
283 NAN);
284}
285
286void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL) const
287{
288 Q_UNUSED(smartRTL);
290}
291
296
297void PX4FirmwarePlugin::_mavCommandResult(int vehicleId, int component, int command, int result, int failureCode)
298{
299 Q_UNUSED(vehicleId); Q_UNUSED(component); Q_UNUSED(failureCode);
300
301 auto* vehicle = qobject_cast<Vehicle*>(sender());
302 if (!vehicle) {
303 qCWarning(PX4FirmwarePluginLog) << "Dynamic cast failed!";
304 return;
305 }
306
307 if (command == MAV_CMD_NAV_TAKEOFF && result == MAV_RESULT_ACCEPTED) {
308 // Now that we are in takeoff mode we can arm the vehicle which will cause it to takeoff.
309 // We specifically don't retry arming if it fails. This way we don't fight with the user if
310 // They are trying to disarm.
311 disconnect(vehicle, &Vehicle::mavCommandResult, this, &PX4FirmwarePlugin::_mavCommandResult);
312 if (!vehicle->armed()) {
313 vehicle->setArmedShowError(true);
314 }
315 }
316}
317
318void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double takeoffAltRel) const
319{
320 double vehicleAltitudeAMSL = vehicle->altitudeAMSL()->rawValue().toDouble();
321 if (qIsNaN(vehicleAltitudeAMSL)) {
322 QGC::showAppMessage(tr("Unable to takeoff, vehicle position not known."));
323 return;
324 }
325
326 double takeoffAltAMSL = takeoffAltRel + vehicleAltitudeAMSL;
327
328 connect(vehicle, &Vehicle::mavCommandResult, this, &PX4FirmwarePlugin::_mavCommandResult);
329 vehicle->sendMavCommand(
330 vehicle->defaultComponentId(),
331 MAV_CMD_NAV_TAKEOFF,
332 true, // show error is fails
333 NAN, // No pitch requested
334 NAN, // param 2 unused
335 0, // param 3 takeoff flags
336 NAN, NAN, NAN, // No yaw, lat, lon
337 static_cast<float>(takeoffAltAMSL)); // AMSL altitude
338}
339
341{
342 QString speedParam("MPC_XY_VEL_MAX");
343
345 return vehicle->parameterManager()->getParameter(ParameterManager::defaultComponentId, speedParam)->rawValue().toDouble();
346 }
347
349}
350
352{
353 QString airspeedMax("FW_AIRSPD_MAX");
354
356 return vehicle->parameterManager()->getParameter(ParameterManager::defaultComponentId, airspeedMax)->rawValue().toDouble();
357 }
358
360}
361
363{
364 QString airspeedMin("FW_AIRSPD_MIN");
365
367 return vehicle->parameterManager()->getParameter(ParameterManager::defaultComponentId, airspeedMin)->rawValue().toDouble();
368 }
369
371}
372
377
383
384bool PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord, double forwardFlightLoiterRadius) const
385{
386 // PX4 doesn't support setting the forward flight loiter radius of
387 // MAV_CMD_DO_REPOSITION
388 Q_UNUSED(forwardFlightLoiterRadius)
389
390 if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) {
391 QGC::showAppMessage(tr("Unable to go to location, vehicle position not known."));
392 return false;
393 }
394
395 if (vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
396 vehicle->sendMavCommandInt(vehicle->defaultComponentId(),
397 MAV_CMD_DO_REPOSITION,
398 MAV_FRAME_GLOBAL,
399 true, // show error is fails
400 -1.0f,
401 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
402 0.0f,
403 NAN,
404 gotoCoord.latitude(),
405 gotoCoord.longitude(),
406 vehicle->altitudeAMSL()->rawValue().toFloat());
407 } else {
408 vehicle->sendMavCommand(vehicle->defaultComponentId(),
409 MAV_CMD_DO_REPOSITION,
410 true, // show error is fails
411 -1.0f,
412 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
413 0.0f,
414 NAN,
415 static_cast<float>(gotoCoord.latitude()),
416 static_cast<float>(gotoCoord.longitude()),
417 vehicle->altitudeAMSL()->rawValue().toFloat());
418 }
419
420 return true;
421}
422
428
429static void _pauseVehicleThenChangeAltResultHandler(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, Vehicle::MavCmdResultFailureCode_t failureCode)
430{
431 if (ack.result != MAV_RESULT_ACCEPTED) {
432 switch (failureCode) {
434 qCDebug(PX4FirmwarePluginLog) << QStringLiteral("MAV_CMD_DO_REPOSITION error(%1)").arg(ack.result);
435 break;
437 qCDebug(PX4FirmwarePluginLog) << "MAV_CMD_DO_REPOSITION no response from vehicle";
438 break;
440 qCDebug(PX4FirmwarePluginLog) << "Internal Error: MAV_CMD_DO_REPOSITION could not be sent due to duplicate command";
441 break;
442 }
443 }
444
445 PauseVehicleThenChangeAltData_t* pData = static_cast<PauseVehicleThenChangeAltData_t*>(resultHandlerData);
446 pData->plugin->_changeAltAfterPause(resultHandlerData, ack.result == MAV_RESULT_ACCEPTED /* pauseSucceeded */);
447}
448
449void PX4FirmwarePlugin::_changeAltAfterPause(void* resultHandlerData, bool pauseSucceeded)
450{
451 PauseVehicleThenChangeAltData_t* pData = static_cast<PauseVehicleThenChangeAltData_t*>(resultHandlerData);
452
453 if (pauseSucceeded) {
454 pData->vehicle->sendMavCommand(
455 pData->vehicle->defaultComponentId(),
456 MAV_CMD_DO_REPOSITION,
457 true, // show error is fails
458 -1.0f, // Don't change groundspeed
459 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
460 0.0f, // Reserved
461 qQNaN(), qQNaN(), qQNaN(), // No change to yaw, lat, lon
462 static_cast<float>(pData->newAMSLAlt));
463 } else {
464 QGC::showAppMessage(tr("Unable to pause vehicle."));
465 }
466
467 delete pData;
468}
469
470void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeChange, bool pauseVehicle)
471{
472 if (!vehicle->homePosition().isValid()) {
473 QGC::showAppMessage(tr("Unable to change altitude, home position unknown."));
474 return;
475 }
476 if (qIsNaN(vehicle->homePosition().altitude())) {
477 QGC::showAppMessage(tr("Unable to change altitude, home position altitude unknown."));
478 return;
479 }
480
481 double currentAltRel = vehicle->altitudeRelative()->rawValue().toDouble();
482 double newAltRel = currentAltRel + altitudeChange;
483
485 resultData->plugin = this;
486 resultData->vehicle = vehicle;
487 resultData->newAMSLAlt = vehicle->homePosition().altitude() + newAltRel;
488
489 if (pauseVehicle) {
490 Vehicle::MavCmdAckHandlerInfo_t handlerInfo = {};
492 handlerInfo.resultHandlerData = resultData;
493
495 &handlerInfo,
496 vehicle->defaultComponentId(),
497 MAV_CMD_DO_REPOSITION,
498 -1.0f, // Don't change groundspeed
499 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
500 0.0f, // Reserved
501 qQNaN(), qQNaN(), qQNaN(), qQNaN()); // No change to yaw, lat, lon, alt
502 } else {
503 _changeAltAfterPause(resultData, true /* pauseSucceeded */);
504 }
505}
506
508{
509
510 vehicle->sendMavCommand(
511 vehicle->defaultComponentId(),
512 MAV_CMD_DO_CHANGE_SPEED,
513 true, // show error is fails
514 1, // 0: airspeed, 1: groundspeed
515 static_cast<float>(groundspeed), // groundspeed setpoint
516 -1, // throttle
517 0, // 0: absolute speed, 1: relative to current
518 NAN, NAN,NAN); // param 5-7 unused
519}
520
522{
523
524 vehicle->sendMavCommand(
525 vehicle->defaultComponentId(),
526 MAV_CMD_DO_CHANGE_SPEED,
527 true, // show error is fails
528 0, // 0: airspeed, 1: groundspeed
529 static_cast<float>(airspeed_equiv), // groundspeed setpoint
530 -1, // throttle
531 0, // 0: absolute speed, 1: relative to current
532 NAN, NAN,NAN); // param 5-7 unused
533}
534
535void PX4FirmwarePlugin::guidedModeChangeHeading(Vehicle* vehicle, const QGeoCoordinate &headingCoord) const
536{
538 QGC::showAppMessage(tr("Vehicle does not support guided rotate"));
539 return;
540 }
541
542 const float radians = qDegreesToRadians(vehicle->coordinate().azimuthTo(headingCoord));
543
544 vehicle->sendMavCommand(
545 vehicle->defaultComponentId(),
546 MAV_CMD_DO_REPOSITION,
547 true,
548 -1.0f, // no change in ground speed
549 MAV_DO_REPOSITION_FLAGS_CHANGE_MODE, // switch to guided mode
550 0.0f, // reserved
551 radians, // change heading
552 NAN, NAN, NAN // no change lat, lon, alt
553 );
554}
555
556bool PX4FirmwarePlugin::guidedModeROI(Vehicle* vehicle, const QGeoCoordinate& roiCenterCoord, double relativeAltitudeMeters) const
557{
558 // PX4 ignores the frame on MAV_CMD_DO_SET_ROI_LOCATION and treats the altitude as AMSL
559 // (PX4-Autopilot#28257), so convert above-home to AMSL before sending.
560 const QGeoCoordinate homePosition = vehicle->homePosition();
561 if (!homePosition.isValid() || qIsNaN(homePosition.altitude())) {
562 QGC::showAppMessage(tr("Unable to set ROI, home position altitude unknown."));
563 return false;
564 }
565
566 _sendROICommand(vehicle, roiCenterCoord, MAV_FRAME_GLOBAL, static_cast<float>(homePosition.altitude() + relativeAltitudeMeters));
567 return true;
568}
569
571{
573 if (!_armVehicleAndValidate(vehicle)) {
574 QGC::showAppMessage(tr("Unable to start takeoff: Vehicle rejected arming."));
575 return;
576 }
577 } else {
578 QGC::showAppMessage(tr("Unable to start takeoff: Vehicle not changing to %1 flight mode.").arg(takeOffFlightMode()));
579 }
580}
581
583{
585 if (!_armVehicleAndValidate(vehicle)) {
586 QGC::showAppMessage(tr("Unable to start mission: Vehicle rejected arming."));
587 return;
588 }
589 } else {
590 QGC::showAppMessage(tr("Unable to start mission: Vehicle not changing to %1 flight mode.").arg(missionFlightMode()));
591 }
592}
593
594void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) const
595{
596 if (guidedMode) {
598 } else {
599 pauseVehicle(vehicle);
600 }
601}
602
607
612
617
622
627
632
637
642
647
648bool PX4FirmwarePlugin::isGuidedMode(const Vehicle* vehicle) const
649{
650 // Not supported by generic vehicle
651 return (vehicle->flightMode() == pauseFlightMode() || vehicle->flightMode() == takeOffFlightMode() || vehicle->flightMode() == landFlightMode());
652}
653
655{
656 //-- Don't process messages to/from UDP Bridge. It doesn't suffer from these issues
657 if (message->compid == MAV_COMP_ID_UDP_BRIDGE) {
658 return true;
659 }
660
661 switch (message->msgid) {
662 case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
663 _handleAutopilotVersion(vehicle, message);
664 break;
665 }
666
667 return true;
668}
669
670void PX4FirmwarePlugin::_handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message)
671{
672 Q_UNUSED(vehicle);
673
674 auto* instanceData = qobject_cast<PX4FirmwarePluginInstanceData*>(vehicle->firmwarePluginInstanceData());
675 if (!instanceData->versionNotified) {
676 bool notifyUser = false;
677 int supportedMajorVersion = 1;
678 int supportedMinorVersion = 4;
679 int supportedPatchVersion = 1;
680
681 mavlink_autopilot_version_t version;
682 mavlink_msg_autopilot_version_decode(message, &version);
683
684 if (version.flight_sw_version != 0) {
685 int majorVersion, minorVersion, patchVersion;
686
687 majorVersion = (version.flight_sw_version >> (8*3)) & 0xFF;
688 minorVersion = (version.flight_sw_version >> (8*2)) & 0xFF;
689 patchVersion = (version.flight_sw_version >> (8*1)) & 0xFF;
690
691 if (majorVersion < supportedMajorVersion) {
692 notifyUser = true;
693 } else if (majorVersion == supportedMajorVersion) {
694 if (minorVersion < supportedMinorVersion) {
695 notifyUser = true;
696 } else if (minorVersion == supportedMinorVersion) {
697 notifyUser = patchVersion < supportedPatchVersion;
698 }
699 }
700 } else {
701 notifyUser = true;
702 }
703
704 if (notifyUser) {
705 instanceData->versionNotified = true;
706 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));
707 }
708 }
709}
710
711uint32_t PX4FirmwarePlugin::highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) const
712{
713 union px4_custom_mode px4_cm;
714 px4_cm.data = 0;
715 px4_cm.custom_mode_hl = hlCustomMode;
716
717 return px4_cm.data;
718}
719
720QString PX4FirmwarePlugin::_getLatestVersionFileUrl(Vehicle* vehicle) const
721{
722 Q_UNUSED(vehicle);
723 return QStringLiteral("https://api.github.com/repos/PX4/Firmware/releases");
724}
725
726QString PX4FirmwarePlugin::_versionRegex() const
727{
728 return QStringLiteral("v([0-9,\\.]*) Stable");
729}
730
732{
733 return ((vehicle->vehicleType() == MAV_TYPE_GROUND_ROVER) || (vehicle->vehicleType() == MAV_TYPE_SUBMARINE));
734}
735
737{
738 static const char* HOOBS_HI = "LND_FLIGHT_T_HI";
739 static const char* HOOBS_LO = "LND_FLIGHT_T_LO";
740 uint64_t hobbsTimeSeconds = 0;
741
746 hobbsTimeSeconds = ((uint64_t)factHi->rawValue().toUInt() << 32 | (uint64_t)factLo->rawValue().toUInt()) / 1000000;
747 qCDebug(PX4FirmwarePluginLog) << "Hobbs Meter raw PX4:" << "(" << factHi->rawValue().toUInt() << factLo->rawValue().toUInt() << ")";
748 }
749
750 int hours = hobbsTimeSeconds / 3600;
751 int minutes = (hobbsTimeSeconds % 3600) / 60;
752 int seconds = hobbsTimeSeconds % 60;
753 QString timeStr = QString::asprintf("%04d:%02d:%02d", hours, minutes, seconds);
754 qCDebug(PX4FirmwarePluginLog) << "Hobbs Meter string:" << timeStr;
755 return timeStr;
756}
757
758bool PX4FirmwarePlugin::hasGripper(const Vehicle* vehicle) const
759{
760 ParameterManager* paramMgr = vehicle->parameterManager();
761
762 // PX4 versions prior to v1.17 use the PD_GRIPPER_EN boolean
763 const QString gripperEnableParam = QStringLiteral("PD_GRIPPER_EN");
764 if (paramMgr->parameterExists(ParameterManager::defaultComponentId, gripperEnableParam)) {
765 return paramMgr->getParameter(ParameterManager::defaultComponentId, gripperEnableParam)->rawValue().toInt() != 0;
766 }
767
768 // PX4 v1.17+ removed PD_GRIPPER_EN; PD_GRIPPER_TYPE >= 0 means enabled (-1 = Undefined)
769 const QString gripperTypeParam = QStringLiteral("PD_GRIPPER_TYPE");
770 if (paramMgr->parameterExists(ParameterManager::defaultComponentId, gripperTypeParam)) {
771 return paramMgr->getParameter(ParameterManager::defaultComponentId, gripperTypeParam)->rawValue().toInt() >= 0;
772 }
773
774 return false;
775}
776
778{
779 for(auto &mode: modeList){
780 PX4CustomMode::Mode cMode = static_cast<PX4CustomMode::Mode>(mode.custom_mode);
781
782 // Update Multi Rotor
783 switch (cMode) {
802 mode.multiRotor = true;
803 break;
808 mode.multiRotor = false;
809 break;
810 }
811
812 // Update Fixed Wing
813 switch (cMode){
819 mode.fixedWing = false;
820 break;
838 mode.fixedWing = true;
839 break;
840 }
841 }
842 _updateFlightModeList(modeList);
843}
844
845QVariant PX4FirmwarePlugin::expandedToolbarIndicatorSource(const Vehicle* /*vehicle*/, const QString& indicatorName) const
846{
847 if (indicatorName == "Battery") {
848 return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4BatteryIndicator.qml"));
849 } else if (indicatorName == "FlightMode") {
850 return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml"));
851 } else if (indicatorName == "MainStatus") {
852 return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4MainStatusIndicator.qml"));
853 }
854
855 return QVariant();
856}
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:17
QVariant rawValue() const
Definition Fact.h:90
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.
@ 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:2146
QString flightMode() const
Definition Vehicle.cc:1468
bool vtol() const
Definition Vehicle.cc:1763
QGeoCoordinate homePosition()
Definition Vehicle.cc:1434
bool multiRotor() const
Definition Vehicle.cc:1758
void setFirmwarePluginInstanceData(FirmwarePluginInstanceData *firmwarePluginInstanceData)
Definition Vehicle.cc:2514
uint64_t capabilityBits() const
Definition Vehicle.h:709
MAV_TYPE vehicleType() const
Definition Vehicle.h:435
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:682
ParameterManager * parameterManager()
Definition Vehicle.h:577
bool fixedWing() const
Definition Vehicle.cc:1738
QGeoCoordinate coordinate()
Definition Vehicle.h:416
void mavCommandResult(int vehicleId, int targetComponent, int command, int ackResult, int failureCode)
class FirmwarePluginInstanceData * firmwarePluginInstanceData()
Definition Vehicle.h:697
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:2170
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