QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SensorsSetup.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5
6import QGroundControl
7import QGroundControl.FactControls
8import QGroundControl.Controls
9
10/// Page for sensor calibration. This control is used within the SensorsComponent control and can also be used
11/// standalone for custom uis. When using standadalone you can use the various show* bools to show/hide what you want.
12Item {
13 id: _root
14
15 property bool showSensorCalibrationCompass: true ///< true: Show this calibration button
16 property bool showSensorCalibrationGyro: true ///< true: Show this calibration button
17 property bool showSensorCalibrationAccel: true ///< true: Show this calibration button
18 property bool showSensorCalibrationLevel: true ///< true: Show this calibration button
19 property bool showSensorCalibrationAirspeed: true ///< true: Show this calibration button
20 property bool showSetOrientations: true ///< true: Show this calibration button
21 property bool showNextButton: false ///< true: Show Next button which will signal nextButtonClicked
22
23 signal nextButtonClicked
24
25 // Help text which is shown both in the status text area prior to pressing a cal button and in the
26 // pre-calibration dialog.
27
28 readonly property string boardRotationText: qsTr("If the orientation is in the direction of flight, select ROTATION_NONE.")
29 readonly property string compassRotationText: qsTr("If the orientation is in the direction of flight, select ROTATION_NONE.")
30
31 readonly property string compassHelp: qsTr("For Compass calibration you will need to rotate your vehicle through a number of positions.")
32 readonly property string gyroHelp: qsTr("For Gyroscope calibration you will need to place your vehicle on a surface and leave it still.")
33 readonly property string accelHelp: qsTr("For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds.")
34 readonly property string levelHelp: qsTr("To level the horizon you need to place the vehicle in its level flight position and leave still.")
35 readonly property string airspeedHelp: qsTr("For Airspeed calibration you will need to keep your airspeed sensor out of any wind and then blow across the sensor. Do not touch the sensor or obstruct any holes during the calibration.")
36
37 readonly property string statusTextAreaDefaultText: qsTr("Start the individual calibration steps by clicking one of the buttons to the left.")
38
39 // Used to pass what type of calibration is being performed to the preCalibrationDialog
40 property string preCalibrationDialogType
41
42 // Used to pass help text to the preCalibrationDialog dialog
43 property string preCalibrationDialogHelp
44
45 property Fact cal_mag0_id: controller.getParameterFact(-1, "CAL_MAG0_ID")
46 property Fact cal_mag1_id: controller.getParameterFact(-1, "CAL_MAG1_ID")
47 property Fact cal_mag2_id: controller.getParameterFact(-1, "CAL_MAG2_ID")
48 property Fact cal_mag0_rot: controller.getParameterFact(-1, "CAL_MAG0_ROT")
49 property Fact cal_mag1_rot: controller.getParameterFact(-1, "CAL_MAG1_ROT")
50 property Fact cal_mag2_rot: controller.getParameterFact(-1, "CAL_MAG2_ROT")
51
52 property Fact cal_gyro0_id: controller.getParameterFact(-1, "CAL_GYRO0_ID")
53 property Fact cal_acc0_id: controller.getParameterFact(-1, "CAL_ACC0_ID")
54
55 property Fact sens_board_rot: controller.getParameterFact(-1, "SENS_BOARD_ROT")
56 property Fact sens_dpres_off: controller.getParameterFact(-1, "SENS_DPRES_OFF")
57
58 // Id > = signals compass available, rot < 0 signals internal compass
59 property bool showCompass0Rot: cal_mag0_id.value > 0 && cal_mag0_rot.value >= 0
60 property bool showCompass1Rot: cal_mag1_id.value > 0 && cal_mag1_rot.value >= 0
61 property bool showCompass2Rot: cal_mag2_id.value > 0 && cal_mag2_rot.value >= 0
62
63 property bool _sensorsHaveFixedOrientation: QGroundControl.corePlugin.options.sensorsHaveFixedOrientation
64 property string _calMagIdParamFormat: "CAL_MAG#_ID"
65 property string _calMagRotParamFormat: "CAL_MAG#_ROT"
66 property bool _allMagsDisabled: controller.parameterExists(-1, "SYS_HAS_MAG") ? controller.getParameterFact(-1, "SYS_HAS_MAG").value === 0 : false
67 property bool _boardOrientationChangeAllowed: !_sensorsHaveFixedOrientation && setOrientationsDialogShowBoardOrientation
68 property bool _compassOrientationChangeAllowed: !_sensorsHaveFixedOrientation
69 property int _arbitrarilyLargeMaxMagIndex: 50
70
71 function currentMagParamCount() {
72 if (_allMagsDisabled) {
73 return 0
74 } else {
75 for (var index=0; index<_arbitrarilyLargeMaxMagIndex; index++) {
76 var magIdParam = _calMagIdParamFormat.replace("#", index)
77 if (!controller.parameterExists(-1, magIdParam)) {
78 return index
79 }
80 }
81 console.warn("SensorSetup.qml:currentMagParamCount internal error")
82 return -1
83 }
84 }
85
86 function currentExternalMagCount() {
87 if (_allMagsDisabled) {
88 return 0
89 } else {
90 var externalMagCount = 0
91 for (var index=0; index<_arbitrarilyLargeMaxMagIndex; index++) {
92 var magIdParam = _calMagIdParamFormat.replace("#", index)
93 if (controller.parameterExists(-1, magIdParam)) {
94 var calMagIdFact = controller.getParameterFact(-1, magIdParam)
95 var calMagRotFact = controller.getParameterFact(-1, _calMagRotParamFormat.replace("#", index))
96 if (calMagIdFact.value > 0 && calMagRotFact.value >= 0) {
97 externalMagCount++
98 }
99 } else {
100 return externalMagCount
101 }
102 }
103 console.warn("SensorSetup.qml:currentExternalMagCount internal error")
104 return 0
105 }
106 }
107
108 function orientationsButtonVisible() {
109 // Deliberately independent of setOrientationsDialogShowBoardOrientation: that is a
110 // transient flag controlling dialog contents only (cleared by onMagCalComplete) and
111 // must not hide the Orientations button after a compass calibration completes.
112 return !_sensorsHaveFixedOrientation && showSetOrientations
113 }
114
115 QGCPalette { id: qgcPal; colorGroupEnabled: true }
116
117 SensorsComponentController {
118 id: controller
119 statusLog: statusTextArea
120 progressBar: progressBar
121 orientationCalAreaHelpText: orientationCalAreaHelpText
122
123 onResetStatusTextArea: statusLog.text = statusTextAreaDefaultText
124
125 onMagCalComplete: {
126 setOrientationsDialogShowBoardOrientation = false
127 setOrientationsDialogFactory.open({ title: qsTr("Compass Calibration Complete"), showRebootMessage: true })
128 }
129
130 onWaitingForCancelChanged: {
131 if (controller.waitingForCancel) {
132 waitForCancelDialogFactory.open()
133 }
134 }
135
136 onCalibrationActiveChanged: {
137 if (controller.calibrationActive) {
138 globals.navigationBlockedReason = qsTr("Complete or cancel the current calibration first")
139 } else {
140 globals.navigationBlockedReason = ""
141 }
142 }
143 }
144
145 Component.onDestruction: globals.navigationBlockedReason = ""
146
147 QGCPopupDialogFactory {
148 id: waitForCancelDialogFactory
149
150 dialogComponent: waitForCancelDialogComponent
151 }
152
153 Component {
154 id: waitForCancelDialogComponent
155
156 QGCSimpleMessageDialog {
157 title: qsTr("Calibration Cancel")
158 text: qsTr("Waiting for Vehicle to response to Cancel. This may take a few seconds.")
159 buttons: 0
160
161 Connections {
162 target: controller
163
164 onWaitingForCancelChanged: {
165 if (!controller.waitingForCancel) {
166 close()
167 }
168 }
169 }
170 }
171 }
172
173 QGCPopupDialogFactory {
174 id: preCalibrationDialogFactory
175
176 dialogComponent: preCalibrationDialogComponent
177 }
178
179 Component {
180 id: preCalibrationDialogComponent
181
182 QGCPopupDialog {
183 buttons: Dialog.Cancel | Dialog.Ok
184
185 onAccepted: {
186 if (preCalibrationDialogType == "gyro") {
187 controller.calibrateGyro()
188 } else if (preCalibrationDialogType == "accel") {
189 controller.calibrateAccel()
190 } else if (preCalibrationDialogType == "level") {
191 controller.calibrateLevel()
192 } else if (preCalibrationDialogType == "compass") {
193 controller.calibrateCompass()
194 } else if (preCalibrationDialogType == "airspeed") {
195 controller.calibrateAirspeed()
196 }
197 }
198
199 ColumnLayout {
200 spacing: ScreenTools.defaultFontPixelHeight
201
202 QGCLabel {
203 Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 50
204 Layout.preferredWidth: innerColumn.width
205 wrapMode: Text.WordWrap
206 text: preCalibrationDialogHelp
207 }
208
209 Column {
210 id: innerColumn
211 spacing: parent.spacing
212
213 QGCLabel {
214 id: boardRotationHelp
215 wrapMode: Text.WordWrap
216 visible: !_sensorsHaveFixedOrientation && (preCalibrationDialogType == "accel" || preCalibrationDialogType == "compass")
217 text: qsTr("Set autopilot orientation before calibrating.")
218 }
219
220 Column {
221 visible: boardRotationHelp.visible
222 QGCLabel { text: qsTr("Autopilot Orientation") }
223
224 FactComboBox {
225 sizeToContents: true
226 fact: sens_board_rot
227 }
228
229 QGCLabel {
230 wrapMode: Text.WordWrap
231 text: qsTr("ROTATION_NONE indicates component points in direction of flight.")
232 }
233 }
234
235 QGCLabel {
236 wrapMode: Text.WordWrap
237 text: qsTr("Click Ok to start calibration.")
238 }
239 }
240 }
241 }
242 }
243
244 property bool setOrientationsDialogShowBoardOrientation: true
245
246 QGCPopupDialogFactory {
247 id: setOrientationsDialogFactory
248
249 dialogComponent: setOrientationsDialogComponent
250 }
251
252 Component {
253 id: setOrientationsDialogComponent
254
255 QGCPopupDialog {
256 buttons: Dialog.Ok
257
258 property bool showRebootMessage: true
259
260 ColumnLayout {
261 spacing: ScreenTools.defaultFontPixelHeight
262
263 QGCLabel {
264 text: qsTr("Reboot the vehicle prior to flight.")
265 visible: showRebootMessage
266 }
267
268 QGCLabel {
269 text: qsTr("Adjust orientations as needed.\n\nROTATION_NONE indicates component points in direction of flight.")
270 visible: _boardOrientationChangeAllowed || (_compassOrientationChangeAllowed && currentExternalMagCount() !== 0)
271 }
272
273 Column {
274 visible: _boardOrientationChangeAllowed
275
276 QGCLabel {
277 text: qsTr("Autopilot Orientation")
278 }
279
280 FactComboBox {
281 sizeToContents: true
282 fact: sens_board_rot
283 }
284 }
285
286 Repeater {
287 model: _compassOrientationChangeAllowed ? currentMagParamCount() : 0
288
289 Column {
290 // id > = signals compass available, rot < 0 signals internal compass
291 visible: calMagIdFact.value > 0 && calMagRotFact.value >= 0
292
293 property Fact calMagIdFact: controller.getParameterFact(-1, _calMagIdParamFormat.replace("#", index))
294 property Fact calMagRotFact: controller.getParameterFact(-1, _calMagRotParamFormat.replace("#", index))
295
296 QGCLabel {
297 text: qsTr("Mag %1 Orientation").arg(index)
298 }
299
300 FactComboBox {
301 sizeToContents: true
302 fact: parent.calMagRotFact
303 }
304 }
305 }
306 } // Column
307 } // QGCPopupDialog
308 } // Component - setOrientationsDialogComponent
309
310 property string sectionIdFilter: ""
311
312 // Completed (green) side indicators are only meaningful for the sensor that was
313 // just calibrated. Return them to the neutral idle state when the preview switches
314 // to a different sensor.
315 onSectionIdFilterChanged: controller.resetSidesToIdle()
316
317 // Section IDs are untranslated — compare with raw literals.
318 function sectionVisible(sectionId) {
319 switch (sectionId) {
320 case "Compass":
321 return !_allMagsDisabled && QGroundControl.corePlugin.options.showSensorCalibrationCompass && showSensorCalibrationCompass
322 case "Gyroscope":
323 return QGroundControl.corePlugin.options.showSensorCalibrationGyro && showSensorCalibrationGyro
324 case "Accelerometer":
325 return QGroundControl.corePlugin.options.showSensorCalibrationAccel && showSensorCalibrationAccel
326 case "Level Horizon":
327 return QGroundControl.corePlugin.options.showSensorCalibrationLevel && showSensorCalibrationLevel
328 case "Airspeed":
329 return vehicleComponent.airspeedCalSupported && QGroundControl.corePlugin.options.showSensorCalibrationAirspeed && showSensorCalibrationAirspeed
330 case "Orientations":
331 return orientationsButtonVisible()
332 default:
333 return true
334 }
335 }
336
337 function _startCalibration(type, help, title) {
338 preCalibrationDialogType = type
339 preCalibrationDialogHelp = help
340 preCalibrationDialogFactory.open({ title: title })
341 }
342
343 // Mag calibration requires the vehicle to be rotated while a side is calibrating
344 function _sideRotating(calState) {
345 return (calState === VehicleRotationCal.CalState.InProgress) && controller.magCalInProgress
346 }
347
348 property bool _showOrientationPreview: !controller.calibrationActive &&
349 (sectionIdFilter === "Accelerometer" || sectionIdFilter === "Compass" || sectionIdFilter === "Gyroscope")
350
351 property bool _showAllSidesPreview: _showOrientationPreview &&
352 (sectionIdFilter === "Accelerometer" || sectionIdFilter === "Compass")
353
354 property bool _showDownOnlyPreview: _showOrientationPreview &&
355 sectionIdFilter === "Gyroscope"
356
357 property bool _showStatusPreview: !controller.calibrationActive &&
358 (sectionIdFilter === "Level Horizon" || sectionIdFilter === "Airspeed")
359
360 ColumnLayout {
361 anchors.fill: parent
362 spacing: 0
363
364 // Calibration trigger buttons — one per section, shown based on sectionIdFilter
365 ColumnLayout {
366 Layout.fillWidth: true
367 spacing: ScreenTools.defaultFontPixelHeight / 2
368 visible: !controller.calibrationActive
369
370 QGCButton {
371 objectName: "sensorsSetup_calibrateCompass"
372 Layout.fillWidth: true
373 text: qsTr("Calibrate Compass")
374 visible: sectionIdFilter === "" || sectionIdFilter === "Compass"
375 onClicked: _startCalibration("compass", compassHelp, qsTr("Calibrate Compass"))
376 }
377
378 QGCButton {
379 Layout.fillWidth: true
380 text: qsTr("Calibrate Gyroscope")
381 visible: sectionIdFilter === "" || sectionIdFilter === "Gyroscope"
382 onClicked: _startCalibration("gyro", gyroHelp, qsTr("Calibrate Gyro"))
383 }
384
385 QGCButton {
386 objectName: "sensorsSetup_calibrateAccel"
387 Layout.fillWidth: true
388 text: qsTr("Calibrate Accelerometer")
389 visible: sectionIdFilter === "" || sectionIdFilter === "Accelerometer"
390 onClicked: _startCalibration("accel", accelHelp, qsTr("Calibrate Accelerometer"))
391 }
392
393 QGCButton {
394 Layout.fillWidth: true
395 text: qsTr("Level Horizon")
396 enabled: cal_acc0_id.value !== 0 && cal_gyro0_id.value !== 0
397 visible: sectionIdFilter === "" || sectionIdFilter === "Level Horizon"
398 onClicked: _startCalibration("level", levelHelp, qsTr("Level Horizon"))
399 }
400
401 QGCButton {
402 Layout.fillWidth: true
403 text: qsTr("Calibrate Airspeed")
404 visible: sectionIdFilter === "" || sectionIdFilter === "Airspeed"
405 onClicked: _startCalibration("airspeed", airspeedHelp, qsTr("Calibrate Airspeed"))
406 }
407
408 QGCButton {
409 Layout.fillWidth: true
410 text: qsTr("Set Orientations")
411 visible: sectionIdFilter === "" || sectionIdFilter === "Orientations"
412 onClicked: {
413 setOrientationsDialogShowBoardOrientation = true
414 setOrientationsDialogFactory.open({ title: qsTr("Set Orientations"), showRebootMessage: false })
415 }
416 }
417
418 QGCButton {
419 Layout.fillWidth: true
420 text: qsTr("Factory Reset")
421 visible: sectionIdFilter === "" || sectionIdFilter === "Orientations"
422 onClicked: controller.resetFactoryParameters()
423 }
424
425 QGCButton {
426 Layout.fillWidth: true
427 text: qsTr("Next")
428 visible: showNextButton
429 onClicked: _root.nextButtonClicked()
430 }
431 }
432
433 // Active calibration area — visible during calibration
434 RowLayout {
435 Layout.fillWidth: true
436 visible: controller.calibrationActive
437 spacing: ScreenTools.defaultFontPixelWidth
438
439 ProgressBar {
440 id: progressBar
441 objectName: "sensorsSetup_progressBar"
442 Layout.fillWidth: true
443 }
444
445 QGCButton {
446 objectName: "sensorsSetup_cancelCalibration"
447 text: qsTr("Cancel")
448 onClicked: controller.cancelCalibration()
449 }
450 }
451
452 Item {
453 Layout.fillWidth: true
454 Layout.fillHeight: true
455 visible: controller.calibrationActive || _showOrientationPreview || _showStatusPreview
456
457 TextArea {
458 id: statusTextArea
459 anchors.fill: parent
460 readOnly: true
461 visible: !orientationCalArea.visible
462 text: statusTextAreaDefaultText
463 color: qgcPal.text
464 background: Rectangle { color: qgcPal.windowShade }
465 }
466
467 Rectangle {
468 id: orientationCalArea
469 anchors.fill: parent
470 visible: controller.showOrientationCalArea || _showOrientationPreview
471 color: qgcPal.windowShade
472
473 QGCLabel {
474 id: orientationCalAreaHelpText
475 anchors.margins: ScreenTools.defaultFontPixelWidth
476 anchors.top: orientationCalArea.top
477 anchors.left: orientationCalArea.left
478 width: parent.width
479 wrapMode: Text.WordWrap
480 font.pointSize: ScreenTools.mediumFontPointSize
481 visible: text !== ""
482 }
483
484 Flow {
485 anchors.topMargin: ScreenTools.defaultFontPixelWidth
486 anchors.top: orientationCalAreaHelpText.bottom
487 anchors.bottom: parent.bottom
488 anchors.left: parent.left
489 anchors.right: parent.right
490 spacing: ScreenTools.defaultFontPixelWidth / 2
491
492 property real indicatorWidth: (width / 3) - (spacing * 2)
493 property real indicatorHeight: (height / 2) - spacing
494
495 VehicleRotationCal {
496 objectName: "sensorsCal_downSide"
497 width: parent.indicatorWidth
498 height: parent.indicatorHeight
499 visible: controller.orientationCalDownSideVisible || _showAllSidesPreview || _showDownOnlyPreview
500 calState: controller.orientationCalDownSideState
501 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
502 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleDownRotate.png" : "qrc:///qmlimages/VehicleDown.png"
503 }
504 VehicleRotationCal {
505 objectName: "sensorsCal_upsideDownSide"
506 width: parent.indicatorWidth
507 height: parent.indicatorHeight
508 visible: controller.orientationCalUpsideDownSideVisible || _showAllSidesPreview
509 calState: controller.orientationCalUpsideDownSideState
510 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
511 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleUpsideDownRotate.png" : "qrc:///qmlimages/VehicleUpsideDown.png"
512 }
513 VehicleRotationCal {
514 objectName: "sensorsCal_noseDownSide"
515 width: parent.indicatorWidth
516 height: parent.indicatorHeight
517 visible: controller.orientationCalNoseDownSideVisible || _showAllSidesPreview
518 calState: controller.orientationCalNoseDownSideState
519 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
520 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleNoseDownRotate.png" : "qrc:///qmlimages/VehicleNoseDown.png"
521 }
522 VehicleRotationCal {
523 objectName: "sensorsCal_tailDownSide"
524 width: parent.indicatorWidth
525 height: parent.indicatorHeight
526 visible: controller.orientationCalTailDownSideVisible || _showAllSidesPreview
527 calState: controller.orientationCalTailDownSideState
528 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
529 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleTailDownRotate.png" : "qrc:///qmlimages/VehicleTailDown.png"
530 }
531 VehicleRotationCal {
532 objectName: "sensorsCal_leftSide"
533 width: parent.indicatorWidth
534 height: parent.indicatorHeight
535 visible: controller.orientationCalLeftSideVisible || _showAllSidesPreview
536 calState: controller.orientationCalLeftSideState
537 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
538 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleLeftRotate.png" : "qrc:///qmlimages/VehicleLeft.png"
539 }
540 VehicleRotationCal {
541 objectName: "sensorsCal_rightSide"
542 width: parent.indicatorWidth
543 height: parent.indicatorHeight
544 visible: controller.orientationCalRightSideVisible || _showAllSidesPreview
545 calState: controller.orientationCalRightSideState
546 calInProgressText: controller.magCalInProgress ? qsTr("Rotate") : qsTr("Hold Still")
547 imageSource: _sideRotating(calState) ? "qrc:///qmlimages/VehicleRightRotate.png" : "qrc:///qmlimages/VehicleRight.png"
548 }
549 }
550 }
551 }
552 }
553}