diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index aae4cfebfc33734c015521edd9ccba2db5bfe315..ffbc0bf66f6fee9b832c12bee1cef512fe25492b 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -676,7 +676,6 @@ HEADERS += \
src/qgcunittest/PX4RCCalibrationTest.h \
src/qgcunittest/LinkManagerTest.h \
src/qgcunittest/MainWindowTest.h \
- src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h \
src/qgcunittest/MavlinkLogTest.h \
src/FactSystem/FactSystemTestBase.h \
src/FactSystem/FactSystemTestPX4.h \
@@ -702,7 +701,6 @@ SOURCES += \
src/qgcunittest/PX4RCCalibrationTest.cc \
src/qgcunittest/LinkManagerTest.cc \
src/qgcunittest/MainWindowTest.cc \
- src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc \
src/qgcunittest/MavlinkLogTest.cc \
src/FactSystem/FactSystemTestBase.cc \
src/FactSystem/FactSystemTestPX4.cc \
@@ -722,7 +720,6 @@ INCLUDEPATH += \
FORMS += \
src/VehicleSetup/ParameterEditor.ui \
src/ui/QGCPX4VehicleConfig.ui \
- src/AutoPilotPlugins/PX4/FlightModeConfig.ui \
src/VehicleSetup/SetupView.ui \
HEADERS+= \
@@ -740,7 +737,7 @@ HEADERS+= \
src/AutoPilotPlugins/PX4/PX4Component.h \
src/AutoPilotPlugins/PX4/RadioComponent.h \
src/AutoPilotPlugins/PX4/FlightModesComponent.h \
- src/AutoPilotPlugins/PX4/FlightModeConfig.h \
+ src/AutoPilotPlugins/PX4/FlightModesComponentController.h \
src/AutoPilotPlugins/PX4/AirframeComponent.h \
src/AutoPilotPlugins/PX4/SensorsComponent.h \
src/AutoPilotPlugins/PX4/SensorsComponentController.h \
@@ -762,7 +759,7 @@ SOURCES += \
src/AutoPilotPlugins/PX4/PX4Component.cc \
src/AutoPilotPlugins/PX4/RadioComponent.cc \
src/AutoPilotPlugins/PX4/FlightModesComponent.cc \
- src/AutoPilotPlugins/PX4/FlightModeConfig.cc \
+ src/AutoPilotPlugins/PX4/FlightModesComponentController.cc \
src/AutoPilotPlugins/PX4/AirframeComponent.cc \
src/AutoPilotPlugins/PX4/SensorsComponent.cc \
src/AutoPilotPlugins/PX4/SensorsComponentController.cc \
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index a75fe6658fbbc5904de15bf5199050fb8cccaa62..16459b2f42d82337cf273530d5f93621be5920bc 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -247,15 +247,14 @@
src/test.qml
src/QmlControls/QmlTest.qml
+
src/FactSystem/FactControls/qmldir
src/FactSystem/FactControls/FactLabel.qml
src/FactSystem/FactControls/FactTextField.qml
src/FactSystem/FactControls/FactCheckBox.qml
src/FactSystem/FactControls/FactComboBox.qml
+
src/QmlControls/qmldir
- src/QmlControls/SubMenuButton.qml
- src/QmlControls/IndicatorButton.qml
- src/QmlControls/VehicleRotationCal.qml
src/QmlControls/QGCButton.qml
src/QmlControls/QGCRadioButton.qml
src/QmlControls/QGCCheckBox.qml
@@ -263,21 +262,31 @@
src/QmlControls/QGCTextField.qml
src/QmlControls/QGCComboBox.qml
src/QmlControls/QGCColoredImage.qml
- src/QmlControls/arrow-down.png
src/QmlControls/QGCToolBarButton.qml
+
+ src/QmlControls/SubMenuButton.qml
+ src/QmlControls/IndicatorButton.qml
+ src/QmlControls/VehicleRotationCal.qml
+
+ src/QmlControls/arrow-down.png
+
files/images/px4/airframes/octo_x.png
files/images/px4/boards/px4fmu_2.x.png
src/VehicleSetup/SetupViewButtons.qml
src/VehicleSetup/VehicleSummary.qml
src/VehicleSetup/FirmwareUpgrade.qml
+
src/AutoPilotPlugins/PX4/SafetyComponent.qml
src/AutoPilotPlugins/PX4/SensorsComponent.qml
+ src/AutoPilotPlugins/PX4/FlightModesComponent.qml
+
src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml
src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml
src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml
src/AutoPilotPlugins/PX4/RadioComponentSummary.qml
src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml
src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml
+
src/AutoPilotPlugins/PX4/Images/SafetyComponentTree.png
src/AutoPilotPlugins/PX4/Images/SafetyComponentHome.png
src/AutoPilotPlugins/PX4/Images/SafetyComponentArrowDown.png
@@ -288,6 +297,7 @@
src/AutoPilotPlugins/PX4/Images/VehicleRight.png
src/AutoPilotPlugins/PX4/Images/VehicleNoseDown.png
src/AutoPilotPlugins/PX4/Images/VehicleTailDown.png
+
files/Setup/cogwheels.png
src/AutoPilotPlugins/PX4/Images/SensorsComponentIcon.png
src/AutoPilotPlugins/PX4/Images/RadioComponentIcon.png
@@ -296,6 +306,7 @@
src/AutoPilotPlugins/PX4/Images/SafetyComponentIcon.png
src/VehicleSetup/FirmwareUpgradeIcon.png
src/VehicleSetup/VehicleSummaryIcon.png
+
src/ui/toolbar/MainToolBar.qml
diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc
index 14b64315cf22b3472cb8d8d32b795fe525ab85df..e573a26c218d3e67d923c3286a144ef755459a54 100644
--- a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc
+++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc
@@ -25,8 +25,8 @@
/// @author Don Gagne
#include "FlightModesComponent.h"
-#include "FlightModeConfig.h"
#include "PX4AutoPilotPlugin.h"
+#include "QGCQmlWidgetHolder.h"
struct SwitchListItem {
const char* param;
@@ -111,7 +111,14 @@ QStringList FlightModesComponent::paramFilterList(void) const
QWidget* FlightModesComponent::setupWidget(void) const
{
- return new FlightModeConfig();
+ QGCQmlWidgetHolder* holder = new QGCQmlWidgetHolder();
+ Q_CHECK_PTR(holder);
+
+ holder->setAutoPilot(_autopilot);
+
+ holder->setSource(QUrl::fromUserInput("qrc:/qml/FlightModesComponent.qml"));
+
+ return holder;
}
QUrl FlightModesComponent::summaryQmlSource(void) const
diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.qml b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml
new file mode 100644
index 0000000000000000000000000000000000000000..8aba6c3494b4be61b95563d5f815530f9b2a7165
--- /dev/null
+++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml
@@ -0,0 +1,870 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtQuick.Controls.Styles 1.2
+
+import QGroundControl.FactSystem 1.0
+import QGroundControl.FactControls 1.0
+import QGroundControl.Palette 1.0
+import QGroundControl.Controls 1.0
+import QGroundControl.Controllers 1.0
+
+Item {
+ Loader {
+ property FlightModesComponentController controller: FlightModesComponentController { }
+ property QGCPalette qgcPal: QGCPalette { colorGroupEnabled: true }
+ property bool loading: true
+
+ anchors.fill: parent
+ sourceComponent: controller.validConfiguration ? validComponent : invalidComponent
+
+ onLoaded: loading = false
+ }
+
+ Component {
+ id: validComponent
+
+ Rectangle {
+ Fact { id: rc_map_throttle; name: "RC_MAP_THROTTLE" }
+ Fact { id: rc_map_yaw; name: "RC_MAP_YAW" }
+ Fact { id: rc_map_pitch; name: "RC_MAP_PITCH" }
+ Fact { id: rc_map_roll; name: "RC_MAP_ROLL" }
+ Fact { id: rc_map_flaps; name: "RC_MAP_FLAPS" }
+ Fact { id: rc_map_aux1; name: "RC_MAP_AUX1" }
+ Fact { id: rc_map_aux2; name: "RC_MAP_AUX2" }
+
+ Fact { id: rc_map_mode_sw; name: "RC_MAP_MODE_SW" }
+ Fact { id: rc_map_posctl_sw; name: "RC_MAP_POSCTL_SW" }
+ Fact { id: rc_map_return_sw; name: "RC_MAP_RETURN_SW" }
+ Fact { id: rc_map_loiter_sw; name: "RC_MAP_LOITER_SW" }
+
+ Fact { id: rc_assist_th; name: "RC_ASSIST_TH" }
+ Fact { id: rc_posctl_th; name: "RC_POSCTL_TH" }
+ Fact { id: rc_auto_th; name: "RC_AUTO_TH" }
+ Fact { id: rc_loiter_th; name: "RC_LOITER_TH" }
+ Fact { id: rc_return_th; name: "RC_RETURN_TH" }
+
+ Fact { id: rc_chan_cnt; name: "RC_CHAN_CNT" }
+ Fact { id: rc_th_user; name: "RC_TH_USER" }
+
+ property int throttleChannel: rc_map_throttle.value
+ property int yawChannel: rc_map_yaw.value
+ property int pitchChannel: rc_map_pitch.value
+ property int rollChannel: rc_map_roll.value
+ property int flapsChannel: rc_map_flaps.value
+ property int aux1Channel: rc_map_aux1.value
+ property int aux2Channel: rc_map_aux2.value
+
+ property int modeChannel: rc_map_mode_sw.value
+ property int posCtlChannel: rc_map_posctl_sw.value
+ property int returnChannel: rc_map_return_sw.value
+ property int loiterChannel: rc_map_loiter_sw.value
+
+ property real rcThUserValue: rc_th_user.value
+
+ readonly property int channelCount: controller.channelCount
+
+ property bool inRedistribution: false
+
+ readonly property int tileWidth: 150
+ readonly property int tileHeight: 30
+
+ readonly property int progressBarHeight: 200
+
+ anchors.fill: parent
+
+
+ color: qgcPal.window
+
+ Component {
+ id: dragHandle
+
+ Item {
+ id: outerItem
+ width: parent.width
+ height: parent.height
+
+ Column {
+ x: 4
+ y: 4
+ spacing: 3
+
+ Repeater {
+ model: (outerItem.height - 8) / 4
+ Rectangle {
+ color: qgcPal.text
+ width: 15
+ height: 1
+ }
+ }
+ }
+ }
+ }
+
+ // This component is used to create draggable tiles for unassigned mode switches. It also
+ // creates the drop area for dragging an assigned mode switch tile back to an unassigned state.
+ // The following properties must be set in the Loader:
+ // tileLabel - label for tile
+ // tileParam - parameter to load from
+ // tileDragEnabled - true: this tile can be dragged
+ Component {
+ id: unassignedModeTileComponent
+
+ Rectangle {
+ Fact { id: fact; name: tileParam }
+ property bool dragEnabled: fact.value == 0
+
+ id: outerRect
+ width: tileWidth
+ height: tileHeight
+ color: qgcPal.windowShadeDark
+ border.width: dragEnabled ? 1 : 0
+ border.color: qgcPal.text
+
+ Drag.active: mouseArea.drag.active
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+ Drag.keys: [ "unassigned"]
+
+ states: [
+ State {
+ when: dropArea.containsDrag
+ PropertyChanges {
+ target: outerRect
+ color: "red"
+ }
+ }
+ ]
+
+ Loader {
+ width: parent.width
+ height: parent.height
+ visible: dragEnabled
+ sourceComponent: dragHandle
+ }
+
+ QGCLabel {
+ width: parent.width
+ height: parent.height
+ text: tileLabel
+ enabled: dragEnabled
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: mouseArea
+ width: parent.width
+ height: parent.height
+ drag.target: dragEnabled ? parent : null
+
+ onReleased: {
+ // Move tile back to original position
+ parent.x = 0; parent.y = 0;
+
+ // If dropped over a channel target remap switch
+ if (parent.Drag.target && parent.Drag.target.dropAllowed) {
+ fact.value = parent.Drag.target.channel
+ }
+ }
+ }
+
+ DropArea {
+ // This will cause to tile to go back to unassigned if dropped here
+ readonly property int channel: 0
+ property bool dropAllowed: true
+
+ id: dropArea
+ width: parent.width
+ height: parent.height
+
+ keys: [ "assigned" ]
+ }
+ }
+ }
+
+ // This component is used to create draggable tiles for currently assigned mode switches. The following
+ // properties must be set in the Loader:
+ // tileLabel - label for tile
+ // tileVisible - visibility for tile
+ // tileDragEnabled - true: this tile can be dragged
+ // tileParam - parameter to load from
+ Component {
+ id: assignedModeTileComponent
+
+ Rectangle {
+ Fact { id: fact; name: tileDragEnabled ? tileParam : "" }
+
+ width: tileWidth
+ height: tileHeight
+ color: qgcPal.windowShadeDark
+ border.width: tileDragEnabled ? 1 : 0
+ border.color: qgcPal.text
+ visible: tileVisible
+
+ Drag.active: mouseArea.drag.active
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+ Drag.keys: [ "assigned" ]
+
+ Loader {
+ width: parent.width
+ height: parent.height
+ visible: tileDragEnabled
+ sourceComponent: dragHandle
+ }
+
+ QGCLabel {
+ width: parent.width
+ height: parent.height
+ enabled: tileDragEnabled
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: tileLabel
+ }
+
+ MouseArea {
+ id: mouseArea
+ width: parent.width
+ height: parent.height
+ drag.target: tileDragEnabled ? parent : null
+
+ onReleased: {
+ // Move tile back to original position
+ parent.x = 0; parent.y = 0;
+
+ // If dropped over a channel target remap switch
+ if (parent.Drag.target && parent.Drag.target.dropAllowed) {
+ fact.value = parent.Drag.target.channel
+ }
+ }
+ }
+ }
+ }
+
+ onModeChannelChanged: if (!inRedistribution) redistributeThresholds()
+ onReturnChannelChanged: if (!inRedistribution) redistributeThresholds()
+ onLoiterChannelChanged: if (!inRedistribution) redistributeThresholds()
+ onPosCtlChannelChanged: if (!inRedistribution) redistributeThresholds()
+ onRcThUserValue: if (!inRedistribution) redistributeThresholds()
+
+ function redistributeThresholds() {
+ if (loading || rcThUserValue != 0) {
+ // User is specifying thresholds, do not auto-calculate
+ return
+ }
+
+ if (modeChannel != 0) {
+ var positions = 3 // Manual/Assist/Auto always exist
+
+ var returnOnModeSwitch = modeChannel == returnChannel
+ var loiterOnModeSwitch = modeChannel == loiterChannel
+ var posCtlOnModeSwitch = modeChannel == posCtlChannel
+
+ positions += returnOnModeSwitch ? 1 : 0
+ positions += loiterOnModeSwitch ? 1 : 0
+ positions += posCtlOnModeSwitch ? 1 : 0
+
+ var increment = 1.0 / positions
+ var currentThreshold = 0.0
+
+ // Make sure we don't re-enter
+ inRedistribution = true
+
+ currentThreshold += increment
+ rc_assist_th.value = currentThreshold
+ if (posCtlOnModeSwitch) {
+ currentThreshold += increment
+ rc_posctl_th.value = currentThreshold
+ }
+ currentThreshold += increment
+ rc_auto_th.value = currentThreshold
+ if (loiterOnModeSwitch) {
+ currentThreshold += increment
+ rc_loiter_th.value = currentThreshold
+ }
+ if (returnOnModeSwitch) {
+ currentThreshold += increment
+ rc_return_th.value = currentThreshold
+ }
+
+ inRedistribution = false
+ }
+
+ if (returnChannel != 0 && returnChannel != modeChannel) {
+ var positions = 2 // On/off always exist
+
+ var loiterOnReturnSwitch = returnChannel == loiterChannel
+
+ positions += loiterOnReturnSwitch ? 1 : 0
+
+ var increment = 1.0 / positions
+ var currentThreshold = 0.0
+
+ if (positions == 2) {
+ // If only two positions don't set threshold at midrange. Setting to 0.25
+ // allows for this channel to work with either two or three position switch
+ increment = 0.25
+ }
+
+ // Make sure we don't re-enter
+ inRedistribution = true
+
+ if (loiterOnReturnSwitch) {
+ currentThreshold += increment
+ rc_loiter_th.value = currentThreshold
+ }
+ currentThreshold += increment
+ rc_return_th.value = currentThreshold
+
+ inRedistribution = false
+ }
+
+ if (loiterChannel != 0 && loiterChannel != modeChannel && loiterChannel != returnChannel) {
+ // If only two positions don't set threshold at midrange. Setting to 0.25
+ // allows for this channel to work with either two or three position switch
+ var increment = 0.25
+
+ var currentThreshold = 0.0
+
+ // Make sure we don't re-enter
+ inRedistribution = true
+
+ currentThreshold += increment
+ rc_loiter_th.value = currentThreshold
+
+ inRedistribution = false
+ }
+
+ if (posCtlChannel != 0 & posCtlChannel != modeChannel) {
+ // If only two positions don't set threshold at midrange. Setting to 0.25
+ // allows for this channel to work with either two or three position switch
+ var increment = 0.25
+
+ var currentThreshold = 0.0
+
+ // Make sure we don't re-enter
+ inRedistribution = true
+
+ currentThreshold += increment
+ rc_posctl_th.value = currentThreshold
+
+ inRedistribution = false
+ }
+
+ }
+
+ Column {
+ anchors.fill: parent
+
+ QGCLabel {
+ text: "FLIGHT MODES CONFIG"
+ font.pointSize: 20
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ QGCLabel {
+ width: parent.width
+ text: "Flight Mode switches can be assigned to any channel which is not currently being used for attitude control. All channels are displayed below. " +
+ "You can drag Flight Modes from the Flight Modes section below to a channel and drop it there. You can also drag switches assigned to a channel " +
+ "to another channel or back to the Unassigned Switches section. The Switch Display section at the very bottom will show you the results of your Flight Mode setup."
+ wrapMode: Text.WordWrap
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ QGCLabel {
+ text: "Channel Assignments"
+ }
+ Flow {
+ width: parent.width
+ spacing: 5
+
+ Repeater {
+ model: channelCount
+
+ Rectangle {
+ property int channel: modelData + 1
+ property bool throttleMapped: channel == throttleChannel
+ property bool yawMapped: channel == yawChannel
+ property bool pitchMapped: channel == pitchChannel
+ property bool rollMapped: channel == rollChannel
+ property bool flapsMapped: channel == flapsChannel
+ property bool aux1Mapped: channel == aux1Channel
+ property bool aux2Mapped: channel == aux2Channel
+
+ property bool modeMapped: channel == modeChannel
+ property bool posCtlMapped: channel == posCtlChannel
+ property bool returnMapped: channel == returnChannel
+ property bool loiterMapped: channel == loiterChannel
+
+ property bool nonFlightModeMapping: throttleMapped | yawMapped | pitchMapped | rollMapped | flapsMapped | aux1Mapped | aux2Mapped
+ property bool unassignedMapping: !(nonFlightModeMapping | modeMapped | posCtlMapped | returnMapped | loiterMapped)
+
+ id: channelTarget
+ width: tileWidth
+ height: channelCol.implicitHeight
+
+ color: qgcPal.windowShadeDark
+
+ states: [
+ State {
+ when: dropArea.containsDrag && dropArea.dropAllowed
+ PropertyChanges {
+ target: channelHeader
+ color: "red"
+ }
+ }
+ ]
+
+ Column {
+ id: channelCol
+ spacing: 3
+
+ Rectangle {
+ id: channelHeader
+ width: tileWidth
+ height: tileHeight
+ color: qgcPal.windowShade
+
+ QGCLabel {
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: "Channel " + (modelData + 1) + (nonFlightModeMapping ? ": Unavailable" : "")
+ }
+ }
+ Loader {
+ property string tileLabel: "Available"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: unassignedMapping
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Throttle"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: throttleMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Yaw"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: yawMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Pitch"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: pitchMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Roll"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: rollMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Flaps Switch"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: flapsMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Aux1 Switch"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: aux1Mapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Aux2 Switch"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: false
+
+ visible: aux2Mapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Main Mode"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: true
+ property string tileParam: "RC_MAP_MODE_SW"
+
+ visible: modeMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Return"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: true
+ property string tileParam: "RC_MAP_RETURN_SW"
+
+ visible: returnMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Loiter"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: true
+ property string tileParam: "RC_MAP_LOITER_SW"
+
+ visible: loiterMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "PosCtl"
+ property bool tileVisible: visible
+ property bool tileDragEnabled: true
+ property string tileParam: "RC_MAP_POSCTL_SW"
+
+ visible: posCtlMapped
+ sourceComponent: assignedModeTileComponent
+ }
+ }
+
+ DropArea {
+ // Drops are not allowed on channels which are mapped to non-flight mode switches
+ property bool dropAllowed: !nonFlightModeMapping
+ property int channel: parent.channel
+
+ id: dropArea
+ width: parent.width
+ height: parent.height
+
+ keys: [ "unassigned", "assigned" ]
+ }
+ }
+ }
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ Row {
+ spacing: 5
+
+ QGCLabel {
+ text: "Flight Modes"
+ }
+ QGCLabel {
+ text: "(Mode Switch must be assigned to channel before flight is allowed)"
+ visible: rc_map_mode_sw.value == 0
+ }
+ }
+ Flow {
+ width: parent.width
+ spacing: 5
+
+ Loader {
+ property string tileLabel: "Main Mode"
+ property string tileParam: "RC_MAP_MODE_SW"
+ sourceComponent: unassignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Return"
+ property string tileParam: "RC_MAP_RETURN_SW"
+ sourceComponent: unassignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "Loiter"
+ property string tileParam: "RC_MAP_LOITER_SW"
+ sourceComponent: unassignedModeTileComponent
+ }
+ Loader {
+ property string tileLabel: "PosCtl"
+ property string tileParam: "RC_MAP_POSCTL_SW"
+ sourceComponent: unassignedModeTileComponent
+ }
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ FactCheckBox {
+ checkedValue: 0
+ uncheckedValue: 1
+ fact: rc_th_user
+ text: "Allow setup to generate the thresholds for the flight mode positions within a switch (recommended)"
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ Row {
+ spacing: 20
+
+ QGCLabel {
+ text: "Switch Display"
+ }
+ QGCCheckBox {
+ checked: controller.sendLiveRCSwitchRanges
+ text: "Show live RC display"
+ onClicked: {
+ controller.sendLiveRCSwitchRanges = checked
+ }
+ }
+ }
+
+ Item { height: 20; width: 10 } // spacer
+
+ Row {
+ property bool modeSwitchVisible: modeChannel != 0
+ property bool returnSwitchVisible: returnChannel != 0 && returnChannel != modeChannel
+ property bool loiterSwitchVisible: loiterChannel != 0 && loiterChannel != modeChannel && loiterChannel != returnChannel
+ property bool posCtlSwitchVisible: posCtlChannel != 0 && posCtlChannel != modeChannel
+
+ width: parent.width
+ spacing: 20
+
+ Column {
+ visible: parent.modeSwitchVisible
+
+ QGCLabel { text: "Mode Switch" }
+
+ Row {
+ Item {
+ height: progressBarHeight
+ width: 150
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2)
+ visible: modeChannel != returnChannel && modeChannel != loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_return_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == returnChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Return"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Loiter"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Mission"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == returnChannel && modeChannel != loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Loiter/Mission"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_assist_th.value)) - (implicitHeight / 2)
+ visible: modeChannel != posCtlChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Assist"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_posctl_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == posCtlChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Assist: PosCtl"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_assist_th.value)) - (implicitHeight / 2)
+ visible: modeChannel == posCtlChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Assist: AltCtl"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: parent.height - (implicitHeight / 2)
+ text: "Manual"
+ horizontalAlignment: Text.AlignRight
+ }
+ }
+
+ ProgressBar {
+ height: progressBarHeight
+ orientation: Qt.Vertical
+ value: controller.modeSwitchLiveRange
+ }
+ }
+ }
+
+ Column {
+ visible: parent.returnSwitchVisible
+
+ QGCLabel { text: "Return Switch" }
+
+ Row {
+ Item {
+ height: progressBarHeight
+ width: 150
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_return_th.value)) - (implicitHeight / 2)
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Return"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2)
+ visible: returnChannel == loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Loiter"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: parent.height - (implicitHeight / 2)
+ visible: returnChannel == loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Mission"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: parent.height - (implicitHeight / 2)
+ visible: returnChannel != loiterChannel
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Return Off"
+ }
+ }
+
+ ProgressBar {
+ height: progressBarHeight
+ orientation: Qt.Vertical
+ value: controller.returnSwitchLiveRange
+ }
+ }
+ }
+
+ Column {
+ visible: parent.loiterSwitchVisible
+
+ QGCLabel { text: "Loiter Switch" }
+
+ Row {
+ Item {
+ height: progressBarHeight
+ width: 150
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2)
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Loiter"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: parent.height - (implicitHeight / 2)
+ horizontalAlignment: Text.AlignRight
+ text: "Auto: Mission"
+ }
+ }
+
+ ProgressBar {
+ height: progressBarHeight
+ orientation: Qt.Vertical
+ value: controller.loiterSwitchLiveRange
+ }
+ }
+ }
+
+ Column {
+ visible: parent.posCtlSwitchVisible
+
+ QGCLabel { text: "PosCtl Switch" }
+
+ Row {
+ Item {
+ height: progressBarHeight
+ width: 150
+
+ QGCLabel {
+ width: parent.width
+ y: (parent.height * (1.0 - rc_posctl_th.value)) - (implicitHeight / 2)
+ horizontalAlignment: Text.AlignRight
+ text: "Assist: PosCtl"
+ }
+
+ QGCLabel {
+ width: parent.width
+ y: parent.height - (implicitHeight / 2)
+ horizontalAlignment: Text.AlignRight
+ text: "Assist: AltCtl"
+ }
+ }
+
+ ProgressBar {
+ height: progressBarHeight
+ orientation: Qt.Vertical
+ value: controller.posCtlSwitchLiveRange
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: invalidComponent
+
+ Rectangle {
+ anchors.fill: parent
+ color: qgcPal.window
+
+ Column {
+ width: parent.width
+ spacing: 20
+
+ QGCLabel {
+ text: "FLIGHT MODES CONFIG"
+ font.pointSize: 20
+ }
+
+ QGCLabel {
+ width: parent.width
+ wrapMode: Text.WordWrap
+ text: "There are errors in your current configuration which will need to be fixed before you can used Flight Config setup. " +
+ "You will need to change Parameters directly using Parameters Setup to remove these errors."
+ }
+
+ QGCLabel {
+ width: parent.width
+ wrapMode: Text.WordWrap
+ text: controller.configurationErrors
+ }
+ }
+ }
+ }
+}
diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ef5c346f8f176395f11d54fa4b22fb986ac8a0ae
--- /dev/null
+++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc
@@ -0,0 +1,196 @@
+/*=====================================================================
+
+ QGroundControl Open Source Ground Control Station
+
+ (c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+ This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+ ======================================================================*/
+
+/// @file
+/// @author Don Gagne
+
+#include "FlightModesComponentController.h"
+#include "QGCMAVLink.h"
+#include "UASManager.h"
+#include "AutoPilotPluginManager.h"
+
+#include
+#include
+
+FlightModesComponentController::FlightModesComponentController(QObject* parent) :
+ QObject(parent),
+ _liveRCValues(false),
+ _validConfiguration(false),
+ _channelCount(18),
+ _autoPilotPlugin(NULL)
+{
+ _uas = UASManager::instance()->getActiveUAS();
+ Q_ASSERT(_uas);
+
+ _autoPilotPlugin = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(_uas);
+ Q_ASSERT(_autoPilotPlugin);
+ Q_ASSERT(_autoPilotPlugin->pluginIsReady());
+
+ _initRcValues();
+ _validateConfiguration();
+}
+
+FlightModesComponentController::~FlightModesComponentController()
+{
+ setSendLiveRCSwitchRanges(false);
+}
+
+
+void FlightModesComponentController::_initRcValues(void)
+{
+ for (int i=0; i<_chanMax; i++) {
+ _rcValues << 1.0;
+ }
+}
+
+/// This will look for parameter settings which would cause the config to not run correctly.
+/// It will set _validConfiguration and _configurationErrors as needed.
+void FlightModesComponentController::_validateConfiguration(void)
+{
+ _validConfiguration = true;
+
+ _channelCount = _autoPilotPlugin->factExists("RC_CHAN_CNT") ? _autoPilotPlugin->getFact("RC_CHAN_CNT")->value().toInt() : 18;
+
+ // Make sure switches are valid and within channel range
+
+ QStringList switchParams, switchNames;
+ QList switchMappings;
+
+ switchParams << "RC_MAP_MODE_SW" << "RC_MAP_RETURN_SW" << "RC_MAP_LOITER_SW" << "RC_MAP_POSCTL_SW";
+ switchNames << "Mode Switch" << "Return Switch" << "Loiter Switch" << "PosCtl Switch";
+
+ for(int i=0; igetFact(switchParams[i])->value().toInt();
+ switchMappings << map;
+
+ if (map < 0 || map > _channelCount) {
+ _validConfiguration = false;
+ _configurationErrors += QString("%1 is set to %2. Mapping must between 0 and %3 (inclusive).\n").arg(switchNames[i]).arg(map).arg(_channelCount);
+ }
+ }
+
+ // Make sure switches are not mapped to attitude control channels
+
+ QStringList attitudeParams, attitudeNames;
+
+ attitudeParams << "RC_MAP_THROTTLE" << "RC_MAP_YAW" << "RC_MAP_PITCH" << "RC_MAP_ROLL" << "RC_MAP_FLAPS" << "RC_MAP_AUX1" << "RC_MAP_AUX2";
+ attitudeNames << "Throttle" << "Yaw" << "Pitch" << "Roll" << "Flaps" << "Aux1" << "Aux2";
+
+ for (int i=0; igetFact(attitudeParams[i])->value().toInt();
+
+ for (int j=0; jgetFact(rcMinParam)->value().toInt();
+ _rgRCMax[i] = _autoPilotPlugin->getFact(rcMaxParam)->value().toInt();
+
+ float floatReversed = _autoPilotPlugin->getFact(rcRevParam)->value().toFloat();
+ _rgRCReversed[i] = floatReversed == -1.0f;
+ }
+
+ _uas->startRadioControlCalibration();
+ connect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged);
+ } else {
+ disconnect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged);
+ _uas->endRadioControlCalibration();
+ _initRcValues();
+ emit switchLiveRangeChanged();
+ }
+}
+
+/// @brief This routine is called whenever a raw value for an RC channel changes.
+/// @param chan RC channel on which signal is coming from (0-based)
+/// @param fval Current value for channel
+void FlightModesComponentController::_remoteControlChannelRawChanged(int chan, float fval)
+{
+ Q_ASSERT(chan >= 0 && chan <= _chanMax);
+
+ if (fval < _rgRCMin[chan]) {
+ fval= _rgRCMin[chan];
+ }
+ if (fval > _rgRCMax[chan]) {
+ fval= _rgRCMax[chan];
+ }
+
+ float percentRange = (fval - _rgRCMin[chan]) / (float)(_rgRCMax[chan] - _rgRCMin[chan]);
+ if (_rgRCReversed[chan]) {
+ percentRange = 1.0 - percentRange;
+ }
+
+ _rcValues[chan] = percentRange;
+ emit switchLiveRangeChanged();
+}
+
+double FlightModesComponentController::_switchLiveRange(const QString& param)
+{
+ QVariant value;
+
+ int channel = _autoPilotPlugin->getFact(param)->value().toInt();
+ if (channel == 0) {
+ return 1.0;
+ } else {
+ return _rcValues[channel - 1];
+ }
+}
+
+double FlightModesComponentController::modeSwitchLiveRange(void)
+{
+ return _switchLiveRange("RC_MAP_MODE_SW");
+}
+
+double FlightModesComponentController::returnSwitchLiveRange(void)
+{
+ return _switchLiveRange("RC_MAP_RETURN_SW");
+}
+
+double FlightModesComponentController::loiterSwitchLiveRange(void)
+{
+ return _switchLiveRange("RC_MAP_LOITER_SW");
+}
+
+double FlightModesComponentController::posCtlSwitchLiveRange(void)
+{
+ return _switchLiveRange("RC_MAP_POSCTL_SW");
+}
+
diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.h b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h
new file mode 100644
index 0000000000000000000000000000000000000000..ccc27715fa6d182e04b11894a6fe0c783d4ea138
--- /dev/null
+++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h
@@ -0,0 +1,95 @@
+/*=====================================================================
+
+ QGroundControl Open Source Ground Control Station
+
+ (c) 2009, 2015 QGROUNDCONTROL PROJECT
+
+ This file is part of the QGROUNDCONTROL project
+
+ QGROUNDCONTROL is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QGROUNDCONTROL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QGROUNDCONTROL. If not, see .
+
+ ======================================================================*/
+
+/// @file
+/// @author Don Gagne
+
+#ifndef FLIGHTMODESCOMPONENTCONTROLLER_H
+#define FLIGHTMODESCOMPONENTCONTROLLER_H
+
+#include
+#include
+#include
+
+#include "UASInterface.h"
+#include "AutoPilotPlugin.h"
+
+/// MVC Controller for FlightModesComponent.qml.
+class FlightModesComponentController : public QObject
+{
+ Q_OBJECT
+
+public:
+ FlightModesComponentController(QObject* parent = NULL);
+ ~FlightModesComponentController();
+
+ Q_PROPERTY(bool validConfiguration MEMBER _validConfiguration CONSTANT)
+ Q_PROPERTY(QString configurationErrors MEMBER _configurationErrors CONSTANT)
+
+ Q_PROPERTY(int channelCount MEMBER _channelCount CONSTANT)
+
+ Q_PROPERTY(double modeSwitchLiveRange READ modeSwitchLiveRange NOTIFY switchLiveRangeChanged)
+ Q_PROPERTY(double returnSwitchLiveRange READ returnSwitchLiveRange NOTIFY switchLiveRangeChanged)
+ Q_PROPERTY(double loiterSwitchLiveRange READ loiterSwitchLiveRange NOTIFY switchLiveRangeChanged)
+ Q_PROPERTY(double posCtlSwitchLiveRange READ posCtlSwitchLiveRange NOTIFY switchLiveRangeChanged)
+
+ Q_PROPERTY(bool sendLiveRCSwitchRanges READ sendLiveRCSwitchRanges WRITE setSendLiveRCSwitchRanges NOTIFY liveRCSwitchRangesChanged)
+
+ double modeSwitchLiveRange(void);
+ double returnSwitchLiveRange(void);
+ double loiterSwitchLiveRange(void);
+ double posCtlSwitchLiveRange(void);
+
+ bool sendLiveRCSwitchRanges(void) { return _liveRCValues; }
+ void setSendLiveRCSwitchRanges(bool start);
+
+signals:
+ void switchLiveRangeChanged(void);
+ void liveRCSwitchRangesChanged(void);
+
+private slots:
+ void _remoteControlChannelRawChanged(int chan, float fval);
+
+private:
+ double _switchLiveRange(const QString& param);
+ void _initRcValues(void);
+ void _validateConfiguration(void);
+
+ static const int _chanMax = 18;
+
+ UASInterface* _uas;
+
+ QList _rcValues;
+ bool _liveRCValues;
+ int _rgRCMin[_chanMax];
+ int _rgRCMax[_chanMax];
+ bool _rgRCReversed[_chanMax];
+
+ bool _validConfiguration;
+ QString _configurationErrors;
+ int _channelCount;
+
+ AutoPilotPlugin* _autoPilotPlugin;
+};
+
+#endif
diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc
index c5aded6dea2a19a791bbd5c3913621acb31cc65b..e9e553efe25e97edaf7450477ee8923dc85786a7 100644
--- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc
+++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc
@@ -26,7 +26,7 @@
#include "UASManager.h"
#include "QGCUASParamManagerInterface.h"
#include "PX4ParameterFacts.h"
-#include "SensorsComponentController.h"
+#include "FlightModesComponentController.h"
#include "QGCMessageBox.h"
/// @file
@@ -74,6 +74,8 @@ PX4AutoPilotPlugin::PX4AutoPilotPlugin(UASInterface* uas, QObject* parent) :
{
Q_ASSERT(uas);
+ qmlRegisterType("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
+
_parameterFacts = new PX4ParameterFacts(uas, this);
Q_CHECK_PTR(_parameterFacts);
diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h
index 7d1f6943be286693564269e86ae3d8e5536a8516..2398a98b5e619e984cde7ade23101c243a301792 100644
--- a/src/FactSystem/Fact.h
+++ b/src/FactSystem/Fact.h
@@ -108,7 +108,7 @@ signals:
/// Signalled when property has been changed by a call to the property write accessor
///
/// This signal is meant for use by Fact container implementations.
- void _containerValueChanged(QVariant& value);
+ void _containerValueChanged(const QVariant& value);
private:
QString _name;
diff --git a/src/FactSystem/FactLoader.cc b/src/FactSystem/FactLoader.cc
index 903fb44a903be29ce452c65cf738c508a902a55f..394d7fb374ace5ddedc0af62e897e2e1e3e6c942 100644
--- a/src/FactSystem/FactLoader.cc
+++ b/src/FactSystem/FactLoader.cc
@@ -57,11 +57,7 @@ FactLoader::FactLoader(UASInterface* uas, QObject* parent) :
FactLoader::~FactLoader()
{
- foreach(Fact* fact, _mapFact2ParameterName.keys()) {
- delete fact;
- }
- _mapParameterName2Variant.clear();
- _mapFact2ParameterName.clear();
+
}
/// Called whenever a parameter is updated or first seen.
@@ -141,7 +137,7 @@ void FactLoader::_parameterUpdate(int uas, int component, QString parameterName,
/// Connected to Fact::valueUpdated
///
/// Sets the new value into the Parameter Manager. Parameter is persisted after send.
-void FactLoader::_valueUpdated(QVariant value)
+void FactLoader::_valueUpdated(const QVariant& value)
{
Fact* fact = qobject_cast(sender());
Q_ASSERT(fact);
diff --git a/src/FactSystem/FactLoader.h b/src/FactSystem/FactLoader.h
index 98a64634d0838ee144e82c4fc4b4f3c3277e35c4..a61b147543b943d2841ff0f63fcf026265ee8402 100644
--- a/src/FactSystem/FactLoader.h
+++ b/src/FactSystem/FactLoader.h
@@ -65,7 +65,7 @@ protected:
private slots:
void _parameterUpdate(int uas, int component, QString parameterName, int mavType, QVariant value);
- void _valueUpdated(QVariant value);
+ void _valueUpdated(const QVariant& value);
void _paramMgrParameterListUpToDate(void);
private:
diff --git a/src/qgcunittest/MockLink.param b/src/qgcunittest/MockLink.param
index 349d7d42c491c4ae16da0b674e6908fd411f1cc7..6fc11ee57c64ac2cfb6f9d07d926651c0a618bc1 100644
--- a/src/qgcunittest/MockLink.param
+++ b/src/qgcunittest/MockLink.param
@@ -389,22 +389,22 @@
1 50 RC1_MAX 2000 9
1 50 RC1_MIN 1000 9
1 50 RC1_REV 1 9
-1 50 RC1_TRIM 1500 9
+1 50 RC1_TRIM 1501 9
1 50 RC2_DZ 10 9
1 50 RC2_MAX 2000 9
1 50 RC2_MIN 1000 9
1 50 RC2_REV 1 9
-1 50 RC2_TRIM 1500 9
+1 50 RC2_TRIM 1501 9
1 50 RC3_DZ 10 9
1 50 RC3_MAX 2000 9
1 50 RC3_MIN 1000 9
1 50 RC3_REV 1 9
-1 50 RC3_TRIM 1500 9
+1 50 RC3_TRIM 1501 9
1 50 RC4_DZ 10 9
1 50 RC4_MAX 2000 9
1 50 RC4_MIN 1000 9
1 50 RC4_REV 1 9
-1 50 RC4_TRIM 1500 9
+1 50 RC4_TRIM 1501 9
1 50 RC5_DZ 10 9
1 50 RC5_MAX 2000 9
1 50 RC5_MIN 1000 9