/*===================================================================== QGroundControl Open Source Ground Control Station (c) 2009 - 2015 QGROUNDCONTROL PROJECT This file is part of the QGROUNDCONTROL project QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . ======================================================================*/ /// @file /// @brief Radio Calibration /// @author Don Gagne import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Controllers 1.0 QGCView { id: rootQGCView viewPanel: panel QGCPalette { id: qgcPal; colorGroupEnabled: panel.enabled } readonly property string dialogTitle: "Radio Config" readonly property real labelToMonitorMargin: defaultTextWidth * 3 property bool controllerCompleted: false property bool controllerAndViewReady: false property Fact rcInMode: controller.getParameterFact(-1, "COM_RC_IN_MODE") function updateChannelCount() { if (controllerAndViewReady) { if (rcInMode.value == 1) { showDialog(joystickEnabledDialogComponent, dialogTitle, 50, 0) } /* FIXME: Turned off for now, since it prevents binding. Need to restructure to allow binding and still check channel count if (controller.channelCount < controller.minChannelCount) { showDialog(channelCountDialogComponent, dialogTitle, 50, 0) } else { hideDialog() } */ } } RadioComponentController { id: controller factPanel: panel statusText: statusText cancelButton: cancelButton nextButton: nextButton skipButton: skipButton onChannelCountChanged: updateChannelCount() Component.onCompleted: { controllerCompleted = true if (rootQGCView.completedSignalled) { controllerAndViewReady = true controller.start() updateChannelCount() } } } onCompleted: { if (controllerCompleted) { controllerAndViewReady = true controller.start() updateChannelCount() } } QGCViewPanel { id: panel anchors.fill: parent Component { id: copyTrimsDialogComponent QGCViewMessage { message: "Center your sticks and move throttle all the way down, then press Ok to copy trims. After pressing Ok, reset the trims on your radio back to zero." function accept() { hideDialog() controller.copyTrims() } } } Component { id: zeroTrimsDialogComponent QGCViewMessage { message: "Before calibrating you should zero all your trims and subtrims. Click Ok to start Calibration." function accept() { hideDialog() controller.nextButtonClicked() } } } Component { id: channelCountDialogComponent QGCViewMessage { message: controller.channelCount == 0 ? "Please turn on transmitter." : controller.minChannelCount + " channels or more are needed to fly." } } Component { id: joystickEnabledDialogComponent QGCViewMessage { message: "Radio Config is disabled since you have a Joystick enabled." } } Component { id: spektrumBindDialogComponent QGCViewDialog { function accept() { controller.spektrumBindMode(radioGroup.current.bindMode) hideDialog() } function reject() { hideDialog() } Column { anchors.fill: parent spacing: 5 QGCLabel { width: parent.width wrapMode: Text.WordWrap text: "Click Ok to place your Spektrum receiver in the bind mode. Select the specific receiver type below:" } ExclusiveGroup { id: radioGroup } QGCRadioButton { exclusiveGroup: radioGroup text: "DSM2 Mode" property int bindMode: RadioComponentController.DSM2 } QGCRadioButton { exclusiveGroup: radioGroup text: "DSMX (7 channels or less)" property int bindMode: RadioComponentController.DSMX7 } QGCRadioButton { exclusiveGroup: radioGroup checked: true text: "DSMX (8 channels or more)" property int bindMode: RadioComponentController.DSMX8 } } } } // Component - spektrumBindDialogComponent // Live channel monitor control component Component { id: channelMonitorDisplayComponent Item { property int rcValue: 1500 property int __lastRcValue: 1500 readonly property int __rcValueMaxJitter: 2 property color __barColor: qgcPal.windowShade // Bar Rectangle { id: bar anchors.verticalCenter: parent.verticalCenter width: parent.width height: parent.height / 2 color: __barColor } // Center point Rectangle { anchors.horizontalCenter: parent.horizontalCenter width: defaultTextWidth / 2 height: parent.height color: qgcPal.window } // Indicator Rectangle { anchors.verticalCenter: parent.verticalCenter width: parent.height * 0.75 height: width x: ((Math.abs((rcValue - 1000) - (reversed ? 1000 : 0)) / 1000) * parent.width) - (width / 2) radius: width / 2 color: qgcPal.text visible: mapped } QGCLabel { anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "Not Mapped" visible: !mapped } ColorAnimation { id: barAnimation target: bar property: "color" from: "yellow" to: __barColor duration: 1500 } /* // FIXME: Bar animation is turned off for now to figure out better usbaility onRcValueChanged: { if (Math.abs(rcValue - __lastRcValue) > __rcValueMaxJitter) { __lastRcValue = rcValue barAnimation.restart() } } // rcValue debugger QGCLabel { anchors.fill: parent text: rcValue } */ } } // Component - channelMonitorDisplayComponent // Main view Qml starts here QGCLabel { id: header font.pixelSize: ScreenTools.largeFontPixelSize text: "RADIO CONFIG" } Item { id: spacer anchors.top: header.bottom width: parent.width height: 10 } // Left side column Column { id: leftColumn anchors.top: spacer.bottom anchors.left: parent.left anchors.right: columnSpacer.left spacing: 10 // Attitude Controls Column { width: parent.width spacing: 5 QGCLabel { text: "Attitude Controls" } Item { width: parent.width height: defaultTextHeight * 2 QGCLabel { id: rollLabel width: defaultTextWidth * 10 text: "Roll" } Loader { id: rollLoader anchors.left: rollLabel.right anchors.right: parent.right height: rootQGCView.defaultTextHeight width: 100 sourceComponent: channelMonitorDisplayComponent property real defaultTextWidth: rootQGCView.defaultTextWidth property bool mapped: controller.rollChannelMapped property bool reversed: controller.rollChannelReversed } Connections { target: controller onRollChannelRCValueChanged: rollLoader.item.rcValue = rcValue } } Item { width: parent.width height: defaultTextHeight * 2 QGCLabel { id: pitchLabel width: defaultTextWidth * 10 text: "Pitch" } Loader { id: pitchLoader anchors.left: pitchLabel.right anchors.right: parent.right height: rootQGCView.defaultTextHeight width: 100 sourceComponent: channelMonitorDisplayComponent property real defaultTextWidth: rootQGCView.defaultTextWidth property bool mapped: controller.pitchChannelMapped property bool reversed: controller.pitchChannelReversed } Connections { target: controller onPitchChannelRCValueChanged: pitchLoader.item.rcValue = rcValue } } Item { width: parent.width height: defaultTextHeight * 2 QGCLabel { id: yawLabel width: defaultTextWidth * 10 text: "Yaw" } Loader { id: yawLoader anchors.left: yawLabel.right anchors.right: parent.right height: rootQGCView.defaultTextHeight width: 100 sourceComponent: channelMonitorDisplayComponent property real defaultTextWidth: rootQGCView.defaultTextWidth property bool mapped: controller.yawChannelMapped property bool reversed: controller.yawChannelReversed } Connections { target: controller onYawChannelRCValueChanged: yawLoader.item.rcValue = rcValue } } Item { width: parent.width height: defaultTextHeight * 2 QGCLabel { id: throttleLabel width: defaultTextWidth * 10 text: "Throttle" } Loader { id: throttleLoader anchors.left: throttleLabel.right anchors.right: parent.right height: rootQGCView.defaultTextHeight width: 100 sourceComponent: channelMonitorDisplayComponent property real defaultTextWidth: rootQGCView.defaultTextWidth property bool mapped: controller.throttleChannelMapped property bool reversed: controller.throttleChannelReversed } Connections { target: controller onThrottleChannelRCValueChanged: throttleLoader.item.rcValue = rcValue } } } // Column - Attitude Control labels // Command Buttons Row { spacing: 10 QGCButton { id: skipButton text: "Skip" onClicked: controller.skipButtonClicked() } QGCButton { id: cancelButton text: "Cancel" onClicked: controller.cancelButtonClicked() } QGCButton { id: nextButton primary: true text: "Calibrate" onClicked: { if (text == "Calibrate") { showDialog(zeroTrimsDialogComponent, dialogTitle, 50, StandardButton.Ok | StandardButton.Cancel) } else { controller.nextButtonClicked() } } } } // Row - Buttons // Status Text QGCLabel { id: statusText width: parent.width wrapMode: Text.WordWrap } Item { width: 10 height: defaultTextHeight * 4 } Rectangle { width: parent.width height: 1 border.color: qgcPal.text border.width: 1 } QGCLabel { text: "Additional Radio setup:" } Row { spacing: 10 QGCLabel { anchors.baseline: bindButton.baseline text: "Place Spektrum satellite receiver in bind mode:" } QGCButton { id: bindButton text: "Spektrum Bind" onClicked: showDialog(spektrumBindDialogComponent, dialogTitle, 50, StandardButton.Ok | StandardButton.Cancel) } } QGCButton { text: "Copy Trims" onClicked: showDialog(copyTrimsDialogComponent, dialogTitle, 50, StandardButton.Ok | StandardButton.Cancel) } } // Column - Left Column Item { id: columnSpacer anchors.right: rightColumn.left width: 20 } // Right side column Column { id: rightColumn anchors.top: spacer.bottom anchors.right: parent.right width: defaultTextWidth * 35 spacing: 10 Row { spacing: 10 ExclusiveGroup { id: modeGroup } QGCRadioButton { exclusiveGroup: modeGroup text: "Mode 1" checked: controller.transmitterMode == 1 onClicked: controller.transmitterMode = 1 } QGCRadioButton { exclusiveGroup: modeGroup text: "Mode 2" checked: controller.transmitterMode == 2 onClicked: controller.transmitterMode = 2 } } Image { width: parent.width height: defaultTextHeight * 15 fillMode: Image.PreserveAspectFit smooth: true source: controller.imageHelp } // Channel monitor Column { width: parent.width spacing: 5 QGCLabel { text: "Channel Monitor" } Connections { target: controller onChannelRCValueChanged: { if (channelMonitorRepeater.itemAt(channel)) { channelMonitorRepeater.itemAt(channel).loader.item.rcValue = rcValue } } } Repeater { id: channelMonitorRepeater model: controller.channelCount width: parent.width Row { spacing: 5 // Need this to get to loader from Connections above property Item loader: theLoader QGCLabel { id: channelLabel text: modelData + 1 } Loader { id: theLoader anchors.verticalCenter: channelLabel.verticalCenter height: rootQGCView.defaultTextHeight width: 200 sourceComponent: channelMonitorDisplayComponent property real defaultTextWidth: rootQGCView.defaultTextWidth property bool mapped: true readonly property bool reversed: false } } } } // Column - Channel Monitor } // Column - Right Column } // QGCViewPanel }