7import QGroundControl.Controls
8import QGroundControl.FactControls
13 QGCPalette { id: qgcPal }
15 property real availableHeight
16 property real availableWidth
20 property var tuningMode
21 property double chartDisplaySec: 8 // number of seconds to display
22 property bool showAutoModeChange: false
23 property bool showAutoTuning: false
24 property bool useAutoTuning: false
26 property real _margins: ScreenTools.defaultFontPixelHeight / 2
27 property int _currentAxis: 0
28 property var _xAxis: xAxis
29 property var _yAxis: yAxis
30 property int _msecs: 0
31 property double _last_t: 0
32 property var _savedTuningParamValues: [ ]
34 readonly property int _tickSeparation: 5
35 readonly property int _maxTickSections: 10
37 property string _chartTitle: ""
38 readonly property var _seriesColors: ["#21be2b", "#c62828", "#1565c0", "#f9a825", "#6a1b9a", "#00838f"]
39 property var _legendModel: []
42 id: lineSeriesComponent
46 function adjustYAxisMin(yAxis, newValue) {
47 var newMin = Math.min(yAxis.min, newValue)
48 if (newMin % 5 != 0) {
50 newMin = Math.floor(newMin / _tickSeparation) * _tickSeparation
55 function adjustYAxisMax(yAxis, newValue) {
56 var newMax = Math.max(yAxis.max, newValue)
57 if (newMax % 5 != 0) {
59 newMax = Math.floor(newMax / _tickSeparation) * _tickSeparation
64 function resetGraphs() {
65 for (var i = 0; i < chart.seriesList.length; ++i) {
66 chart.seriesList[i].clear()
76 // Save the current set of tuning values so we can reset to them
77 function saveTuningParamValues() {
78 _savedTuningParamValues = [ ]
79 for (var i=0; i<axis[_currentAxis].params.count; i++) {
80 var currentTuneParam = controller.getParameterFact(-1, axis[_currentAxis].params.get(i).param)
81 _savedTuningParamValues.push(currentTuneParam.valueString)
83 savedRepeater.model = _savedTuningParamValues
86 function resetToSavedTuningParamValues() {
87 for (var i=0; i<axis[_currentAxis].params.count; i++) {
88 var currentTuneParam = controller.getParameterFact(-1,
89 axis[_currentAxis].params.get(i).param)
90 currentTuneParam.value = _savedTuningParamValues[i]
94 function axisIndexChanged() {
95 while (chart.seriesList.length > 0) {
96 // Do not call s.destroy() here. QGraphsView holds an internal
97 // pointer to the series that is only cleared during the next
98 // updatePolish() pass. Destroying the series before that pass
99 // causes a SIGSEGV in QGraphsView::updatePolish(). The series is
100 // parented to chart so it is freed when the chart is destroyed.
101 // GPU/graph resources are released by removeSeries().
102 chart.removeSeries(chart.seriesList[0])
105 axis[_currentAxis].plot.forEach(function(e, idx) {
106 var color = _seriesColors[idx % _seriesColors.length]
107 var series = lineSeriesComponent.createObject(chart, {name: e.name, color: color})
108 chart.addSeries(series)
109 legendItems.push({name: e.name, color: color})
111 _legendModel = legendItems
112 var chartTitle = axis[_currentAxis].plotTitle
113 if (chartTitle == null)
114 chartTitle = axis[_currentAxis].name
115 _chartTitle = chartTitle + " " + title
116 saveTuningParamValues()
120 Component.onCompleted: {
122 globals.activeVehicle.setPIDTuningTelemetryMode(tuningMode)
123 saveTuningParamValues()
126 Component.onDestruction: globals.activeVehicle.setPIDTuningTelemetryMode(Vehicle.ModeDisabled)
127 on_CurrentAxisChanged: axisIndexChanged()
136 _xAxis.max = _msecs / 1000
137 _xAxis.min = _msecs / 1000 - chartDisplaySec
139 var firstPoint = _msecs == 0
141 var len = axis[_currentAxis].plot.length
142 for (var i = 0; i < len; ++i) {
143 var value = axis[_currentAxis].plot[i].value
145 chart.seriesList[i].append(_msecs/1000, value)
150 adjustYAxisMin(_yAxis, value)
151 adjustYAxisMax(_yAxis, value)
154 var minSec = _msecs/1000 - 3*60
155 while (chart.seriesList[i].count > 0 && chart.seriesList[i].at(0).x < minSec) {
156 chart.seriesList[i].remove(0)
161 var t = new Date().getTime() // in ms
167 property int _maxPointCount: 10000 / interval
172 Layout.fillWidth: true
173 Layout.alignment: Qt.AlignTop
174 spacing: ScreenTools.defaultFontPixelHeight / 4
175 clip: true // chart has redraw problems
180 font.pointSize: ScreenTools.defaultFontPointSize
181 font.family: ScreenTools.normalFontFamily
182 anchors.horizontalCenter: parent.horizontalCenter
187 width: Math.max(_minChartWidth, availableWidth - rightPanel.width - parent.spacing - _margins)
188 height: Math.max(_minChartHeight, availableHeight - leftPanelBottomColumn.height - chartTitleLabel.height - legendRow.height - parent.spacing * 3 - _margins)
190 property real _minChartWidth: ScreenTools.defaultFontPixelWidth * 40
191 property real _minChartHeight: ScreenTools.defaultFontPixelHeight * 15
194 colorScheme: qgcPal.globalTheme === QGCPalette.Light ? GraphsTheme.ColorScheme.Light : GraphsTheme.ColorScheme.Dark
195 plotAreaBackgroundColor: qgcPal.window
196 grid.mainColor: Qt.rgba(qgcPal.text.r, qgcPal.text.g, qgcPal.text.b, 0.5)
197 grid.subColor: Qt.rgba(qgcPal.text.r, qgcPal.text.g, qgcPal.text.b, 0.3)
199 labelBackgroundVisible: false
200 labelTextColor: qgcPal.text
208 titleText: ScreenTools.isShortScreen ? "" : qsTr("sec")
209 titleFont.pointSize: ScreenTools.defaultFontPointSize
210 titleFont.family: ScreenTools.normalFontFamily
218 tickInterval: _tickSeparation
219 titleFont.pointSize: ScreenTools.defaultFontPointSize
220 titleFont.family: ScreenTools.normalFontFamily
223 // enable mouse dragging
225 property var _startPoint: undefined
226 property double _scaling: 0
228 onPressed: (mouse) => {
229 _startPoint = Qt.point(mouse.x, mouse.y)
230 if (chart.seriesList.length > 0) {
231 var start = chart.seriesList[0].dataPointCoordinatesAt(_startPoint.x, _startPoint.y)
232 var next = chart.seriesList[0].dataPointCoordinatesAt(mouse.x+1, mouse.y+1)
233 _scaling = next.x - start.x
236 onWheel: (wheel) => {
237 if (wheel.angleDelta.y > 0)
238 chartDisplaySec /= 1.2
240 chartDisplaySec *= 1.2
241 _xAxis.min = _xAxis.max - chartDisplaySec
243 onPositionChanged: (mouse) => {
244 if(_startPoint != undefined) {
245 dataTimer.running = false
246 var cp = Qt.point(mouse.x, mouse.y)
247 var dx = (cp.x - _startPoint.x) * _scaling
255 _startPoint = undefined
262 spacing: ScreenTools.defaultFontPixelWidth
263 anchors.horizontalCenter: parent.horizontalCenter
268 spacing: ScreenTools.defaultFontPixelWidth / 2
270 width: ScreenTools.defaultFontPixelHeight
271 height: ScreenTools.defaultFontPixelHeight / 3
272 color: modelData.color
273 anchors.verticalCenter: parent.verticalCenter
277 font.pointSize: ScreenTools.smallFontPointSize
284 id: leftPanelBottomColumn
285 spacing: ScreenTools.defaultFontPixelHeight / 4
292 onClicked: resetGraphs()
296 text: dataTimer.running ? qsTr("Stop") : qsTr("Start")
298 dataTimer.running = !dataTimer.running
300 if (showAutoModeChange && autoModeChange.checked) {
301 globals.activeVehicle.flightMode = dataTimer.running ? globals.activeVehicle.stabilizedFlightMode : globals.activeVehicle.pauseFlightMode
306 target: globals.activeVehicle
308 if (armed && !dataTimer.running) { // start plotting on arming if not already running
309 dataTimer.running = true
317 visible: showAutoModeChange
319 text: qsTr("Automatic Flight Mode Switching")
322 dataTimer.running = false
327 visible: autoModeChange.checked
329 text: qsTr("Switches to 'Stabilized' when you click Start.")
330 font.pointSize: ScreenTools.smallFontPointSize
334 text: qsTr("Switches to '%1' when you click Stop.").arg(globals.activeVehicle.pauseFlightMode)
335 font.pointSize: ScreenTools.smallFontPointSize
343 Layout.alignment: Qt.AlignTop
346 visible: showAutoTuning
349 id: useAutoTuningRadio
350 text: qsTr("Use auto-tuning")
351 checked: useAutoTuning
352 onClicked: useAutoTuning = true
355 id: useManualTuningRadio
356 text: qsTr("Use manual tuning")
357 checked: !useAutoTuning
358 onClicked: useAutoTuning = false
363 visible: showAutoTuning && useAutoTuningRadio.checked
367 visible: !showAutoTuning || useManualTuningRadio.checked
372 visible: axis.length > 1
374 QGCLabel { text: qsTr("Select Tuning:") }
379 objectName: "pidTuning_axisButton_" + modelData.name.replace(/ /g, "")
381 checked: index == _currentAxis
382 onClicked: _currentAxis = index
388 // Instantiate all sliders (instead of switching the model), so that
389 // values are not changed unexpectedly if they do not match with a tick value.
395 model: axis[index].params
397 property int axisIndex: index
399 SettingsGroupLayout {
402 headingDescription: description
403 visible: _currentAxis === paramRepeater.axisIndex
404 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 40
407 fact: controller.getParameterFact(-1, param)
410 majorTickStepSize: step
411 Layout.fillWidth: true
418 QGCLabel { text: qsTr("Clipboard Values:") }
421 rows: savedRepeater.model.length
422 flow: GridLayout.TopToBottom
424 columnSpacing: _margins
427 model: axis[_currentAxis].params
429 QGCLabel { text: param }
435 QGCLabel { text: modelData }
444 text: qsTr("Save To Clipboard")
445 onClicked: saveTuningParamValues()
449 text: qsTr("Restore From Clipboard")
450 onClicked: resetToSavedTuningParamValues()