Skip to content
Snippets Groups Projects
Vehicle.cc 133 KiB
Newer Older
  • Learn to ignore specific revisions
  • /****************************************************************************
     *
     *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
     *
     * QGroundControl is licensed according to the terms in the file
     * COPYING.md in the root of the source code directory.
     *
     ****************************************************************************/
    
    dheideman's avatar
    dheideman committed
    #include <QTime>
    #include <QDateTime>
    #include <QLocale>
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    #include <QQuaternion>
    
    
    #include "Vehicle.h"
    #include "MAVLinkProtocol.h"
    #include "FirmwarePluginManager.h"
    #include "LinkManager.h"
    #include "FirmwarePlugin.h"
    
    #include "JoystickManager.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "MissionManager.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "MissionController.h"
    
    #include "PlanMasterController.h"
    
    #include "GeoFenceManager.h"
    #include "RallyPointManager.h"
    
    #include "CoordinateVector.h"
    
    #include "ParameterManager.h"
    
    #include "QGCApplication.h"
    
    dogmaphobic's avatar
    dogmaphobic committed
    #include "QGCImageProvider.h"
    
    #include "AudioOutput.h"
    
    Jimmy Johnson's avatar
    Jimmy Johnson committed
    #include "FollowMe.h"
    
    #include "MissionCommandTree.h"
    
    #include "QGroundControlQmlGlobal.h"
    
    #include "SettingsManager.h"
    
    #include "QGCQGeoCoordinate.h"
    
    #include "QGCCorePlugin.h"
    
    #include "ADSBVehicle.h"
    
    #include "QGCCameraManager.h"
    
    #include "VideoReceiver.h"
    #include "VideoManager.h"
    
    
    QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog")
    
    
    #define UPDATE_TIMER 50
    #define DEFAULT_LAT  38.965767f
    #define DEFAULT_LON -120.083923f
    
    
    const QString guided_mode_not_supported_by_vehicle = QObject::tr("Guided mode not supported by Vehicle.");
    
    const char* Vehicle::_settingsGroup =               "Vehicle%1";        // %1 replaced with mavlink system id
    const char* Vehicle::_joystickModeSettingsKey =     "JoystickMode";
    const char* Vehicle::_joystickEnabledSettingsKey =  "JoystickEnabled";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* Vehicle::_rollFactName =                "roll";
    const char* Vehicle::_pitchFactName =               "pitch";
    const char* Vehicle::_headingFactName =             "heading";
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    const char* Vehicle::_rollRateFactName =             "rollRate";
    const char* Vehicle::_pitchRateFactName =           "pitchRate";
    const char* Vehicle::_yawRateFactName =             "yawRate";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* Vehicle::_airSpeedFactName =            "airSpeed";
    const char* Vehicle::_groundSpeedFactName =         "groundSpeed";
    const char* Vehicle::_climbRateFactName =           "climbRate";
    const char* Vehicle::_altitudeRelativeFactName =    "altitudeRelative";
    const char* Vehicle::_altitudeAMSLFactName =        "altitudeAMSL";
    
    const char* Vehicle::_flightDistanceFactName =      "flightDistance";
    
    const char* Vehicle::_flightTimeFactName =          "flightTime";
    
    const char* Vehicle::_distanceToHomeFactName =      "distanceToHome";
    
    const char* Vehicle::_hobbsFactName =               "hobbs";
    
    const char* Vehicle::_gpsFactGroupName =            "gps";
    const char* Vehicle::_batteryFactGroupName =        "battery";
    const char* Vehicle::_windFactGroupName =           "wind";
    const char* Vehicle::_vibrationFactGroupName =      "vibration";
    const char* Vehicle::_temperatureFactGroupName =    "temperature";
    const char* Vehicle::_clockFactGroupName =          "clock";
    const char* Vehicle::_distanceSensorFactGroupName = "distanceSensor";
    
    Vehicle::Vehicle(LinkInterface*             link,
                     int                        vehicleId,
    
                     int                        defaultComponentId,
    
                     MAV_AUTOPILOT              firmwareType,
                     MAV_TYPE                   vehicleType,
                     FirmwarePluginManager*     firmwarePluginManager,
                     JoystickManager*           joystickManager)
    
    Don Gagne's avatar
    Don Gagne committed
        : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json")
        , _id(vehicleId)
    
        , _defaultComponentId(defaultComponentId)
    
        , _active(false)
    
        , _offlineEditingVehicle(false)
    
        , _firmwareType(firmwareType)
    
        , _vehicleType(vehicleType)
    
        , _firmwarePlugin(NULL)
    
        , _firmwarePluginInstanceData(NULL)
    
        , _autopilotPlugin(NULL)
    
        , _mavlink(NULL)
        , _soloFirmware(false)
    
        , _toolbox(qgcApp()->toolbox())
        , _settingsManager(_toolbox->settingsManager())
    
        , _joystickMode(JoystickModeRC)
    
        , _joystickEnabled(false)
    
        , _uas(NULL)
    
        , _mav(NULL)
        , _currentMessageCount(0)
        , _messageCount(0)
        , _currentErrorCount(0)
        , _currentWarningCount(0)
        , _currentNormalCount(0)
        , _currentMessageType(MessageNone)
        , _updateCount(0)
    
        , _rcRSSI(255)
        , _rcRSSIstore(255)
    
    Don Gagne's avatar
    Don Gagne committed
        , _autoDisconnect(false)
    
    Don Gagne's avatar
    Don Gagne committed
        , _flying(false)
    
        , _vtolInFwdFlight(false)
    
        , _onboardControlSensorsPresent(0)
        , _onboardControlSensorsEnabled(0)
        , _onboardControlSensorsHealth(0)
        , _onboardControlSensorsUnhealthy(0)
    
        , _gpsRawIntMessageAvailable(false)
        , _globalPositionIntMessageAvailable(false)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _defaultCruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble())
        , _defaultHoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble())
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _telemetryRRSSI(0)
        , _telemetryLRSSI(0)
        , _telemetryRXErrors(0)
        , _telemetryFixed(0)
        , _telemetryTXBuffer(0)
        , _telemetryLNoise(0)
        , _telemetryRNoise(0)
    
        , _vehicleCapabilitiesKnown(false)
    
        , _capabilityBits(0)
    
        , _highLatencyLink(false)
    
        , _cameras(NULL)
    
        , _connectionLost(false)
        , _connectionLostEnabled(true)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _initialPlanRequestComplete(false)
    
        , _missionManager(NULL)
    
        , _missionManagerInitialRequestSent(false)
    
        , _geoFenceManager(NULL)
    
        , _geoFenceManagerInitialRequestSent(false)
        , _rallyPointManager(NULL)
        , _rallyPointManagerInitialRequestSent(false)
    
    Don Gagne's avatar
    Don Gagne committed
        , _armed(false)
        , _base_mode(0)
        , _custom_mode(0)
    
        , _nextSendMessageMultipleIndex(0)
    
        , _firmwarePluginManager(firmwarePluginManager)
        , _joystickManager(joystickManager)
    
    dogmaphobic's avatar
    dogmaphobic committed
        , _flowImageIndex(0)
    
    Don Gagne's avatar
    Don Gagne committed
        , _allLinksInactiveSent(false)
    
        , _messagesReceived(0)
        , _messagesSent(0)
        , _messagesLost(0)
        , _messageSeq(0)
        , _compID(0)
        , _heardFrom(false)
    
        , _firmwareMajorVersion(versionNotSetValue)
        , _firmwareMinorVersion(versionNotSetValue)
        , _firmwarePatchVersion(versionNotSetValue)
    
        , _firmwareCustomMajorVersion(versionNotSetValue)
        , _firmwareCustomMinorVersion(versionNotSetValue)
        , _firmwareCustomPatchVersion(versionNotSetValue)
    
        , _firmwareVersionType(FIRMWARE_VERSION_TYPE_OFFICIAL)
    
        , _gitHash(versionNotSetValue)
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _uid(0)
    
        , _lastAnnouncedLowBatteryPercent(100)
    
    Don Gagne's avatar
    Don Gagne committed
        , _rollFact             (0, _rollFactName,              FactMetaData::valueTypeDouble)
        , _pitchFact            (0, _pitchFactName,             FactMetaData::valueTypeDouble)
        , _headingFact          (0, _headingFactName,           FactMetaData::valueTypeDouble)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _rollRateFact         (0, _rollRateFactName,          FactMetaData::valueTypeDouble)
        , _pitchRateFact        (0, _pitchRateFactName,         FactMetaData::valueTypeDouble)
        , _yawRateFact          (0, _yawRateFactName,           FactMetaData::valueTypeDouble)
    
    Don Gagne's avatar
    Don Gagne committed
        , _groundSpeedFact      (0, _groundSpeedFactName,       FactMetaData::valueTypeDouble)
        , _airSpeedFact         (0, _airSpeedFactName,          FactMetaData::valueTypeDouble)
        , _climbRateFact        (0, _climbRateFactName,         FactMetaData::valueTypeDouble)
        , _altitudeRelativeFact (0, _altitudeRelativeFactName,  FactMetaData::valueTypeDouble)
        , _altitudeAMSLFact     (0, _altitudeAMSLFactName,      FactMetaData::valueTypeDouble)
    
        , _flightDistanceFact   (0, _flightDistanceFactName,    FactMetaData::valueTypeDouble)
    
        , _flightTimeFact       (0, _flightTimeFactName,        FactMetaData::valueTypeElapsedTimeInSeconds)
    
        , _distanceToHomeFact   (0, _distanceToHomeFactName,    FactMetaData::valueTypeDouble)
    
        , _hobbsFact            (0, _hobbsFactName,             FactMetaData::valueTypeString)
    
    Don Gagne's avatar
    Don Gagne committed
        , _gpsFactGroup(this)
    
    Don Gagne's avatar
    Don Gagne committed
        , _batteryFactGroup(this)
    
    Don Gagne's avatar
    Don Gagne committed
        , _windFactGroup(this)
    
        , _vibrationFactGroup(this)
    
        , _temperatureFactGroup(this)
    
    dheideman's avatar
    dheideman committed
        , _clockFactGroup(this)
    
        , _distanceSensorFactGroup(this)
    
    {
        _addLink(link);
    
        connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_loadSettings);
        connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleAvailableChanged, this, &Vehicle::_loadSettings);
    
        _mavlink = _toolbox->mavlinkProtocol();
    
    dogmaphobic's avatar
    dogmaphobic committed
        connect(_mavlink, &MAVLinkProtocol::messageReceived,     this, &Vehicle::_mavlinkMessageReceived);
    
        connect(this, &Vehicle::_sendMessageOnLinkOnThread, this, &Vehicle::_sendMessageOnLink, Qt::QueuedConnection);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(this, &Vehicle::flightModeChanged,          this, &Vehicle::_handleFlightModeChanged);
    
        connect(this, &Vehicle::armedChanged,               this, &Vehicle::_announceArmedChanged);
    
        connect(_toolbox->multiVehicleManager(), &MultiVehicleManager::parameterReadyVehicleAvailableChanged, this, &Vehicle::_vehicleParamLoaded);
    
    
        _uas = new UAS(_mavlink, this, _firmwarePluginManager);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(_uas, &UAS::imageReady,                     this, &Vehicle::_imageReady);
        connect(this, &Vehicle::remoteControlRSSIChanged,   this, &Vehicle::_remoteControlRSSIChanged);
    
    Don Gagne's avatar
    Don Gagne committed
        _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this);
    
    Jimmy Johnson's avatar
    Jimmy Johnson committed
        // connect this vehicle to the follow me handle manager
    
        connect(this, &Vehicle::flightModeChanged,_toolbox->followMe(), &FollowMe::followMeHandleManager);
    
    Jimmy Johnson's avatar
    Jimmy Johnson committed
    
    
    Don Gagne's avatar
    Don Gagne committed
        // PreArm Error self-destruct timer
        connect(&_prearmErrorTimer, &QTimer::timeout, this, &Vehicle::_prearmErrorTimeout);
        _prearmErrorTimer.setInterval(_prearmErrorTimeoutMSecs);
        _prearmErrorTimer.setSingleShot(true);
    
    
        // Connection Lost timer
        _connectionLostTimer.setInterval(_connectionLostTimeoutMSecs);
    
        _connectionLostTimer.setSingleShot(false);
        _connectionLostTimer.start();
        connect(&_connectionLostTimer, &QTimer::timeout, this, &Vehicle::_connectionLostTimeout);
    
        // Send MAV_CMD ack timer
        _mavCommandAckTimer.setSingleShot(true);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _mavCommandAckTimer.setInterval(_highLatencyLink ? _mavCommandAckTimeoutMSecsHighLatency : _mavCommandAckTimeoutMSecs);
    
        connect(&_mavCommandAckTimer, &QTimer::timeout, this, &Vehicle::_sendMavCommandAgain);
    
    
        // Listen for system messages
    
        connect(_toolbox->uasMessageHandler(), &UASMessageHandler::textMessageCountChanged,  this, &Vehicle::_handleTextMessage);
        connect(_toolbox->uasMessageHandler(), &UASMessageHandler::textMessageReceived,      this, &Vehicle::_handletextMessageReceived);
    
        // Now connect the new UAS
        connect(_mav, SIGNAL(attitudeChanged                    (UASInterface*,double,double,double,quint64)),              this, SLOT(_updateAttitude(UASInterface*, double, double, double, quint64)));
        connect(_mav, SIGNAL(attitudeChanged                    (UASInterface*,int,double,double,double,quint64)),          this, SLOT(_updateAttitude(UASInterface*,int,double, double, double, quint64)));
    
        if (_highLatencyLink) {
            // High latency links don't request information
            _setMaxProtoVersion(100);
            _setCapabilities(0);
            _initialPlanRequestComplete = true;
            _missionManagerInitialRequestSent = true;
            _geoFenceManagerInitialRequestSent = true;
            _rallyPointManagerInitialRequestSent = true;
        } else {
            // Ask the vehicle for protocol version info.
            sendMavCommand(MAV_COMP_ID_ALL,                     // Don't know default component id yet.
                           MAV_CMD_REQUEST_PROTOCOL_VERSION,
                           false,                               // No error shown if fails
                           1);                                  // Request protocol version
    
            // Ask the vehicle for firmware version info.
            sendMavCommand(MAV_COMP_ID_ALL,                         // Don't know default component id yet.
                           MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES,
                           false,                                   // No error shown if fails
                           1);                                      // Request firmware version
        }
    
        _firmwarePlugin->initializeVehicle(this);
    
        _sendMultipleTimer.start(_sendMessageMultipleIntraMessageDelay);
        connect(&_sendMultipleTimer, &QTimer::timeout, this, &Vehicle::_sendMessageMultipleNext);
    
        _mapTrajectoryTimer.setInterval(_mapTrajectoryMsecsBetweenPoints);
        connect(&_mapTrajectoryTimer, &QTimer::timeout, this, &Vehicle::_addNewMapTrajectoryPoint);
    
    
        // Create camera manager instance
    
        _cameras = _firmwarePlugin->createCameraManager(this);
    
        emit dynamicCamerasChanged();
    
    // Disconnected Vehicle for offline editing
    Vehicle::Vehicle(MAV_AUTOPILOT              firmwareType,
                     MAV_TYPE                   vehicleType,
                     FirmwarePluginManager*     firmwarePluginManager,
                     QObject*                   parent)
    
        : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json", parent)
        , _id(0)
    
        , _defaultComponentId(MAV_COMP_ID_ALL)
    
        , _active(false)
    
        , _offlineEditingVehicle(true)
    
        , _firmwareType(firmwareType)
        , _vehicleType(vehicleType)
    
        , _firmwarePlugin(NULL)
    
        , _firmwarePluginInstanceData(NULL)
    
        , _autopilotPlugin(NULL)
    
        , _mavlink(NULL)
        , _soloFirmware(false)
    
        , _toolbox(qgcApp()->toolbox())
        , _settingsManager(_toolbox->settingsManager())
    
        , _joystickMode(JoystickModeRC)
        , _joystickEnabled(false)
        , _uas(NULL)
        , _mav(NULL)
        , _currentMessageCount(0)
        , _messageCount(0)
        , _currentErrorCount(0)
        , _currentWarningCount(0)
        , _currentNormalCount(0)
        , _currentMessageType(MessageNone)
        , _updateCount(0)
    
        , _rcRSSI(255)
        , _rcRSSIstore(255)
    
        , _autoDisconnect(false)
    
        , _vtolInFwdFlight(false)
    
        , _onboardControlSensorsPresent(0)
        , _onboardControlSensorsEnabled(0)
        , _onboardControlSensorsHealth(0)
        , _onboardControlSensorsUnhealthy(0)
    
        , _gpsRawIntMessageAvailable(false)
        , _globalPositionIntMessageAvailable(false)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _defaultCruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble())
        , _defaultHoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble())
    
        , _vehicleCapabilitiesKnown(true)
    
        , _capabilityBits(_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA ? 0 : MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY)
    
        , _highLatencyLink(false)
    
        , _cameras(NULL)
    
        , _connectionLost(false)
        , _connectionLostEnabled(true)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _initialPlanRequestComplete(false)
    
        , _missionManager(NULL)
    
        , _missionManagerInitialRequestSent(false)
    
        , _geoFenceManager(NULL)
    
        , _geoFenceManagerInitialRequestSent(false)
        , _rallyPointManager(NULL)
        , _rallyPointManagerInitialRequestSent(false)
    
        , _armed(false)
        , _base_mode(0)
        , _custom_mode(0)
        , _nextSendMessageMultipleIndex(0)
    
        , _firmwarePluginManager(firmwarePluginManager)
    
        , _joystickManager(NULL)
        , _flowImageIndex(0)
        , _allLinksInactiveSent(false)
        , _messagesReceived(0)
        , _messagesSent(0)
        , _messagesLost(0)
        , _messageSeq(0)
        , _compID(0)
        , _heardFrom(false)
    
        , _firmwareMajorVersion(versionNotSetValue)
        , _firmwareMinorVersion(versionNotSetValue)
        , _firmwarePatchVersion(versionNotSetValue)
    
        , _firmwareCustomMajorVersion(versionNotSetValue)
        , _firmwareCustomMinorVersion(versionNotSetValue)
        , _firmwareCustomPatchVersion(versionNotSetValue)
        , _firmwareVersionType(FIRMWARE_VERSION_TYPE_OFFICIAL)
    
        , _gitHash(versionNotSetValue)
    
    Gus Grubba's avatar
    Gus Grubba committed
        , _uid(0)
    
        , _lastAnnouncedLowBatteryPercent(100)
    
        , _rollFact             (0, _rollFactName,              FactMetaData::valueTypeDouble)
        , _pitchFact            (0, _pitchFactName,             FactMetaData::valueTypeDouble)
        , _headingFact          (0, _headingFactName,           FactMetaData::valueTypeDouble)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        , _rollRateFact         (0, _rollRateFactName,          FactMetaData::valueTypeDouble)
        , _pitchRateFact        (0, _pitchRateFactName,         FactMetaData::valueTypeDouble)
        , _yawRateFact          (0, _yawRateFactName,           FactMetaData::valueTypeDouble)
    
        , _groundSpeedFact      (0, _groundSpeedFactName,       FactMetaData::valueTypeDouble)
        , _airSpeedFact         (0, _airSpeedFactName,          FactMetaData::valueTypeDouble)
        , _climbRateFact        (0, _climbRateFactName,         FactMetaData::valueTypeDouble)
        , _altitudeRelativeFact (0, _altitudeRelativeFactName,  FactMetaData::valueTypeDouble)
        , _altitudeAMSLFact     (0, _altitudeAMSLFactName,      FactMetaData::valueTypeDouble)
    
        , _flightDistanceFact   (0, _flightDistanceFactName,    FactMetaData::valueTypeDouble)
        , _flightTimeFact       (0, _flightTimeFactName,        FactMetaData::valueTypeElapsedTimeInSeconds)
    
        , _distanceToHomeFact   (0, _distanceToHomeFactName,    FactMetaData::valueTypeDouble)
    
        , _hobbsFact            (0, _hobbsFactName,             FactMetaData::valueTypeString)
    
        , _gpsFactGroup(this)
        , _batteryFactGroup(this)
        , _windFactGroup(this)
        , _vibrationFactGroup(this)
    
    dheideman's avatar
    dheideman committed
        , _clockFactGroup(this)
    
        , _distanceSensorFactGroup(this)
    
        _firmwarePlugin->initializeVehicle(this);
    
    }
    
    void Vehicle::_commonInit(void)
    {
        _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
    
        connect(_firmwarePlugin, &FirmwarePlugin::toolbarIndicatorsChanged, this, &Vehicle::toolBarIndicatorsChanged);
    
    
        connect(this, &Vehicle::coordinateChanged,      this, &Vehicle::_updateDistanceToHome);
        connect(this, &Vehicle::homePositionChanged,    this, &Vehicle::_updateDistanceToHome);
    
        connect(this, &Vehicle::hobbsMeterChanged,      this, &Vehicle::_updateHobbsMeter);
    
        _missionManager = new MissionManager(this);
    
        connect(_missionManager, &MissionManager::error,                    this, &Vehicle::_missionManagerError);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_missionLoadComplete);
    
        connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_clearCameraTriggerPoints);
        connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_clearTrajectoryPoints);
        connect(_missionManager, &MissionManager::sendComplete,             this, &Vehicle::_clearCameraTriggerPoints);
        connect(_missionManager, &MissionManager::sendComplete,             this, &Vehicle::_clearTrajectoryPoints);
    
        _parameterManager = new ParameterManager(this);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady);
    
        // GeoFenceManager needs to access ParameterManager so make sure to create after
    
        _geoFenceManager = new GeoFenceManager(this);
    
        connect(_geoFenceManager, &GeoFenceManager::error,          this, &Vehicle::_geoFenceManagerError);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        connect(_geoFenceManager, &GeoFenceManager::loadComplete,   this, &Vehicle::_geoFenceLoadComplete);
    
        _rallyPointManager = new RallyPointManager(this);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        connect(_rallyPointManager, &RallyPointManager::error,          this, &Vehicle::_rallyPointManagerError);
        connect(_rallyPointManager, &RallyPointManager::loadComplete,   this, &Vehicle::_rallyPointLoadComplete);
    
        // Offline editing vehicle tracks settings changes for offline editing settings
    
        connect(_settingsManager->appSettings()->offlineEditingFirmwareType(),  &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged);
        connect(_settingsManager->appSettings()->offlineEditingVehicleType(),   &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged);
        connect(_settingsManager->appSettings()->offlineEditingCruiseSpeed(),   &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged);
        connect(_settingsManager->appSettings()->offlineEditingHoverSpeed(),    &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged);
    
        // Flight modes can differ based on advanced mode
        connect(_toolbox->corePlugin(), &QGCCorePlugin::showAdvancedUIChanged, this, &Vehicle::flightModesChanged);
    
    
        // Build FactGroup object model
    
        _addFact(&_rollFact,                _rollFactName);
        _addFact(&_pitchFact,               _pitchFactName);
        _addFact(&_headingFact,             _headingFactName);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _addFact(&_rollRateFact,            _rollRateFactName);
        _addFact(&_pitchRateFact,           _pitchRateFactName);
        _addFact(&_yawRateFact,             _yawRateFactName);
    
        _addFact(&_groundSpeedFact,         _groundSpeedFactName);
        _addFact(&_airSpeedFact,            _airSpeedFactName);
        _addFact(&_climbRateFact,           _climbRateFactName);
        _addFact(&_altitudeRelativeFact,    _altitudeRelativeFactName);
        _addFact(&_altitudeAMSLFact,        _altitudeAMSLFactName);
    
        _addFact(&_flightDistanceFact,      _flightDistanceFactName);
    
        _addFact(&_flightTimeFact,          _flightTimeFactName);
    
        _addFact(&_distanceToHomeFact,      _distanceToHomeFactName);
    
    
        _hobbsFact.setRawValue(QVariant(QString("0000:00:00")));
    
        _addFact(&_hobbsFact,               _hobbsFactName);
    
        _addFactGroup(&_gpsFactGroup,               _gpsFactGroupName);
        _addFactGroup(&_batteryFactGroup,           _batteryFactGroupName);
        _addFactGroup(&_windFactGroup,              _windFactGroupName);
        _addFactGroup(&_vibrationFactGroup,         _vibrationFactGroupName);
        _addFactGroup(&_temperatureFactGroup,       _temperatureFactGroupName);
        _addFactGroup(&_clockFactGroup,             _clockFactGroupName);
        _addFactGroup(&_distanceSensorFactGroup,    _distanceSensorFactGroupName);
    
    Jacob Walser's avatar
    Jacob Walser committed
        // Add firmware-specific fact groups, if provided
    
        QMap<QString, FactGroup*>* fwFactGroups = _firmwarePlugin->factGroups();
        if (fwFactGroups) {
            QMapIterator<QString, FactGroup*> i(*fwFactGroups);
            while(i.hasNext()) {
                i.next();
                _addFactGroup(i.value(), i.key());
            }
    
        _flightDistanceFact.setRawValue(0);
    
        _flightTimeFact.setRawValue(0);
    
    Vehicle::~Vehicle()
    {
    
    Don Gagne's avatar
    Don Gagne committed
        qCDebug(VehicleLog) << "~Vehicle" << this;
    
    
        delete _missionManager;
        _missionManager = NULL;
    
        delete _autopilotPlugin;
        _autopilotPlugin = NULL;
    
    
        delete _mav;
        _mav = NULL;
    
    void Vehicle::prepareDelete()
    {
        if(_cameras) {
            delete _cameras;
            _cameras = NULL;
            emit dynamicCamerasChanged();
            qApp->processEvents();
        }
    }
    
    
    void Vehicle::_offlineFirmwareTypeSettingChanged(QVariant value)
    {
        _firmwareType = static_cast<MAV_AUTOPILOT>(value.toInt());
    
        _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
    
        emit firmwareTypeChanged();
    
        if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
            _capabilityBits = 0;
        } else {
            _capabilityBits = MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY;
        }
        emit capabilityBitsChanged(_capabilityBits);
    
    }
    
    void Vehicle::_offlineVehicleTypeSettingChanged(QVariant value)
    {
        _vehicleType = static_cast<MAV_TYPE>(value.toInt());
        emit vehicleTypeChanged();
    }
    
    void Vehicle::_offlineCruiseSpeedSettingChanged(QVariant value)
    {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _defaultCruiseSpeed = value.toDouble();
        emit defaultCruiseSpeedChanged(_defaultCruiseSpeed);
    
    }
    
    void Vehicle::_offlineHoverSpeedSettingChanged(QVariant value)
    {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _defaultHoverSpeed = value.toDouble();
        emit defaultHoverSpeedChanged(_defaultHoverSpeed);
    
    }
    
    QString Vehicle::firmwareTypeString(void) const
    {
        if (px4Firmware()) {
            return QStringLiteral("PX4 Pro");
        } else if (apmFirmware()) {
            return QStringLiteral("ArduPilot");
        } else {
            return tr("MAVLink Generic");
        }
    }
    
    QString Vehicle::vehicleTypeString(void) const
    {
        if (fixedWing()) {
            return tr("Fixed Wing");
        } else if (multiRotor()) {
            return tr("Multi-Rotor");
        } else if (vtol()) {
            return tr("VTOL");
        } else if (rover()) {
            return tr("Rover");
        } else if (sub()) {
            return tr("Sub");
        } else {
            return tr("Unknown");
        }
    }
    
    void Vehicle::resetCounters()
    
    {
        _messagesReceived   = 0;
        _messagesSent       = 0;
        _messagesLost       = 0;
        _messageSeq         = 0;
        _heardFrom          = false;
    }
    
    
    void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message)
    {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        // if the minimum supported version of MAVLink is already 2.0
        // set our max proto version to it.
        unsigned mavlinkVersion = _mavlink->getCurrentVersion();
        if (_maxProtoVersion != mavlinkVersion && mavlinkVersion >= 200) {
            _maxProtoVersion = _mavlink->getCurrentVersion();
            qCDebug(VehicleLog) << "Vehicle::_mavlinkMessageReceived setting _maxProtoVersion" << _maxProtoVersion;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        if (message.sysid != _id && message.sysid != 0) {
    
            // We allow RADIO_STATUS messages which come from a link the vehicle is using to pass through and be handled
            if (!(message.msgid == MAVLINK_MSG_ID_RADIO_STATUS && _containsLink(link))) {
                return;
            }
    
        if (!_containsLink(link)) {
            _addLink(link);
        }
    
        //-- Check link status
        _messagesReceived++;
        emit messagesReceivedChanged();
        if(!_heardFrom) {
            if(message.msgid == MAVLINK_MSG_ID_HEARTBEAT) {
                _heardFrom = true;
                _compID = message.compid;
                _messageSeq = message.seq + 1;
            }
        } else {
            if(_compID == message.compid) {
                uint16_t seq_received = (uint16_t)message.seq;
                uint16_t packet_lost_count = 0;
                //-- Account for overflow during packet loss
                if(seq_received < _messageSeq) {
                    packet_lost_count = (seq_received + 255) - _messageSeq;
                } else {
                    packet_lost_count = seq_received - _messageSeq;
                }
                _messageSeq = message.seq + 1;
                _messagesLost += packet_lost_count;
                if(packet_lost_count)
                    emit messagesLostChanged();
            }
        }
    
    
        // Mark this vehicle as active - but only if the traffic is coming from
        // the actual vehicle
        if (message.sysid == _id) {
            _connectionActive();
        }
    
    Don Gagne's avatar
    Don Gagne committed
        // Give the plugin a change to adjust the message contents
    
        if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) {
            return;
        }
    
        // Give the Core Plugin access to all mavlink traffic
        if (!_toolbox->corePlugin()->mavlinkMessage(this, link, message)) {
            return;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        switch (message.msgid) {
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_HOME_POSITION:
            _handleHomePosition(message);
            break;
        case MAVLINK_MSG_ID_HEARTBEAT:
            _handleHeartbeat(message);
            break;
    
        case MAVLINK_MSG_ID_RADIO_STATUS:
            _handleRadioStatus(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_RC_CHANNELS:
            _handleRCChannels(message);
            break;
        case MAVLINK_MSG_ID_RC_CHANNELS_RAW:
            _handleRCChannelsRaw(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_BATTERY_STATUS:
            _handleBatteryStatus(message);
            break;
        case MAVLINK_MSG_ID_SYS_STATUS:
            _handleSysStatus(message);
            break;
    
        case MAVLINK_MSG_ID_RAW_IMU:
            emit mavlinkRawImu(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU:
            emit mavlinkScaledImu1(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU2:
            emit mavlinkScaledImu2(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU3:
            emit mavlinkScaledImu3(message);
            break;
    
        case MAVLINK_MSG_ID_VIBRATION:
            _handleVibration(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_EXTENDED_SYS_STATE:
            _handleExtendedSysState(message);
            break;
    
        case MAVLINK_MSG_ID_COMMAND_ACK:
            _handleCommandAck(message);
            break;
    
        case MAVLINK_MSG_ID_COMMAND_LONG:
            _handleCommandLong(message);
            break;
    
        case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
    
    Gus Grubba's avatar
    Gus Grubba committed
            _handleAutopilotVersion(link, message);
    
        case MAVLINK_MSG_ID_PROTOCOL_VERSION:
            _handleProtocolVersion(link, message);
            break;
    
        case MAVLINK_MSG_ID_WIND_COV:
            _handleWindCov(message);
            break;
    
        case MAVLINK_MSG_ID_HIL_ACTUATOR_CONTROLS:
            _handleHilActuatorControls(message);
            break;
    
        case MAVLINK_MSG_ID_LOGGING_DATA:
            _handleMavlinkLoggingData(message);
            break;
        case MAVLINK_MSG_ID_LOGGING_DATA_ACKED:
            _handleMavlinkLoggingDataAcked(message);
            break;
    
        case MAVLINK_MSG_ID_GPS_RAW_INT:
            _handleGpsRawInt(message);
            break;
        case MAVLINK_MSG_ID_GLOBAL_POSITION_INT:
            _handleGlobalPositionInt(message);
            break;
        case MAVLINK_MSG_ID_ALTITUDE:
            _handleAltitude(message);
            break;
        case MAVLINK_MSG_ID_VFR_HUD:
            _handleVfrHud(message);
            break;
    
        case MAVLINK_MSG_ID_SCALED_PRESSURE:
            _handleScaledPressure(message);
            break;
        case MAVLINK_MSG_ID_SCALED_PRESSURE2:
            _handleScaledPressure2(message);
            break;
        case MAVLINK_MSG_ID_SCALED_PRESSURE3:
            _handleScaledPressure3(message);
    
            break;
    
        case MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED:
            _handleCameraImageCaptured(message);
    
        case MAVLINK_MSG_ID_ADSB_VEHICLE:
            _handleADSBVehicle(message);
            break;
    
        case MAVLINK_MSG_ID_HIGH_LATENCY2:
            _handleHighLatency2(message);
            break;
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        case MAVLINK_MSG_ID_ATTITUDE:
            _handleAttitude(message);
            break;
        case MAVLINK_MSG_ID_ATTITUDE_TARGET:
            _handleAttitudeTarget(message);
            break;
    
        case MAVLINK_MSG_ID_DISTANCE_SENSOR:
            _handleDistanceSensor(message);
            break;
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        case MAVLINK_MSG_ID_PING:
            _handlePing(link, message);
            break;
    
        case MAVLINK_MSG_ID_SERIAL_CONTROL:
        {
            mavlink_serial_control_t ser;
            mavlink_msg_serial_control_decode(&message, &ser);
            emit mavlinkSerialControl(ser.device, ser.flags, ser.timeout, ser.baudrate, QByteArray(reinterpret_cast<const char*>(ser.data), ser.count));
        }
            break;
    
            // Following are ArduPilot dialect messages
    
    #if !defined(NO_ARDUPILOT_DIALECT)
        case MAVLINK_MSG_ID_CAMERA_FEEDBACK:
            _handleCameraFeedback(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_WIND:
            _handleWind(message);
            break;
    
        // This must be emitted after the vehicle processes the message. This way the vehicle state is up to date when anyone else
        // does processing.
    
    Don Gagne's avatar
    Don Gagne committed
        emit mavlinkMessageReceived(message);
    
        _uas->receiveMessage(message);
    }
    
    
    void Vehicle::_handleCameraFeedback(const mavlink_message_t& message)
    {
        mavlink_camera_feedback_t feedback;
    
        mavlink_msg_camera_feedback_decode(&message, &feedback);
    
        QGeoCoordinate imageCoordinate((double)feedback.lat / qPow(10.0, 7.0), (double)feedback.lng / qPow(10.0, 7.0), feedback.alt_msl);
        qCDebug(VehicleLog) << "_handleCameraFeedback coord:index" << imageCoordinate << feedback.img_idx;
        _cameraTriggerPoints.append(new QGCQGeoCoordinate(imageCoordinate, this));
    }
    
    
    void Vehicle::_handleCameraImageCaptured(const mavlink_message_t& message)
    {
        mavlink_camera_image_captured_t feedback;
    
        mavlink_msg_camera_image_captured_decode(&message, &feedback);
    
        QGeoCoordinate imageCoordinate((double)feedback.lat / qPow(10.0, 7.0), (double)feedback.lon / qPow(10.0, 7.0), feedback.alt);
        qCDebug(VehicleLog) << "_handleCameraFeedback coord:index" << imageCoordinate << feedback.image_index << feedback.capture_result;
        if (feedback.capture_result == 1) {
            _cameraTriggerPoints.append(new QGCQGeoCoordinate(imageCoordinate, this));
        }
    }
    
    
    void Vehicle::_handleVfrHud(mavlink_message_t& message)
    {
        mavlink_vfr_hud_t vfrHud;
        mavlink_msg_vfr_hud_decode(&message, &vfrHud);
    
        _airSpeedFact.setRawValue(qIsNaN(vfrHud.airspeed) ? 0 : vfrHud.airspeed);
        _groundSpeedFact.setRawValue(qIsNaN(vfrHud.groundspeed) ? 0 : vfrHud.groundspeed);
        _climbRateFact.setRawValue(qIsNaN(vfrHud.climb) ? 0 : vfrHud.climb);
    }
    
    
    void Vehicle::_handleDistanceSensor(mavlink_message_t& message)
    {
        mavlink_distance_sensor_t distanceSensor;
    
    
        mavlink_msg_distance_sensor_decode(&message, &distanceSensor);\
    
        if (!_distanceSensorFactGroup.idSet()) {
            _distanceSensorFactGroup.setIdSet(true);
    
            _distanceSensorFactGroup.setId(distanceSensor.id);
    
        if (_distanceSensorFactGroup.id() != distanceSensor.id) {
    
            // We can only handle a single sensor reporting
            return;
        }
    
    
        struct orientation2Fact_s {
            MAV_SENSOR_ORIENTATION  orientation;
            Fact*                   fact;
        };
    
        orientation2Fact_s rgOrientation2Fact[] =
        {
    
            { MAV_SENSOR_ROTATION_NONE,         _distanceSensorFactGroup.rotationNone() },
            { MAV_SENSOR_ROTATION_YAW_45,       _distanceSensorFactGroup.rotationYaw45() },
            { MAV_SENSOR_ROTATION_YAW_90,       _distanceSensorFactGroup.rotationYaw90() },
            { MAV_SENSOR_ROTATION_YAW_135,      _distanceSensorFactGroup.rotationYaw135() },
            { MAV_SENSOR_ROTATION_YAW_180,      _distanceSensorFactGroup.rotationYaw180() },
            { MAV_SENSOR_ROTATION_YAW_225,      _distanceSensorFactGroup.rotationYaw225() },
            { MAV_SENSOR_ROTATION_YAW_270,      _distanceSensorFactGroup.rotationYaw270() },
            { MAV_SENSOR_ROTATION_YAW_315,      _distanceSensorFactGroup.rotationYaw315() },
            { MAV_SENSOR_ROTATION_PITCH_90,     _distanceSensorFactGroup.rotationPitch90() },
            { MAV_SENSOR_ROTATION_PITCH_270,    _distanceSensorFactGroup.rotationPitch270() },
    
        };
    
        for (size_t i=0; i<sizeof(rgOrientation2Fact)/sizeof(rgOrientation2Fact[0]); i++) {
            const orientation2Fact_s& orientation2Fact = rgOrientation2Fact[i];
            if (orientation2Fact.orientation == distanceSensor.orientation) {
    
                orientation2Fact.fact->setRawValue(distanceSensor.current_distance / 100.0); // cm to meters
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    void Vehicle::_handleAttitudeTarget(mavlink_message_t& message)
    {
        mavlink_attitude_target_t attitudeTarget;
    
        mavlink_msg_attitude_target_decode(&message, &attitudeTarget);
    
        float roll, pitch, yaw;
        mavlink_quaternion_to_euler(attitudeTarget.q, &roll, &pitch, &yaw);
    
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _setpointFactGroup.roll()->setRawValue(qRadiansToDegrees(roll));
        _setpointFactGroup.pitch()->setRawValue(qRadiansToDegrees(pitch));
        _setpointFactGroup.yaw()->setRawValue(qRadiansToDegrees(yaw));
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    
        _setpointFactGroup.rollRate()->setRawValue(qRadiansToDegrees(attitudeTarget.body_roll_rate));
        _setpointFactGroup.pitchRate()->setRawValue(qRadiansToDegrees(attitudeTarget.body_pitch_rate));
        _setpointFactGroup.yawRate()->setRawValue(qRadiansToDegrees(attitudeTarget.body_yaw_rate));
    }
    
    void Vehicle::_handleAttitude(mavlink_message_t& message)
    {
        mavlink_attitude_t attitude;
    
        mavlink_msg_attitude_decode(&message, &attitude);
    
        rollRate()->setRawValue(qRadiansToDegrees(attitude.rollspeed));
        pitchRate()->setRawValue(qRadiansToDegrees(attitude.pitchspeed));
        yawRate()->setRawValue(qRadiansToDegrees(attitude.yawspeed));
    }
    
    
    void Vehicle::_handleGpsRawInt(mavlink_message_t& message)
    {
        mavlink_gps_raw_int_t gpsRawInt;
        mavlink_msg_gps_raw_int_decode(&message, &gpsRawInt);
    
    
        _gpsRawIntMessageAvailable = true;
    
    
        if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) {
    
            if (!_globalPositionIntMessageAvailable) {
    
                QGeoCoordinate newPosition(gpsRawInt.lat  / (double)1E7, gpsRawInt.lon / (double)1E7, gpsRawInt.alt  / 1000.0);
                if (newPosition != _coordinate) {
                    _coordinate = newPosition;
                    emit coordinateChanged(_coordinate);
                }
    
                _altitudeAMSLFact.setRawValue(gpsRawInt.alt / 1000.0);
    
        _gpsFactGroup.lat()->setRawValue(gpsRawInt.lat * 1e-7);
        _gpsFactGroup.lon()->setRawValue(gpsRawInt.lon * 1e-7);
    
        _gpsFactGroup.count()->setRawValue(gpsRawInt.satellites_visible == 255 ? 0 : gpsRawInt.satellites_visible);
    
        _gpsFactGroup.hdop()->setRawValue(gpsRawInt.eph == UINT16_MAX ? std::numeric_limits<double>::quiet_NaN() : gpsRawInt.eph / 100.0);
        _gpsFactGroup.vdop()->setRawValue(gpsRawInt.epv == UINT16_MAX ? std::numeric_limits<double>::quiet_NaN() : gpsRawInt.epv / 100.0);
        _gpsFactGroup.courseOverGround()->setRawValue(gpsRawInt.cog == UINT16_MAX ? std::numeric_limits<double>::quiet_NaN() : gpsRawInt.cog / 100.0);
    
        _gpsFactGroup.lock()->setRawValue(gpsRawInt.fix_type);
    }
    
    void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message)
    {
        mavlink_global_position_int_t globalPositionInt;
        mavlink_msg_global_position_int_decode(&message, &globalPositionInt);
    
    
        _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0);
        _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0);
    
        // ArduPilot sends bogus GLOBAL_POSITION_INT messages with lat/lat 0/0 even when it has no gps signal
        // Apparently, this is in order to transport relative altitude information.
    
        if (globalPositionInt.lat == 0 && globalPositionInt.lon == 0) {
    
        _globalPositionIntMessageAvailable = true;
    
        QGeoCoordinate newPosition(globalPositionInt.lat  / (double)1E7, globalPositionInt.lon / (double)1E7, globalPositionInt.alt  / 1000.0);
        if (newPosition != _coordinate) {
            _coordinate = newPosition;
            emit coordinateChanged(_coordinate);
        }
    
    void Vehicle::_handleHighLatency2(mavlink_message_t& message)
    {
        mavlink_high_latency2_t highLatency2;
        mavlink_msg_high_latency2_decode(&message, &highLatency2);
    
    
        QString previousFlightMode;
        if (_base_mode != 0 || _custom_mode != 0){
            // Vehicle is initialized with _base_mode=0 and _custom_mode=0. Don't pass this to flightMode() since it will complain about
            // bad modes while unit testing.
            previousFlightMode = flightMode();
        }
        _base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
        _custom_mode = _firmwarePlugin->highLatencyCustomModeTo32Bits(highLatency2.custom_mode);
        if (previousFlightMode != flightMode()) {
            emit flightModeChanged(flightMode());
        }
    
        // Assume armed since we don't know
        if (_armed != true) {
            _armed = true;
            emit armedChanged(_armed);
        }
    
    
        _coordinate.setLatitude(highLatency2.latitude  / (double)1E7);
        _coordinate.setLongitude(highLatency2.longitude / (double)1E7);
        _coordinate.setAltitude(highLatency2.altitude);
        emit coordinateChanged(_coordinate);
    
        _airSpeedFact.setRawValue((double)highLatency2.airspeed / 5.0);
        _groundSpeedFact.setRawValue((double)highLatency2.groundspeed / 5.0);
        _climbRateFact.setRawValue((double)highLatency2.climb_rate / 10.0);
        _headingFact.setRawValue((double)highLatency2.heading * 2.0);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        _altitudeRelativeFact.setRawValue(std::numeric_limits<double>::quiet_NaN());
        _altitudeAMSLFact.setRawValue(highLatency2.altitude);
    
    
        _windFactGroup.direction()->setRawValue((double)highLatency2.wind_heading * 2.0);
        _windFactGroup.speed()->setRawValue((double)highLatency2.windspeed / 5.0);
    
        _batteryFactGroup.percentRemaining()->setRawValue(highLatency2.battery);
    
        _temperatureFactGroup.temperature1()->setRawValue(highLatency2.temperature_air);
    
        _gpsFactGroup.lat()->setRawValue(highLatency2.latitude * 1e-7);
        _gpsFactGroup.lon()->setRawValue(highLatency2.longitude * 1e-7);
        _gpsFactGroup.count()->setRawValue(0);
        _gpsFactGroup.hdop()->setRawValue(highLatency2.eph == UINT8_MAX ? std::numeric_limits<double>::quiet_NaN() : highLatency2.eph / 10.0);
        _gpsFactGroup.vdop()->setRawValue(highLatency2.epv == UINT8_MAX ? std::numeric_limits<double>::quiet_NaN() : highLatency2.epv / 10.0);
    
    
        struct failure2Sensor_s {
    
            HL_FAILURE_FLAG         failureBit;
    
            MAV_SYS_STATUS_SENSOR   sensorBit;
        };
    
        static const failure2Sensor_s rgFailure2Sensor[] = {
    
            { HL_FAILURE_FLAG_GPS,                      MAV_SYS_STATUS_SENSOR_GPS },
            { HL_FAILURE_FLAG_DIFFERENTIAL_PRESSURE,    MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE },
            { HL_FAILURE_FLAG_ABSOLUTE_PRESSURE,        MAV_SYS_STATUS_SENSOR_ABSOLUTE_PRESSURE },
            { HL_FAILURE_FLAG_3D_ACCEL,                 MAV_SYS_STATUS_SENSOR_3D_ACCEL },
            { HL_FAILURE_FLAG_3D_GYRO,                  MAV_SYS_STATUS_SENSOR_3D_GYRO },
            { HL_FAILURE_FLAG_3D_MAG,                   MAV_SYS_STATUS_SENSOR_3D_MAG },
    
        };
    
        // Map from MAV_FAILURE bits to standard SYS_STATUS message handling
        uint32_t newOnboardControlSensorsEnabled = 0;
        for (size_t i=0; i<sizeof(rgFailure2Sensor)/sizeof(failure2Sensor_s); i++) {
            const failure2Sensor_s* pFailure2Sensor = &rgFailure2Sensor[i];
            if (highLatency2.failure_flags & pFailure2Sensor->failureBit) {
                // Assume if reporting as unhealthy that is it present and enabled
                newOnboardControlSensorsEnabled |= pFailure2Sensor->sensorBit;
            }
        }
        if (newOnboardControlSensorsEnabled != _onboardControlSensorsEnabled) {
            _onboardControlSensorsEnabled = newOnboardControlSensorsEnabled;
            _onboardControlSensorsPresent = newOnboardControlSensorsEnabled;
            _onboardControlSensorsUnhealthy = 0;
            emit unhealthySensorsChanged();
        }
    
    void Vehicle::_handleAltitude(mavlink_message_t& message)
    {
        mavlink_altitude_t altitude;
        mavlink_msg_altitude_decode(&message, &altitude);