diff --git a/ChangeLog.md b/ChangeLog.md index 05bb58114c511a3cb5200fdae46659c99367cc83..ab2df547ca036dc7ed117191610fdcf8145ccbd6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,6 +8,7 @@ Note: This file only contains high level features or important fixes. * ArduPilot: Copter - Add suppor for Simple and Super Simple flight modes * ArduPilot: Flight Mode setup - Switch Options were not showing up for all firmware revs +* ArduCopter: Add PID Tuning page to Tuning Setup * ArduPilot: Copter - Advanced Tuning support * ArduPilot: Rover - Frame setup support * ArduPilot: Copter - Update support to 3.5+ @@ -21,12 +22,21 @@ Note: This file only contains high level features or important fixes. * ArduPilot: Support configurable mavlink stream rates. Available from Settings/Mavlink page. * Major rewrite and bug fix pass through Structure Scan. Previous version had such bad problems that it can no longer be supported. Plans with Structure Scan will need to be recreated. New QGC will not load old Structure Scan plans. -### 3.5.1 - Not yet released +### 3.5.3 - Not yet released +* Fix crash when clicking on GeoFence polygon vertex +* PX4: Fix missing ```MC_YAW_FF``` parameter in PID Tuning +* ArduPilot: Fix parameter file save generating bad characters from git hash + +### 3.5.2 - Stable +* Fix Ubuntu AppImage startup failure + +### 3.5.1 +* Update Windows usb drivers * Add ArduPilot CubeBlack Service Bulletin check * Fix visibility of PX4/ArduPilot logo in toolbar * Fix tile set count but in OfflineMaps which would cause image and elevation tile set to have incorrect counts and be incorrectly marked as download incomplete. -### 3.5.0 - Stable +### 3.5.0 * Plan GeoFence: Fix loading of fence from intermediate 3.4 code * Structure Scan: Fix loading of structure scan height * ArduPilot: Fix location of planned home position when not connected to vehicle. Issue #6840. diff --git a/QGCSetup.pri b/QGCSetup.pri index 84cc7f1f895d0f5dd1aa573ab5615e1b4ce307cd..0150338bc2958ddca8dc8804ba9bb028b3c0c7c7 100644 --- a/QGCSetup.pri +++ b/QGCSetup.pri @@ -138,8 +138,9 @@ LinuxBuild { !contains(DEFINES, __rasp_pi2__) { # Some Qt distributions link with *.so.56 QT_LIB_LIST += \ - libicudata.so* \ - libicuuc.so* + libicudata.so.56 \ + libicui18n.so.56 \ + libicuuc.so.56 } for(QT_LIB, QT_LIB_LIST) { diff --git a/deploy/px4driver.msi b/deploy/px4driver.msi index 4e2b0866d45b6735affffce5955a2b5775c22589..8bd3e5034b8219e7ab06137ab18319e425ce5477 100644 Binary files a/deploy/px4driver.msi and b/deploy/px4driver.msi differ diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index ee194b386220779065408295c7c6d86fe7fb611f..318dfed265c3925408eb446b122467cbb060fa10 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -201,7 +201,7 @@ void APMAutoPilotPlugin::_checkForBadCubeBlack(void) if (paramMgr->parameterExists(-1, paramAcc3) && paramMgr->getParameter(-1, paramAcc3)->rawValue().toInt() == 0 && paramMgr->parameterExists(-1, paramGyr3) && paramMgr->getParameter(-1, paramGyr3)->rawValue().toInt() == 0 && paramMgr->parameterExists(-1, paramEnableMask) && paramMgr->getParameter(-1, paramEnableMask)->rawValue().toInt() >= 7) { - qgcApp()->showMessage(tr("WARNING: The flight board you are using has a critical service bulletin against ti which advise against flying. https://discuss.cubepilot.org/t/sb-0000002-critical-service-bulletin-for-cubes-purchased-between-january-2019-to-present-do-not-fly/406")); + qgcApp()->showMessage(tr("WARNING: The flight board you are using has a critical service bulletin against it which advises against flying. For details see: https://discuss.cubepilot.org/t/sb-0000002-critical-service-bulletin-for-cubes-purchased-between-january-2019-to-present-do-not-fly/406")); } } diff --git a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml index 77395d00e8a887db7ce24aecdd589ed285d41fa1..9efd5efdf5dac5a83058a0f0e9be49e98b693c52 100644 --- a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml +++ b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml @@ -26,16 +26,13 @@ SetupPage { id: tuningPageComponent Column { - width: availableWidth - spacing: _margins + width: availableWidth FactPanelController { id: controller; factPanel: tuningPage.viewPanel } QGCPalette { id: palette; colorGroupEnabled: true } - property bool _rcFeelAvailable: controller.parameterExists(-1, "RC_FEEL") property bool _atcInputTCAvailable: controller.parameterExists(-1, "ATC_INPUT_TC") - property Fact _rcFeel: controller.getParameterFact(-1, "RC_FEEL", false) property Fact _atcInputTC: controller.getParameterFact(-1, "ATC_INPUT_TC", false) property Fact _rateRollP: controller.getParameterFact(-1, "ATC_RAT_RLL_P") property Fact _rateRollI: controller.getParameterFact(-1, "ATC_RAT_RLL_I") @@ -69,6 +66,7 @@ SetupPage { ExclusiveGroup { id: returnAltRadioGroup } Component.onCompleted: { + showAdvanced = !ScreenTools.isMobile // Qml Sliders have a strange behavior in which they first set Slider::value to some internal // setting and then set Slider::value to the bound properties value. If you have an onValueChanged // handler which updates your property with the new value, this first value change will trash @@ -76,9 +74,6 @@ SetupPage { // after Qml load is done. We also don't track value changes until Qml load completes. rollPitch.value = _rateRollP.value climb.value = _rateClimbP.value - if (_rcFeelAvailable) { - rcFeel.value = _rcFeel.value - } if (_atcInputTCAvailable) { atcInputTC.value = _atcInputTC.value } @@ -124,382 +119,365 @@ SetupPage { Connections { target: _ch11Opt; onValueChanged: calcAutoTuneChannel() } Connections { target: _ch12Opt; onValueChanged: calcAutoTuneChannel() } - QGCLabel { - text: qsTr("Basic Tuning") - font.family: ScreenTools.demiboldFontFamily - } - - Rectangle { - id: basicTuningRect + Column { anchors.left: parent.left anchors.right: parent.right - height: basicTuningColumn.y + basicTuningColumn.height + _margins - color: palette.windowShade + spacing: _margins + visible: !advanced + + QGCLabel { + text: qsTr("Basic Tuning") + font.family: ScreenTools.demiboldFontFamily + } - Column { - id: basicTuningColumn - anchors.margins: _margins + Rectangle { + id: basicTuningRect anchors.left: parent.left anchors.right: parent.right - anchors.top: parent.top - spacing: _margins + height: basicTuningColumn.y + basicTuningColumn.height + _margins + color: palette.windowShade Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Roll/Pitch Sensitivity") - font.family: ScreenTools.demiboldFontFamily - } + id: basicTuningColumn + anchors.margins: _margins + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: _margins - QGCLabel { - text: qsTr("Slide to the right if the copter is sluggish or slide to the left if the copter is twitchy") - } - - Slider { - id: rollPitch + Column { anchors.left: parent.left anchors.right: parent.right - minimumValue: 0.08 - maximumValue: 0.4 - stepSize: 0.01 - tickmarksEnabled: true - - onValueChanged: { - if (_loadComplete) { - _rateRollP.value = value - _rateRollI.value = value - _ratePitchP.value = value - _ratePitchI.value = value - } - } - } - } - Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Climb Sensitivity") - font.family: ScreenTools.demiboldFontFamily - } + QGCLabel { + text: qsTr("Roll/Pitch Sensitivity") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { - text: qsTr("Slide to the right to climb more aggressively or slide to the left to climb more gently") - } + QGCLabel { + text: qsTr("Slide to the right if the copter is sluggish or slide to the left if the copter is twitchy") + } - Slider { - id: climb - anchors.left: parent.left - anchors.right: parent.right - minimumValue: 0.3 - maximumValue: 1.0 - stepSize: 0.02 - tickmarksEnabled: true - value: _rateClimbP.value - - onValueChanged: { - if (_loadComplete) { - _rateClimbP.value = value - _rateClimbI.value = value * 2 + Slider { + id: rollPitch + anchors.left: parent.left + anchors.right: parent.right + minimumValue: 0.08 + maximumValue: 0.4 + stepSize: 0.01 + tickmarksEnabled: true + + onValueChanged: { + if (_loadComplete) { + _rateRollP.value = value + _rateRollI.value = value + _ratePitchP.value = value + _ratePitchI.value = value + } } } } - } - Column { - anchors.left: parent.left - anchors.right: parent.right - visible: _rcFeelAvailable + Column { + anchors.left: parent.left + anchors.right: parent.right - QGCLabel { - text: qsTr("RC Roll/Pitch Feel") - font.family: ScreenTools.demiboldFontFamily - } + QGCLabel { + text: qsTr("Climb Sensitivity") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { - text: qsTr("Slide to the left for soft control, slide to the right for crisp control") - } + QGCLabel { + text: qsTr("Slide to the right to climb more aggressively or slide to the left to climb more gently") + } - Slider { - id: rcFeel - anchors.left: parent.left - anchors.right: parent.right - minimumValue: 0 - maximumValue: 100 - stepSize: 5.0 - tickmarksEnabled: true - - onValueChanged: { - if (_loadComplete) { - _rcFeel.value = value + Slider { + id: climb + anchors.left: parent.left + anchors.right: parent.right + minimumValue: 0.3 + maximumValue: 1.0 + stepSize: 0.02 + tickmarksEnabled: true + value: _rateClimbP.value + + onValueChanged: { + if (_loadComplete) { + _rateClimbP.value = value + _rateClimbI.value = value * 2 + } } } } - } - Column { - anchors.left: parent.left - anchors.right: parent.right - visible: _atcInputTCAvailable + Column { + anchors.left: parent.left + anchors.right: parent.right + visible: _atcInputTCAvailable - QGCLabel { - text: qsTr("RC Roll/Pitch Feel") - font.family: ScreenTools.demiboldFontFamily - } + QGCLabel { + text: qsTr("RC Roll/Pitch Feel") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { - text: qsTr("Slide to the left for soft control, slide to the right for crisp control") - } + QGCLabel { + text: qsTr("Slide to the left for soft control, slide to the right for crisp control") + } - Slider { - id: atcInputTC - anchors.left: parent.left - anchors.right: parent.right - minimumValue: _atcInputTC.min - maximumValue: _atcInputTC.max - stepSize: _atcInputTC.increment - tickmarksEnabled: true - - onValueChanged: { - if (_loadComplete) { - _atcInputTC.value = value + Slider { + id: atcInputTC + anchors.left: parent.left + anchors.right: parent.right + minimumValue: _atcInputTC.min + maximumValue: _atcInputTC.max + stepSize: _atcInputTC.increment + tickmarksEnabled: true + + onValueChanged: { + if (_loadComplete) { + _atcInputTC.value = value + } } } } - } - } - } // Rectangle - Basic tuning - - QGCLabel { - text: qsTr("Advanced Tuning") - font.family: ScreenTools.demiboldFontFamily - } - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: advColumnLayout.y + advColumnLayout.height + _margins - color: palette.windowShade - - ColumnLayout { - id: advColumnLayout - anchors.margins: _margins - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: _margins - - Column { - Layout.fillWidth: true + Column { + anchors.left: parent.left + anchors.right: parent.right - QGCLabel { - text: qsTr("Spin While Armed") - font.family: ScreenTools.demiboldFontFamily - } + QGCLabel { + text: qsTr("Spin While Armed") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { - text: qsTr("Adjust the amount the motors spin to indicate armed") - } + QGCLabel { + text: qsTr("Adjust the amount the motors spin to indicate armed") + } - Slider { - anchors.left: parent.left - anchors.right: parent.right - minimumValue: 0 - maximumValue: Math.max(0.3, _motSpinArm.rawValue) - stepSize: 0.01 - tickmarksEnabled: true - value: _motSpinArm.rawValue - - onValueChanged: { - if (_loadComplete) { - _motSpinArm.rawValue = value + Slider { + anchors.left: parent.left + anchors.right: parent.right + minimumValue: 0 + maximumValue: Math.max(0.3, _motSpinArm.rawValue) + stepSize: 0.01 + tickmarksEnabled: true + value: _motSpinArm.rawValue + + onValueChanged: { + if (_loadComplete) { + _motSpinArm.rawValue = value + } } } } - } - Column { - id: lastAdvTuningColumn - Layout.fillWidth: true + Column { + anchors.left: parent.left + anchors.right: parent.right - QGCLabel { - text: qsTr("Minimum Thrust") - font.family: ScreenTools.demiboldFontFamily - } + QGCLabel { + text: qsTr("Minimum Thrust") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { - text: qsTr("Adjust the minimum amount of thrust for a stable minimum throttle descent") - } + QGCLabel { + text: qsTr("Adjust the minimum amount of thrust require for the vehicle to move") + } - QGCLabel { - text: qsTr("Warning: This setting should be higher than 'Spin While Armed'") - color: palette.warningText - visible: _motSpinMin.rawValue < _motSpinArm.rawValue - } + QGCLabel { + text: qsTr("Warning: This setting should be higher than 'Spin While Armed'") + color: palette.warningText + visible: _motSpinMin.rawValue < _motSpinArm.rawValue + } - Slider { - anchors.left: parent.left - anchors.right: parent.right - minimumValue: 0 - maximumValue: Math.max(0.3, _motSpinMin.rawValue) - stepSize: 0.01 - tickmarksEnabled: true - value: _motSpinMin.rawValue - - onValueChanged: { - if (_loadComplete) { - _motSpinMin.rawValue = value + Slider { + anchors.left: parent.left + anchors.right: parent.right + minimumValue: 0 + maximumValue: Math.max(0.3, _motSpinMin.rawValue) + stepSize: 0.01 + tickmarksEnabled: true + value: _motSpinMin.rawValue + + onValueChanged: { + if (_loadComplete) { + _motSpinMin.rawValue = value + } } } } } - } - } // Rectangle - Advanced tuning - - Flow { - id: flowLayout - anchors.left: parent.left - anchors.right: parent.right - spacing: _margins + } // Rectangle - Basic tuning - Rectangle { - height: autoTuneLabel.height + autoTuneRect.height - width: autoTuneRect.width - color: palette.window - - QGCLabel { - id: autoTuneLabel - text: qsTr("AutoTune") - font.family: ScreenTools.demiboldFontFamily - } + Flow { + id: flowLayout + Layout.fillWidth: true + spacing: _margins Rectangle { - id: autoTuneRect - width: autoTuneColumn.x + autoTuneColumn.width + _margins - height: autoTuneColumn.y + autoTuneColumn.height + _margins - anchors.top: autoTuneLabel.bottom - color: palette.windowShade + height: autoTuneLabel.height + autoTuneRect.height + width: autoTuneRect.width + color: palette.window - Column { - id: autoTuneColumn - anchors.margins: _margins - anchors.left: parent.left - anchors.top: parent.top - spacing: _margins - - Row { - spacing: _margins + QGCLabel { + id: autoTuneLabel + text: qsTr("AutoTune") + font.family: ScreenTools.demiboldFontFamily + } - QGCLabel { text: qsTr("Axes to AutoTune:") } - FactBitmask { fact: _autoTuneAxes } - } + Rectangle { + id: autoTuneRect + width: autoTuneColumn.x + autoTuneColumn.width + _margins + height: autoTuneColumn.y + autoTuneColumn.height + _margins + anchors.top: autoTuneLabel.bottom + color: palette.windowShade + + Column { + id: autoTuneColumn + anchors.margins: _margins + anchors.left: parent.left + anchors.top: parent.top + spacing: _margins + + Row { + spacing: _margins + + QGCLabel { text: qsTr("Axes to AutoTune:") } + FactBitmask { fact: _autoTuneAxes } + } - Row { - spacing: _margins + Row { + spacing: _margins - QGCLabel { - anchors.baseline: autoTuneChannelCombo.baseline - text: qsTr("Channel for AutoTune switch:") - } + QGCLabel { + anchors.baseline: autoTuneChannelCombo.baseline + text: qsTr("Channel for AutoTune switch:") + } - QGCComboBox { - id: autoTuneChannelCombo - width: ScreenTools.defaultFontPixelWidth * 14 - model: [qsTr("None"), qsTr("Channel 7"), qsTr("Channel 8"), qsTr("Channel 9"), qsTr("Channel 10"), qsTr("Channel 11"), qsTr("Channel 12") ] - currentIndex: _autoTuneSwitchChannelIndex + QGCComboBox { + id: autoTuneChannelCombo + width: ScreenTools.defaultFontPixelWidth * 14 + model: [qsTr("None"), qsTr("Channel 7"), qsTr("Channel 8"), qsTr("Channel 9"), qsTr("Channel 10"), qsTr("Channel 11"), qsTr("Channel 12") ] + currentIndex: _autoTuneSwitchChannelIndex - onActivated: { - var channel = index + onActivated: { + var channel = index - if (channel > 0) { - channel += 6 + if (channel > 0) { + channel += 6 + } + setChannelAutoTuneOption(channel) } - setChannelAutoTuneOption(channel) } } } - } - } // Rectangle - AutoTune - } // Rectangle - AutoTuneWrap - - Rectangle { - height: inFlightTuneLabel.height + channel6TuningOption.height - width: channel6TuningOption.width - color: palette.window - - QGCLabel { - id: inFlightTuneLabel - text: qsTr("In Flight Tuning") - font.family: ScreenTools.demiboldFontFamily - } + } // Rectangle - AutoTune + } // Rectangle - AutoTuneWrap Rectangle { - id: channel6TuningOption - width: channel6TuningOptColumn.width + (_margins * 2) - height: channel6TuningOptColumn.height + ScreenTools.defaultFontPixelHeight - anchors.top: inFlightTuneLabel.bottom - color: qgcPal.windowShade + height: inFlightTuneLabel.height + channel6TuningOption.height + width: channel6TuningOption.width + color: palette.window - Column { - id: channel6TuningOptColumn - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.left: parent.left - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelHeight + QGCLabel { + id: inFlightTuneLabel + text: qsTr("In Flight Tuning") + font.family: ScreenTools.demiboldFontFamily + } - Row { - spacing: ScreenTools.defaultFontPixelWidth - property Fact nullFact: Fact { } + Rectangle { + id: channel6TuningOption + width: channel6TuningOptColumn.width + (_margins * 2) + height: channel6TuningOptColumn.height + ScreenTools.defaultFontPixelHeight + anchors.top: inFlightTuneLabel.bottom + color: qgcPal.windowShade + + Column { + id: channel6TuningOptColumn + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.left: parent.left + anchors.top: parent.top + spacing: ScreenTools.defaultFontPixelHeight + + Row { + spacing: ScreenTools.defaultFontPixelWidth + property Fact nullFact: Fact { } + + QGCLabel { + anchors.baseline: optCombo.baseline + text: qsTr("RC Channel 6 Option (Tuning):") + //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text + } - QGCLabel { - anchors.baseline: optCombo.baseline - text: qsTr("RC Channel 6 Option (Tuning):") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text + FactComboBox { + id: optCombo + width: ScreenTools.defaultFontPixelWidth * 15 + fact: controller.getParameterFact(-1, "TUNE") + indexModel: false + } } - FactComboBox { - id: optCombo - width: ScreenTools.defaultFontPixelWidth * 15 - fact: controller.getParameterFact(-1, "TUNE") - indexModel: false - } - } + Row { + spacing: ScreenTools.defaultFontPixelWidth + property Fact nullFact: Fact { } - Row { - spacing: ScreenTools.defaultFontPixelWidth - property Fact nullFact: Fact { } + QGCLabel { + anchors.baseline: tuneMinField.baseline + text: qsTr("Min:") + //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text + } - QGCLabel { - anchors.baseline: tuneMinField.baseline - text: qsTr("Min:") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text - } + FactTextField { + id: tuneMinField + validator: DoubleValidator {bottom: 0; top: 32767;} + fact: controller.getParameterFact(-1, "TUNE_LOW") + } - FactTextField { - id: tuneMinField - validator: DoubleValidator {bottom: 0; top: 32767;} - fact: controller.getParameterFact(-1, "TUNE_LOW") - } + QGCLabel { + anchors.baseline: tuneMaxField.baseline + text: qsTr("Max:") + //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text + } - QGCLabel { - anchors.baseline: tuneMaxField.baseline - text: qsTr("Max:") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text + FactTextField { + id: tuneMaxField + validator: DoubleValidator {bottom: 0; top: 32767;} + fact: controller.getParameterFact(-1, "TUNE_HIGH") + } } + } // Column - Channel 6 Tuning option + } // Rectangle - Channel 6 Tuning options + } // Rectangle - Channel 6 Tuning options wrap + } // Flow - Tune + } - FactTextField { - id: tuneMaxField - validator: DoubleValidator {bottom: 0; top: 32767;} - fact: controller.getParameterFact(-1, "TUNE_HIGH") - } - } - } // Column - Channel 6 Tuning option - } // Rectangle - Channel 6 Tuning options - } // Rectangle - Channel 6 Tuning options wrap - } // Flow - Tune + Loader { + anchors.left: parent.left + anchors.right: parent.right + sourceComponent: advanced ? advancePageComponent : undefined + } + + Component { + id: advancePageComponent + + PIDTuning { + anchors.left: parent.left + anchors.right: parent.right + tuneList: [ qsTr("Roll"), qsTr("Pitch"), qsTr("Yaw") ] + params: [ + [ controller.getParameterFact(-1, "ATC_ANG_RLL_P"), + controller.getParameterFact(-1, "ATC_RAT_RLL_P"), + controller.getParameterFact(-1, "ATC_RAT_RLL_I"), + controller.getParameterFact(-1, "ATC_RAT_RLL_D") ], + [ controller.getParameterFact(-1, "ATC_ANG_PIT_P"), + controller.getParameterFact(-1, "ATC_RAT_PIT_P"), + controller.getParameterFact(-1, "ATC_RAT_PIT_I"), + controller.getParameterFact(-1, "ATC_RAT_PIT_D") ], + [ controller.getParameterFact(-1, "ATC_ANG_YAW_P"), + controller.getParameterFact(-1, "ATC_RAT_YAW_P"), + controller.getParameterFact(-1, "ATC_RAT_YAW_I") ] ] + } + } // Component - Advanced Page } // Column } // Component } // SetupView diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml index 4c57ab20399f02f8fff24b6de755f77cd72db68c..9e6e5a8d49a520fbf3754d9141edd3f33080c333 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml @@ -61,26 +61,6 @@ SetupPage { max: 15 step: 1 } - /* - These seem to have disappeared from PX4 firmware! - ListElement { - title: qsTr("Roll sensitivity") - description: qsTr("Slide to the left to make roll control faster and more accurate. Slide to the right if roll oscillates or is too twitchy.") - param: "MC_ROLL_TC" - min: 0.15 - max: 0.25 - step: 0.01 - } - - ListElement { - title: qsTr("Pitch sensitivity") - description: qsTr("Slide to the left to make pitch control faster and more accurate. Slide to the right if pitch oscillates or is too twitchy.") - param: "MC_PITCH_TC" - min: 0.15 - max: 0.25 - step: 0.01 - } -*/ } } @@ -112,7 +92,6 @@ SetupPage { controller.getParameterFact(-1, "MC_YAWRATE_P"), controller.getParameterFact(-1, "MC_YAWRATE_I"), controller.getParameterFact(-1, "MC_YAWRATE_D"), - controller.getParameterFact(-1, "MC_YAW_FF"), controller.getParameterFact(-1, "MC_YAWRATE_FF") ] ] } } // Component - Advanced Page diff --git a/src/FactSystem/FactGroup.cc b/src/FactSystem/FactGroup.cc index c67c02d8f55a2a2ec118db30cf9dd7b46747aa6d..55aedaf9869c6f4bc047fc5954ef7414c5918139 100644 --- a/src/FactSystem/FactGroup.cc +++ b/src/FactSystem/FactGroup.cc @@ -46,7 +46,8 @@ void FactGroup::_setupTimer() if (_updateRateMSecs > 0) { connect(&_updateTimer, &QTimer::timeout, this, &FactGroup::_updateAllValues); _updateTimer.setSingleShot(false); - _updateTimer.start(_updateRateMSecs); + _updateTimer.setInterval(_updateRateMSecs); + _updateTimer.start(); } } @@ -125,3 +126,19 @@ void FactGroup::_updateAllValues(void) fact->sendDeferredValueChangedSignal(); } } + +void FactGroup::setLiveUpdates(bool liveUpdates) +{ + if (_updateTimer.interval() == 0) { + return; + } + + if (liveUpdates) { + _updateTimer.stop(); + } else { + _updateTimer.start(); + } + for(Fact* fact: _nameToFactMap) { + fact->setSendValueChangedSignals(liveUpdates); + } +} diff --git a/src/FactSystem/FactGroup.h b/src/FactSystem/FactGroup.h index 78a97fcb60c79ddc140b68a7476d50ae2e8930f2..9e8320fca0cfcc977f8f70f23e23aac04a58ba6b 100644 --- a/src/FactSystem/FactGroup.h +++ b/src/FactSystem/FactGroup.h @@ -38,6 +38,9 @@ public: /// @return FactGroup for specified name, NULL if not found Q_INVOKABLE FactGroup* getFactGroup(const QString& name); + /// Turning on live updates will allow value changes to flow through as they are received. + Q_INVOKABLE void setLiveUpdates(bool liveUpdates); + QStringList factNames(void) const { return _factNames; } QStringList factGroupNames(void) const { return _nameToFactGroupMap.keys(); } diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index 01b97b37aed727fa407390ac4893d7b073af2041..6a4bcabab00f9858f8f0532a67541551ab8fcfd1 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -644,6 +644,29 @@ Note: ekf2 will limit the delta velocity bias estimate magnitude to be less than The default allows to arm the vehicle without GPS signal. + + Airspeed fault detection (Experimental) + Failsafe action when bad airspeed measurements are detected. Ensure the COM_ASPD_STALL parameter is set correctly before use. + + disabled + log a message + log a message, warn the user + log a message, warn the user, switch to non-airspeed TECS mode + log a message, warn the user, switch to non-airspeed TECS mode, switch to Return mode after COM_ASPD_FS_DLY seconds + + + + Airspeed fault detection delay before RTL (Experimental) + RTL delay after bad airspeed measurements are detected if COM_ASPD_FS_ACT is set to 4. Ensure the COM_ASPD_STALL parameter is set correctly before use. The failsafe start and stop delays are controlled by the COM_TAS_FS_T1 and COM_TAS_FS_T2 parameters. Additional protection against persistent airspeed sensor errors can be enabled using the COM_TAS_FS_INNOV parameter, but these addtional checks are more prone to false positives in windy conditions. + 0 + 300 + s + + + Airspeed fault detection stall airspeed. (Experimental) + This is the minimum indicated airspeed at which the wing can produce 1g of lift. It is used by the airspeed sensor fault detection and failsafe calculation to detect a significant airspeed low measurement error condition and should be set based on flight test for reliable operation. The failsafe response is controlled by the COM_ASPD_FS_ACT parameter. + m/s + Time-out for auto disarm after landing A non-zero, positive value specifies the time-out period in seconds after which the vehicle will be automatically disarmed in case a landing situation has been detected during this period. The vehicle will also auto-disarm right after arming if it has not even flown, however the time will always be 10 seconds such that the pilot has enough time to take off. A negative value means that automatic disarming triggered by landing detection is disabled. @@ -872,6 +895,14 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action s 1 + + Position control navigation loss response + This sets the flight mode that will be used if navigation accuracy is no longer adequate for position control. Navigation accuracy checks can be disabled using the CBRK_VELPOSERR parameter, but doing so will remove protection for all flight modes. + + Assume use of remote control after fallback. Switch to Altitude mode if a height estimate is available, else switch to MANUAL. + Assume no use of remote control after fallback. Switch to Land mode if a height estimate is available, else switch to TERMINATION. + + Loss of position failsafe activation delay This sets number of seconds that the position checks need to be failed before the failsafe will activate. The default value has been optimised for rotary wing applications. For fixed wing applications, a larger value between 5 and 10 should be used. @@ -942,6 +973,32 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action 0 0.05 + + Airspeed failsafe consistency threshold (Experimental) + This specifies the minimum airspeed test ratio as logged in estimator_status.tas_test_ratio required to trigger a failsafe. Larger values make the check less sensitive, smaller values make it more sensitive. Start with a value of 1.0 when tuning. When estimator_status.tas_test_ratio is > 1.0 it indicates the inconsistency between predicted and measured airspeed is large enough to cause the navigation EKF to reject airspeed measurements. The time required to detect a fault when the threshold is exceeded depends on the size of the exceedance and is controlled by the COM_TAS_FS_INTEG parameter. The subsequent failsafe response is controlled by the COM_ASPD_FS_ACT parameter. + 0.5 + 3.0 + + + Airspeed failsafe consistency delay (Experimental) + This sets the time integral of airspeed test ratio exceedance above COM_TAS_FS_INNOV required to trigger a failsafe. For example if COM_TAS_FS_INNOV is 100 and estimator_status.tas_test_ratio is 2.0, then the exceedance is 1.0 and the integral will rise at a rate of 1.0/second. A negative value disables the check. Larger positive values make the check less sensitive, smaller positive values make it more sensitive. The failsafe response is controlled by the COM_ASPD_FS_ACT parameter. + 30.0 + s + + + Airspeed failsafe stop delay (Experimental) + Delay before stopping use of airspeed sensor if checks indicate sensor is bad. The failsafe response is controlled by the COM_ASPD_FS_ACT parameter. + 1 + 10 + s + + + Airspeed failsafe start delay (Experimental) + Delay before switching back to using airspeed sensor if checks indicate sensor is good. The failsafe response is controlled by the COM_ASPD_FS_ACT parameter. + 10 + 1000 + s + Horizontal velocity error threshold This is the horizontal velocity error (EVH) threshold that will trigger a failsafe. The default is appropriate for a multicopter. Can be increased for a fixed-wing. @@ -3862,14 +3919,6 @@ Used to calculate increased terrain random walk nosie due to movementFlag to enable obstacle avoidance - - Position control navigation loss response - This sets the flight mode that will be used if navigation accuracy is no longer adequate for position control. Navigation accuracy checks can be disabled using the CBRK_VELPOSERR parameter, but doing so will remove protection for all flight modes. - - Assume use of remote control after fallback. Switch to Altitude mode if a height estimate is available, else switch to MANUAL. - Assume no use of remote control after fallback. Switch to Land mode if a height estimate is available, else switch to TERMINATION. - - Action after TAKEOFF has been accepted The mode transition after TAKEOFF has completed successfully. diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index 8d4bce9d4c13f13d055f2ab58e166f2a7d1bf04b..d1e537cca40831d9b92e2d07a6ab8c5f03ef8a74 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -106,14 +106,6 @@ void MissionCommandTree::_collapseHierarchy(Vehicle* _baseVehicleInfo(vehicle, baseFirmwareType, baseVehicleType); for (MAV_CMD command: cmdList->commandIds()) { - // Only add supported command to tree (MAV_CMD_NAV_LAST is used for planned home position) - if (!qgcApp()->runningUnitTests() - && !vehicle->firmwarePlugin()->supportedMissionCommands().isEmpty() - && !vehicle->firmwarePlugin()->supportedMissionCommands().contains(command) - && command != MAV_CMD_NAV_LAST) { - continue; - } - MissionCommandUIInfo* uiInfo = cmdList->getUIInfo(command); if (uiInfo) { if (collapsedTree.contains(command)) { @@ -125,22 +117,20 @@ void MissionCommandTree::_collapseHierarchy(Vehicle* } } -void MissionCommandTree::_buildAvailableCommands(Vehicle* vehicle) +void MissionCommandTree::_buildAllCommands(Vehicle* vehicle) { MAV_AUTOPILOT baseFirmwareType; MAV_TYPE baseVehicleType; _baseVehicleInfo(vehicle, baseFirmwareType, baseVehicleType); - if (_availableCommands.contains(baseFirmwareType) && - _availableCommands[baseFirmwareType].contains(baseVehicleType)) { - // Available commands list already built + if (_allCommands.contains(baseFirmwareType) && + _allCommands[baseFirmwareType].contains(baseVehicleType)) { + // Already built return; } - // Build new available commands list - - QMap& collapsedTree = _availableCommands[baseFirmwareType][baseVehicleType]; + QMap& collapsedTree = _allCommands[baseFirmwareType][baseVehicleType]; // Any Firmware, Any Vehicle _collapseHierarchy(vehicle, _staticCommandTree[MAV_AUTOPILOT_GENERIC][MAV_TYPE_GENERIC], collapsedTree); @@ -160,16 +150,17 @@ void MissionCommandTree::_buildAvailableCommands(Vehicle* vehicle) } } - // Build category list - QMapIterator iter(collapsedTree); - while (iter.hasNext()) { - iter.next(); - QString newCategory = iter.value()->category(); - if (!_availableCategories[baseFirmwareType][baseVehicleType].contains(newCategory)) { - _availableCategories[baseFirmwareType][baseVehicleType].append(newCategory); + // Build category list from supported commands + QList supportedCommands = vehicle->firmwarePlugin()->supportedMissionCommands(); + for (MAV_CMD cmd: collapsedTree.keys()) { + if (supportedCommands.contains(cmd)) { + QString newCategory = collapsedTree[cmd]->category(); + if (!_supportedCategories[baseFirmwareType][baseVehicleType].contains(newCategory)) { + _supportedCategories[baseFirmwareType][baseVehicleType].append(newCategory); + } } } - _availableCategories[baseFirmwareType][baseVehicleType].append(_allCommandsCategory); + _supportedCategories[baseFirmwareType][baseVehicleType].append(_allCommandsCategory); } QStringList MissionCommandTree::_availableCategoriesForVehicle(Vehicle* vehicle) @@ -178,9 +169,9 @@ QStringList MissionCommandTree::_availableCategoriesForVehicle(Vehicle* vehicle) MAV_TYPE baseVehicleType; _baseVehicleInfo(vehicle, baseFirmwareType, baseVehicleType); - _buildAvailableCommands(vehicle); + _buildAllCommands(vehicle); - return _availableCategories[baseFirmwareType][baseVehicleType]; + return _supportedCategories[baseFirmwareType][baseVehicleType]; } QString MissionCommandTree::friendlyName(MAV_CMD command) @@ -191,7 +182,7 @@ QString MissionCommandTree::friendlyName(MAV_CMD command) if (uiInfo) { return uiInfo->friendlyName(); } else { - return QString("MAV_CMD(%1)").arg((int)command); + return QStringLiteral("MAV_CMD(%1)").arg((int)command); } } @@ -203,7 +194,7 @@ QString MissionCommandTree::rawName(MAV_CMD command) if (uiInfo) { return uiInfo->rawName(); } else { - return QString("MAV_CMD(%1)").arg((int)command); + return QStringLiteral("MAV_CMD(%1)").arg((int)command); } } @@ -218,13 +209,13 @@ const MissionCommandUIInfo* MissionCommandTree::getUIInfo(Vehicle* vehicle, MAV_ MAV_TYPE baseVehicleType; _baseVehicleInfo(vehicle, baseFirmwareType, baseVehicleType); - _buildAvailableCommands(vehicle); + _buildAllCommands(vehicle); - const QMap& infoMap = _availableCommands[baseFirmwareType][baseVehicleType]; + const QMap& infoMap = _allCommands[baseFirmwareType][baseVehicleType]; if (infoMap.contains(command)) { return infoMap[command]; } else { - return NULL; + return nullptr; } } @@ -232,21 +223,19 @@ QVariantList MissionCommandTree::getCommandsForCategory(Vehicle* vehicle, const { MAV_AUTOPILOT baseFirmwareType; MAV_TYPE baseVehicleType; + QList supportedCommands = vehicle->firmwarePlugin()->supportedMissionCommands(); _baseVehicleInfo(vehicle, baseFirmwareType, baseVehicleType); - _buildAvailableCommands(vehicle); + _buildAllCommands(vehicle); QVariantList list; - QMap commandMap = _availableCommands[baseFirmwareType][baseVehicleType]; + QMap commandMap = _allCommands[baseFirmwareType][baseVehicleType]; for (MAV_CMD command: commandMap.keys()) { - if (command == MAV_CMD_NAV_LAST) { - // MAV_CMD_NAV_LAST is used for Mission Settings item. Although we want to be able to get command info for it. - // The user should not be able to use it as a command. - continue; - } - MissionCommandUIInfo* uiInfo = commandMap[command]; - if (uiInfo->category() == category || category == _allCommandsCategory) { - list.append(QVariant::fromValue(uiInfo)); + if (supportedCommands.contains(command)) { + MissionCommandUIInfo* uiInfo = commandMap[command]; + if (uiInfo->category() == category || category == _allCommandsCategory) { + list.append(QVariant::fromValue(uiInfo)); + } } } diff --git a/src/MissionManager/MissionCommandTree.h b/src/MissionManager/MissionCommandTree.h index 13639b2eec4699193e69105a9b09f85fb13d7d04..13884358604949aa0c1a5a70781dcae0282112fa 100644 --- a/src/MissionManager/MissionCommandTree.h +++ b/src/MissionManager/MissionCommandTree.h @@ -41,7 +41,7 @@ class MissionCommandTreeTest; /// For known firmwares, the override files are requested from the FirmwarePlugin. /// /// When ui info is requested for a specific vehicle the static hierarchy in _staticCommandTree is collapsed into the set of available commands in -/// _availableCommands taking into account the appropriate set of overrides for the MAV_AUTOPILOT/MAV_TYPE combination associated with the vehicle. +/// _allCommands taking into account the appropriate set of overrides for the MAV_AUTOPILOT/MAV_TYPE combination associated with the vehicle. /// class MissionCommandTree : public QGCTool { @@ -68,12 +68,12 @@ public: virtual void setToolbox(QGCToolbox* toolbox); private: - void _collapseHierarchy(Vehicle* vehicle, const MissionCommandList* cmdList, QMap& collapsedTree); - MAV_TYPE _baseVehicleType(MAV_TYPE mavType) const; - MAV_AUTOPILOT _baseFirmwareType(MAV_AUTOPILOT firmwareType) const; - void _buildAvailableCommands(Vehicle* vehicle); - QStringList _availableCategoriesForVehicle(Vehicle* vehicle); - void _baseVehicleInfo(Vehicle* vehicle, MAV_AUTOPILOT& baseFirmwareType, MAV_TYPE& baseVehicleType) const; + void _collapseHierarchy(Vehicle* vehicle, const MissionCommandList* cmdList, QMap& collapsedTree); + MAV_TYPE _baseVehicleType(MAV_TYPE mavType) const; + MAV_AUTOPILOT _baseFirmwareType(MAV_AUTOPILOT firmwareType) const; + void _buildAllCommands(Vehicle* vehicle); + QStringList _availableCategoriesForVehicle(Vehicle* vehicle); + void _baseVehicleInfo(Vehicle* vehicle, MAV_AUTOPILOT& baseFirmwareType, MAV_TYPE& baseVehicleType) const; private: QString _allCommandsCategory; ///< Category which contains all available commands @@ -85,10 +85,10 @@ private: QMap> _staticCommandTree; /// Collapsed hierarchy for specific vehicle type - QMap>> _availableCommands; + QMap>> _allCommands; /// Collapsed hierarchy for specific vehicle type - QMap> _availableCategories; + QMap> _supportedCategories; #ifdef UNITTEST_BUILD diff --git a/src/PlanView/GeoFenceMapVisuals.qml b/src/PlanView/GeoFenceMapVisuals.qml index 18bc19b37bacd8639d7cf676090e55fcd929bf70..6ef0e4fa05c4a72ae7c818ee959325da7e48c21a 100644 --- a/src/PlanView/GeoFenceMapVisuals.qml +++ b/src/PlanView/GeoFenceMapVisuals.qml @@ -20,6 +20,7 @@ import QGroundControl.FlightMap 1.0 /// GeoFence map visuals Item { + id: _root z: QGroundControl.zOrderMapItems property var map @@ -88,10 +89,15 @@ Item { _paramCircleFenceComponent.destroy() } + // By default the parent for Instantiator.delegate item is the Instatiator itself. By there is a bug + // in Qt which will cause a crash if this delete item has Menu item within it. Since the Menu item + // doesn't like having a non-visual item as parent. This is likely related to hybrid QQuickWidtget+QML + // Hence Qt folks are going to care. In order to workaround you have to parent the item to _root Item instead. Instantiator { model: _polygons delegate : QGCMapPolygonVisuals { + parent: _root mapControl: map mapPolygon: object borderWidth: object.inclusion ? _borderWidthInclusion : _borderWidthExclusion @@ -105,6 +111,7 @@ Item { model: _circles delegate : QGCMapCircleVisuals { + parent: _root mapControl: map mapCircle: object borderWidth: object.inclusion ? _borderWidthInclusion : _borderWidthExclusion diff --git a/src/QmlControls/FactSliderPanel.qml b/src/QmlControls/FactSliderPanel.qml index ac4cc10d722d69cda79475f30eefd20087e0eca9..3720e5d2a1476b144ffdf8bb0aaedb85c7999d8f 100644 --- a/src/QmlControls/FactSliderPanel.qml +++ b/src/QmlControls/FactSliderPanel.qml @@ -34,6 +34,8 @@ Column { property real _margins: ScreenTools.defaultFontPixelHeight property bool _loadComplete: false + Component.onCompleted: _loadComplete = true + FactPanelController { id: controller factPanel: qgcViewPanel @@ -71,10 +73,22 @@ Column { font.family: ScreenTools.demiboldFontFamily } - FactValueSlider { - digitCount: fact.maxString.length - incrementSlots: 3 - fact: controller.getParameterFact(-1, param) + Slider { + anchors.left: parent.left + anchors.right: parent.right + minimumValue: min + maximumValue: max + stepSize: step + tickmarksEnabled: true + value: _fact.value + + property Fact _fact: controller.getParameterFact(-1, param) + + onValueChanged: { + if (_loadComplete) { + _fact.value = value + } + } } QGCLabel { diff --git a/src/QmlControls/PIDTuning.qml b/src/QmlControls/PIDTuning.qml index 6467ce0109451f4ce55df7fc6264c6d63ef1dda2..d563b7894d3d18c3865d22d6548a5638c23ce851 100644 --- a/src/QmlControls/PIDTuning.qml +++ b/src/QmlControls/PIDTuning.qml @@ -142,9 +142,12 @@ RowLayout { } Component.onCompleted: { + _activeVehicle.setPIDTuningTelemetryMode(true) saveTuningParamValues() } + Component.onDestruction: _activeVehicle.setPIDTuningTelemetryMode(false) + on_CurrentTuneTypeChanged: { saveTuningParamValues() resetGraphs() @@ -159,7 +162,8 @@ RowLayout { min: 0 max: 0 labelFormat: "%d" - titleText: "sec" + titleText: "msec" + tickCount: 11 } ValueAxis { @@ -167,7 +171,8 @@ RowLayout { min: 0 max: 0 labelFormat: "%d" - titleText: "sec" + titleText: "msec" + tickCount: 11 } ValueAxis { @@ -193,40 +198,28 @@ RowLayout { repeat: true onTriggered: { - var seconds = _msecs / 1000 - _valueXAxis.max = seconds - _valueRateXAxis.max = seconds + _valueXAxis.max = _msecs + _valueRateXAxis.max = _msecs getValues() - valueSeries.append(seconds, _value) + valueSeries.append(_msecs, _value) adjustYAxisMin(_valueYAxis, _value) adjustYAxisMax(_valueYAxis, _value) - valueSetpointSeries.append(seconds, _valueSetpoint) + valueSetpointSeries.append(_msecs, _valueSetpoint) adjustYAxisMin(_valueYAxis, _valueSetpoint) adjustYAxisMax(_valueYAxis, _valueSetpoint) - valueRateSeries.append(seconds, _valueRate) + valueRateSeries.append(_msecs, _valueRate) adjustYAxisMin(_valueRateYAxis, _valueRate) adjustYAxisMax(_valueRateYAxis, _valueRate) - valueRateSetpointSeries.append(seconds, _valueRateSetpoint) + valueRateSetpointSeries.append(_msecs, _valueRateSetpoint) adjustYAxisMin(_valueRateYAxis, _valueRateSetpoint) adjustYAxisMax(_valueRateYAxis, _valueRateSetpoint) _msecs += interval - /* - Testing with just start/stop for now. No time limit. - if (valueSeries.count > _maxPointCount) { - valueSeries.remove(0) - valueSetpointSeries.remove(0) - valueRateSeries.remove(0) - valueRateSetpointSeries.remove(0) - valueXAxis.min = valueSeries.at(0).x - valueRateXAxis.min = valueSeries.at(0).x - } - */ } property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle @@ -237,25 +230,25 @@ RowLayout { spacing: _margins Layout.alignment: Qt.AlignTop - QGCLabel { text: qsTr("Tuning Axis:") } + Column { + QGCLabel { text: qsTr("Tuning Axis:") } - RowLayout { - spacing: _margins + RowLayout { + spacing: _margins - Repeater { - model: tuneList - QGCRadioButton { - text: modelData - checked: _currentTuneType === modelData - exclusiveGroup: tuneTypeRadios + Repeater { + model: tuneList + QGCRadioButton { + text: modelData + checked: _currentTuneType === modelData + exclusiveGroup: tuneTypeRadios - onClicked: _currentTuneType = modelData + onClicked: _currentTuneType = modelData + } } } } - Item { width: 1; height: 1 } - QGCLabel { text: qsTr("Tuning Values:") } GridLayout { @@ -279,7 +272,10 @@ RowLayout { text: "-" onClicked: { var value = modelData.value - modelData.value -= value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value + var newValue = value - (value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value) + if (newValue >= modelData.min) { + modelData.value = newValue + } } } } @@ -301,7 +297,10 @@ RowLayout { text: "+" onClicked: { var value = modelData.value - modelData.value += value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value + var newValue = value + (value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value) + if (newValue <= modelData.max) { + modelData.value = newValue + } } } } @@ -321,26 +320,27 @@ RowLayout { } } } - Item { width: 1; height: 1 } - QGCLabel { text: qsTr("Saved Tuning Values:") } + Column { + QGCLabel { text: qsTr("Clipboard Values:") } - GridLayout { - rows: savedRepeater.model.length - flow: GridLayout.TopToBottom - rowSpacing: _margins - columnSpacing: _margins + GridLayout { + rows: savedRepeater.model.length + flow: GridLayout.TopToBottom + rowSpacing: 0 + columnSpacing: _margins - Repeater { - model: params[tuneList.indexOf(_currentTuneType)] + Repeater { + model: params[tuneList.indexOf(_currentTuneType)] - QGCLabel { text: modelData.name } - } + QGCLabel { text: modelData.name } + } - Repeater { - id: savedRepeater + Repeater { + id: savedRepeater - QGCLabel { text: modelData } + QGCLabel { text: modelData } + } } } @@ -348,12 +348,12 @@ RowLayout { spacing: _margins QGCButton { - text: qsTr("Save Values") + text: qsTr("Save To Clipboard") onClicked: saveTuningParamValues() } QGCButton { - text: qsTr("Reset To Saved Values") + text: qsTr("Restore From Clipboard") onClicked: resetToSavedTuningParamValues() } } @@ -372,7 +372,30 @@ RowLayout { QGCButton { text: dataTimer.running ? qsTr("Stop") : qsTr("Start") - onClicked: dataTimer.running = !dataTimer.running + onClicked: { + dataTimer.running = !dataTimer.running + if (autoModeChange.checked) { + _activeVehicle.flightMode = dataTimer.running ? "Stabilized" : _activeVehicle.pauseFlightMode + } + } + } + } + + QGCCheckBox { + id: autoModeChange + text: qsTr("Automatic Flight Mode Switching") + } + + Column { + visible: autoModeChange.checked + QGCLabel { + text: qsTr("Switches to 'Stabilized' when you click Start.") + font.pointSize: ScreenTools.smallFontPointSize + } + + QGCLabel { + text: qsTr("Switches to '%1' when you click Stop.").arg(_activeVehicle.pauseFlightMode) + font.pointSize: ScreenTools.smallFontPointSize } } } diff --git a/src/Terrain/TerrainQuery.cc b/src/Terrain/TerrainQuery.cc index b2d1dada9cf3c539581f261fc570ba2d46e411f1..52687493587b0036794cd13261c88229b0df612a 100644 --- a/src/Terrain/TerrainQuery.cc +++ b/src/Terrain/TerrainQuery.cc @@ -489,7 +489,7 @@ void TerrainTileManager::_terrainDone(QByteArray responseBytes, QNetworkReply::N return; } - qWarning() << "Received some bytes of terrain data: " << responseBytes.size(); + qCDebug(TerrainQueryLog) << "Received some bytes of terrain data: " << responseBytes.size(); TerrainTile* terrainTile = new TerrainTile(responseBytes); if (terrainTile->isValid()) { diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 518d741e9e5138d59e1a934691851813f9d6d5a6..c366dd45913fd8bd409f2960fe9047453d79e200 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -185,6 +185,8 @@ Vehicle::Vehicle(LinkInterface* link, , _lastAnnouncedLowBatteryPercent(100) , _priorityLinkCommanded(false) , _orbitActive(false) + , _pidTuningTelemetryMode(false) + , _pidTuningWaitingForRates(false) , _rollFact (0, _rollFactName, FactMetaData::valueTypeDouble) , _pitchFact (0, _pitchFactName, FactMetaData::valueTypeDouble) , _headingFact (0, _headingFactName, FactMetaData::valueTypeDouble) @@ -386,6 +388,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _uid(0) , _lastAnnouncedLowBatteryPercent(100) , _orbitActive(false) + , _pidTuningTelemetryMode(false) + , _pidTuningWaitingForRates(false) , _rollFact (0, _rollFactName, FactMetaData::valueTypeDouble) , _pitchFact (0, _pitchFactName, FactMetaData::valueTypeDouble) , _headingFact (0, _headingFactName, FactMetaData::valueTypeDouble) @@ -519,6 +523,8 @@ void Vehicle::_commonInit(void) } } #endif + + _pidTuningMessages << MAVLINK_MSG_ID_ATTITUDE << MAVLINK_MSG_ID_ATTITUDE_TARGET; } Vehicle::~Vehicle() @@ -794,7 +800,9 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes case MAVLINK_MSG_ID_ORBIT_EXECUTION_STATUS: _handleOrbitExecutionStatus(message); break; - + case MAVLINK_MSG_ID_MESSAGE_INTERVAL: + _handleMessageInterval(message); + break; case MAVLINK_MSG_ID_PING: _handlePing(link, message); break; @@ -1309,13 +1317,15 @@ void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& me // PX4 Firmware stores the first 16 characters of the git hash as binary, with the individual bytes in reverse order _gitHash = ""; - QByteArray array((char*)autopilotVersion.flight_custom_version, 8); for (int i = 7; i >= 0; i--) { _gitHash.append(QString("%1").arg(autopilotVersion.flight_custom_version[i], 2, 16, QChar('0'))); } } else { // APM Firmware stores the first 8 characters of the git hash as an ASCII character string - _gitHash = QString::fromUtf8((char*)autopilotVersion.flight_custom_version, 8); + char nullStr[9]; + strncpy(nullStr, (char*)autopilotVersion.flight_custom_version, 8); + nullStr[8] = 0; + _gitHash = nullStr; } if (_toolbox->corePlugin()->options()->checkFirmwareVersion()) { _firmwarePlugin->checkIfIsLatestStable(this); @@ -3873,6 +3883,74 @@ int Vehicle::versionCompare(int major, int minor, int patch) return _firmwarePlugin->versionCompare(this, major, minor, patch); } +void Vehicle::_handleMessageInterval(const mavlink_message_t& message) +{ + if (_pidTuningWaitingForRates) { + mavlink_message_interval_t messageInterval; + + mavlink_msg_message_interval_decode(&message, &messageInterval); + + int msgId = messageInterval.message_id; + if (_pidTuningMessages.contains(msgId)) { + _pidTuningMessageRatesUsecs[msgId] = messageInterval.interval_us; + } + + if (_pidTuningMessageRatesUsecs.count() == _pidTuningMessages.count()) { + // We have back all the rates we requested + _pidTuningWaitingForRates = false; + _pidTuningAdjustRates(true); + } + } +} + +void Vehicle::setPIDTuningTelemetryMode(bool pidTuning) +{ + if (pidTuning) { + if (!_pidTuningTelemetryMode) { + // First step is to get the current message rates before we adjust them + _pidTuningTelemetryMode = true; + _pidTuningWaitingForRates = true; + _pidTuningMessageRatesUsecs.clear(); + for (int telemetry: _pidTuningMessages) { + sendMavCommand(defaultComponentId(), + MAV_CMD_GET_MESSAGE_INTERVAL, + true, // show error + telemetry); + } + } + } else { + if (_pidTuningTelemetryMode) { + _pidTuningTelemetryMode = false; + if (_pidTuningWaitingForRates) { + // We never finished waiting for previous rates + _pidTuningWaitingForRates = false; + } else { + _pidTuningAdjustRates(false); + } + } + } +} + +void Vehicle::_pidTuningAdjustRates(bool setRatesForTuning) +{ + int requestedRate = (int)(1000000.0 / 30.0); // 30 Hz in usecs + + for (int telemetry: _pidTuningMessages) { + + if (requestedRate < _pidTuningMessageRatesUsecs[telemetry]) { + sendMavCommand(defaultComponentId(), + MAV_CMD_SET_MESSAGE_INTERVAL, + true, // show error + telemetry, + setRatesForTuning ? requestedRate : _pidTuningMessageRatesUsecs[telemetry]); + } + } + + setLiveUpdates(setRatesForTuning); + _setpointFactGroup.setLiveUpdates(setRatesForTuning); +} + + #if !defined(NO_ARDUPILOT_DIALECT) void Vehicle::flashBootloader(void) { diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index a71e6a552d8c04110ae33b25e595bd39e95c60a8..bc194260f31377b0efca82821c91e1b9e982e47f 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -758,6 +758,8 @@ public: /// @param percent 0-no power, 100-full power Q_INVOKABLE void motorTest(int motor, int percent); + Q_INVOKABLE void setPIDTuningTelemetryMode(bool pidTuning); + #if !defined(NO_ARDUPILOT_DIALECT) Q_INVOKABLE void flashBootloader(void); #endif @@ -1265,6 +1267,7 @@ private: void _handleEstimatorStatus(mavlink_message_t& message); void _handleStatusText(mavlink_message_t& message, bool longVersion); void _handleOrbitExecutionStatus(const mavlink_message_t& message); + void _handleMessageInterval(const mavlink_message_t& message); // ArduPilot dialect messages #if !defined(NO_ARDUPILOT_DIALECT) void _handleCameraFeedback(const mavlink_message_t& message); @@ -1291,6 +1294,7 @@ private: void _setCapabilities(uint64_t capabilityBits); void _updateArmed(bool armed); bool _apmArmingNotRequired(void); + void _pidTuningAdjustRates(bool setRatesForTuning); int _id; ///< Mavlink system id int _defaultComponentId; @@ -1473,6 +1477,12 @@ private: QTimer _orbitTelemetryTimer; static const int _orbitTelemetryTimeoutMsecs = 3000; // No telemetry for this amount and orbit will go inactive + // PID Tuning telemetry mode + bool _pidTuningTelemetryMode; + bool _pidTuningWaitingForRates; + QList _pidTuningMessages; + QMap _pidTuningMessageRatesUsecs; + // FactGroup facts Fact _rollFact; diff --git a/src/comm/LogReplayLink.cc b/src/comm/LogReplayLink.cc index a97a84b33b045c408bfba35fc6f895b95dadf921..579b45322e9d84da8ea2816f6d3d607160b06bfc 100644 --- a/src/comm/LogReplayLink.cc +++ b/src/comm/LogReplayLink.cc @@ -14,6 +14,7 @@ #include #include +#include const char* LogReplayLinkConfiguration::_logFilenameKey = "logFilename"; @@ -368,7 +369,7 @@ void LogReplayLink::_readNextLogEntry(void) timeToNextExecutionMSecs = desiredPacedTimeMSecs - currentTimeMSecs; } - emit currentLogTimeSecs((_logCurrentTimeUSecs - _logStartTimeUSecs) / 1000000); + _signalCurrentLogTimeSecs(); // And schedule the next execution of this function. _readTickTimer.start(timeToNextExecutionMSecs); @@ -450,8 +451,12 @@ void LogReplayLink::_resetPlaybackToBeginning(void) void LogReplayLink::movePlayhead(int percentComplete) { if (isPlaying()) { - qWarning() << "Should not move playhead while playing, pause first"; - return; + _pauseOnThread(); + QSignalSpy waitForPause(this, SIGNAL(playbackPaused)); + waitForPause.wait(); + if (_readTickTimer.isActive()) { + return; + } } if (percentComplete < 0 || percentComplete > 100) { @@ -495,7 +500,8 @@ void LogReplayLink::movePlayhead(int percentComplete) // And scan until we reach the start of a MAVLink message. We make sure to record this timestamp for // smooth jumping around the file. _logCurrentTimeUSecs = _seekToNextMavlinkMessage(&dummy); - + _signalCurrentLogTimeSecs(); + // Now update the UI with our actual final position. newRelativeTimeUSecs = (float)(_logCurrentTimeUSecs - _logStartTimeUSecs); percentComplete = (newRelativeTimeUSecs / _logDurationUSecs) * 100; @@ -561,3 +567,8 @@ void LogReplayLink::_playbackError(void) _logFile.close(); emit playbackError(); } + +void LogReplayLink::_signalCurrentLogTimeSecs(void) +{ + emit currentLogTimeSecs((_logCurrentTimeUSecs - _logStartTimeUSecs) / 1000000); +} diff --git a/src/comm/LogReplayLink.h b/src/comm/LogReplayLink.h index 6f369b35f3ca1a20d6e8be1fce605a902d9b5644..fb5de6ee6abd74edca13dfbaf483cd84b3490c73 100644 --- a/src/comm/LogReplayLink.h +++ b/src/comm/LogReplayLink.h @@ -120,6 +120,7 @@ private: void _finishPlayback(void); void _playbackError(void); void _resetPlaybackToBeginning(void); + void _signalCurrentLogTimeSecs(void); // Virtuals from LinkInterface virtual bool _connect(void); diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 7a98324b0c0ba2952d8ecf82c0c2b1a2314a67a4..6dc0e56f9521c91ba66d1e2f54aa96ce59d8a40f 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -271,7 +271,7 @@ void MainWindow::_buildCommonWidgets(void) // Log player // TODO: Make this optional with a preferences setting or under a "View" menu logPlayer = new QGCMAVLinkLogPlayer(statusBar()); - statusBar()->addPermanentWidget(logPlayer); + statusBar()->addPermanentWidget(logPlayer, 1); // Populate widget menu for (int i = 0, end = ARRAY_SIZE(rgDockWidgetNames); i < end; i++) { diff --git a/src/ui/QGCMAVLinkLogPlayer.cc b/src/ui/QGCMAVLinkLogPlayer.cc index 6a7d83b6eefe3cb054a0463bdff7d206823a0cc7..e28824f361dd0d6d2ee9a7331383829e2bda27c2 100644 --- a/src/ui/QGCMAVLinkLogPlayer.cc +++ b/src/ui/QGCMAVLinkLogPlayer.cc @@ -142,7 +142,6 @@ void QGCMAVLinkLogPlayer::_playbackStarted(void) _enablePlaybackControls(true); _ui->playButton->setChecked(true); _ui->playButton->setIcon(QIcon(":/res/Pause")); - _ui->positionSlider->setEnabled(false); } /// Signalled from LogReplayLink when replay is paused @@ -150,7 +149,6 @@ void QGCMAVLinkLogPlayer::_playbackPaused(void) { _ui->playButton->setIcon(QIcon(":/res/Play")); _ui->playButton->setChecked(false); - _ui->positionSlider->setEnabled(true); } void QGCMAVLinkLogPlayer::_playbackPercentCompleteChanged(int percentComplete) diff --git a/src/ui/QGCMAVLinkLogPlayer.ui b/src/ui/QGCMAVLinkLogPlayer.ui index f80f6ac95533a8a71b45360e7c7eedfea3560d73..2253c908d1b3f10ecfbfdbf914d1a8bffb812650 100644 --- a/src/ui/QGCMAVLinkLogPlayer.ui +++ b/src/ui/QGCMAVLinkLogPlayer.ui @@ -75,7 +75,7 @@ 100 - false + true Qt::Horizontal