Unverified Commit 4ef7a813 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #7841 from DonLakeFlyer/FollowMeSetup

ArduPilot: Follow Me setup
parents c657ae65 b912322e
......@@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes.
### 3.6.0 - Daily Build
* ArduCopter/Rover: Follow Me setup page
* More performant flight path display algorithm. Mobile builds no longer show limited path length.
* ArduCopter/Rover: Add support for Follow Me
* ArduPilot: Add Motor Test vehicle setup page
......
......@@ -61,7 +61,7 @@
<file alias="camera.svg">resources/camera.svg</file>
<file alias="camera_photo.svg">src/Camera/images/camera_photo.svg</file>
<file alias="camera_video.svg">src/Camera/images/camera_video.svg</file>
<file alias="CameraComponentIcon.png">src/AutoPilotPlugins/PX4/Images/CameraComponentIcon.png</file>
<file alias="CameraComponentIcon.png">src/AutoPilotPlugins/Common/Images/CameraComponentIcon.png</file>
<file alias="CameraIcon.svg">src/ui/toolbar/Images/CameraIcon.svg</file>
<file alias="CameraTrigger.svg">src/AutoPilotPlugins/PX4/Images/CameraTrigger.svg</file>
<file alias="check.svg">resources/check.svg</file>
......@@ -78,7 +78,8 @@
<file alias="Disarmed.svg">src/ui/toolbar/Images/Disarmed.svg</file>
<file alias="Disconnect.svg">src/ui/toolbar/Images/Disconnect.svg</file>
<file alias="FirmwareUpgradeIcon.png">src/VehicleSetup/FirmwareUpgradeIcon.png</file>
<file alias="FlightModesComponentIcon.png">src/AutoPilotPlugins/PX4/Images/FlightModesComponentIcon.png</file>
<file alias="FollowComponentIcon.png">src/AutoPilotPlugins/Common/Images/FlightModesComponentIcon.png</file>
<file alias="FlightModesComponentIcon.png">src/AutoPilotPlugins/Common/Images/FlightModesComponentIcon.png</file>
<file alias="Frames/BlueROV1.png">src/AutoPilotPlugins/APM/Images/bluerov-frame.png</file>
<file alias="Frames/SimpleROV-3.png">src/AutoPilotPlugins/APM/Images/simple3-frame.png</file>
<file alias="Frames/SimpleROV-4.png">src/AutoPilotPlugins/APM/Images/simple4-frame.png</file>
......@@ -132,10 +133,10 @@
<file alias="PowerComponentBattery_04cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_04cell.svg</file>
<file alias="PowerComponentBattery_05cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_05cell.svg</file>
<file alias="PowerComponentBattery_06cell.svg">src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_06cell.svg</file>
<file alias="PowerComponentIcon.png">src/AutoPilotPlugins/PX4/Images/PowerComponentIcon.png</file>
<file alias="PowerComponentIcon.png">src/AutoPilotPlugins/Common/Images/PowerComponentIcon.png</file>
<file alias="PX4/BrandImage">src/FirmwarePlugin/PX4/PX4BrandImage.png</file>
<file alias="Quad.svg">src/ui/toolbar/Images/Quad.svg</file>
<file alias="RadioComponentIcon.png">src/AutoPilotPlugins/PX4/Images/RadioComponentIcon.png</file>
<file alias="RadioComponentIcon.png">src/AutoPilotPlugins/Common/Images/RadioComponentIcon.png</file>
<file alias="RC.svg">src/ui/toolbar/Images/RC.svg</file>
<file alias="RCLoss.svg">src/AutoPilotPlugins/PX4/Images/RCLoss.svg</file>
<file alias="RCLossLight.svg">src/AutoPilotPlugins/PX4/Images/RCLossLight.svg</file>
......@@ -144,12 +145,12 @@
<file alias="rollDialWhite.svg">src/FlightMap/Images/rollDialWhite.svg</file>
<file alias="rollPointerWhite.svg">src/FlightMap/Images/rollPointerWhite.svg</file>
<file alias="RTK.svg">src/ui/toolbar/Images/RTK.svg</file>
<file alias="SafetyComponentIcon.png">src/AutoPilotPlugins/PX4/Images/SafetyComponentIcon.png</file>
<file alias="SafetyComponentIcon.png">src/AutoPilotPlugins/Common/Images/SafetyComponentIcon.png</file>
<file alias="scale.png">src/FlightMap/Images/scale.png</file>
<file alias="scale_end.png">src/FlightMap/Images/scale_end.png</file>
<file alias="scale_endLight.png">src/FlightMap/Images/scale_endLight.png</file>
<file alias="scaleLight.png">src/FlightMap/Images/scaleLight.png</file>
<file alias="SensorsComponentIcon.png">src/AutoPilotPlugins/PX4/Images/SensorsComponentIcon.png</file>
<file alias="SensorsComponentIcon.png">src/AutoPilotPlugins/Common/Images/SensorsComponentIcon.png</file>
<file alias="Signal0.svg">src/ui/toolbar/Images/Signal0.svg</file>
<file alias="Signal100.svg">src/ui/toolbar/Images/Signal100.svg</file>
<file alias="Signal20.svg">src/ui/toolbar/Images/Signal20.svg</file>
......@@ -161,7 +162,7 @@
<file alias="subMenuButtonImage.png">resources/CogWheels.png</file>
<file alias="subVehicleArrowOpaque.png">src/FlightMap/Images/sub.png</file>
<file alias="TelemRSSI.svg">src/ui/toolbar/Images/TelemRSSI.svg</file>
<file alias="TuningComponentIcon.png">src/AutoPilotPlugins/PX4/Images/TuningComponentIcon.png</file>
<file alias="TuningComponentIcon.png">src/AutoPilotPlugins/Common/Images/TuningComponentIcon.png</file>
<file alias="vehicleArrowOpaque.svg">src/FlightMap/Images/vehicleArrowOpaque.svg</file>
<file alias="vehicleArrowOutline.svg">src/FlightMap/Images/vehicleArrowOutline.svg</file>
<file alias="VehicleDown.png">src/AutoPilotPlugins/PX4/Images/VehicleDown.png</file>
......
......@@ -1068,6 +1068,8 @@ APMFirmwarePlugin {
src/AutoPilotPlugins/APM/APMCompassCal.h \
src/AutoPilotPlugins/APM/APMFlightModesComponent.h \
src/AutoPilotPlugins/APM/APMFlightModesComponentController.h \
src/AutoPilotPlugins/APM/APMFollowComponent.h \
src/AutoPilotPlugins/APM/APMFollowComponentController.h \
src/AutoPilotPlugins/APM/APMHeliComponent.h \
src/AutoPilotPlugins/APM/APMLightsComponent.h \
src/AutoPilotPlugins/APM/APMSubFrameComponent.h \
......@@ -1093,6 +1095,8 @@ APMFirmwarePlugin {
src/AutoPilotPlugins/APM/APMCompassCal.cc \
src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \
src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc \
src/AutoPilotPlugins/APM/APMFollowComponent.cc \
src/AutoPilotPlugins/APM/APMFollowComponentController.cc \
src/AutoPilotPlugins/APM/APMHeliComponent.cc \
src/AutoPilotPlugins/APM/APMLightsComponent.cc \
src/AutoPilotPlugins/APM/APMSubFrameComponent.cc \
......
......@@ -25,6 +25,7 @@
#include "APMCameraComponent.h"
#include "APMLightsComponent.h"
#include "APMSubFrameComponent.h"
#include "APMFollowComponent.h"
#include "ESP8266Component.h"
#include "APMHeliComponent.h"
#include "QGCApplication.h"
......@@ -51,6 +52,7 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent)
, _tuningComponent (nullptr)
, _esp8266Component (nullptr)
, _heliComponent (nullptr)
, _followComponent (nullptr)
{
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
connect(vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &APMAutoPilotPlugin::_checkForBadCubeBlack);
......@@ -101,6 +103,13 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void)
_safetyComponent->setupTriggerSignals();
_components.append(QVariant::fromValue((VehicleComponent*)_safetyComponent));
if ((qobject_cast<ArduCopterFirmwarePlugin*>(_vehicle->firmwarePlugin()) || qobject_cast<ArduCopterFirmwarePlugin*>(_vehicle->firmwarePlugin())) &&
_vehicle->parameterManager()->parameterExists(-1, QStringLiteral("FOLL_ENABLE"))) {
_followComponent = new APMFollowComponent(_vehicle, this);
_followComponent->setupTriggerSignals();
_components.append(QVariant::fromValue((VehicleComponent*)_followComponent));
}
if (_vehicle->vehicleType() == MAV_TYPE_HELICOPTER) {
_heliComponent = new APMHeliComponent(_vehicle, this);
_heliComponent->setupTriggerSignals();
......
......@@ -27,6 +27,7 @@ class APMLightsComponent;
class APMSubFrameComponent;
class ESP8266Component;
class APMHeliComponent;
class APMFollowComponent;
/// This is the APM specific implementation of the AutoPilot class.
class APMAutoPilotPlugin : public AutoPilotPlugin
......@@ -56,6 +57,7 @@ protected:
APMTuningComponent* _tuningComponent;
ESP8266Component* _esp8266Component;
APMHeliComponent* _heliComponent;
APMFollowComponent* _followComponent;
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
private slots:
......
[
{
"name": "angle",
"shortDescription": "Angle from ground station to vehicle",
"type": "double",
"min": 0,
"max": 360,
"decimalPlaces": 1,
"units": "deg",
"defaultValue": 45
},
{
"name": "distance",
"shortDescription": "Horizontal distance from ground station to vehicle",
"type": "double",
"min": 0,
"decimalPlaces": 1,
"units": "m",
"defaultValue": 5
},
{
"name": "height",
"shortDescription": "Vertical distance from ground station to vehicle",
"type": "double",
"min": 0,
"decimalPlaces": 1,
"units": "m",
"defaultValue": 5
}
]
/****************************************************************************
*
* (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.
*
****************************************************************************/
#include "APMFollowComponent.h"
#include "APMAutoPilotPlugin.h"
#include "APMAirframeComponent.h"
#include "ParameterManager.h"
APMFollowComponent::APMFollowComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent)
: VehicleComponent(vehicle, autopilot, parent),
_name(tr("Follow Me"))
{
}
QString APMFollowComponent::name(void) const
{
return _name;
}
QString APMFollowComponent::description(void) const
{
return tr("Follow Me Setup is used to configure support for the vehicle following the ground station location.");
}
QString APMFollowComponent::iconResource(void) const
{
return QStringLiteral("/qmlimages/FollowComponentIcon.png");
}
QUrl APMFollowComponent::setupSource(void) const
{
return QUrl::fromUserInput(QStringLiteral("qrc:/qml/APMFollowComponent.qml"));
}
QUrl APMFollowComponent::summaryQmlSource(void) const
{
return QUrl::fromUserInput(QStringLiteral("qrc:/qml/APMFollowComponentSummary.qml"));
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
#pragma once
#include "VehicleComponent.h"
class APMFollowComponent : public VehicleComponent
{
Q_OBJECT
public:
APMFollowComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr);
// Overrides from VehicleComponent
QStringList setupCompleteChangedTriggerList(void) const override { return QStringList(); }
// Virtuals from VehicleComponent
QString name (void) const override;
QString description (void) const override;
QString iconResource (void) const override;
bool requiresSetup (void) const override { return false; }
bool setupComplete (void) const override { return true; }
QUrl setupSource (void) const override;
QUrl summaryQmlSource (void) const override;
bool allowSetupWhileArmed (void) const override { return true; }
bool allowSetupWhileFlying (void) const override { return true; }
private:
const QString _name;
QVariantList _summaryItems;
};
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controllers 1.0
SetupPage {
id: followPage
pageComponent: followPageComponent
FactPanelController {
id: controller
}
Component {
id: followPageComponent
ColumnLayout {
id: flowLayout
spacing: _margins
property Fact _followEnabled: controller.getParameterFact(-1, "FOLL_ENABLE")
property bool _followParamsAvailable: controller.parameterExists(-1, "FOLL_SYSID")
property Fact _followDistanceMax: controller.getParameterFact(-1, "FOLL_DIST_MAX", false /* reportMissing */)
property Fact _followSysId: controller.getParameterFact(-1, "FOLL_SYSID", false /* reportMissing */)
property Fact _followOffsetX: controller.getParameterFact(-1, "FOLL_OFS_X", false /* reportMissing */)
property Fact _followOffsetY: controller.getParameterFact(-1, "FOLL_OFS_Y", false /* reportMissing */)
property Fact _followOffsetZ: controller.getParameterFact(-1, "FOLL_OFS_Z", false /* reportMissing */)
property Fact _followOffsetType: controller.getParameterFact(-1, "FOLL_OFS_TYPE", false /* reportMissing */)
property Fact _followAltitudeType: controller.getParameterFact(-1, "FOLL_ALT_TYPE", false /* reportMissing */)
property Fact _followYawBehavior: controller.getParameterFact(-1, "FOLL_YAW_BEHAVE", false /* reportMissing */)
property int _followComboMaintainIndex: 0
property int _followComboSpecifyIndex: 1
property bool _followMaintain: followPositionCombo.currentIndex === _followComboMaintainIndex
property bool _isFollowMeSetup: _followEnabled.rawValue == 1 && _followParamsAvailable && _followOffsetType.rawValue == 1 && _followAltitudeType.rawValue == 1 && _followSysId.rawValue == 0
readonly property int _followYawBehaviorNone: 0
readonly property int _followYawBehaviorFace: 1
readonly property int _followYawBehaviorSame: 2
readonly property int _followYawBehaviorFlight: 3
Component.onCompleted: _setUIFromParams()
function _setUIFromParams() {
if (!_followParamsAvailable) {
return
}
if (_followOffsetX.rawValue == 0 && _followOffsetY.rawValue == 0 && _followOffsetZ.rawValue == 0) {
followPositionCombo.currentIndex =_followComboMaintainIndex
controller.distance.rawValue = 0
controller.angle.rawValue = 0
controller.height.rawValue = 0
} else {
followPositionCombo.currentIndex =_followComboSpecifyIndex
var angleRadians = Math.atan2(_followOffsetX.rawValue, _followOffsetY.rawValue)
if (angleRadians == 0) {
controller.distance.rawValue = _followOffsetY.rawValue
} else {
controller.distance.rawValue = _followOffsetX.rawValue / Math.sin(angleRadians)
}
controller.angle.rawValue = _radiansToHeading(angleRadians)
}
controller.height.rawValue = -_followOffsetZ.rawValue
pointVehicleCombo.currentIndex = _followYawBehavior.rawValue
}
function _setFollowMeParamDefaults() {
_followOffsetType.rawValue = 1 // Relative to vehicle
_followAltitudeType.rawValue = 1 // Altitude is relative
_followSysId.rawValue = 0 // Follow first message sent
controller.distance.value = controller.distance.defaultValue
controller.angle.value = controller.angle.defaultValue
controller.height.value = controller.height.defaultValue
_setXYOffsetByAngleAndDistance(controller.angle.rawValue, controller.distance.rawValue)
_followOffsetZ.rawValue = -controller.height.rawValue
_setUIFromParams()
}
function _setXYOffsetByAngleAndDistance(headingAngleDegrees, distance) {
if (distance == 0) {
_followOffsetX.rawValue = _followOffsetY.rawValue = 0
} else {
var angleRadians = _headingToRadians(headingAngleDegrees)
if (angleRadians == 0) {
_followOffsetX.rawValue = 0
_followOffsetY.rawValue = distance
} else {
_followOffsetX.rawValue = Math.sin(angleRadians) * distance
_followOffsetY.rawValue = Math.cos(angleRadians) * distance
if (_followOffsetX.rawValue < 0.0001) {
_followOffsetX.rawValue = 0
}
if (_followOffsetY.rawValue < 0.0001) {
_followOffsetY.rawValue = 0
}
}
}
}
function _radiansToHeading(radians) {
var geometricAngle = QGroundControl.radiansToDegrees(radians)
var headingAngle = 90 - geometricAngle
if (headingAngle < 0) {
headingAngle += 360
} else if (headingAngle > 360) {
headingAngle -= 360
}
return headingAngle
}
function _headingToRadians(heading) {
var geometricAngle = -(heading - 90)
return QGroundControl.degreesToRadians(geometricAngle)
}
APMFollowComponentController {
id: controller
onMissingParametersAvailable: {
_followDistanceMax = controller.getParameterFact(-1, "FOLL_DIST_MAX")
_followSysId = controller.getParameterFact(-1, "FOLL_SYSID")
_followOffsetX = controller.getParameterFact(-1, "FOLL_OFS_X")
_followOffsetY = controller.getParameterFact(-1, "FOLL_OFS_Y")
_followOffsetZ = controller.getParameterFact(-1, "FOLL_OFS_Z")
_followOffsetType = controller.getParameterFact(-1, "FOLL_OFS_TYPE")
_followAltitudeType = controller.getParameterFact(-1, "FOLL_ALT_TYPE")
_followYawBehavior = controller.getParameterFact(-1, "FOLL_YAW_BEHAVE")
_followParamsAvailable = true
vehicleParamRefreshLabel.visible = false
_followYawBehavior.rawValue = 1 // Point at GCS
_setFollowMeParamDefaults()
}
}
QGCPalette { id: ggcPal; colorGroupEnabled: true }
QGCCheckBox {
text: qsTr("Enable Follow Me")
checked: _isFollowMeSetup
onClicked: {
if (checked) {
_followEnabled.rawValue = 1
controller.getMissingParameters([ "FOLL_DIST_MAX", "FOLL_SYSID", "FOLL_OFS_X", "FOLL_OFS_Y", "FOLL_OFS_Z", "FOLL_OFS_TYPE", "FOLL_ALT_TYPE", "FOLL_YAW_BEHAVE" ])
vehicleParamRefreshLabel.visible = true
} else {
_followEnabled.rawValue = 0
}
}
}
QGCLabel {
id: vehicleParamRefreshLabel
text: qsTr("Waiting for Vehicle to update")
visible: false
}
ColumnLayout {
Layout.fillWidth: true
spacing: ScreenTools.defaultFontPixelWidth
visible: _isFollowMeSetup
ColumnLayout {
Layout.fillWidth: true
spacing: ScreenTools.defaultFontPixelWidth
visible: _followParamsAvailable && _isFollowMeSetup
GridLayout {
Layout.fillWidth: true
columns: 2
QGCLabel { text: qsTr("Vehicle Position") }
QGCComboBox {
id: followPositionCombo
Layout.fillWidth: true
model: [ qsTr("Maintain Current Offsets"), qsTr("Specify Offsets")]
onActivated: {
if (index == 0) {
_followOffsetX.rawValue = _followOffsetY.rawValue = _followOffsetZ.rawValue = 0
_setUIFromParams()
} else {
_setFollowMeParamDefaults()
}
}
}
QGCLabel { text: qsTr("Point Vehicle") }
QGCComboBox {
id: pointVehicleCombo
Layout.fillWidth: true
// NOTE: The indices for the model below must match the values for FOLL_YAW_BEHAVE
model: [ qsTr("Maintain current vehicle orientation"), qsTr("Point at ground station location"), qsTr("Same orientation as ground station"), qsTr("Same direction as ground station movement") ]
onActivated: _followYawBehavior.rawValue = index
}
}
GridLayout {
Layout.fillWidth: true
columns: 4
visible: !_followMaintain
QGCLabel {
Layout.columnSpan: 2
Layout.alignment: Qt.AlignHCenter
text: qsTr("Vehicle Offsets")
}
QGCLabel { text: qsTr("Angle") }
FactTextField {
fact: controller.angle
onUpdated: { console.log("updated"); _setXYOffsetByAngleAndDistance(controller.angle.rawValue, controller.distance.rawValue) }
}
QGCLabel { text: qsTr("Distance") }
FactTextField {
fact: controller.distance
onUpdated: _setXYOffsetByAngleAndDistance(controller.angle.rawValue, controller.distance.rawValue)
}
QGCLabel { text: qsTr("Height") }
FactTextField {
fact: controller.height
visible: !_followMaintain
onUpdated: _followOffsetZ.rawValue = -controller.height.rawValue
}
}
}
}
RowLayout {
spacing: ScreenTools.defaultFontPixelWidth * 2
visible: _isFollowMeSetup && !_followMaintain
Item {
height: ScreenTools.defaultFontPixelWidth * 50
width: height
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 3
color: qgcPal.windowShade
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
height: 3
color: qgcPal.windowShade
}
QGCLabel {
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: parent.height / 4
anchors.top: parent.top
text: qsTr("Click in the graphic to change angle")
opacity: 0.5
}
Image {
id: gcsIcon
anchors.centerIn: parent
source: "/res/QGCLogoArrow"
mipmap: true
antialiasing: true
fillMode: Image.PreserveAspectFit
height: ScreenTools.defaultFontPixelHeight * 2.5
sourceSize.height: height
}
Item {
id: vehicleHolder
anchors.fill: parent
transform: Rotation {
origin.x: vehicleHolder.width / 2
origin.y: vehicleHolder.height / 2
angle: controller.angle.rawValue
}
Image {
id: vehicleIcon
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
source: controller.vehicle.vehicleImageOpaque
mipmap: true
height: ScreenTools.defaultFontPixelHeight * 2.5
sourceSize.height: height
fillMode: Image.PreserveAspectFit
transform: Rotation {
origin.x: vehicleIcon.width / 2
origin.y: vehicleIcon.height / 2
angle: _followYawBehavior.rawValue == _followYawBehaviorNone ?
0 :
(_followYawBehavior.rawValue == _followYawBehaviorFace ?
180 :
-controller.angle.rawValue)
}
}
Rectangle {
id: distanceLine
x: parent.width / 2
y: vehicleIcon.height
height: (parent.height / 2) - (vehicleIcon.height + (gcsIcon.height / 2))
width: 2
color: qgcPal.text
opacity: 0.4
Rectangle {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: ScreenTools.defaultFontPixelWidth * 2
height: 2
color: qgcPal.text
}
Rectangle {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: ScreenTools.defaultFontPixelWidth * 2
height: 2
color: qgcPal.text
}
}
QGCLabel {
id: distanceLabel
anchors.centerIn: distanceLine
text: controller.distance.valueString + " " + QGroundControl.appSettingsDistanceUnitsString
transform: Rotation {
origin.x: distanceLabel.width / 2
origin.y: distanceLabel.height / 2
angle: -controller.angle.rawValue
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
// Translate x,y to centered
var x = mouse.x - (width / 2)
var y = (height - mouse.y) - (height / 2)
controller.angle.rawValue = _radiansToHeading(Math.atan2(y, x))
}
}
}
ColumnLayout {
Layout.fillHeight: true
spacing: 0
Image {
id: vehicleIconHeight
source: controller.vehicle.vehicleImageOpaque
mipmap: true
height: ScreenTools.defaultFontPixelHeight * 2.5
sourceSize.height: height
fillMode: Image.PreserveAspectFit
transform: Rotation {
origin.x: vehicleIconHeight.width / 2
origin.y: vehicleIconHeight.height / 2
angle: 65
axis { x: 1; y: 0; z: 0 }
}
}
Item {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
width: Math.max(ScreenTools.defaultFontPixelWidth * 2, heightLabel.width)
Rectangle {
id: heightLine
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 2
color: qgcPal.text
opacity: 0.4
Rectangle {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: ScreenTools.defaultFontPixelWidth * 2
height: 2
color: qgcPal.text
}
Rectangle {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: ScreenTools.defaultFontPixelWidth * 2
height: 2
color: qgcPal.text
}
}
QGCLabel {
id: heightLabel
anchors.centerIn: parent
text: controller.height.valueString + " " + QGroundControl.appSettingsDistanceUnitsString
}
}
Image {
id: gcsIconHeight
source: "/res/QGCLogoArrow"
mipmap: true
antialiasing: true
fillMode: Image.PreserveAspectFit
height: ScreenTools.defaultFontPixelHeight * 2.5
sourceSize.height: height
transform: Rotation {
origin.x: gcsIconHeight.width / 2
origin.y: gcsIconHeight.height / 2
angle: 65
axis { x: 1; y: 0; z: 0 }
}
}
}
}
}
}
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
#include "APMFollowComponentController.h"
const char* APMFollowComponentController::settingsGroup = "APMFollow";
const char* APMFollowComponentController::angleName = "angle";
const char* APMFollowComponentController::distanceName = "distance";
const char* APMFollowComponentController::heightName = "height";
APMFollowComponentController::APMFollowComponentController(void)
: _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/APMFollowComponent.FactMetaData.json"), this))
, _angleFact (settingsGroup, _metaDataMap[angleName])
, _distanceFact (settingsGroup, _metaDataMap[distanceName])
, _heightFact (settingsGroup, _metaDataMap[heightName])
{
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
#pragma once
#include "FactPanelController.h"
class APMFollowComponentController : public FactPanelController
{
Q_OBJECT
public:
APMFollowComponentController(void);
Q_PROPERTY(Fact* angle READ angleFact CONSTANT)
Q_PROPERTY(Fact* distance READ distanceFact CONSTANT)
Q_PROPERTY(Fact* height READ heightFact CONSTANT)
Fact* angleFact (void) { return &_angleFact; }
Fact* distanceFact (void) { return &_distanceFact; }
Fact* heightFact (void) { return &_heightFact; }
static const char* settingsGroup;
static const char* angleName;
static const char* distanceName;
static const char* heightName;
private:
QMap<QString, FactMetaData*> _metaDataMap;
SettingsFact _angleFact;
SettingsFact _distanceFact;
SettingsFact _heightFact;
};
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Controls 1.0
import QGroundControl.Palette 1.0
Item {
anchors.fill: parent
FactPanelController { id: controller; }
property Fact _batt1Monitor: controller.getParameterFact(-1, "BATT_MONITOR")
property Fact _batt2Monitor: controller.getParameterFact(-1, "BATT2_MONITOR", false /* reportMissing */)
property bool _batt2MonitorAvailable: controller.parameterExists(-1, "BATT2_MONITOR")
property bool _batt1MonitorEnabled: _batt1Monitor.rawValue !== 0
property bool _batt2MonitorEnabled: _batt2MonitorAvailable && _batt2Monitor.rawValue !== 0
property Fact _battCapacity: controller.getParameterFact(-1, "BATT_CAPACITY", false /* reportMissing */)
property Fact _batt2Capacity: controller.getParameterFact(-1, "BATT2_CAPACITY", false /* reportMissing */)
property bool _battCapacityAvailable: controller.parameterExists(-1, "BATT_CAPACITY")
Column {
anchors.fill: parent
VehicleSummaryRow {
labelText: qsTr("Batt1 monitor")
valueText: _batt1Monitor.enumStringValue
}
VehicleSummaryRow {
labelText: qsTr("Batt1 capacity")
valueText: _batt1MonitorEnabled ? _battCapacity.valueString + " " + _battCapacity.units : ""
visible: _batt1MonitorEnabled
}
VehicleSummaryRow {
labelText: qsTr("Batt2 monitor")
valueText: _batt2MonitorAvailable ? _batt2Monitor.enumStringValue : ""
visible: _batt2MonitorAvailable
}
VehicleSummaryRow {
labelText: qsTr("Batt2 capacity")
valueText: _batt2MonitorEnabled ? _batt2Capacity.valueString + " " + _batt2Capacity.units : ""
visible: _batt2MonitorEnabled
}
}
}
......@@ -51,14 +51,13 @@ Item {
QGCFlickable {
anchors.fill: parent
contentWidth: pageLoader.x + pageLoader.item.width
contentHeight: pageLoader.y + pageLoader.item.height
contentWidth: Math.max(availableWidth, pageLoader.x + pageLoader.item.width)
contentHeight: Math.max(availableHeight, pageLoader.y + pageLoader.item.height)
clip: true
RowLayout {
id: headingRow
anchors.left: parent.left
anchors.right: parent.right
width: availableWidth
spacing: _margins
layoutDirection: Qt.RightToLeft
......@@ -68,22 +67,22 @@ Item {
visible: showAdvanced
}
Column {
ColumnLayout {
spacing: _margins
Layout.fillWidth: true
QGCLabel {
font.pointSize: ScreenTools.largeFontPointSize
text: !setupView.enabled ? _pageTitle + "<font color=\"red\">" + qsTr(" (Disabled while the vehicle is %1)").arg(_disableReason) + "</font>" : _pageTitle
visible: !ScreenTools.isShortScreen
Layout.fillWidth: true
font.pointSize: ScreenTools.largeFontPointSize
text: !setupView.enabled ? _pageTitle + "<font color=\"red\">" + qsTr(" (Disabled while the vehicle is %1)").arg(_disableReason) + "</font>" : _pageTitle
visible: !ScreenTools.isShortScreen
}
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap
text: pageDescription
visible: pageDescription !== "" && !ScreenTools.isShortScreen
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: pageDescription
visible: pageDescription !== "" && !ScreenTools.isShortScreen
}
}
}
......@@ -96,7 +95,7 @@ Item {
// to be disabled
Rectangle {
visible: !setupView.enabled
anchors.fill: pageLoader
anchors.fill: parent
color: "black"
opacity: 0.5
}
......
......@@ -29,6 +29,10 @@ FactPanelController::FactPanelController()
} else {
_vehicle = qgcApp()->toolbox()->multiVehicleManager()->offlineEditingVehicle();
}
_missingParametersTimer.setInterval(500);
_missingParametersTimer.setSingleShot(true);
connect(&_missingParametersTimer, &QTimer::timeout, this, &FactPanelController::_checkForMissingParameters);
}
void FactPanelController::_notifyPanelMissingParameter(const QString& missingParam)
......@@ -114,3 +118,29 @@ void FactPanelController::_showInternalError(const QString& errorMsg)
qCWarning(FactPanelControllerLog) << "Internal Error" << errorMsg;
qgcApp()->showMessage(errorMsg);
}
void FactPanelController::getMissingParameters(QStringList rgNames)
{
for (const QString& name: rgNames) {
_missingParameterWaitList.append(name);
_vehicle->parameterManager()->refreshParameter(MAV_COMP_ID_AUTOPILOT1, name);
}
_missingParametersTimer.start();
}
void FactPanelController::_checkForMissingParameters(void)
{
QStringList waitList = _missingParameterWaitList;
for (const QString& name: waitList) {
if (_vehicle->parameterManager()->parameterExists(MAV_COMP_ID_AUTOPILOT1, name)) {
_missingParameterWaitList.removeOne(name);
}
}
if (_missingParameterWaitList.isEmpty()) {
emit missingParametersAvailable();
} else {
_missingParametersTimer.start();
}
}
......@@ -34,6 +34,12 @@ public:
Q_INVOKABLE Fact* getParameterFact (int componentId, const QString& name, bool reportMissing = true);
Q_INVOKABLE bool parameterExists (int componentId, const QString& name);
/// Queries the vehicle for parameters which were not available on initial download but should be available now.
/// Signals missingParametersAvailable when done. Only works for MAV_COMP_ID_AUTOPILOT1 parameters.
Q_INVOKABLE void getMissingParameters(QStringList rgNames);
signals:
void missingParametersAvailable(void);
protected:
/// Checks for existence of the specified parameters
......@@ -47,10 +53,15 @@ protected:
UASInterface* _uas = nullptr;
AutoPilotPlugin* _autopilot = nullptr;
private slots:
void _checkForMissingParameters(void);
private:
void _notifyPanelMissingParameter(const QString& missingParam);
void _notifyPanelErrorMsg(const QString& errorMsg);
void _showInternalError(const QString& errorMsg);
QStringList _delayedMissingParams;
QStringList _missingParameterWaitList;
QTimer _missingParametersTimer;
};
......@@ -14,6 +14,7 @@
#include "APMFlightModesComponentController.h"
#include "APMAirframeComponentController.h"
#include "APMSensorsComponentController.h"
#include "APMFollowComponentController.h"
#include "MissionManager.h"
#include "ParameterManager.h"
#include "QGCFileDownload.h"
......@@ -158,6 +159,7 @@ APMFirmwarePlugin::APMFirmwarePlugin(void)
qmlRegisterType<APMFlightModesComponentController> ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
qmlRegisterType<APMAirframeComponentController> ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
qmlRegisterType<APMSensorsComponentController> ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
qmlRegisterType<APMFollowComponentController> ("QGroundControl.Controllers", 1, 0, "APMFollowComponentController");
}
AutoPilotPlugin* APMFirmwarePlugin::autopilotPlugin(Vehicle* vehicle)
......@@ -396,7 +398,8 @@ bool APMFirmwarePlugin::_handleIncomingStatusText(Vehicle* vehicle, mavlink_mess
}
if (supportedMajorNumber != -1) {
if (firmwareVersion.majorNumber() < supportedMajorNumber || firmwareVersion.minorNumber() < supportedMinorNumber) {
if (firmwareVersion.majorNumber() < supportedMajorNumber ||
(firmwareVersion.majorNumber() == supportedMajorNumber && firmwareVersion.minorNumber() < supportedMinorNumber)) {
qgcApp()->showMessage(tr("QGroundControl fully supports Version %1.%2 and above. You are using a version prior to that. This combination is untested, you may run into unpredictable results.").arg(supportedMajorNumber).arg(supportedMinorNumber));
}
}
......@@ -1101,7 +1104,7 @@ void APMFirmwarePlugin::_sendGCSMotionReport(Vehicle* vehicle, FollowMe::GCSMoti
return;
}
if (!(estimationCapabilities & (FollowMe::POS | FollowMe::VEL))) {
if (!(estimationCapabilities & (FollowMe::POS | FollowMe::VEL | FollowMe::HEADING))) {
static bool sentOnce = false;
if (!sentOnce) {
sentOnce = true;
......@@ -1124,7 +1127,7 @@ void APMFirmwarePlugin::_sendGCSMotionReport(Vehicle* vehicle, FollowMe::GCSMoti
globalPositionInt.vx = static_cast<int16_t>(motionReport.vxMetersPerSec * 100); // cm/sec
globalPositionInt.vy = static_cast<int16_t>(motionReport.vyMetersPerSec * 100); // cm/sec
globalPositionInt.vy = static_cast<int16_t>(motionReport.vzMetersPerSec * 100); // cm/sec
globalPositionInt.hdg = UINT16_MAX;
globalPositionInt.hdg = static_cast<uint16_t>(motionReport.headingDegrees * 100.0); // centi-degrees
mavlink_message_t message;
mavlink_msg_global_position_int_encode_chan(static_cast<uint8_t>(mavlinkProtocol->getSystemId()),
......
......@@ -3507,7 +3507,7 @@
<field name="Units">m</field>
<field name="UnitText">meters</field>
</param>
<param humanName="Follow offset type" name="FOLL_OFS_TYPE" documentation="Follow offset type" user="Standard">
<param humanName="Follow offset type" name="ffolFOLL_OFS_TYPE" documentation="Follow offset type" user="Standard">
<values>
<value code="0">North-East-Down</value>
<value code=" 1">Relative to lead vehicle heading</value>
......
......@@ -7,10 +7,12 @@
<file alias="APMCameraComponentSummary.qml">../../AutoPilotPlugins/APM/APMCameraComponentSummary.qml</file>
<file alias="APMFlightModesComponent.qml">../../AutoPilotPlugins/APM/APMFlightModesComponent.qml</file>
<file alias="APMFlightModesComponentSummary.qml">../../AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml</file>
<file alias="APMFollowComponent.qml">../../AutoPilotPlugins/APM/APMFollowComponent.qml</file>
<file alias="APMFollowComponentSummary.qml">../../AutoPilotPlugins/APM/APMFollowComponentSummary.qml</file>
<file alias="APMHeliComponent.qml">../../AutoPilotPlugins/APM/APMHeliComponent.qml</file>
<file alias="APMLightsComponent.qml">../../AutoPilotPlugins/APM/APMLightsComponent.qml</file>
<file alias="APMLightsComponentSummary.qml">../../AutoPilotPlugins/APM/APMLightsComponentSummary.qml</file>
<file alias="APMMotorComponent.qml">../../AutoPilotPlugins/APM/APMMotorComponent.qml</file>
<file alias="APMMotorComponent.qml">../../AutoPilotPlugins/APM/APMMotorComponent.qml</file>
<file alias="APMSubFrameComponent.qml">../../AutoPilotPlugins/APM/APMSubFrameComponent.qml</file>
<file alias="APMSubFrameComponentSummary.qml">../../AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml</file>
<file alias="APMSubMotorComponent.qml">../../AutoPilotPlugins/APM/APMSubMotorComponent.qml</file>
......@@ -37,6 +39,7 @@
<file alias="APM/MavCmdInfoRover.json">MavCmdInfoRover.json</file>
<file alias="APM/MavCmdInfoSub.json">MavCmdInfoSub.json</file>
<file alias="APM/MavCmdInfoVTOL.json">MavCmdInfoVTOL.json</file>
<file alias="APMFollowComponent.FactMetaData.json">../../AutoPilotPlugins/APM/APMFollowComponent.FactMetaData.json</file>
</qresource>
<qresource prefix="/FirmwarePlugin/APM">
<file alias="APMParameterFactMetaData.Plane.3.8.xml">APMParameterFactMetaData.Plane.3.8.xml</file>
......
......@@ -93,6 +93,7 @@ void FollowMe::_sendGCSMotionReport()
estimatation_capabilities |= (1 << POS);
if (geoPositionInfo.hasAttribute(QGeoPositionInfo::Direction) == true) {
estimatation_capabilities |= (1 << HEADING);
motionReport.headingDegrees = geoPositionInfo.attribute(QGeoPositionInfo::Direction);
}
......
......@@ -47,7 +47,8 @@ public:
POS = 0,
VEL = 1,
ACCEL = 2,
ATT_RATES = 3
ATT_RATES = 3,
HEADING = 4
};
void setToolbox(QGCToolbox* toolbox) override;
......
......@@ -162,6 +162,9 @@ public:
Q_INVOKABLE bool linesIntersect(QPointF xLine1, QPointF yLine1, QPointF xLine2, QPointF yLine2);
Q_INVOKABLE double degreesToRadians(double degrees) { return qDegreesToRadians(degrees); }
Q_INVOKABLE double radiansToDegrees(double radians) { return qRadiansToDegrees(radians); }
// Property accesors
QString appName () { return qgcApp()->applicationName(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment