/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ /// @file /// @author Don Gagne #include "PX4FirmwarePlugin.h" #include "PX4ParameterMetaData.h" #include "QGCApplication.h" #include "AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h" // FIXME: Hack #include #include "px4_custom_mode.h" struct Modes2Name { uint8_t main_mode; uint8_t sub_mode; const char* name; ///< Name for flight mode bool canBeSet; ///< true: Vehicle can be set to this flight mode bool fixedWing; /// fixed wing compatible bool multiRotor; /// multi rotor compatible }; const char* PX4FirmwarePlugin::manualFlightMode = "Manual"; const char* PX4FirmwarePlugin::altCtlFlightMode = "Altitude"; const char* PX4FirmwarePlugin::posCtlFlightMode = "Position"; const char* PX4FirmwarePlugin::missionFlightMode = "Mission"; const char* PX4FirmwarePlugin::holdFlightMode = "Hold"; const char* PX4FirmwarePlugin::takeoffFlightMode = "Takeoff"; const char* PX4FirmwarePlugin::landingFlightMode = "Land"; const char* PX4FirmwarePlugin::rtlFlightMode = "Return"; const char* PX4FirmwarePlugin::acroFlightMode = "Acro"; const char* PX4FirmwarePlugin::offboardFlightMode = "Offboard"; const char* PX4FirmwarePlugin::stabilizedFlightMode = "Stabilized"; const char* PX4FirmwarePlugin::rattitudeFlightMode = "Rattitude"; const char* PX4FirmwarePlugin::followMeFlightMode = "Follow Me"; const char* PX4FirmwarePlugin::rtgsFlightMode = "Return to Groundstation"; const char* PX4FirmwarePlugin::readyFlightMode = "Ready"; // unused /// Tranlates from PX4 custom modes to flight mode names static const struct Modes2Name rgModes2Name[] = { //main_mode sub_mode name canBeSet FW MC { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::manualFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::stabilizedFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::acroFlightMode, true, false, true }, { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::rattitudeFlightMode, true, false, true }, { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::altCtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::posCtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::holdFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::missionFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::rtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::followMeFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::offboardFlightMode, true, true, true }, // modes that can't be directly set by the user { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::landingFlightMode, false, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::readyFlightMode, false, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTGS, PX4FirmwarePlugin::rtgsFlightMode, false, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF, PX4FirmwarePlugin::takeoffFlightMode, false, true, true }, }; PX4FirmwarePlugin::PX4FirmwarePlugin(void) : _versionNotified(false) { } QList PX4FirmwarePlugin::componentsForVehicle(AutoPilotPlugin* vehicle) { Q_UNUSED(vehicle); return QList(); } QStringList PX4FirmwarePlugin::flightModes(Vehicle* vehicle) { QStringList flightModes; for (size_t i=0; icanBeSet) { bool fw = (vehicle->fixedWing() && pModes2Name->fixedWing); bool mc = (vehicle->multiRotor() && pModes2Name->multiRotor); // show all modes for generic, vtol, etc bool other = !vehicle->fixedWing() && !vehicle->multiRotor(); if (fw || mc || other) { flightModes += pModes2Name->name; } } } return flightModes; } QString PX4FirmwarePlugin::flightMode(uint8_t base_mode, uint32_t custom_mode) const { QString flightMode = "Unknown"; if (base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) { union px4_custom_mode px4_mode; px4_mode.data = custom_mode; bool found = false; for (size_t i=0; imain_mode == px4_mode.main_mode && pModes2Name->sub_mode == px4_mode.sub_mode) { flightMode = pModes2Name->name; found = true; break; } } if (!found) { qWarning() << "Unknown flight mode" << custom_mode; } } else { qWarning() << "PX4 Flight Stack flight mode without custom mode enabled?"; } return flightMode; } bool PX4FirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) { *base_mode = 0; *custom_mode = 0; bool found = false; for (size_t i=0; iname, Qt::CaseInsensitive) == 0) { union px4_custom_mode px4_mode; px4_mode.data = 0; px4_mode.main_mode = pModes2Name->main_mode; px4_mode.sub_mode = pModes2Name->sub_mode; *base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED; *custom_mode = px4_mode.data; found = true; break; } } if (!found) { qWarning() << "Unknown flight Mode" << flightMode; } return found; } int PX4FirmwarePlugin::manualControlReservedButtonCount(void) { return 0; // 0 buttons reserved for rc switch simulation } bool PX4FirmwarePlugin::supportsManualControl(void) { return true; } bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) { if(vehicle->multiRotor()) { return (capabilities & (MavCmdPreflightStorageCapability | GuidedModeCapability | SetFlightModeCapability | PauseVehicleCapability | OrbitModeCapability)) == capabilities; } return (capabilities & (MavCmdPreflightStorageCapability | GuidedModeCapability | SetFlightModeCapability | PauseVehicleCapability)) == capabilities; } void PX4FirmwarePlugin::initializeVehicle(Vehicle* vehicle) { Q_UNUSED(vehicle); // PX4 Flight Stack doesn't need to do any extra work } bool PX4FirmwarePlugin::sendHomePositionToVehicle(void) { // PX4 stack does not want home position sent in the first position. // Subsequent sequence numbers must be adjusted. return false; } void PX4FirmwarePlugin::addMetaDataToFact(QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) { PX4ParameterMetaData* px4MetaData = qobject_cast(parameterMetaData); if (px4MetaData) { px4MetaData->addMetaDataToFact(fact, vehicleType); } else { qWarning() << "Internal error: pointer passed to PX4FirmwarePlugin::addMetaDataToFact not PX4ParameterMetaData"; } } QList PX4FirmwarePlugin::supportedMissionCommands(void) { QList list; list << MAV_CMD_NAV_WAYPOINT << MAV_CMD_NAV_LOITER_UNLIM << MAV_CMD_NAV_LOITER_TIME << MAV_CMD_NAV_LOITER_TO_ALT << MAV_CMD_NAV_LAND << MAV_CMD_NAV_TAKEOFF << MAV_CMD_DO_JUMP << MAV_CMD_DO_VTOL_TRANSITION << MAV_CMD_NAV_VTOL_TAKEOFF << MAV_CMD_NAV_VTOL_LAND << MAV_CMD_DO_DIGICAM_CONTROL << MAV_CMD_DO_SET_CAM_TRIGG_DIST << MAV_CMD_DO_SET_SERVO << MAV_CMD_DO_CHANGE_SPEED << MAV_CMD_DO_LAND_START << MAV_CMD_DO_MOUNT_CONFIGURE << MAV_CMD_DO_MOUNT_CONTROL; return list; } QString PX4FirmwarePlugin::missionCommandOverrides(MAV_TYPE vehicleType) const { switch (vehicleType) { case MAV_TYPE_GENERIC: return QStringLiteral(":/json/PX4/MavCmdInfoCommon.json"); break; case MAV_TYPE_FIXED_WING: return QStringLiteral(":/json/PX4/MavCmdInfoFixedWing.json"); break; case MAV_TYPE_QUADROTOR: return QStringLiteral(":/json/PX4/MavCmdInfoMultiRotor.json"); break; case MAV_TYPE_VTOL_QUADROTOR: return QStringLiteral(":/json/APM/MavCmdInfoVTOL.json"); break; case MAV_TYPE_SUBMARINE: return QStringLiteral(":/json/PX4/MavCmdInfoSub.json"); break; case MAV_TYPE_GROUND_ROVER: return QStringLiteral(":/json/PX4/MavCmdInfoRover.json"); break; default: qWarning() << "PX4FirmwarePlugin::missionCommandOverrides called with bad MAV_TYPE:" << vehicleType; return QString(); } } QObject* PX4FirmwarePlugin::loadParameterMetaData(const QString& metaDataFile) { PX4ParameterMetaData* metaData = new PX4ParameterMetaData; metaData->loadParameterFactMetaDataFile(metaDataFile); return metaData; } void PX4FirmwarePlugin::pauseVehicle(Vehicle* vehicle) { // then tell it to loiter at the current position mavlink_message_t msg; mavlink_command_long_t cmd; cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; cmd.confirmation = 0; cmd.param1 = -1.0f; cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; cmd.param3 = 0.0f; cmd.param4 = NAN; cmd.param5 = NAN; cmd.param6 = NAN; cmd.param7 = NAN; cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); vehicle->sendMessageOnPriorityLink(msg); } void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle) { vehicle->setFlightMode(rtlFlightMode); } void PX4FirmwarePlugin::guidedModeLand(Vehicle* vehicle) { vehicle->setFlightMode(landingFlightMode); } void PX4FirmwarePlugin::guidedModeOrbit(Vehicle* vehicle, const QGeoCoordinate& centerCoord, double radius, double velocity, double altitude) { //-- If not in "guided" mode, make it so. if(!isGuidedMode(vehicle)) setGuidedMode(vehicle, true); MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); mavlink_message_t msg; mavlink_command_long_t cmd; cmd.command = (uint16_t)MAV_CMD_SET_GUIDED_SUBMODE_CIRCLE; cmd.confirmation = 0; cmd.param1 = radius; cmd.param2 = velocity; cmd.param3 = altitude; cmd.param4 = NAN; cmd.param5 = centerCoord.isValid() ? centerCoord.latitude() : NAN; cmd.param6 = centerCoord.isValid() ? centerCoord.longitude() : NAN; cmd.param7 = centerCoord.isValid() ? centerCoord.altitude() : NAN; cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); vehicle->sendMessageOnPriorityLink(msg); } void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) { Q_UNUSED(altitudeRel); if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) { qgcApp()->showMessage(QStringLiteral("Unable to takeoff, vehicle position not known.")); return; } MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); // Set destination altitude mavlink_message_t msg; mavlink_command_long_t cmd; cmd.command = (uint16_t)MAV_CMD_NAV_TAKEOFF; cmd.confirmation = 0; cmd.param1 = -1.0f; cmd.param2 = 0.0f; cmd.param3 = 0.0f; cmd.param4 = NAN; cmd.param5 = NAN; cmd.param6 = NAN; cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble() + altitudeRel; cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); vehicle->sendMessageOnPriorityLink(msg); } void PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord) { if (qIsNaN(vehicle->altitudeRelative()->rawValue().toDouble())) { qgcApp()->showMessage(QStringLiteral("Unable to go to location, vehicle position not known.")); return; } mavlink_message_t msg; mavlink_command_long_t cmd; cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; cmd.confirmation = 0; cmd.param1 = -1.0f; cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; cmd.param3 = 0.0f; cmd.param4 = NAN; cmd.param5 = gotoCoord.latitude(); cmd.param6 = gotoCoord.longitude(); cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble(); cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); vehicle->sendMessageOnPriorityLink(msg); } void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeRel) { if (qIsNaN(vehicle->altitudeRelative()->rawValue().toDouble())) { qgcApp()->showMessage(QStringLiteral("Unable to change altitude, vehicle altitude not known.")); return; } mavlink_message_t msg; mavlink_command_long_t cmd; cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; cmd.confirmation = 0; cmd.param1 = -1.0f; cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; cmd.param3 = 0.0f; cmd.param4 = NAN; cmd.param5 = NAN; cmd.param6 = NAN; cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble() + altitudeRel; cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); vehicle->sendMessageOnPriorityLink(msg); } void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) { if (guidedMode) { vehicle->setFlightMode(holdFlightMode); } else { pauseVehicle(vehicle); } } bool PX4FirmwarePlugin::isGuidedMode(const Vehicle* vehicle) const { // Not supported by generic vehicle return (vehicle->flightMode() == holdFlightMode || vehicle->flightMode() == takeoffFlightMode || vehicle->flightMode() == landingFlightMode); } bool PX4FirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message) { //-- Don't process messages to/from UDP Bridge. It doesn't suffer from these issues if (message->compid == MAV_COMP_ID_UDP_BRIDGE) { return true; } switch (message->msgid) { case MAVLINK_MSG_ID_AUTOPILOT_VERSION: _handleAutopilotVersion(vehicle, message); break; } return true; } void PX4FirmwarePlugin::_handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message) { Q_UNUSED(vehicle); if (!_versionNotified) { bool notifyUser = false; int supportedMajorVersion = 1; int supportedMinorVersion = 4; int supportedPatchVersion = 1; mavlink_autopilot_version_t version; mavlink_msg_autopilot_version_decode(message, &version); if (version.flight_sw_version != 0) { int majorVersion, minorVersion, patchVersion; majorVersion = (version.flight_sw_version >> (8*3)) & 0xFF; minorVersion = (version.flight_sw_version >> (8*2)) & 0xFF; patchVersion = (version.flight_sw_version >> (8*1)) & 0xFF; notifyUser = majorVersion < supportedMajorVersion || minorVersion < supportedMinorVersion || patchVersion < supportedPatchVersion; } else { notifyUser = true; } if (notifyUser) { _versionNotified = true; qgcApp()->showMessage(QString("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)); } } }