5 changed files with 501 additions and 453 deletions
@ -0,0 +1,429 @@ |
|||||||
|
/**************************************************************************** |
||||||
|
* |
||||||
|
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> |
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
import QtQuick 2.3 |
||||||
|
import QtQuick.Controls 1.2 |
||||||
|
import QtCharts 2.2 |
||||||
|
import QtQuick.Layouts 1.2 |
||||||
|
|
||||||
|
import QGroundControl 1.0 |
||||||
|
import QGroundControl.Controls 1.0 |
||||||
|
import QGroundControl.FactSystem 1.0 |
||||||
|
import QGroundControl.FactControls 1.0 |
||||||
|
import QGroundControl.ScreenTools 1.0 |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
layoutDirection: Qt.RightToLeft |
||||||
|
|
||||||
|
property var tuneList |
||||||
|
property var params |
||||||
|
|
||||||
|
property real _chartHeight: ScreenTools.defaultFontPixelHeight * 20 |
||||||
|
property real _margins: ScreenTools.defaultFontPixelHeight / 2 |
||||||
|
property string _currentTuneType: tuneList[0] |
||||||
|
property real _roll: _activeVehicle.roll.value |
||||||
|
property real _rollSetpoint: _activeVehicle.setpoint.roll.value |
||||||
|
property real _rollRate: _activeVehicle.rollRate.value |
||||||
|
property real _rollRateSetpoint: _activeVehicle.setpoint.rollRate.value |
||||||
|
property real _pitch: _activeVehicle.pitch.value |
||||||
|
property real _pitchSetpoint: _activeVehicle.setpoint.pitch.value |
||||||
|
property real _pitchRate: _activeVehicle.pitchRate.value |
||||||
|
property real _pitchRateSetpoint: _activeVehicle.setpoint.pitchRate.value |
||||||
|
property real _yaw: _activeVehicle.heading.value |
||||||
|
property real _yawSetpoint: _activeVehicle.setpoint.yaw.value |
||||||
|
property real _yawRate: _activeVehicle.yawRate.value |
||||||
|
property real _yawRateSetpoint: _activeVehicle.setpoint.yawRate.value |
||||||
|
property var _valueXAxis: valueXAxis |
||||||
|
property var _valueRateXAxis: valueRateXAxis |
||||||
|
property var _valueYAxis: valueYAxis |
||||||
|
property var _valueRateYAxis: valueRateYAxis |
||||||
|
property int _msecs: 0 |
||||||
|
property var _savedTuningParamValues: [ ] |
||||||
|
|
||||||
|
// The following are set when getValues is called |
||||||
|
property real _value |
||||||
|
property real _valueSetpoint |
||||||
|
property real _valueRate |
||||||
|
property real _valueRateSetpoint |
||||||
|
|
||||||
|
readonly property int _tickSeparation: 5 |
||||||
|
readonly property int _maxTickSections: 10 |
||||||
|
readonly property int _tuneListRollIndex: 0 |
||||||
|
readonly property int _tuneListPitchIndex: 1 |
||||||
|
readonly property int _tuneListYawIndex: 2 |
||||||
|
|
||||||
|
function adjustYAxisMin(yAxis, newValue) { |
||||||
|
var newMin = Math.min(yAxis.min, newValue) |
||||||
|
if (newMin % 5 != 0) { |
||||||
|
newMin -= 5 |
||||||
|
newMin = Math.floor(newMin / _tickSeparation) * _tickSeparation |
||||||
|
} |
||||||
|
yAxis.min = newMin |
||||||
|
} |
||||||
|
|
||||||
|
function adjustYAxisMax(yAxis, newValue) { |
||||||
|
var newMax = Math.max(yAxis.max, newValue) |
||||||
|
if (newMax % 5 != 0) { |
||||||
|
newMax += 5 |
||||||
|
newMax = Math.floor(newMax / _tickSeparation) * _tickSeparation |
||||||
|
} |
||||||
|
yAxis.max = newMax |
||||||
|
} |
||||||
|
|
||||||
|
function getValues() { |
||||||
|
if (_currentTuneType === tuneList[_tuneListRollIndex]) { |
||||||
|
_value = _roll |
||||||
|
_valueSetpoint = _rollSetpoint |
||||||
|
_valueRate = _rollRate |
||||||
|
_valueRateSetpoint = _rollRateSetpoint |
||||||
|
} else if (_currentTuneType === tuneList[_tuneListPitchIndex]) { |
||||||
|
_value = _pitch |
||||||
|
_valueSetpoint = _pitchSetpoint |
||||||
|
_valueRate = _pitchRate |
||||||
|
_valueRateSetpoint = _pitchRateSetpoint |
||||||
|
} else if (_currentTuneType === tuneList[_tuneListYawIndex]) { |
||||||
|
_value = _yaw |
||||||
|
_valueSetpoint = _yawSetpoint |
||||||
|
_valueRate = _yawRate |
||||||
|
_valueRateSetpoint = _yawRateSetpoint |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function resetGraphs() { |
||||||
|
valueSeries.removePoints(0, valueSeries.count) |
||||||
|
valueSetpointSeries.removePoints(0, valueSetpointSeries.count) |
||||||
|
valueRateSeries.removePoints(0, valueRateSeries.count) |
||||||
|
valueRateSetpointSeries.removePoints(0, valueRateSetpointSeries.count) |
||||||
|
_valueXAxis.min = 0 |
||||||
|
_valueXAxis.max = 0 |
||||||
|
_valueRateXAxis.min = 0 |
||||||
|
_valueRateXAxis.max = 0 |
||||||
|
_valueYAxis.min = 0 |
||||||
|
_valueYAxis.max = 10 |
||||||
|
_valueRateYAxis.min = 0 |
||||||
|
_valueRateYAxis.max = 10 |
||||||
|
_msecs = 0 |
||||||
|
} |
||||||
|
|
||||||
|
function currentTuneTypeIndex() { |
||||||
|
if (_currentTuneType === tuneList[_tuneListRollIndex]) { |
||||||
|
return _tuneListRollIndex |
||||||
|
} else if (_currentTuneType === tuneList[_tuneListPitchIndex]) { |
||||||
|
return _tuneListPitchIndex |
||||||
|
} else if (_currentTuneType === tuneList[_tuneListYawIndex]) { |
||||||
|
return _tuneListYawIndex |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Save the current set of tuning values so we can reset to them |
||||||
|
function saveTuningParamValues() { |
||||||
|
var tuneTypeIndex = currentTuneTypeIndex() |
||||||
|
|
||||||
|
_savedTuningParamValues = [ ] |
||||||
|
var currentTuneParams = params[tuneTypeIndex] |
||||||
|
for (var i=0; i<currentTuneParams.length; i++) { |
||||||
|
_savedTuningParamValues.push(currentTuneParams[i].valueString) |
||||||
|
} |
||||||
|
savedRepeater.model = _savedTuningParamValues |
||||||
|
} |
||||||
|
|
||||||
|
function resetToSavedTuningParamValues() { |
||||||
|
var tuneTypeIndex = currentTuneTypeIndex() |
||||||
|
|
||||||
|
for (var i=0; i<_savedTuningParamValues.length; i++) { |
||||||
|
params[tuneTypeIndex][i].value = _savedTuningParamValues[i] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Component.onCompleted: { |
||||||
|
saveTuningParamValues() |
||||||
|
} |
||||||
|
|
||||||
|
on_CurrentTuneTypeChanged: { |
||||||
|
saveTuningParamValues() |
||||||
|
resetGraphs() |
||||||
|
} |
||||||
|
|
||||||
|
ExclusiveGroup { |
||||||
|
id: tuneTypeRadios |
||||||
|
} |
||||||
|
|
||||||
|
ValueAxis { |
||||||
|
id: valueXAxis |
||||||
|
min: 0 |
||||||
|
max: 0 |
||||||
|
labelFormat: "%d" |
||||||
|
titleText: "sec" |
||||||
|
} |
||||||
|
|
||||||
|
ValueAxis { |
||||||
|
id: valueRateXAxis |
||||||
|
min: 0 |
||||||
|
max: 0 |
||||||
|
labelFormat: "%d" |
||||||
|
titleText: "sec" |
||||||
|
} |
||||||
|
|
||||||
|
ValueAxis { |
||||||
|
id: valueYAxis |
||||||
|
min: 0 |
||||||
|
max: 10 |
||||||
|
titleText: "deg" |
||||||
|
tickCount: Math.min(((max - min) / _tickSeparation), _maxTickSections) + 1 |
||||||
|
} |
||||||
|
|
||||||
|
ValueAxis { |
||||||
|
id: valueRateYAxis |
||||||
|
min: 0 |
||||||
|
max: 10 |
||||||
|
titleText: "deg/s" |
||||||
|
tickCount: Math.min(((max - min) / _tickSeparation), _maxTickSections) + 1 |
||||||
|
} |
||||||
|
|
||||||
|
Timer { |
||||||
|
id: dataTimer |
||||||
|
interval: 10 |
||||||
|
running: false |
||||||
|
repeat: true |
||||||
|
|
||||||
|
onTriggered: { |
||||||
|
var seconds = _msecs / 1000 |
||||||
|
_valueXAxis.max = seconds |
||||||
|
_valueRateXAxis.max = seconds |
||||||
|
|
||||||
|
getValues() |
||||||
|
|
||||||
|
valueSeries.append(seconds, _value) |
||||||
|
adjustYAxisMin(_valueYAxis, _value) |
||||||
|
adjustYAxisMax(_valueYAxis, _value) |
||||||
|
|
||||||
|
valueSetpointSeries.append(seconds, _valueSetpoint) |
||||||
|
adjustYAxisMin(_valueYAxis, _valueSetpoint) |
||||||
|
adjustYAxisMax(_valueYAxis, _valueSetpoint) |
||||||
|
|
||||||
|
valueRateSeries.append(seconds, _valueRate) |
||||||
|
adjustYAxisMin(_valueRateYAxis, _valueRate) |
||||||
|
adjustYAxisMax(_valueRateYAxis, _valueRate) |
||||||
|
|
||||||
|
valueRateSetpointSeries.append(seconds, _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 |
||||||
|
property int _maxPointCount: 10000 / interval |
||||||
|
} |
||||||
|
|
||||||
|
Column { |
||||||
|
spacing: _margins |
||||||
|
Layout.alignment: Qt.AlignTop |
||||||
|
|
||||||
|
QGCLabel { text: qsTr("Tuning Axis:") } |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
spacing: _margins |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: tuneList |
||||||
|
QGCRadioButton { |
||||||
|
text: modelData |
||||||
|
checked: _currentTuneType === modelData |
||||||
|
exclusiveGroup: tuneTypeRadios |
||||||
|
|
||||||
|
onClicked: _currentTuneType = modelData |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Item { width: 1; height: 1 } |
||||||
|
|
||||||
|
QGCLabel { text: qsTr("Tuning Values:") } |
||||||
|
|
||||||
|
GridLayout { |
||||||
|
rows: factList.length |
||||||
|
flow: GridLayout.TopToBottom |
||||||
|
rowSpacing: _margins |
||||||
|
columnSpacing: _margins |
||||||
|
|
||||||
|
property var factList: params[tuneList.indexOf(_currentTuneType)] |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: parent.factList |
||||||
|
|
||||||
|
QGCLabel { text: modelData.name } |
||||||
|
} |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: parent.factList |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: "-" |
||||||
|
onClicked: { |
||||||
|
var value = modelData.value |
||||||
|
modelData.value -= value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: parent.factList |
||||||
|
|
||||||
|
FactTextField { |
||||||
|
Layout.fillWidth: true |
||||||
|
fact: modelData |
||||||
|
showUnits: false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: parent.factList |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: "+" |
||||||
|
onClicked: { |
||||||
|
var value = modelData.value |
||||||
|
modelData.value += value * adjustPercentModel.get(adjustPercentCombo.currentIndex).value |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
QGCLabel { text: qsTr("Increment/Decrement %") } |
||||||
|
|
||||||
|
QGCComboBox { |
||||||
|
id: adjustPercentCombo |
||||||
|
model: ListModel { |
||||||
|
id: adjustPercentModel |
||||||
|
ListElement { text: "5"; value: 0.05 } |
||||||
|
ListElement { text: "10"; value: 0.10 } |
||||||
|
ListElement { text: "15"; value: 0.15 } |
||||||
|
ListElement { text: "20"; value: 0.20 } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Item { width: 1; height: 1 } |
||||||
|
|
||||||
|
QGCLabel { text: qsTr("Saved Tuning Values:") } |
||||||
|
|
||||||
|
GridLayout { |
||||||
|
rows: savedRepeater.model.length |
||||||
|
flow: GridLayout.TopToBottom |
||||||
|
rowSpacing: _margins |
||||||
|
columnSpacing: _margins |
||||||
|
|
||||||
|
Repeater { |
||||||
|
model: params[tuneList.indexOf(_currentTuneType)] |
||||||
|
|
||||||
|
QGCLabel { text: modelData.name } |
||||||
|
} |
||||||
|
|
||||||
|
Repeater { |
||||||
|
id: savedRepeater |
||||||
|
|
||||||
|
QGCLabel { text: modelData } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
spacing: _margins |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: qsTr("Save Values") |
||||||
|
onClicked: saveTuningParamValues() |
||||||
|
} |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: qsTr("Reset To Saved Values") |
||||||
|
onClicked: resetToSavedTuningParamValues() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Item { width: 1; height: 1 } |
||||||
|
|
||||||
|
QGCLabel { text: qsTr("Chart:") } |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
spacing: _margins |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: qsTr("Clear") |
||||||
|
onClicked: resetGraphs() |
||||||
|
} |
||||||
|
|
||||||
|
QGCButton { |
||||||
|
text: dataTimer.running ? qsTr("Stop") : qsTr("Start") |
||||||
|
onClicked: dataTimer.running = !dataTimer.running |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Column { |
||||||
|
Layout.fillWidth: true |
||||||
|
|
||||||
|
ChartView { |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: parent.right |
||||||
|
height: availableHeight / 2 |
||||||
|
title: _currentTuneType |
||||||
|
antialiasing: true |
||||||
|
legend.alignment: Qt.AlignRight |
||||||
|
|
||||||
|
LineSeries { |
||||||
|
id: valueSeries |
||||||
|
name: "Response" |
||||||
|
axisY: valueYAxis |
||||||
|
axisX: valueXAxis |
||||||
|
} |
||||||
|
|
||||||
|
LineSeries { |
||||||
|
id: valueSetpointSeries |
||||||
|
name: "Command" |
||||||
|
axisY: valueYAxis |
||||||
|
axisX: valueXAxis |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ChartView { |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: parent.right |
||||||
|
height: availableHeight / 2 |
||||||
|
title: _currentTuneType + qsTr(" Rate") |
||||||
|
antialiasing: true |
||||||
|
legend.alignment: Qt.AlignRight |
||||||
|
|
||||||
|
LineSeries { |
||||||
|
id: valueRateSeries |
||||||
|
name: "Response" |
||||||
|
axisY: valueRateYAxis |
||||||
|
axisX: valueRateXAxis |
||||||
|
} |
||||||
|
|
||||||
|
LineSeries { |
||||||
|
id: valueRateSetpointSeries |
||||||
|
name: "Command" |
||||||
|
axisY: valueRateYAxis |
||||||
|
axisX: valueRateXAxis |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} // RowLayout |
Loading…
Reference in new issue