diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index 1f4bb7924275bceba004c24d0ece61d5069f40bf..c61200b5bf93204626dd3e8cae158a04ada7acce 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -56,6 +56,7 @@
src/QmlControls/FactSliderPanel.qml
src/QmlControls/FlightModeDropdown.qml
src/QmlControls/FlightModeMenu.qml
+ src/MissionEditor/FWLandingPatternMapVisual.qml
src/QmlControls/GuidedBar.qml
src/QmlControls/IndicatorButton.qml
src/QmlControls/JoystickThumbPad.qml
diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml
index e30049adef473ceea47a035eb7bcc0a17e2b6326..6d82d6a46b48ba1e706d0279c66589c0e7b5db11 100644
--- a/src/MissionEditor/FWLandingPatternEditor.qml
+++ b/src/MissionEditor/FWLandingPatternEditor.qml
@@ -31,11 +31,25 @@ Rectangle {
//property real availableWidth ///< Width for control
//property var missionItem ///< Mission Item for editor
- property real _margin: ScreenTools.defaultFontPixelWidth * 0.25
+ property real _margin: ScreenTools.defaultFontPixelWidth * 0.25
Column {
- id: editorColumn
+ id: editorColumn
+ anchors.margins: _margin
+ anchors.left: parent.left
+ anchors.right: parent.right
- QGCLabel { text: "WIP" }
+ QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" }
+
+ FactTextFieldGrid {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ factList: missionItem.textFieldFacts
+ }
+
+ FactCheckBox {
+ text: missionItem.loiterClockwise.name
+ fact: missionItem.loiterClockwise
+ }
}
}
diff --git a/src/MissionEditor/FWLandingPatternMapVisual.qml b/src/MissionEditor/FWLandingPatternMapVisual.qml
new file mode 100644
index 0000000000000000000000000000000000000000..5a6adc4f08a24b48c1bfb2cb9050389b89ecfc4a
--- /dev/null
+++ b/src/MissionEditor/FWLandingPatternMapVisual.qml
@@ -0,0 +1,64 @@
+/****************************************************************************
+ *
+ * (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.
+ *
+ ****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtLocation 5.3
+import QtPositioning 5.2
+
+import QGroundControl.ScreenTools 1.0
+import QGroundControl.Palette 1.0
+import QGroundControl.Controls 1.0
+
+/// Fixed Wing Landing Pattern map visuals
+Item {
+ property var map ///< Map control to place item in
+
+ property var _loiterPoint
+ property var _flightPath
+
+ Component.onCompleted: {
+ _flightPath = flightPathComponent.createObject(map)
+ _loiterPoint = loiterComponent.createObject(map)
+ map.addMapItem(_flightPath)
+ map.addMapItem(_loiterPoint)
+ }
+
+ Component.onDestruction: {
+ _loiterPoint.destroy()
+ _flightPath.destroy()
+ }
+
+ // Flight path
+ Component {
+ id: flightPathComponent
+
+ MapPolyline {
+ line.color: "white"
+ line.width: 2
+ path: [ object.loiterCoordinate, object.exitCoordinate ]
+ }
+ }
+
+ // Loiter point
+ Component {
+ id: loiterComponent
+
+ MapQuickItem {
+ anchorPoint.x: sourceItem.width / 2
+ anchorPoint.y: sourceItem.height / 2
+ coordinate: object.loiterCoordinate
+
+ sourceItem:
+ MissionItemIndexLabel {
+ label: "L"
+ }
+ }
+ }
+}
diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json
index 0d4f101c7a37a4c875e6999bee1a287fdb733380..d606af68458d6734119c8531cad01ceb165aac17 100644
--- a/src/MissionManager/FWLandingPattern.FactMetaData.json
+++ b/src/MissionManager/FWLandingPattern.FactMetaData.json
@@ -1,2 +1,41 @@
[
+{
+ "name": "Landing distance",
+ "shortDescription": "Distance between landing and loiter points.",
+ "type": "double",
+ "units": "m",
+ "decimalPlaces": 1,
+ "defaultValue": 100.0
+},
+{
+ "name": "Landing heading",
+ "shortDescription": "Heading from land point to loiter point.",
+ "type": "double",
+ "units": "deg",
+ "decimalPlaces": 0,
+ "defaultValue": 0.0
+},
+{
+ "name": "Loiter altitude",
+ "shortDescription": "Altitude to loiter prior to landing.",
+ "type": "double",
+ "units": "m",
+ "decimalPlaces": 1,
+ "defaultValue": 40.0
+},
+{
+ "name": "Loiter radius",
+ "shortDescription": "Loiter radius.",
+ "type": "double",
+ "decimalPlaces": 1,
+ "min": 0.1,
+ "units": "m",
+ "defaultValue": 75.0
+},
+{
+ "name": "Clockwise loiter",
+ "shortDescription": "If true, loiter will be clockwise. False, loiter will be counter-clockwise.",
+ "type": "bool",
+ "defaultValue": true
+}
]
diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc
index 55fd5dfb70a958f487fb381a6aeaef61dc356885..9303f653bae20e7330d5bdcdf7ae3851affa9166 100644
--- a/src/MissionManager/FixedWingLandingComplexItem.cc
+++ b/src/MissionManager/FixedWingLandingComplexItem.cc
@@ -19,23 +19,57 @@ QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "FixedWingLandingComplexIte
const char* FixedWingLandingComplexItem::jsonComplexItemTypeValue = "fwLandingPattern";
+const char* FixedWingLandingComplexItem::_loiterToLandDistanceName = "Landing distance";
+const char* FixedWingLandingComplexItem::_landingHeadingName = "Landing heading";
+const char* FixedWingLandingComplexItem::_loiterAltitudeName = "Loiter altitude";
+const char* FixedWingLandingComplexItem::_loiterRadiusName = "Loiter radius";
+const char* FixedWingLandingComplexItem::_loiterClockwiseName = "Clockwise loiter";
+
QMap FixedWingLandingComplexItem::_metaDataMap;
-FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent)
+FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent)
: ComplexMissionItem(vehicle, parent)
, _sequenceNumber(0)
, _dirty(false)
+ , _loiterToLandDistanceFact (0, _loiterToLandDistanceName, FactMetaData::valueTypeDouble)
+ , _loiterAltitudeFact (0, _loiterAltitudeName, FactMetaData::valueTypeDouble)
+ , _loiterRadiusFact (0, _loiterRadiusName, FactMetaData::valueTypeDouble)
+ , _loiterClockwiseFact (0, _loiterClockwiseName, FactMetaData::valueTypeBool)
+ , _landingHeadingFact (0, _landingHeadingName, FactMetaData::valueTypeDouble)
{
_editorQml = "qrc:/qml/FWLandingPatternEditor.qml";
if (_metaDataMap.isEmpty()) {
_metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/FWLandingPattern.FactMetaData.json"), NULL /* metaDataParent */);
}
+
+ _loiterToLandDistanceFact.setMetaData (_metaDataMap[_loiterToLandDistanceName]);
+ _loiterAltitudeFact.setMetaData (_metaDataMap[_loiterAltitudeName]);
+ _loiterRadiusFact.setMetaData (_metaDataMap[_loiterRadiusName]);
+ _loiterClockwiseFact.setMetaData (_metaDataMap[_loiterClockwiseName]);
+ _landingHeadingFact.setMetaData (_metaDataMap[_landingHeadingName]);
+
+ _loiterToLandDistanceFact.setRawValue (_loiterToLandDistanceFact.rawDefaultValue());
+ _loiterAltitudeFact.setRawValue (_loiterAltitudeFact.rawDefaultValue());
+ _loiterRadiusFact.setRawValue (_loiterRadiusFact.rawDefaultValue());
+ _loiterClockwiseFact.setRawValue (_loiterClockwiseFact.rawDefaultValue());
+ _landingHeadingFact.setRawValue (_landingHeadingFact.rawDefaultValue());
+
+ connect(&_loiterToLandDistanceFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition);
+ connect(&_landingHeadingFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition);
+
+ _textFieldFacts << QVariant::fromValue(&_loiterToLandDistanceFact) << QVariant::fromValue(&_loiterAltitudeFact) << QVariant::fromValue(&_loiterRadiusFact) << QVariant::fromValue(&_landingHeadingFact);
+
+ // Exit coordinate is our land point
+ _exitCoordinate = mapClickCoordinate;
+
+ _recalcLoiterPosition();
}
int FixedWingLandingComplexItem::lastSequenceNumber(void) const
{
- return _sequenceNumber;
+ // land start, loiter, land
+ return _sequenceNumber + 2;
}
void FixedWingLandingComplexItem::setCoordinate(const QGeoCoordinate& coordinate)
@@ -117,54 +151,46 @@ bool FixedWingLandingComplexItem::specifiesCoordinate(void) const
QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const
{
- // FIXME: Need real implementation
QmlObjectListModel* pMissionItems = new QmlObjectListModel;
-#if 0
int seqNum = _sequenceNumber;
- for (int i=0; i<_gridPoints.count(); i++) {
- QGeoCoordinate coord = _gridPoints[i].value();
- double altitude = _gridAltitudeFact.rawValue().toDouble();
-
- MissionItem* item = new MissionItem(seqNum++, // sequence number
- MAV_CMD_NAV_WAYPOINT, // MAV_CMD
- _gridAltitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, // MAV_FRAME
- 0.0, 0.0, 0.0, 0.0, // param 1-4
- coord.latitude(),
- coord.longitude(),
- altitude,
- true, // autoContinue
- false, // isCurrentItem
- pMissionItems); // parent - allow delete on pMissionItems to delete everthing
- pMissionItems->append(item);
-
- if (_cameraTrigger && i == 0) {
- // Turn on camera
- MissionItem* item = new MissionItem(seqNum++, // sequence number
- MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD
- MAV_FRAME_MISSION, // MAV_FRAME
- _cameraTriggerDistanceFact.rawValue().toDouble(), // trigger distance
- 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7
- true, // autoContinue
- false, // isCurrentItem
- pMissionItems); // parent - allow delete on pMissionItems to delete everthing
- pMissionItems->append(item);
- }
- }
- if (_cameraTrigger) {
- // Turn off camera
- MissionItem* item = new MissionItem(seqNum++, // sequence number
- MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD
- MAV_FRAME_MISSION, // MAV_FRAME
- 0.0, // trigger distance
- 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7
- true, // autoContinue
- false, // isCurrentItem
- pMissionItems); // parent - allow delete on pMissionItems to delete everthing
- pMissionItems->append(item);
- }
-#endif
+ MissionItem* item = new MissionItem(seqNum++, // sequence number
+ MAV_CMD_DO_LAND_START, // MAV_CMD
+ MAV_FRAME_GLOBAL_RELATIVE_ALT, // MAV_FRAME
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 1-7
+ true, // autoContinue
+ false, // isCurrentItem
+ pMissionItems); // parent - allow delete on pMissionItems to delete everthing
+ pMissionItems->append(item);
+
+ float loiterRadius = _loiterRadiusFact.rawValue().toDouble() * (_loiterClockwiseFact.rawValue().toBool() ? 1.0 : -1.0);
+ item = new MissionItem(seqNum++,
+ MAV_CMD_NAV_LOITER_TO_ALT,
+ MAV_FRAME_GLOBAL_RELATIVE_ALT,
+ 1.0, // Heading required = true
+ loiterRadius, // Loiter radius
+ 0.0, // param 3 - unused
+ 0.0, // Exit crosstrack - center of waypoint
+ _loiterCoordinate.latitude(),
+ _loiterCoordinate.longitude(),
+ _loiterCoordinate.altitude(),
+ true, // autoContinue
+ false, // isCurrentItem
+ pMissionItems); // parent - allow delete on pMissionItems to delete everthing
+ pMissionItems->append(item);
+
+ item = new MissionItem(seqNum++,
+ MAV_CMD_NAV_LAND,
+ MAV_FRAME_GLOBAL_RELATIVE_ALT,
+ 0.0, 0.0, 0.0, 0.0, // param 1-4
+ _exitCoordinate.latitude(),
+ _exitCoordinate.longitude(),
+ 0.0, // altitude
+ true, // autoContinue
+ false, // isCurrentItem
+ pMissionItems); // parent - allow delete on pMissionItems to delete everthing
+ pMissionItems->append(item);
return pMissionItems;
}
@@ -180,3 +206,32 @@ void FixedWingLandingComplexItem::setCruiseSpeed(double cruiseSpeed)
// FIXME: Need real implementation
Q_UNUSED(cruiseSpeed);
}
+
+void FixedWingLandingComplexItem::_recalcLoiterPosition(void)
+{
+ double north, east, down;
+ QGeoCoordinate tangentOrigin = _exitCoordinate;
+
+ convertGeoToNed(_exitCoordinate, tangentOrigin, &north, &east, &down);
+
+ QPointF originPoint(east, north);
+ north += _loiterToLandDistanceFact.rawValue().toDouble();
+ QPointF loiterPoint(east, north);
+ QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, _landingHeadingFact.rawValue().toDouble());
+
+ convertNedToGeo(rotatedLoiterPoint.y(), rotatedLoiterPoint.x(), down, tangentOrigin, &_loiterCoordinate);
+
+ emit loiterCoordinateChanged(_loiterCoordinate);
+ setCoordinate(_loiterCoordinate);
+}
+
+QPointF FixedWingLandingComplexItem::_rotatePoint(const QPointF& point, const QPointF& origin, double angle)
+{
+ QPointF rotated;
+ double radians = (M_PI / 180.0) * angle;
+
+ rotated.setX(((point.x() - origin.x()) * cos(radians)) - ((point.y() - origin.y()) * sin(radians)) + origin.x());
+ rotated.setY(((point.x() - origin.x()) * sin(radians)) + ((point.y() - origin.y()) * cos(radians)) + origin.y());
+
+ return rotated;
+}
diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h
index 26a5c47664e246faa1ff83607954511a3f07c7df..c7485c368e78c514e05eb1cf377d3a786ff887d4 100644
--- a/src/MissionManager/FixedWingLandingComplexItem.h
+++ b/src/MissionManager/FixedWingLandingComplexItem.h
@@ -22,7 +22,13 @@ class FixedWingLandingComplexItem : public ComplexMissionItem
Q_OBJECT
public:
- FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent = NULL);
+ FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent = NULL);
+
+ Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT)
+ Q_PROPERTY(Fact* loiterClockwise READ loiterClockwise CONSTANT)
+ Q_PROPERTY(QGeoCoordinate loiterCoordinate MEMBER _loiterCoordinate NOTIFY loiterCoordinateChanged)
+
+ Fact* loiterClockwise(void) { return &_loiterClockwiseFact; }
// Overrides from ComplexMissionItem
@@ -32,7 +38,7 @@ public:
bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final;
double greatestDistanceTo (const QGeoCoordinate &other) const final;
void setCruiseSpeed (double cruiseSpeed) final;
- QString mapVisualQML (void) const final { return QString(); }
+ QString mapVisualQML (void) const final { return QStringLiteral("FWLandingPatternMapVisual.qml"); }
// Overrides from VisualMissionItem
@@ -60,18 +66,36 @@ public:
static const char* jsonComplexItemTypeValue;
signals:
+ void loiterCoordinateChanged(QGeoCoordinate coordinate);
private slots:
+ void _recalcLoiterPosition(void);
private:
void _setExitCoordinate(const QGeoCoordinate& coordinate);
+ QPointF _rotatePoint(const QPointF& point, const QPointF& origin, double angle);
int _sequenceNumber;
bool _dirty;
QGeoCoordinate _coordinate;
QGeoCoordinate _exitCoordinate;
+ QGeoCoordinate _loiterCoordinate;
+
+ Fact _loiterToLandDistanceFact;
+ Fact _loiterAltitudeFact;
+ Fact _loiterRadiusFact;
+ Fact _loiterClockwiseFact;
+ Fact _landingHeadingFact;
static QMap _metaDataMap;
+
+ QVariantList _textFieldFacts;
+
+ static const char* _loiterToLandDistanceName;
+ static const char* _loiterAltitudeName;
+ static const char* _loiterRadiusName;
+ static const char* _loiterClockwiseName;
+ static const char* _landingHeadingName;
};
#endif
diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc
index 2c6d53dc9e40874bf5bd7d3dabdc1fbfb296b3ef..f6f949be1656c7f4d5ce0ee10e4d5d45ef4981a2 100644
--- a/src/MissionManager/MissionController.cc
+++ b/src/MissionManager/MissionController.cc
@@ -226,14 +226,14 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate
int sequenceNumber = _nextSequenceNumber();
if (itemName == _surveyMissionItemName) {
newItem = new SurveyMissionItem(_activeVehicle, _visualItems);
+ newItem->setCoordinate(mapCenterCoordinate);
} else if (itemName == _fwLandingMissionItemName) {
- newItem = new FixedWingLandingComplexItem(_activeVehicle, _visualItems);
+ newItem = new FixedWingLandingComplexItem(_activeVehicle, mapCenterCoordinate, _visualItems);
} else {
qWarning() << "Internal error: Unknown complex item:" << itemName;
return sequenceNumber;
}
newItem->setSequenceNumber(sequenceNumber);
- newItem->setCoordinate(mapCenterCoordinate);
_initVisualItem(newItem);
_visualItems->insert(i, newItem);