Commit 1888a8dc authored by Don Gagne's avatar Don Gagne

New Joystick config, new Joystick control classes

parent 58d4ff15
......@@ -142,6 +142,7 @@ INCLUDEPATH += \
src/comm \
src/FlightDisplay \
src/input \
src/Joystick \
src/lib/qmapcontrol \
src/QmlControls \
src/uas \
......@@ -214,9 +215,6 @@ FORMS += \
!MobileBuild {
FORMS += \
src/ui/JoystickButton.ui \
src/ui/JoystickAxis.ui \
src/ui/JoystickWidget.ui \
src/ui/QGCHilConfiguration.ui \
src/ui/QGCHilFlightGearConfiguration.ui \
src/ui/QGCHilJSBSimConfiguration.ui \
......@@ -242,6 +240,8 @@ HEADERS += \
src/FlightDisplay/FlightDisplayView.h \
src/GAudioOutput.h \
src/HomePositionManager.h \
src/Joystick/Joystick.h \
src/Joystick/JoystickManager.h \
src/LogCompressor.h \
src/MG.h \
src/QGC.h \
......@@ -352,15 +352,12 @@ HEADERS += \
src/comm/QGCHilLink.h \
src/comm/QGCJSBSimLink.h \
src/comm/QGCXPlaneLink.h \
src/input/JoystickInput.h \
src/ui/CameraView.h \
src/ui/JoystickAxis.h \
src/ui/JoystickButton.h \
src/ui/JoystickWidget.h \
src/ui/QGCHilConfiguration.h \
src/ui/QGCHilFlightGearConfiguration.h \
src/ui/QGCHilJSBSimConfiguration.h \
src/ui/QGCHilXPlaneConfiguration.h \
src/VehicleSetup/JoystickConfigController.h \
}
SOURCES += \
......@@ -379,6 +376,8 @@ SOURCES += \
src/FlightDisplay/FlightDisplayView.cc \
src/GAudioOutput.cc \
src/HomePositionManager.cc \
src/Joystick/Joystick.cc \
src/Joystick/JoystickManager.cc \
src/LogCompressor.cc \
src/main.cc \
src/QGC.cc \
......@@ -482,15 +481,12 @@ SOURCES += \
src/comm/QGCFlightGearLink.cc \
src/comm/QGCJSBSimLink.cc \
src/comm/QGCXPlaneLink.cc \
src/input/JoystickInput.cc \
src/ui/CameraView.cc \
src/ui/JoystickAxis.cc \
src/ui/JoystickButton.cc \
src/ui/JoystickWidget.cc \
src/ui/QGCHilConfiguration.cc \
src/ui/QGCHilFlightGearConfiguration.cc \
src/ui/QGCHilJSBSimConfiguration.cc \
src/ui/QGCHilXPlaneConfiguration.cc \
src/VehicleSetup/JoystickConfigController.cc \
}
#
......
......@@ -99,15 +99,16 @@
<file alias="QGroundControl/Controls/MissionItemIndexLabel.qml">src/QmlControls/MissionItemIndexLabel.qml</file>
<file alias="QGroundControl/Controls/MissionItemSummary.qml">src/QmlControls/MissionItemSummary.qml</file>
<file alias="ScreenToolsFontQuery.qml">src/QmlControls/ScreenToolsFontQuery.qml</file>
<file alias="ParameterEditorWidget.qml">src/ViewWidgets/ParameterEditorWidget.qml</file>
<file alias="CustomCommandWidget.qml">src/ViewWidgets/CustomCommandWidget.qml</file>
<!-- Vehicle Setup -->
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
<file alias="SetupViewButtonsConnected.qml">src/VehicleSetup/SetupViewButtonsConnected.qml</file>
<file alias="SetupViewButtonsDisconnected.qml">src/VehicleSetup/SetupViewButtonsDisconnected.qml</file>
<file alias="VehicleSummary.qml">src/VehicleSetup/VehicleSummary.qml</file>
<file alias="FirmwareUpgrade.qml">src/VehicleSetup/FirmwareUpgrade.qml</file>
<file alias="JoystickConfig.qml">src/VehicleSetup/JoystickConfig.qml</file>
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
<file alias="ScreenToolsFontQuery.qml">src/QmlControls/ScreenToolsFontQuery.qml</file>
<file alias="ParameterEditorWidget.qml">src/ViewWidgets/ParameterEditorWidget.qml</file>
<file alias="CustomCommandWidget.qml">src/ViewWidgets/CustomCommandWidget.qml</file>
<file alias="SafetyComponent.qml">src/AutoPilotPlugins/PX4/SafetyComponent.qml</file>
<file alias="RadioComponent.qml">src/AutoPilotPlugins/PX4/RadioComponent.qml</file>
<file alias="PowerComponent.qml">src/AutoPilotPlugins/PX4/PowerComponent.qml</file>
......@@ -222,6 +223,7 @@
<file alias="accel_up.png">resources/calibration/accel_up.png</file>
<file alias="accel_left.png">resources/calibration/accel_left.png</file>
</qresource>
<qresource prefix="/qml/calibration/mode1">
<file alias="radioCenter.png">resources/calibration/mode1/radioCenter.png</file>
<file alias="radioHome.png">resources/calibration/mode1/radioHome.png</file>
......@@ -235,6 +237,7 @@
<file alias="radioThrottleDown.png">resources/calibration/mode1/radioThrottleDown.png</file>
<file alias="radioSwitchMinMax.png">resources/calibration/mode1/radioSwitchMinMax.png</file>
</qresource>
<qresource prefix="/qml/calibration/mode2">
<file alias="radioCenter.png">resources/calibration/mode2/radioCenter.png</file>
<file alias="radioHome.png">resources/calibration/mode2/radioHome.png</file>
......@@ -248,6 +251,19 @@
<file alias="radioThrottleDown.png">resources/calibration/mode2/radioThrottleDown.png</file>
<file alias="radioSwitchMinMax.png">resources/calibration/mode2/radioSwitchMinMax.png</file>
</qresource>
<qresource prefix="/qml/calibration/joystick">
<file alias="joystickCenter.png">resources/calibration/joystick/joystickCenter.png</file>
<file alias="joystickRollLeft.png">resources/calibration/joystick/joystickRollLeft.png</file>
<file alias="joystickRollRight.png">resources/calibration/joystick/joystickRollRight.png</file>
<file alias="joystickPitchUp.png">resources/calibration/joystick/joystickPitchUp.png</file>
<file alias="joystickPitchDown.png">resources/calibration/joystick/joystickPitchDown.png</file>
<file alias="joystickYawLeft.png">resources/calibration/joystick/joystickYawLeft.png</file>
<file alias="joystickYawRight.png">resources/calibration/joystick/joystickYawRight.png</file>
<file alias="joystickThrottleUp.png">resources/calibration/joystick/joystickThrottleUp.png</file>
<file alias="joystickThrottleDown.png">resources/calibration/joystick/joystickThrottleDown.png</file>
</qresource>
<qresource prefix="/res/styles">
<file alias="style-dark.css">resources/styles/style-dark.css</file>
<file alias="style-light.css">resources/styles/style-light.css</file>
......
......@@ -50,6 +50,9 @@ QGCView {
function updateChannelCount()
{
if (controllerAndViewReady) {
if (joystickManager.activeJoystick && joystickManager.activeJoystick.enabled) {
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
......@@ -128,6 +131,14 @@ QGCView {
}
}
Component {
id: joystickEnabledDialogComponent
QGCViewMessage {
message: "Radio Config is disabled since you have a Joystick enabled."
}
}
Component {
id: spektrumBindDialogComponent
......
......@@ -76,6 +76,12 @@ public:
/// @param[out] custom_mode Custom mode for SET_MODE mavlink message
virtual bool setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) = 0;
/// Returns the number of buttons which are reserved for firmware use in the MANUAL_CONTROL mavlink
/// message. For example PX4 Flight Stack reserves the first 8 buttons to simulate rc switches.
/// The remainder can be assigned to Vehicle actions.
/// @return -1: reserver all buttons, >0 number of buttons to reserve
virtual int manualControlReservedButtonCount(void) = 0;
protected:
FirmwarePlugin(QObject* parent = NULL) : QGCSingleton(parent) { }
};
......
......@@ -89,3 +89,10 @@ bool GenericFirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* ba
return false;
}
int GenericFirmwarePlugin::manualControlReservedButtonCount(void)
{
// We don't know whether the firmware is going to used any of these buttons.
// So reserve them all.
return -1;
}
......@@ -43,6 +43,7 @@ public:
virtual QStringList flightModes(void) { return QStringList(); }
virtual QString flightMode(uint8_t base_mode, uint32_t custom_mode);
virtual bool setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode);
virtual int manualControlReservedButtonCount(void);
private:
/// All access to singleton is through AutoPilotPluginManager::instance
......
......@@ -176,3 +176,8 @@ bool PX4FirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* base_m
return found;
}
int PX4FirmwarePlugin::manualControlReservedButtonCount(void)
{
return 8; // 8 buttons reserved for rc switch simulation
}
......@@ -43,6 +43,7 @@ public:
virtual QStringList flightModes(void);
virtual QString flightMode(uint8_t base_mode, uint32_t custom_mode);
virtual bool setFlightMode(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode);
virtual int manualControlReservedButtonCount(void);
private:
/// All access to singleton is through AutoPilotPluginManager::instance
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#include "Joystick.h"
#include "QGC.h"
#include "MultiVehicleManager.h"
#include "AutoPilotPlugin.h"
#include "UAS.h"
#include <QSettings>
#ifdef Q_OS_MAC
#include <SDL.h>
#else
#include <SDL/SDL.h>
#endif
QGC_LOGGING_CATEGORY(JoystickLog, "JoystickLog")
const char* Joystick::_settingsGroup = "Joysticks";
const char* Joystick::_calibratedSettingsKey = "Calibrated";
const char* Joystick::_buttonActionSettingsKey = "ButtonAction%1";
const char* Joystick::_throttleModeSettingsKey = "ThrottleMode";
const char* Joystick::_enabledSettingsKey = "Enabled";
const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = {
"RollAxis",
"PitchAxis",
"YawAxis",
"ThrottleAxis"
};
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int sdlIndex)
: _sdlIndex(sdlIndex)
, _exitThread(false)
, _name(name)
, _enabled(false)
, _calibrated(false)
, _calibrating(false)
, _axisCount(axisCount)
, _buttonCount(buttonCount)
, _lastButtonBits(0)
, _throttleMode(ThrottleModeCenterZero)
{
for (int i=0; i<_cAxes; i++) {
_rgAxisValues[i] = 0;
}
for (int i=0; i<_cButtons; i++) {
_rgButtonValues[i] = false;
_rgButtonActions[i] = -1;
}
_loadSettings();
}
Joystick::~Joystick()
{
}
void Joystick::_loadSettings(void)
{
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
bool badSettings = false;
bool convertOk;
qCDebug(JoystickLog) << "_loadSettings " << _name;
_calibrated = settings.value(_calibratedSettingsKey, false).toBool();
_enabled = settings.value(_enabledSettingsKey, false).toBool();
_throttleMode = (ThrottleMode_t)settings.value(_throttleModeSettingsKey, ThrottleModeCenterZero).toInt(&convertOk);
badSettings |= !convertOk;
qCDebug(JoystickLog) << "_loadSettings calibrated:enabled:throttlemode:badsettings" << _calibrated << _enabled << _throttleMode << badSettings;
QString minTpl ("Axis%1Min");
QString maxTpl ("Axis%1Max");
QString trimTpl ("Axis%1Trim");
QString revTpl ("Axis%1Rev");
for (int axis=0; axis<_cAxes; axis++) {
Calibration_t* calibration = &_rgCalibration[axis];
calibration->center = settings.value(trimTpl.arg(axis), 0).toInt(&convertOk);
badSettings |= !convertOk;
calibration->min = settings.value(minTpl.arg(axis), -32768).toInt(&convertOk);
badSettings |= !convertOk;
calibration->max = settings.value(maxTpl.arg(axis), 32768).toInt(&convertOk);
badSettings |= !convertOk;
calibration->reversed = settings.value(revTpl.arg(axis), false).toBool();
qCDebug(JoystickLog) << "_loadSettings axis:min:max:trim:reversed:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << badSettings;
}
for (int function=0; function<maxFunction; function++) {
int functionAxis;
functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk);
badSettings |= !convertOk || (functionAxis == -1);
_rgFunctionAxis[function] = functionAxis;
qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings;
}
for (int button=0; button<_cButtons; button++) {
_rgButtonActions[button] = settings.value(QString(_buttonActionSettingsKey).arg(button), -1).toInt(&convertOk);
badSettings |= !convertOk;
qCDebug(JoystickLog) << "_loadSettings button:action:badsettings" << button << _rgButtonActions[button] << badSettings;
}
if (badSettings) {
_calibrated = false;
_enabled = false;
settings.setValue(_calibratedSettingsKey, false);
settings.setValue(_calibratedSettingsKey, false);
}
}
void Joystick::_saveSettings(void)
{
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
settings.setValue(_calibratedSettingsKey, _calibrated);
settings.setValue(_enabledSettingsKey, _enabled && _calibrated);
settings.setValue(_throttleModeSettingsKey, _throttleMode);
qCDebug(JoystickLog) << "_saveSettings calibrated:enabled:throttlemode" << _calibrated << _enabled << _throttleMode;
QString minTpl ("Axis%1Min");
QString maxTpl ("Axis%1Max");
QString trimTpl ("Axis%1Trim");
QString revTpl ("Axis%1Rev");
for (int axis=0; axis<_cAxes; axis++) {
Calibration_t* calibration = &_rgCalibration[axis];
settings.setValue(trimTpl.arg(axis), calibration->center);
settings.setValue(minTpl.arg(axis), calibration->min);
settings.setValue(maxTpl.arg(axis), calibration->max);
settings.setValue(revTpl.arg(axis), calibration->reversed);
qCDebug(JoystickLog) << "_saveSettings name:axis:min:max:trim:reversed"
<< _name
<< axis
<< calibration->min
<< calibration->max
<< calibration->center
<< calibration->reversed;
}
for (int function=0; function<maxFunction; function++) {
settings.setValue(_rgFunctionSettingsKey[function], _rgFunctionAxis[function]);
qCDebug(JoystickLog) << "_saveSettings name:function:axis" << _name << function << _rgFunctionSettingsKey[function];
}
for (int button=0; button<_cButtons; button++) {
settings.setValue(QString(_buttonActionSettingsKey).arg(button), _rgButtonActions[button]);
qCDebug(JoystickLog) << "_saveSettings button:action" << button << _rgButtonActions[button];
}
}
/// Adjust the raw axis value to the -1:1 range given calibration information
float Joystick::_adjustRange(int value, Calibration_t calibration)
{
float valueNormalized;
float axisLength;
float axisBasis;
if (value > calibration.center) {
axisBasis = 1.0f;
valueNormalized = value - calibration.center;
axisLength = calibration.max - calibration.center;
} else {
axisBasis = -1.0f;
valueNormalized = calibration.center - value;
axisLength = calibration.center - calibration.min;
}
float axisPercent = valueNormalized / axisLength;
float correctedValue = axisBasis * axisPercent;
if (calibration.reversed) {
correctedValue *= -1.0f;
}
#if 0
qCDebug(JoystickLog) << "_adjustRange corrected:value:min:max:center:reversed:basis:normalized:length"
<< correctedValue
<< value
<< calibration.min
<< calibration.max
<< calibration.center
<< calibration.center
<< axisBasis
<< valueNormalized
<< axisLength;
#endif
return correctedValue;
}
void Joystick::run(void)
{
SDL_Joystick* sdlJoystick = SDL_JoystickOpen(_sdlIndex);
Vehicle * activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (!sdlJoystick) {
qCWarning(JoystickLog) << "SDL_JoystickOpen failed:" << SDL_GetError();
return;
}
while (!_exitThread) {
SDL_JoystickUpdate();
// Update axes
for (int axisIndex=0; axisIndex<_axisCount; axisIndex++) {
int newAxisValue = SDL_JoystickGetAxis(sdlJoystick, axisIndex);
// Calibration code requires signal to be emitted even if value hasn't changed
_rgAxisValues[axisIndex] = newAxisValue;
emit rawAxisValueChanged(axisIndex, newAxisValue);
}
// Update buttons
for (int buttonIndex=0; buttonIndex<_buttonCount; buttonIndex++) {
bool newButtonValue = !!SDL_JoystickGetButton(sdlJoystick, buttonIndex);
if (newButtonValue != _rgButtonValues[buttonIndex]) {
_rgButtonValues[buttonIndex] = newButtonValue;
emit rawButtonPressedChanged(buttonIndex, newButtonValue);
}
}
if (_calibrated && _enabled && !_calibrating) {
int axis = _rgFunctionAxis[rollFunction];
float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis]);
axis = _rgFunctionAxis[pitchFunction];
float pitch = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis]);
axis = _rgFunctionAxis[yawFunction];
float yaw = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis]);
axis = _rgFunctionAxis[throttleFunction];
float throttle = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis]);
roll = std::max(-1.0f, std::min(roll, 1.0f));
pitch = std::max(-1.0f, std::min(pitch, 1.0f));
yaw = std::max(-1.0f, std::min(yaw, 1.0f));
throttle = std::max(-1.0f, std::min(throttle, 1.0f));
// Adjust throttle to 0:1 range
if (_throttleMode == ThrottleModeCenterZero) {
throttle = std::max(0.0f, throttle);
throttle = (throttle * 2.0f) - 1.0f;
} else {
throttle = (throttle + 1.0f) / 2.0f;
}
// Set up button pressed information
// We only send the buttons the firmwware has reserved
int reservedButtonCount = activeVehicle->manualControlReservedButtonCount();
if (reservedButtonCount == -1) {
reservedButtonCount = _buttonCount;
}
quint16 newButtonBits = 0; // New set of button which are down
quint16 buttonPressedBits = 0; // Buttons pressed for manualControl signal
for (int buttonIndex=0; buttonIndex<_buttonCount; buttonIndex++) {
quint16 buttonBit = 1 << buttonIndex;
if (_rgButtonValues[buttonIndex]) {
// Button pressed down, just record it
newButtonBits |= buttonBit;
} else {
if (_lastButtonBits & buttonBit) {
// Button was down last time through, but is now up which indicates a button press
qCDebug(JoystickLog) << "button triggered" << buttonIndex;
if (buttonIndex >= reservedButtonCount) {
// Button is above firmware reserved set
int buttonAction =_rgButtonActions[buttonIndex];
if (buttonAction != -1) {
qCDebug(JoystickLog) << "buttonActionTriggered" << buttonAction;
emit buttonActionTriggered(buttonAction);
}
} else {
// Button is within firmware reserved set
// Record the button press for manualControl signal
buttonPressedBits |= buttonBit;
qCDebug(JoystickLog) << "button press recorded for manualControl" << buttonIndex;
}
}
}
}
_lastButtonBits = newButtonBits;
emit manualControl(roll, -pitch, yaw, throttle, buttonPressedBits, activeVehicle->joystickMode());
}
// Sleep, update rate of joystick is approx. 25 Hz (1000 ms / 25 = 40 ms)
QGC::SLEEP::msleep(40);
}
SDL_JoystickClose(sdlJoystick);
}
void Joystick::startPolling(void)
{
if (enabled()) {
UAS* uas = MultiVehicleManager::instance()->activeVehicle()->uas();
connect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
connect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
}
_exitThread = false;
start();
}
void Joystick::stopPolling(void)
{
UAS* uas = MultiVehicleManager::instance()->activeVehicle()->uas();
disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
disconnect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
_exitThread = true;
}
void Joystick::setCalibration(int axis, Calibration_t& calibration)
{
if (axis < 0 || axis > _cAxes) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return;
}
_calibrated = true;
_rgCalibration[axis] = calibration;
_saveSettings();
emit calibratedChanged(_calibrated);
}
Joystick::Calibration_t Joystick::getCalibration(int axis)
{
if (axis < 0 || axis > _cAxes) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
}
return _rgCalibration[axis];
}
void Joystick::setFunctionAxis(AxisFunction_t function, int axis)
{
if (axis < 0 || axis > _cAxes) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return;
}
_calibrated = true;
_rgFunctionAxis[function] = axis;
_saveSettings();
emit calibratedChanged(_calibrated);
}
int Joystick::getFunctionAxis(AxisFunction_t function)
{
if (function < 0 || function >= maxFunction) {
qCWarning(JoystickLog) << "Invalid function" << function;
}
return _rgFunctionAxis[function];
}
QStringList Joystick::actions(void)
{
QStringList list;
foreach(QAction* action, MultiVehicleManager::instance()->activeVehicle()->uas()->getActions()) {
list += action->text();
}
return list;
}
void Joystick::setButtonAction(int button, int action)
{
if (button < 0 || button > _cButtons) {
qCWarning(JoystickLog) << "Invalid button index" << button;
return;
}
_rgButtonActions[button] = action;
_saveSettings();
emit buttonActionsChanged(buttonActions());
}
int Joystick::getButtonAction(int button)
{
if (button < 0 || button > _cButtons) {
qCWarning(JoystickLog) << "Invalid button index" << button;
}
return _rgButtonActions[button];
}
QVariantList Joystick::buttonActions(void)
{
QVariantList list;
for (int button=0; button<_buttonCount; button++) {
list += QVariant::fromValue(_rgButtonActions[button]);
}
return list;
}
int Joystick::throttleMode(void)
{
return _throttleMode;
}
void Joystick::setThrottleMode(int mode)
{
if (mode < 0 || mode >= ThrottleModeMax) {
qCWarning(JoystickLog) << "Invalid throttle mode" << mode;
return;
}
_throttleMode = (ThrottleMode_t)mode;
_saveSettings();
emit throttleModeChanged(_throttleMode);
}
bool Joystick::enabled(void)
{
Fact* fact = MultiVehicleManager::instance()->activeVehicle()->autopilotPlugin()->getParameterFact(FactSystem::defaultComponentId, "COM_RC_IN_MODE");
if (!fact) {
qCWarning(JoystickLog) << "Missing COM_RC_IN_MODE parameter";
return false;
}
return _enabled && _calibrated && (fact->value().toInt() == 2);
}
void Joystick::setEnabled(bool enabled)
{
if (!_calibrated) {
return;
}
Fact* fact = MultiVehicleManager::instance()->activeVehicle()->autopilotPlugin()->getParameterFact(FactSystem::defaultComponentId, "COM_RC_IN_MODE");
if (!fact) {
qCWarning(JoystickLog) << "Missing COM_RC_IN_MODE parameter";
return;
}
_enabled = enabled;
fact->setValue(enabled ? 2 : 0);
_saveSettings();
emit enabledChanged(_enabled);
if (_enabled) {
startPolling();
} else {
stopPolling();
}
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef Joystick_H
#define Joystick_H
#include <QObject>
#include <QThread>
#include "QGCLoggingCategory.h"
#include "Vehicle.h"
Q_DECLARE_LOGGING_CATEGORY(JoystickLog)
class Joystick : public QThread
{
Q_OBJECT
public:
Joystick(const QString& name, int axisCount, int buttonCount, int sdlIndex);
~Joystick();
typedef struct {
int min;
int max;
int center;
bool reversed;
} Calibration_t;
typedef enum {
rollFunction,
pitchFunction,
yawFunction,
throttleFunction,
maxFunction
} AxisFunction_t;
typedef enum {
ThrottleModeCenterZero,
ThrottleModeDownZero,
ThrottleModeMax
} ThrottleMode_t;
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(bool calibrated MEMBER _calibrated NOTIFY calibratedChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(int buttonCount MEMBER _buttonCount CONSTANT)
Q_PROPERTY(int axisCount MEMBER _axisCount CONSTANT)
Q_PROPERTY(QStringList actions READ actions CONSTANT)
Q_PROPERTY(QVariantList buttonActions READ buttonActions NOTIFY buttonActionsChanged)
Q_INVOKABLE void setButtonAction(int button, int action);
Q_INVOKABLE int getButtonAction(int button);
Q_PROPERTY(int throttleMode READ throttleMode WRITE setThrottleMode NOTIFY throttleModeChanged)
/// Start the polling thread which will in turn emit joystick signals
void startPolling(void);
void stopPolling(void);
void setCalibration(int axis, Calibration_t& calibration);
Calibration_t getCalibration(int axis);
void setFunctionAxis(AxisFunction_t function, int axis);
int getFunctionAxis(AxisFunction_t function);
QStringList actions(void);
QVariantList buttonActions(void);
QString name(void) { return _name; }
int throttleMode(void);
void setThrottleMode(int mode);
bool enabled(void);
void setEnabled(bool enabled);
bool calibrating(void) { return _calibrating; }
void setCalibrating(bool calibrating) { _calibrating = calibrating; }
signals:
void calibratedChanged(bool calibrated);
// The raw signals are only meant for use by calibration
void rawAxisValueChanged(int index, int value);
void rawButtonPressedChanged(int index, int pressed);
void buttonActionsChanged(QVariantList actions);
void throttleModeChanged(int mode);
void enabledChanged(bool enabled);
/// Signal containing new joystick information
/// @param roll Range is -1:1, negative meaning roll left, positive meaning roll right
/// @param pitch Range i -1:1, negative meaning pitch down, positive meaning pitch up
/// @param yaw Range is -1:1, negative meaning yaw left, positive meaning yaw right
/// @param throttle Range is 0:1, 0 meaning no throttle, 1 meaning full throttle
/// @param mode See Vehicle::JoystickMode_t enum
void manualControl(float roll, float pitch, float yaw, float throttle, quint16 buttons, int joystickMmode);
void buttonActionTriggered(int action);
private:
void _saveSettings(void);
void _loadSettings(void);
float _adjustRange(int value, Calibration_t calibration);
// Override from QThread
virtual void run(void);
private:
int _sdlIndex; ///< Index for SDL_JoystickOpen
bool _exitThread; ///< true: signal thread to exit
QString _name;
bool _enabled;
bool _calibrated;
bool _calibrating;
int _axisCount;
int _buttonCount;
static const int _cAxes = 4;
int _rgAxisValues[_cAxes];
Calibration_t _rgCalibration[_cAxes];
int _rgFunctionAxis[maxFunction];
static const char* _rgFunctionSettingsKey[maxFunction];
static const int _cButtons = 12;
int _rgButtonValues[_cButtons];
int _rgButtonActions[_cButtons];
quint16 _lastButtonBits;
ThrottleMode_t _throttleMode;
static const char* _settingsGroup;
static const char* _calibratedSettingsKey;
static const char* _buttonActionSettingsKey;
static const char* _throttleModeSettingsKey;
static const char* _enabledSettingsKey;
};
#endif
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#include "JoystickManager.h"
#include <QQmlEngine>
#ifdef Q_OS_MAC
#include <SDL.h>
#else
#include <SDL/SDL.h>
#endif
QGC_LOGGING_CATEGORY(JoystickManagerLog, "JoystickManagerLog")
IMPLEMENT_QGC_SINGLETON(JoystickManager, JoystickManager)
const char * JoystickManager::_settingsGroup = "JoystickManager";
const char * JoystickManager::_settingsKeyActiveJoystick = "ActiveJoystick";
JoystickManager::JoystickManager(QObject* parent)
: QGCSingleton(parent)
, _activeJoystick(NULL)
{
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) {
qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError();
return;
}
// Load available joysticks
qCDebug(JoystickManagerLog) << "Available joysticks";
for (int i=0; i<SDL_NumJoysticks(); i++) {
QString name = SDL_JoystickName(i);
if (!_name2JoystickMap.contains(name)) {
int axisCount, buttonCount;
SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i);
axisCount = SDL_JoystickNumAxes(sdlJoystick);
buttonCount = SDL_JoystickNumButtons(sdlJoystick);
SDL_JoystickClose(sdlJoystick);
qCDebug(JoystickManagerLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount;
_name2JoystickMap[name] = new Joystick(name, axisCount, buttonCount, i);
} else {
qCDebug(JoystickManagerLog) << "\tSkipping duplicate" << name;
}
}
if (!_name2JoystickMap.count()) {
qCDebug(JoystickManagerLog) << "\tnone found";
return;
}
_setActiveJoystickFromSettings();
}
JoystickManager::~JoystickManager()
{
}
void JoystickManager::_setActiveJoystickFromSettings(void)
{
QSettings settings;
settings.beginGroup(_settingsGroup);
QString name = settings.value(_settingsKeyActiveJoystick).toString();
if (name.isEmpty()) {
name = _name2JoystickMap.first()->name();
}
setActiveJoystick(_name2JoystickMap.value(name, _name2JoystickMap.first()));
settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name());
}
Joystick* JoystickManager::activeJoystick(void)
{
return _activeJoystick;
}
void JoystickManager::setActiveJoystick(Joystick* joystick)
{
QSettings settings;
if (!_name2JoystickMap.contains(joystick->name())) {
qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name();
return;
}
if (_activeJoystick) {
_activeJoystick->stopPolling();
}
_activeJoystick = joystick;
settings.beginGroup(_settingsGroup);
settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name());
emit activeJoystickChanged(_activeJoystick);
emit activeJoystickNameChanged(_activeJoystick->name());
}
QVariantList JoystickManager::joysticks(void)
{
QVariantList list;
foreach (QString name, _name2JoystickMap.keys()) {
list += QVariant::fromValue(_name2JoystickMap[name]);
}
return list;
}
QStringList JoystickManager::joystickNames(void)
{
return _name2JoystickMap.keys();
}
QString JoystickManager::activeJoystickName(void)
{
return _activeJoystick ? _activeJoystick->name() : QString();
}
void JoystickManager::setActiveJoystickName(const QString& name)
{
if (!_name2JoystickMap.contains(name)) {
qCWarning(JoystickManagerLog) << "Set active not in map" << name;
return;
}
setActiveJoystick(_name2JoystickMap[name]);
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef JoystickManager_H
#define JoystickManager_H
#include "QGCSingleton.h"
#include "QGCLoggingCategory.h"
#include "Joystick.h"
#include <QVariantList>
Q_DECLARE_LOGGING_CATEGORY(JoystickManagerLog)
class JoystickManager : public QGCSingleton
{
Q_OBJECT
DECLARE_QGC_SINGLETON(JoystickManager, JoystickManager)
public:
/// List of available joysticks
Q_PROPERTY(QVariantList joysticks READ joysticks CONSTANT)
Q_PROPERTY(QStringList joystickNames READ joystickNames CONSTANT)
/// Active joystick
Q_PROPERTY(Joystick* activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged)
Q_PROPERTY(QString activeJoystickName READ activeJoystickName WRITE setActiveJoystickName NOTIFY activeJoystickNameChanged)
QVariantList joysticks();
QStringList joystickNames(void);
Joystick* activeJoystick(void);
void setActiveJoystick(Joystick* joystick);
QString activeJoystickName(void);
void setActiveJoystickName(const QString& name);
signals:
void activeJoystickChanged(Joystick* joystick);
void activeJoystickNameChanged(const QString& name);
private slots:
private:
/// All access to singleton is through JoystickManager::instance
JoystickManager(QObject* parent = NULL);
~JoystickManager();
void _setActiveJoystickFromSettings(void);
private:
Joystick* _activeJoystick;
QMap<QString, Joystick*> _name2JoystickMap;
static const char * _settingsGroup;
static const char * _settingsKeyActiveJoystick;
};
#endif
......@@ -89,6 +89,8 @@ G_END_DECLS
#include "PX4/PX4FirmwarePlugin.h"
#include "Vehicle.h"
#include "MavlinkQmlSingleton.h"
#include "JoystickManager.h"
#include "JoystickConfigController.h"
#ifdef QGC_RTLAB_ENABLED
#include "OpalLink.h"
......@@ -327,16 +329,19 @@ void QGCApplication::_initCommon(void)
qmlRegisterUncreatableType<AutoPilotPlugin>("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Can only reference, cannot create");
qmlRegisterUncreatableType<VehicleComponent>("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Can only reference, cannot create");
qmlRegisterUncreatableType<Vehicle>("QGroundControl.Vehicle", 1, 0, "Vehicle", "Can only reference, cannot create");
qmlRegisterUncreatableType<JoystickManager> ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", "Reference only");
qmlRegisterUncreatableType<Joystick> ("QGroundControl.JoystickManager", 1, 0, "Joystick", "Reference only");
qmlRegisterType<ViewWidgetController>("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
qmlRegisterType<ParameterEditorController>("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
qmlRegisterType<CustomCommandWidgetController>("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
qmlRegisterType<SensorsComponentController>("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
qmlRegisterType<PowerComponentController>("QGroundControl.Controllers", 1, 0, "PowerComponentController");
qmlRegisterType<RadioComponentController>("QGroundControl.Controllers", 1, 0, "RadioComponentController");
qmlRegisterType<ScreenToolsController>("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
qmlRegisterType<ViewWidgetController> ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
qmlRegisterType<ParameterEditorController> ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
qmlRegisterType<CustomCommandWidgetController> ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
qmlRegisterType<FlightModesComponentController> ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
qmlRegisterType<AirframeComponentController> ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
qmlRegisterType<SensorsComponentController> ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
qmlRegisterType<PowerComponentController> ("QGroundControl.Controllers", 1, 0, "PowerComponentController");
qmlRegisterType<RadioComponentController> ("QGroundControl.Controllers", 1, 0, "RadioComponentController");
qmlRegisterType<ScreenToolsController> ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
qmlRegisterType<JoystickConfigController> ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
#ifndef __mobile__
qmlRegisterType<FirmwareUpgradeController>("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
......@@ -576,6 +581,11 @@ void QGCApplication::_createSingletons(void)
Q_UNUSED(multiVehicleManager);
Q_ASSERT(multiVehicleManager);
// No dependencies
JoystickManager* joystickManager = JoystickManager::_createSingleton();
Q_UNUSED(joystickManager);
Q_ASSERT(joystickManager);
// No dependencies
GAudioOutput* audio = GAudioOutput::_createSingleton();
Q_UNUSED(audio);
......@@ -636,6 +646,7 @@ void QGCApplication::_destroySingletons(void)
HomePositionManager::_deleteSingleton();
LinkManager::_deleteSingleton();
GAudioOutput::_deleteSingleton();
JoystickManager::_deleteSingleton();
MultiVehicleManager::_deleteSingleton();
FirmwarePluginManager::_deleteSingleton();
GenericFirmwarePlugin::_deleteSingleton();
......
......@@ -25,6 +25,7 @@
#include "AutoPilotPluginManager.h"
#include "QGCMessageBox.h"
#include "MultiVehicleManager.h"
#include "JoystickManager.h"
#include <QQmlContext>
#include <QQmlEngine>
......@@ -40,6 +41,7 @@ QGCQuickWidget::QGCQuickWidget(QWidget* parent) :
{
rootContext()->engine()->addImportPath("qrc:/qml");
rootContext()->setContextProperty("multiVehicleManager", MultiVehicleManager::instance());
rootContext()->setContextProperty("joystickManager", JoystickManager::instance());
}
void QGCQuickWidget::setAutoPilot(AutoPilotPlugin* autoPilot)
......
......@@ -26,6 +26,9 @@
#include "MultiVehicleManager.h"
#include "AutoPilotPlugin.h"
#include "JoystickManager.h"
#include "MAVLinkProtocol.h"
#include "UAS.h"
IMPLEMENT_QGC_SINGLETON(MultiVehicleManager, MultiVehicleManager)
......@@ -107,6 +110,15 @@ void MultiVehicleManager::_deleteVehiclePhase1(void)
qWarning() << "Vehicle not found in map!";
}
// Disconnect the vehicle from the uas
vehicle->uas()->clearVehicle();
// Disconnect joystick
Joystick* joystick = JoystickManager::instance()->activeJoystick();
if (joystick) {
joystick->stopPolling();
}
// First we must signal that a vehicle is no longer available.
_activeVehicleAvailable = false;
_parameterReadyVehicleAvailable = false;
......@@ -150,6 +162,12 @@ void MultiVehicleManager::setActiveVehicle(Vehicle* vehicle)
{
if (vehicle != _activeVehicle) {
if (_activeVehicle) {
// Disconnect joystick
Joystick* joystick = JoystickManager::instance()->activeJoystick();
if (joystick) {
joystick->stopPolling();
}
// The sequence of signals is very important in order to not leave Qml elements connected
// to a non-existent vehicle.
......@@ -195,6 +213,12 @@ void MultiVehicleManager::_autopilotPluginReadyChanged(bool pluginReady)
}
if (autopilot->vehicle() == _activeVehicle) {
// Connect joystick
Joystick* joystick = JoystickManager::instance()->activeJoystick();
if (joystick && joystick->enabled()) {
joystick->startPolling();
}
_parameterReadyVehicleAvailable = pluginReady;
emit parameterReadyVehicleAvailableChanged(pluginReady);
}
......
......@@ -28,6 +28,7 @@
#include "FirmwarePlugin.h"
#include "AutoPilotPluginManager.h"
#include "UASMessageHandler.h"
#include "UAS.h"
QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog")
......@@ -35,9 +36,15 @@ QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog")
#define DEFAULT_LAT 38.965767f
#define DEFAULT_LON -120.083923f
const char* Vehicle::_settingsGroup = "Vehicle%1"; // %1 replace with mavlink system id
const char* Vehicle::_joystickModeSettingsKey = "JoystickMode";
Vehicle::Vehicle(LinkInterface* link, int vehicleId, MAV_AUTOPILOT firmwareType)
: _id(vehicleId)
, _firmwareType(firmwareType)
, _firmwarePlugin(NULL)
, _autopilotPlugin(NULL)
, _joystickMode(JoystickModeRC)
, _uas(NULL)
, _mav(NULL)
, _currentMessageCount(0)
......@@ -74,6 +81,8 @@ Vehicle::Vehicle(LinkInterface* link, int vehicleId, MAV_AUTOPILOT firmwareType)
, _wpm(NULL)
, _updateCount(0)
{
_loadSettings();
_addLink(link);
connect(MAVLinkProtocol::instance(), &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived);
......@@ -809,3 +818,57 @@ void Vehicle::resetMessages()
emit messageTypeChanged();
}
}
int Vehicle::manualControlReservedButtonCount(void)
{
return _firmwarePlugin->manualControlReservedButtonCount();
}
void Vehicle::_loadSettings(void)
{
QSettings settings;
settings.beginGroup(QString(_settingsGroup).arg(_id));
bool convertOk;
_joystickMode = (JoystickMode_t)settings.value(_joystickModeSettingsKey, JoystickModeRC).toInt(&convertOk);
if (!convertOk) {
_joystickMode = JoystickModeRC;
}
}
void Vehicle::_saveSettings(void)
{
QSettings settings;
settings.beginGroup(QString(_settingsGroup).arg(_id));
settings.setValue(_joystickModeSettingsKey, _joystickMode);
}
int Vehicle::joystickMode(void)
{
return _joystickMode;
}
void Vehicle::setJoystickMode(int mode)
{
if (mode < 0 || mode >= JoystickModeMax) {
qCWarning(VehicleLog) << "Invalid joystick mode" << mode;
return;
}
_joystickMode = (JoystickMode_t)mode;
_saveSettings();
emit joystickModeChanged(mode);
}
QStringList Vehicle::joystickModes(void)
{
QStringList list;
list << "Simulate RC" << "Attitude" << "Position" << "Force" << "Velocity";
return list;
}
......@@ -29,13 +29,17 @@
#include <QObject>
#include <QGeoCoordinate>
#include <QQmlListProperty>
#include "LinkInterface.h"
#include "QGCMAVLink.h"
#include "UAS.h"
#include "MissionItem.h"
class UAS;
class UASInterface;
class FirmwarePlugin;
class AutoPilotPlugin;
class UASWaypointManager;
Q_DECLARE_LOGGING_CATEGORY(VehicleLog)
......@@ -91,6 +95,30 @@ public:
//-- MissionItem management
Q_PROPERTY(QQmlListProperty<MissionItem> missionItems READ missionItems NOTIFY missionItemsChanged)
/// Returns the number of buttons which are reserved for firmware use in the MANUAL_CONTROL mavlink
/// message. For example PX4 Flight Stack reserves the first 8 buttons to simulate rc switches.
/// The remainder can be assigned to Vehicle actions.
/// @return -1: reserver all buttons, >0 number of buttons to reserve
Q_PROPERTY(int manualControlReservedButtonCount READ manualControlReservedButtonCount CONSTANT)
typedef enum {
JoystickModeRC, ///< Joystick emulates am RC Transmitter
JoystickModeAttitude,
JoystickModePosition,
JoystickModeForce,
JoystickModeVelocity,
JoystickModeMax
} JoystickMode_t;
/// The joystick mode associated with this vehicle. Joystick modes are stored keyed by mavlink system id.
Q_PROPERTY(int joystickMode READ joystickMode WRITE setJoystickMode NOTIFY joystickModeChanged)
int joystickMode(void);
void setJoystickMode(int mode);
/// List of joystick mode names
Q_PROPERTY(QStringList joystickModes READ joystickModes CONSTANT)
QStringList joystickModes(void);
// Property accesors
int id(void) { return _id; }
MAV_AUTOPILOT firmwareType(void) { return _firmwareType; }
......@@ -106,6 +134,8 @@ public:
QList<LinkInterface*> links(void);
int manualControlReservedButtonCount(void);
typedef enum {
MessageNone,
MessageNormal,
......@@ -171,6 +201,7 @@ public slots:
signals:
void allLinksDisconnected(void);
void coordinateChanged(QGeoCoordinate coordinate);
void joystickModeChanged(int mode);
/// Used internally to move sendMessage call to main thread
void _sendMessageOnThread(mavlink_message_t message);
......@@ -241,6 +272,9 @@ private slots:
private:
bool _containsLink(LinkInterface* link);
void _addLink(LinkInterface* link);
void _loadSettings(void);
void _saveSettings(void);
bool _isAirplane ();
void _addChange (int id);
float _oneDecimal (float value);
......@@ -257,6 +291,8 @@ private:
/// This way Link deletion works correctly.
QList<SharedLinkInterface> _links;
JoystickMode_t _joystickMode;
UAS* _uas;
QGeoCoordinate _geoCoordinate;
......@@ -302,5 +338,8 @@ private:
UASWaypointManager* _wpm;
int _updateCount;
QList<MissionItem*>_waypoints;
static const char* _settingsGroup;
static const char* _joystickModeSettingsKey;
};
#endif
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controllers 1.0
/// Joystick Config
QGCView {
id: rootQGCView
viewPanel: panel
QGCPalette { id: qgcPal; colorGroupEnabled: panel.enabled }
readonly property string dialogTitle: "Joystick Config"
readonly property real labelToMonitorMargin: defaultTextWidth * 3
property bool controllerCompleted: false
property bool controllerAndViewReady: false
property var _activeVehicle: multiVehicleManager.activeVehicle
property var _activeJoystick: joystickManager.activeJoystick
function updateAxisCount()
{
if (controllerAndViewReady) {
if (controller.axisCount < controller.minAxisCount) {
showDialog(axisCountDialogComponent, dialogTitle, 50, 0)
} else {
hideDialog()
}
}
}
JoystickConfigController {
id: controller
factPanel: panel
statusText: statusText
cancelButton: cancelButton
nextButton: nextButton
skipButton: skipButton
onAxisCountChanged: updateAxisCount()
Component.onCompleted: {
controllerCompleted = true
if (rootQGCView.completedSignalled) {
controllerAndViewReady = true
controller.start()
updateAxisCount()
}
}
}
onCompleted: {
if (controllerCompleted) {
controllerAndViewReady = true
controller.start()
updateAxisCount()
}
}
QGCViewPanel {
id: panel
anchors.fill: parent
Component {
id: axisCountDialogComponent
QGCViewMessage {
message: controller.axisCount == 0 ? "No joystick axes deteced." : controller.minAxisCount + " joystick axes or more are needed to fly."
}
}
// Live axis monitor control component
Component {
id: axisMonitorDisplayComponent
Item {
property int axisValue: 0
property int __lastAxisValue: 0
readonly property int __axisValueMaxJitter: 100
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: (reversed ? (parent.width - _indicatorPosition) : _indicatorPosition) - (width / 2)
radius: width / 2
color: qgcPal.text
visible: mapped
property real _percentAxisValue: ((axisValue + 32768.0) / (32768.0 * 2))
property real _indicatorPosition: parent.width * _percentAxisValue
}
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
}
/*
// Axis value debugger
QGCLabel {
anchors.fill: parent
text: axisValue
}
*/
}
} // Component - axisMonitorDisplayComponent
// Main view Qml starts here
QGCLabel {
id: header
font.pixelSize: ScreenTools.largeFontPixelSize
text: "JOYSTICK CONFIG"
}
Item {
id: spacer
anchors.top: header.bottom
width: parent.width
height: 10
}
// Left side column
Column {
id: leftColumn
anchors.rightMargin: ScreenTools.defaultFontPixelWidth
anchors.top: spacer.bottom
anchors.left: parent.left
anchors.right: rightColumn.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: axisMonitorDisplayComponent
property real defaultTextWidth: rootQGCView.defaultTextWidth
property bool mapped: controller.rollAxisMapped
property bool reversed: controller.rollAxisReversed
}
Connections {
target: controller
onRollAxisValueChanged: rollLoader.item.axisValue = value
}
}
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: axisMonitorDisplayComponent
property real defaultTextWidth: rootQGCView.defaultTextWidth
property bool mapped: controller.pitchAxisMapped
property bool reversed: controller.pitchAxisReversed
}
Connections {
target: controller
onPitchAxisValueChanged: pitchLoader.item.axisValue = value
}
}
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: axisMonitorDisplayComponent
property real defaultTextWidth: rootQGCView.defaultTextWidth
property bool mapped: controller.yawAxisMapped
property bool reversed: controller.yawAxisReversed
}
Connections {
target: controller
onYawAxisValueChanged: yawLoader.item.axisValue = value
}
}
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: axisMonitorDisplayComponent
property real defaultTextWidth: rootQGCView.defaultTextWidth
property bool mapped: controller.throttleAxisMapped
property bool reversed: controller.throttleAxisReversed
}
Connections {
target: controller
onThrottleAxisValueChanged: throttleLoader.item.axisValue = value
}
}
} // 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: controller.nextButtonClicked()
}
} // Row - Buttons
// Status Text
QGCLabel {
id: statusText
width: parent.width
wrapMode: Text.WordWrap
}
Rectangle {
width: parent.width
height: 1
border.color: qgcPal.text
border.width: 1
}
// Settings
Row {
width: parent.width
spacing: ScreenTools.defaultFontPixelWidth
// Left column settings
Column {
width: parent.width / 2
spacing: ScreenTools.defaultFontPixelHeight
QGCLabel { text: "Additional Joystick settings:" }
Column {
width: parent.width
spacing: ScreenTools.defaultFontPixelHeight
Row {
width: parent.width
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
id: activeJoystickLabel
anchors.baseline: joystickCombo.baseline
text: "Active joystick:"
}
QGCComboBox {
id: joystickCombo
width: parent.width - activeJoystickLabel.width - parent.spacing
model: joystickManager.joystickNames
onActivated: _activeJoystick.setActiveJoystickName(textAt(index))
}
}
QGCCheckBox {
enabled: calibrated
text: calibrated ? "Enable joystick input, disable RC input" : "Enable joystick input (Calibrate First)"
checked: _activeJoystick.enabled
property bool calibrated: _activeJoystick.calibrated
onClicked: _activeJoystick.enabled = checked
}
Column {
spacing: ScreenTools.defaultFontPixelHeight / 3
ExclusiveGroup { id: throttleModeExclusiveGroup }
QGCRadioButton {
exclusiveGroup: throttleModeExclusiveGroup
text: "Center stick is zero throttle"
checked: _activeJoystick.throttleMode == 0
onClicked: _activeJoystick.throttleMode = 0
}
QGCRadioButton {
exclusiveGroup: throttleModeExclusiveGroup
text: "Full down stick is zero throttle"
checked: _activeJoystick.throttleMode == 1
onClicked: _activeJoystick.throttleMode = 1
}
}
QGCCheckBox {
id: advancedSettings
checked: _activeVehicle.joystickMode != 0
text: "Advanced settings (careful!)"
onClicked: {
if (!checked) {
_activeVehicle.joystickMode = 0
}
}
}
Row {
width: parent.width
spacing: ScreenTools.defaultFontPixelWidth
visible: advancedSettings.checked
QGCLabel {
id: joystickModeLabel
anchors.baseline: joystickModeCombo.baseline
text: "Joystick mode:"
}
QGCComboBox {
id: joystickModeCombo
currentIndex: _activeVehicle.joystickMode
width: ScreenTools.defaultFontPixelWidth * 20
model: _activeVehicle.joystickModes
onActivated: _activeVehicle.joystickMode = index
}
}
}
} // Column - left column
// Right column settings
Column {
width: parent.width / 2
spacing: ScreenTools.defaultFontPixelHeight
Connections {
target: _activeJoystick
onRawButtonPressedChanged: {
if (buttonActionRepeater.itemAt(index)) {
buttonActionRepeater.itemAt(index).pressed = pressed
}
}
}
QGCLabel { text: "Button actions:" }
Column {
width: parent.width
spacing: ScreenTools.defaultFontPixelHeight / 3
QGCLabel {
visible: _activeVehicle.manualControlReservedButtonCount != 0
text: "Buttons 0-" + reservedButtonCount + " reserved for firmware use"
property int reservedButtonCount: _activeVehicle.manualControlReservedButtonCount == -1 ? _activeJoystick.buttonCount : _activeVehicle.manualControlReservedButtonCount
}
Repeater {
id: buttonActionRepeater
model: _activeJoystick.buttonCount
Row {
spacing: ScreenTools.defaultFontPixelWidth
visible: _activeVehicle.manualControlReservedButtonCount == -1 ? false : modelData >= _activeVehicle.manualControlReservedButtonCount
property bool pressed
QGCCheckBox {
anchors.verticalCenter: parent.verticalCenter
checked: _activeJoystick.buttonActions[modelData] != -1
onClicked: _activeJoystick.setButtonAction(modelData, checked ? buttonActionCombo.currentIndex : -1)
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: ScreenTools.defaultFontPixelHeight * 1.5
height: width
border.width: 1
border.color: qgcPal.text
color: pressed ? qgcPal.buttonHighlight : qgcPal.button
QGCLabel {
anchors.fill: parent
color: pressed ? qgcPal.buttonHighlightText : qgcPal.buttonText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: modelData
}
}
QGCComboBox {
id: buttonActionCombo
width: ScreenTools.defaultFontPixelWidth * 20
model: _activeJoystick.actions
currentIndex: _activeJoystick.buttonActions[modelData]
onActivated: _activeJoystick.setButtonAction(modelData, index)
}
}
} // Repeater
} // Column
} // Column - right setting column
} // Row - Settings
} // Column - Left Main Column
// Right side column
Column {
id: rightColumn
anchors.top: parent.top
anchors.right: parent.right
width: defaultTextWidth * 35
spacing: 10
Image {
//width: parent.width
height: defaultTextHeight * 15
fillMode: Image.PreserveAspectFit
smooth: true
source: controller.imageHelp
}
// Axis monitor
Column {
width: parent.width
spacing: 5
QGCLabel { text: "Axis Monitor" }
Connections {
target: controller
onAxisValueChanged: {
if (axisMonitorRepeater.itemAt(axis)) {
axisMonitorRepeater.itemAt(axis).loader.item.axisValue = value
}
}
}
Repeater {
id: axisMonitorRepeater
model: controller.axisCount
width: parent.width
Row {
spacing: 5
// Need this to get to loader from Connections above
property Item loader: theLoader
QGCLabel {
id: axisLabel
text: modelData
}
Loader {
id: theLoader
anchors.verticalCenter: axisLabel.verticalCenter
height: rootQGCView.defaultTextHeight
width: 200
sourceComponent: axisMonitorDisplayComponent
property real defaultTextWidth: rootQGCView.defaultTextWidth
property bool mapped: true
readonly property bool reversed: false
}
}
}
} // Column - Axis Monitor
// Button monitor
Column {
width: parent.width
spacing: ScreenTools.defaultFontPixelHeight
QGCLabel { text: "Button Monitor" }
Connections {
target: _activeJoystick
onRawButtonPressedChanged: {
if (buttonMonitorRepeater.itemAt(index)) {
buttonMonitorRepeater.itemAt(index).pressed = pressed
}
}
}
Row {
spacing: -1
Repeater {
id: buttonMonitorRepeater
model: _activeJoystick.buttonCount
Rectangle {
width: ScreenTools.defaultFontPixelHeight * 1.5
height: width
border.width: 1
border.color: qgcPal.text
color: pressed ? qgcPal.buttonHighlight : qgcPal.button
property bool pressed
QGCLabel {
anchors.fill: parent
color: pressed ? qgcPal.buttonHighlightText : qgcPal.buttonText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: modelData
}
}
} // Repeater
} // Row
} // Column - Axis Monitor
} // Column - Right Column
} // QGCViewPanel
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#include "JoystickConfigController.h"
#include "JoystickManager.h"
#include "QGCMessageBox.h"
#include <QSettings>
QGC_LOGGING_CATEGORY(JoystickConfigControllerLog, "JoystickConfigControllerLog")
const int JoystickConfigController::_updateInterval = 150; ///< Interval for timer which updates radio channel widgets
const int JoystickConfigController::_calCenterPoint = 0;
const int JoystickConfigController::_calValidMinValue = -32768; ///< Largest valid minimum axis value
const int JoystickConfigController::_calValidMaxValue = 32768; ///< Smallest valid maximum axis value
const int JoystickConfigController::_calDefaultMinValue = -32768; ///< Default value for Min if not set
const int JoystickConfigController::_calDefaultMaxValue = 32768; ///< Default value for Max if not set
const int JoystickConfigController::_calRoughCenterDelta = 500; ///< Delta around center point which is considered to be roughly centered
const int JoystickConfigController::_calMoveDelta = 32768/2; ///< Amount of delta past center which is considered stick movement
const int JoystickConfigController::_calSettleDelta = 100; ///< Amount of delta which is considered no stick movement
const int JoystickConfigController::_calMinDelta = 1000; ///< Amount of delta allowed around min value to consider channel at min
const int JoystickConfigController::_stickDetectSettleMSecs = 500;
const char* JoystickConfigController::_imageFilePrefix = "calibration/";
const char* JoystickConfigController::_imageFileMode2Dir = "joystick/";
const char* JoystickConfigController::_imageCenter = "joystickCenter.png";
const char* JoystickConfigController::_imageThrottleUp = "joystickThrottleUp.png";
const char* JoystickConfigController::_imageThrottleDown = "joystickThrottleDown.png";
const char* JoystickConfigController::_imageYawLeft = "joystickYawLeft.png";
const char* JoystickConfigController::_imageYawRight = "joystickYawRight.png";
const char* JoystickConfigController::_imageRollLeft = "joystickRollLeft.png";
const char* JoystickConfigController::_imageRollRight = "joystickRollRight.png";
const char* JoystickConfigController::_imagePitchUp = "joystickPitchUp.png";
const char* JoystickConfigController::_imagePitchDown = "joystickPitchDown.png";
const char* JoystickConfigController::_settingsGroup = "Joysticks";
JoystickConfigController::JoystickConfigController(void) :
_currentStep(-1),
_axisCount(0),
_calState(calStateAxisWait),
_statusText(NULL),
_cancelButton(NULL),
_nextButton(NULL),
_skipButton(NULL)
{
// FIXME: Needs to handle active joystick change
connect(JoystickManager::instance()->activeJoystick(), &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged);
JoystickManager::instance()->activeJoystick()->startPolling();
_loadSettings();
_resetInternalCalibrationValues();
}
void JoystickConfigController::start(void)
{
_stopCalibration();
_setInternalCalibrationValuesFromSettings();
}
JoystickConfigController::~JoystickConfigController()
{
_storeSettings();
}
/// @brief Returns the state machine entry for the specified state.
const JoystickConfigController::stateMachineEntry* JoystickConfigController::_getStateMachineEntry(int step)
{
static const char* msgBegin = "Allow all sticks to center as shown in diagram.\n\nClick Next to continue";
static const char* msgThrottleUp = "Move the Throttle stick all the way up and hold it there...";
static const char* msgThrottleDown = "Move the Throttle stick all the way down and hold it there...";
static const char* msgYawLeft = "Move the Yaw stick all the way to the left and hold it there...";
static const char* msgYawRight = "Move the Yaw stick all the way to the right and hold it there...";
static const char* msgRollLeft = "Move the Roll stick all the way to the left and hold it there...";
static const char* msgRollRight = "Move the Roll stick all the way to the right and hold it there...";
static const char* msgPitchDown = "Move the Pitch stick all the way down and hold it there...";
static const char* msgPitchUp = "Move the Pitch stick all the way up and hold it there...";
static const char* msgPitchCenter = "Allow the Pitch stick to move back to center...";
static const char* msgComplete = "All settings have been captured. Click Next to Save.";
static const stateMachineEntry rgStateMachine[] = {
//Function
{ Joystick::maxFunction, msgBegin, _imageCenter, &JoystickConfigController::_inputCenterWaitBegin, &JoystickConfigController::_saveAllTrims, NULL },
{ Joystick::throttleFunction, msgThrottleUp, _imageThrottleUp, &JoystickConfigController::_inputStickDetect, NULL, NULL },
{ Joystick::throttleFunction, msgThrottleDown, _imageThrottleDown, &JoystickConfigController::_inputStickMin, NULL, NULL },
{ Joystick::yawFunction, msgYawRight, _imageYawRight, &JoystickConfigController::_inputStickDetect, NULL, NULL },
{ Joystick::yawFunction, msgYawLeft, _imageYawLeft, &JoystickConfigController::_inputStickMin, NULL, NULL },
{ Joystick::rollFunction, msgRollRight, _imageRollRight, &JoystickConfigController::_inputStickDetect, NULL, NULL },
{ Joystick::rollFunction, msgRollLeft, _imageRollLeft, &JoystickConfigController::_inputStickMin, NULL, NULL },
{ Joystick::pitchFunction, msgPitchUp, _imagePitchUp, &JoystickConfigController::_inputStickDetect, NULL, NULL },
{ Joystick::pitchFunction, msgPitchDown, _imagePitchDown, &JoystickConfigController::_inputStickMin, NULL, NULL },
{ Joystick::pitchFunction, msgPitchCenter, _imageCenter, &JoystickConfigController::_inputCenterWait, NULL, NULL },
{ Joystick::maxFunction, msgComplete, _imageCenter, NULL, &JoystickConfigController::_writeCalibration, NULL },
};
Q_ASSERT(step >=0 && step < (int)(sizeof(rgStateMachine) / sizeof(rgStateMachine[0])));
return &rgStateMachine[step];
}
void JoystickConfigController::_advanceState(void)
{
_currentStep++;
_setupCurrentState();
}
/// @brief Sets up the state machine according to the current step from _currentStep.
void JoystickConfigController::_setupCurrentState(void)
{
const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
_statusText->setProperty("text", state->instructions);
_setHelpImage(state->image);
_stickDetectAxis = _axisMax;
_stickDetectSettleStarted = false;
_calSaveCurrentValues();
_nextButton->setEnabled(state->nextFn != NULL);
_skipButton->setEnabled(state->skipFn != NULL);
}
void JoystickConfigController::_axisValueChanged(int axis, int value)
{
if (axis >= 0 && axis <= _axisMax) {
// We always update raw values
_axisRawValue[axis] = value;
emit axisValueChanged(axis, _axisRawValue[axis]);
// Signal attitude axis values to Qml if mapped
if (_rgAxisInfo[axis].function != Joystick::maxFunction) {
switch (_rgAxisInfo[axis].function) {
case Joystick::rollFunction:
emit rollAxisValueChanged(_axisRawValue[axis]);
break;
case Joystick::pitchFunction:
emit pitchAxisValueChanged(_axisRawValue[axis]);
break;
case Joystick::yawFunction:
emit yawAxisValueChanged(_axisRawValue[axis]);
break;
case Joystick::throttleFunction:
emit throttleAxisValueChanged(_axisRawValue[axis]);
break;
default:
break;
}
}
//qCDebug(JoystickConfigControllerLog) << "Raw value" << axis << value;
if (_currentStep == -1) {
// Track the axis count by keeping track of how many axes we see
if (axis + 1 > (int)_axisCount) {
_axisCount = axis + 1;
emit axisCountChanged(_axisCount);
}
}
if (_currentStep != -1) {
const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
Q_ASSERT(state);
if (state->rcInputFn) {
(this->*state->rcInputFn)(state->function, axis, value);
}
}
}
}
void JoystickConfigController::nextButtonClicked(void)
{
if (_currentStep == -1) {
// Need to have enough channels
if (_axisCount < _axisMinimum) {
QGCMessageBox::warning(tr("Joystick"), tr("Detected %1 joystick axes. To operate PX4, you need at least %2 axes.").arg(_axisCount).arg(_axisMinimum));
return;
}
_startCalibration();
} else {
const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
Q_ASSERT(state);
Q_ASSERT(state->nextFn);
(this->*state->nextFn)();
}
}
void JoystickConfigController::skipButtonClicked(void)
{
Q_ASSERT(_currentStep != -1);
const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
Q_ASSERT(state);
Q_ASSERT(state->skipFn);
(this->*state->skipFn)();
}
void JoystickConfigController::cancelButtonClicked(void)
{
_stopCalibration();
}
void JoystickConfigController::_saveAllTrims(void)
{
// We save all trims as the first step. At this point no axes are mapped but it should still
// allow us to get good trims for the roll/pitch/yaw/throttle even though we don't know which
// axiss they are yet. AS we continue through the process the other axes will get their
// trims reset to correct values.
for (int i=0; i<_axisCount; i++) {
qCDebug(JoystickConfigControllerLog) << "_saveAllTrims trim" << _axisRawValue[i];
_rgAxisInfo[i].axisTrim = _axisRawValue[i];
}
_advanceState();
}
/// @brief Waits for the sticks to be centered, enabling Next when done.
void JoystickConfigController::_inputCenterWaitBegin(Joystick::AxisFunction_t function, int axis, int value)
{
Q_UNUSED(function);
Q_UNUSED(axis);
Q_UNUSED(value);
// FIXME: Doesn't wait for center
_nextButton->setEnabled(true);
}
bool JoystickConfigController::_stickSettleComplete(int value)
{
// We are waiting for the stick to settle out to a max position
if (abs(_stickDetectValue - value) > _calSettleDelta) {
// Stick is moving too much to consider stopped
qCDebug(JoystickConfigControllerLog) << "_stickSettleComplete still moving, _stickDetectValue:value" << _stickDetectValue << value;
_stickDetectValue = value;
_stickDetectSettleStarted = false;
} else {
// Stick is still positioned within the specified small range
if (_stickDetectSettleStarted) {
// We have already started waiting
if (_stickDetectSettleElapsed.elapsed() > _stickDetectSettleMSecs) {
// Stick has stayed positioned in one place long enough, detection is complete.
return true;
}
} else {
// Start waiting for the stick to stay settled for _stickDetectSettleWaitMSecs msecs
qCDebug(JoystickConfigControllerLog) << "_stickSettleComplete starting settle timer, _stickDetectValue:value" << _stickDetectValue << value;
_stickDetectSettleStarted = true;
_stickDetectSettleElapsed.start();
}
}
return false;
}
void JoystickConfigController::_inputStickDetect(Joystick::AxisFunction_t function, int axis, int value)
{
qCDebug(JoystickConfigControllerLog) << "_inputStickDetect function:axis:value" << function << axis << value;
// If this axis is already used in a mapping we can't use it again
if (_rgAxisInfo[axis].function != Joystick::maxFunction) {
return;
}
if (_stickDetectAxis == _axisMax) {
// We have not detected enough movement on a axis yet
if (abs(_axisValueSave[axis] - value) > _calMoveDelta) {
// Stick has moved far enough to consider it as being selected for the function
qCDebug(JoystickConfigControllerLog) << "_inputStickDetect starting settle wait, function:axis:value" << function << axis << value;
// Setup up to detect stick being pegged to min or max value
_stickDetectAxis = axis;
_stickDetectInitialValue = value;
_stickDetectValue = value;
}
} else if (axis == _stickDetectAxis) {
if (_stickSettleComplete(value)) {
AxisInfo* info = &_rgAxisInfo[axis];
qCDebug(JoystickConfigControllerLog) << "_inputStickDetect settle complete, function:axis:value" << function << axis << value;
// Stick detection is complete. Stick should be at max position.
// Map the axis to the function
_rgFunctionAxisMapping[function] = axis;
info->function = function;
// Axis should be at max value, if it is below initial set point the the axis is reversed.
info->reversed = value < _axisValueSave[axis];
qCDebug(JoystickConfigControllerLog) << "_inputStickDetect reversed:value:_axisValueSave" << info->reversed << value << _axisValueSave[axis];
if (info->reversed) {
_rgAxisInfo[axis].axisMin = value;
} else {
_rgAxisInfo[axis].axisMax = value;
}
_signalAllAttiudeValueChanges();
_advanceState();
}
}
}
void JoystickConfigController::_inputStickMin(Joystick::AxisFunction_t function, int axis, int value)
{
// We only care about the axis mapped to the function we are working on
if (_rgFunctionAxisMapping[function] != axis) {
return;
}
if (_stickDetectAxis == _axisMax) {
// Setup up to detect stick being pegged to extreme position
if (_rgAxisInfo[axis].reversed) {
if (value > _calCenterPoint + _calMoveDelta) {
_stickDetectAxis = axis;
_stickDetectInitialValue = value;
_stickDetectValue = value;
}
} else {
if (value < _calCenterPoint - _calMoveDelta) {
_stickDetectAxis = axis;
_stickDetectInitialValue = value;
_stickDetectValue = value;
}
}
} else {
// We are waiting for the selected axis to settle out
if (_stickSettleComplete(value)) {
AxisInfo* info = &_rgAxisInfo[axis];
// Stick detection is complete. Stick should be at min position.
if (info->reversed) {
_rgAxisInfo[axis].axisMax = value;
} else {
_rgAxisInfo[axis].axisMin = value;
}
// Check if this is throttle and set trim accordingly
if (function == Joystick::throttleFunction) {
_rgAxisInfo[axis].axisTrim = value;
}
// XXX to support configs which can reverse they need to check a reverse
// flag here and not do this.
_advanceState();
}
}
}
void JoystickConfigController::_inputCenterWait(Joystick::AxisFunction_t function, int axis, int value)
{
// We only care about the axis mapped to the function we are working on
if (_rgFunctionAxisMapping[function] != axis) {
return;
}
if (_stickDetectAxis == _axisMax) {
// Sticks have not yet moved close enough to center
if (abs(_calCenterPoint - value) < _calRoughCenterDelta) {
// Stick has moved close enough to center that we can start waiting for it to settle
_stickDetectAxis = axis;
_stickDetectInitialValue = value;
_stickDetectValue = value;
}
} else {
if (_stickSettleComplete(value)) {
_advanceState();
}
}
}
/// @brief Resets internal calibration values to their initial state in preparation for a new calibration sequence.
void JoystickConfigController::_resetInternalCalibrationValues(void)
{
// Set all raw axiss to not reversed and center point values
for (size_t i=0; i<_axisMax; i++) {
struct AxisInfo* info = &_rgAxisInfo[i];
info->function = Joystick::maxFunction;
info->reversed = false;
info->axisMin = JoystickConfigController::_calCenterPoint;
info->axisMax = JoystickConfigController::_calCenterPoint;
info->axisTrim = JoystickConfigController::_calCenterPoint;
}
// Initialize attitude function mapping to function axis not set
for (size_t i=0; i<Joystick::maxFunction; i++) {
_rgFunctionAxisMapping[i] = _axisMax;
}
_signalAllAttiudeValueChanges();
}
/// @brief Sets internal calibration values from the stored settings
void JoystickConfigController::_setInternalCalibrationValuesFromSettings(void)
{
Joystick* joystick = JoystickManager::instance()->activeJoystick();
// Initialize all function mappings to not set
for (size_t i=0; i<_axisMax; i++) {
struct AxisInfo* info = &_rgAxisInfo[i];
info->function = Joystick::maxFunction;
}
for (size_t i=0; i<Joystick::maxFunction; i++) {
_rgFunctionAxisMapping[i] = _axisMax;
}
for (int axis=0; axis<_axisMax; axis++) {
struct AxisInfo* info = &_rgAxisInfo[axis];
Joystick::Calibration_t calibration = joystick->getCalibration(axis);
info->axisTrim = calibration.center;
info->axisMin = calibration.min;
info->axisMax = calibration.max;
info->reversed = calibration.reversed;
qCDebug(JoystickConfigControllerLog) << "Read settings axis:min:max:trim:reversed" << axis << info->axisMin << info->axisMax << info->axisTrim << info->reversed;
}
for (int function=0; function<Joystick::maxFunction; function++) {
int paramAxis;
paramAxis = joystick->getFunctionAxis((Joystick::AxisFunction_t)function);
_rgFunctionAxisMapping[function] = paramAxis;
_rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function;
}
_signalAllAttiudeValueChanges();
}
/// @brief Validates the current settings against the calibration rules resetting values as necessary.
void JoystickConfigController::_validateCalibration(void)
{
for (int chan = 0; chan<_axisMax; chan++) {
struct AxisInfo* info = &_rgAxisInfo[chan];
if (chan < _axisCount) {
// Validate Min/Max values. Although the axis appears as available we still may
// not have good min/max/trim values for it. Set to defaults if needed.
if (info->axisMin > _calValidMinValue || info->axisMax < _calValidMaxValue) {
qCDebug(JoystickConfigControllerLog) << "_validateCalibration resetting axis" << chan;
info->axisMin = _calDefaultMinValue;
info->axisMax = _calDefaultMaxValue;
info->axisTrim = info->axisMin + ((info->axisMax - info->axisMin) / 2);
} else {
switch (_rgAxisInfo[chan].function) {
case Joystick::throttleFunction:
case Joystick::yawFunction:
case Joystick::rollFunction:
case Joystick::pitchFunction:
// Make sure trim is within min/max
if (info->axisTrim < info->axisMin) {
info->axisTrim = info->axisMin;
} else if (info->axisTrim > info->axisMax) {
info->axisTrim = info->axisMax;
}
break;
default:
// Non-attitude control axiss have calculated trim
info->axisTrim = info->axisMin + ((info->axisMax - info->axisMin) / 2);
break;
}
}
} else {
// Unavailable axiss are set to defaults
qCDebug(JoystickConfigControllerLog) << "_validateCalibration resetting unavailable axis" << chan;
info->axisMin = _calDefaultMinValue;
info->axisMax = _calDefaultMaxValue;
info->axisTrim = info->axisMin + ((info->axisMax - info->axisMin) / 2);
info->reversed = false;
}
}
}
/// @brief Saves the rc calibration values to the board parameters.
void JoystickConfigController::_writeCalibration(void)
{
Joystick* joystick = JoystickManager::instance()->activeJoystick();
_validateCalibration();
for (int axis=0; axis<_axisMax; axis++) {
Joystick::Calibration_t calibration;
struct AxisInfo* info = &_rgAxisInfo[axis];
calibration.center = info->axisTrim;
calibration.min = info->axisMin;
calibration.max = info->axisMax;
calibration.reversed = info->reversed;
joystick->setCalibration(axis, calibration);
}
// Write function mapping parameters
for (int function=0; function<Joystick::maxFunction; function++) {
joystick->setFunctionAxis((Joystick::AxisFunction_t)function, _rgFunctionAxisMapping[function]);
}
_stopCalibration();
_setInternalCalibrationValuesFromSettings();
}
/// @brief Starts the calibration process
void JoystickConfigController::_startCalibration(void)
{
Q_ASSERT(_axisCount >= _axisMinimum);
_resetInternalCalibrationValues();
_nextButton->setProperty("text", "Next");
_cancelButton->setEnabled(true);
_currentStep = 0;
_setupCurrentState();
JoystickManager::instance()->activeJoystick()->setCalibrating(true);
}
/// @brief Cancels the calibration process, setting things back to initial state.
void JoystickConfigController::_stopCalibration(void)
{
_currentStep = -1;
_setInternalCalibrationValuesFromSettings();
_statusText->setProperty("text", "");
_nextButton->setProperty("text", "Calibrate");
_nextButton->setEnabled(true);
_cancelButton->setEnabled(false);
_skipButton->setEnabled(false);
_setHelpImage(_imageCenter);
JoystickManager::instance()->activeJoystick()->setCalibrating(false);
}
/// @brief Saves the current axis values, so that we can detect when the use moves an input.
void JoystickConfigController::_calSaveCurrentValues(void)
{
qCDebug(JoystickConfigControllerLog) << "_calSaveCurrentValues";
for (unsigned i = 0; i < _axisMax; i++) {
_axisValueSave[i] = _axisRawValue[i];
}
}
/// @brief Set up the Save state of calibration.
void JoystickConfigController::_calSave(void)
{
_calState = calStateSave;
_statusText->setProperty("text",
"The current calibration settings are now displayed for each axis on screen.\n\n"
"Click the Next button to upload calibration to board. Click Cancel if you don't want to save these values.");
_nextButton->setEnabled(true);
_skipButton->setEnabled(false);
_cancelButton->setEnabled(true);
// This updates the internal values according to the validation rules. Then _updateView will tick and update ui
// such that the settings that will be written our are displayed.
_validateCalibration();
}
void JoystickConfigController::_loadSettings(void)
{
QSettings settings;
settings.beginGroup(_settingsGroup);
}
void JoystickConfigController::_storeSettings(void)
{
QSettings settings;
settings.beginGroup(_settingsGroup);
}
void JoystickConfigController::_setHelpImage(const char* imageFile)
{
QString file = _imageFilePrefix;
file += _imageFileMode2Dir;
file += imageFile;
qCDebug(JoystickConfigControllerLog) << "_setHelpImage" << file;
_imageHelp = file;
emit imageHelpChanged(file);
}
int JoystickConfigController::axisCount(void)
{
return _axisCount;
}
int JoystickConfigController::rollAxisValue(void)
{
if (_rgFunctionAxisMapping[Joystick::rollFunction] != _axisMax) {
return _axisRawValue[Joystick::rollFunction];
} else {
return 1500;
}
}
int JoystickConfigController::pitchAxisValue(void)
{
if (_rgFunctionAxisMapping[Joystick::pitchFunction] != _axisMax) {
return _axisRawValue[Joystick::pitchFunction];
} else {
return 1500;
}
}
int JoystickConfigController::yawAxisValue(void)
{
if (_rgFunctionAxisMapping[Joystick::yawFunction] != _axisMax) {
return _axisRawValue[Joystick::yawFunction];
} else {
return 1500;
}
}
int JoystickConfigController::throttleAxisValue(void)
{
if (_rgFunctionAxisMapping[Joystick::throttleFunction] != _axisMax) {
return _axisRawValue[Joystick::throttleFunction];
} else {
return 1500;
}
}
bool JoystickConfigController::rollAxisMapped(void)
{
return _rgFunctionAxisMapping[Joystick::rollFunction] != _axisMax;
}
bool JoystickConfigController::pitchAxisMapped(void)
{
return _rgFunctionAxisMapping[Joystick::pitchFunction] != _axisMax;
}
bool JoystickConfigController::yawAxisMapped(void)
{
return _rgFunctionAxisMapping[Joystick::yawFunction] != _axisMax;
}
bool JoystickConfigController::throttleAxisMapped(void)
{
return _rgFunctionAxisMapping[Joystick::throttleFunction] != _axisMax;
}
bool JoystickConfigController::rollAxisReversed(void)
{
if (_rgFunctionAxisMapping[Joystick::rollFunction] != _axisMax) {
return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::rollFunction]].reversed;
} else {
return false;
}
}
bool JoystickConfigController::pitchAxisReversed(void)
{
if (_rgFunctionAxisMapping[Joystick::pitchFunction] != _axisMax) {
return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::pitchFunction]].reversed;
} else {
return false;
}
}
bool JoystickConfigController::yawAxisReversed(void)
{
if (_rgFunctionAxisMapping[Joystick::yawFunction] != _axisMax) {
return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::yawFunction]].reversed;
} else {
return false;
}
}
bool JoystickConfigController::throttleAxisReversed(void)
{
if (_rgFunctionAxisMapping[Joystick::throttleFunction] != _axisMax) {
return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::throttleFunction]].reversed;
} else {
return false;
}
}
void JoystickConfigController::_signalAllAttiudeValueChanges(void)
{
emit rollAxisMappedChanged(rollAxisMapped());
emit pitchAxisMappedChanged(pitchAxisMapped());
emit yawAxisMappedChanged(yawAxisMapped());
emit throttleAxisMappedChanged(throttleAxisMapped());
emit rollAxisReversedChanged(rollAxisReversed());
emit pitchAxisReversedChanged(pitchAxisReversed());
emit yawAxisReversedChanged(yawAxisReversed());
emit throttleAxisReversedChanged(throttleAxisReversed());
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @brief Radio Config Qml Controller
/// @author Don Gagne <don@thegagnes.com
#ifndef JoystickConfigController_H
#define JoystickConfigController_H
#include <QTimer>
#include "FactPanelController.h"
#include "QGCLoggingCategory.h"
#include "Joystick.h"
Q_DECLARE_LOGGING_CATEGORY(JoystickConfigControllerLog)
class RadioConfigest;
namespace Ui {
class JoystickConfigController;
}
class JoystickConfigController : public FactPanelController
{
Q_OBJECT
friend class RadioConfigTest; ///< This allows our unit test to access internal information needed.
public:
JoystickConfigController(void);
~JoystickConfigController();
Q_PROPERTY(int minAxisCount MEMBER _axisMinimum CONSTANT)
Q_PROPERTY(int axisCount READ axisCount NOTIFY axisCountChanged)
Q_PROPERTY(QQuickItem* statusText MEMBER _statusText)
Q_PROPERTY(QQuickItem* cancelButton MEMBER _cancelButton)
Q_PROPERTY(QQuickItem* nextButton MEMBER _nextButton)
Q_PROPERTY(QQuickItem* skipButton MEMBER _skipButton)
Q_PROPERTY(bool rollAxisMapped READ rollAxisMapped NOTIFY rollAxisMappedChanged)
Q_PROPERTY(bool pitchAxisMapped READ pitchAxisMapped NOTIFY pitchAxisMappedChanged)
Q_PROPERTY(bool yawAxisMapped READ yawAxisMapped NOTIFY yawAxisMappedChanged)
Q_PROPERTY(bool throttleAxisMapped READ throttleAxisMapped NOTIFY throttleAxisMappedChanged)
Q_PROPERTY(int rollAxisValue READ rollAxisValue NOTIFY rollAxisValueChanged)
Q_PROPERTY(int pitchAxisValue READ pitchAxisValue NOTIFY pitchAxisValueChanged)
Q_PROPERTY(int yawAxisValue READ yawAxisValue NOTIFY yawAxisValueChanged)
Q_PROPERTY(int throttleAxisValue READ throttleAxisValue NOTIFY throttleAxisValueChanged)
Q_PROPERTY(int rollAxisReversed READ rollAxisReversed NOTIFY rollAxisReversedChanged)
Q_PROPERTY(int pitchAxisReversed READ pitchAxisReversed NOTIFY pitchAxisReversedChanged)
Q_PROPERTY(int yawAxisReversed READ yawAxisReversed NOTIFY yawAxisReversedChanged)
Q_PROPERTY(int throttleAxisReversed READ throttleAxisReversed NOTIFY throttleAxisReversedChanged)
Q_PROPERTY(QString imageHelp MEMBER _imageHelp NOTIFY imageHelpChanged)
Q_INVOKABLE void cancelButtonClicked(void);
Q_INVOKABLE void skipButtonClicked(void);
Q_INVOKABLE void nextButtonClicked(void);
Q_INVOKABLE void start(void);
int rollAxisValue(void);
int pitchAxisValue(void);
int yawAxisValue(void);
int throttleAxisValue(void);
bool rollAxisMapped(void);
bool pitchAxisMapped(void);
bool yawAxisMapped(void);
bool throttleAxisMapped(void);
bool rollAxisReversed(void);
bool pitchAxisReversed(void);
bool yawAxisReversed(void);
bool throttleAxisReversed(void);
int axisCount(void);
signals:
void axisCountChanged(int axisCount);
void axisValueChanged(int axis, int value);
void rollAxisMappedChanged(bool mapped);
void pitchAxisMappedChanged(bool mapped);
void yawAxisMappedChanged(bool mapped);
void throttleAxisMappedChanged(bool mapped);
void rollAxisValueChanged(int value);
void pitchAxisValueChanged(int value);
void yawAxisValueChanged(int value);
void throttleAxisValueChanged(int value);
void rollAxisReversedChanged(bool reversed);
void pitchAxisReversedChanged(bool reversed);
void yawAxisReversedChanged(bool reversed);
void throttleAxisReversedChanged(bool reversed);
void imageHelpChanged(QString source);
// @brief Signalled when in unit test mode and a message box should be displayed by the next button
void nextButtonMessageBoxDisplayed(void);
private slots:
void _axisValueChanged(int axis, int value);
private:
/// @brief The states of the calibration state machine.
enum calStates {
calStateAxisWait,
calStateBegin,
calStateIdentify,
calStateMinMax,
calStateCenterThrottle,
calStateDetectInversion,
calStateTrims,
calStateSave
};
typedef void (JoystickConfigController::*inputFn)(Joystick::AxisFunction_t function, int axis, int value);
typedef void (JoystickConfigController::*buttonFn)(void);
struct stateMachineEntry {
Joystick::AxisFunction_t function;
const char* instructions;
const char* image;
inputFn rcInputFn;
buttonFn nextFn;
buttonFn skipFn;
};
/// @brief A set of information associated with a radio axis.
struct AxisInfo {
Joystick::AxisFunction_t function; ///< Function mapped to this axis, Joystick::maxFunction for none
bool reversed; ///< true: axis is reverse, false: not reversed
int axisMin; ///< Minimum axis value
int axisMax; ///< Maximum axis value
int axisTrim; ///< Trim position
};
int _currentStep; ///< Current step of state machine
const struct stateMachineEntry* _getStateMachineEntry(int step);
void _advanceState(void);
void _setupCurrentState(void);
void _inputCenterWaitBegin (Joystick::AxisFunction_t function, int axis, int value);
void _inputStickDetect (Joystick::AxisFunction_t function, int axis, int value);
void _inputStickMin (Joystick::AxisFunction_t function, int axis, int value);
void _inputCenterWait (Joystick::AxisFunction_t function, int axis, int value);
void _switchDetect(Joystick::AxisFunction_t function, int axis, int value, bool moveToNextStep);
void _saveFlapsDown(void);
void _skipFlaps(void);
void _saveAllTrims(void);
bool _stickSettleComplete(int value);
void _validateCalibration(void);
void _writeCalibration(void);
void _resetInternalCalibrationValues(void);
void _setInternalCalibrationValuesFromSettings(void);
void _startCalibration(void);
void _stopCalibration(void);
void _calSave(void);
void _calSaveCurrentValues(void);
void _setHelpImage(const char* imageFile);
void _loadSettings(void);
void _storeSettings(void);
void _signalAllAttiudeValueChanges(void);
// Member variables
static const char* _imageFileMode2Dir;
static const char* _imageFilePrefix;
static const char* _imageCenter;
static const char* _imageThrottleUp;
static const char* _imageThrottleDown;
static const char* _imageYawLeft;
static const char* _imageYawRight;
static const char* _imageRollLeft;
static const char* _imageRollRight;
static const char* _imagePitchUp;
static const char* _imagePitchDown;
static const char* _settingsGroup;
static const int _updateInterval; ///< Interval for ui update timer
int _rgFunctionAxisMapping[Joystick::maxFunction]; ///< Maps from joystick function to axis index. _axisMax indicates axis not set for this function.
static const int _attitudeControls = 5;
int _axisCount; ///< Number of actual joystick axes available
static const int _axisMax = 4; ///< Maximum number of supported joystick axes
static const int _axisMinimum = 4; ///< Minimum numner of joystick axes required to run PX4
struct AxisInfo _rgAxisInfo[_axisMax]; ///< Information associated with each axis
enum calStates _calState; ///< Current calibration state
int _calStateCurrentAxis; ///< Current axis being worked on in calStateIdentify and calStateDetectInversion
bool _calStateAxisComplete; ///< Work associated with current axis is complete
int _calStateIdentifyOldMapping; ///< Previous mapping for axis being currently identified
int _calStateReverseOldMapping; ///< Previous mapping for axis being currently used to detect inversion
static const int _calCenterPoint;
static const int _calValidMinValue;
static const int _calValidMaxValue;
static const int _calDefaultMinValue;
static const int _calDefaultMaxValue;
static const int _calRoughCenterDelta;
static const int _calMoveDelta;
static const int _calSettleDelta;
static const int _calMinDelta;
int _axisValueSave[_axisMax]; ///< Saved values prior to detecting axis movement
int _axisRawValue[_axisMax]; ///< Current set of raw axis values
int _stickDetectAxis;
int _stickDetectInitialValue;
int _stickDetectValue;
bool _stickDetectSettleStarted;
QTime _stickDetectSettleElapsed;
static const int _stickDetectSettleMSecs;
QQuickItem* _statusText;
QQuickItem* _cancelButton;
QQuickItem* _nextButton;
QQuickItem* _skipButton;
QString _imageHelp;
};
#endif // JoystickConfigController_H
......@@ -72,6 +72,16 @@ Rectangle {
}
}
function showJoystickPanel()
{
if (multiVehicleManager.activeVehicleAvailable && multiVehicleManager.activeVehicle.autopilot.armed) {
messagePanelText = armedVehicleText
panelLoader.sourceComponent = messagePanelComponent
} else {
panelLoader.source = "JoystickConfig.qml";
}
}
function showParametersPanel()
{
panelLoader.source = "SetupParameterEditor.qml";
......@@ -170,6 +180,18 @@ Rectangle {
onClicked: showFirmwarePanel()
}
SubMenuButton {
id: joystickButton
width: buttonWidth
setupIndicator: true
setupComplete: joystickManager.activeJoystick.calibrated
exclusiveGroup: setupButtonGroup
visible: multiVehicleManager.parameterReadyVehicleAvailable && joystickManager.joysticks.length != 0
text: "JOYSTICK"
onClicked: showJoystickPanel()
}
Repeater {
model: multiVehicleManager.parameterReadyVehicleAvailable ? multiVehicleManager.activeVehicle.autopilot.vehicleComponents : 0
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtGraphicalEffects 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
Rectangle {
id: topLevel
QGCPalette { id: palette; colorGroupEnabled: true }
color: palette.window
ExclusiveGroup { id: setupButtonGroup }
Column {
anchors.fill: parent
SubMenuButton {
id: summaryButton; objectName: "summaryButton"
width: parent.width
text: "SUMMARY"
imageResource: "VehicleSummaryIcon.png"
setupIndicator: false
exclusiveGroup: setupButtonGroup
onClicked: controller.summaryButtonClicked()
}
SubMenuButton {
id: firmwareButton; objectName: "firmwareButton"
width: parent.width
text: "FIRMWARE"
imageResource: "FirmwareUpgradeIcon.png"
setupIndicator: false
exclusiveGroup: setupButtonGroup
onClicked: controller.firmwareButtonClicked()
}
Repeater {
model: autopilot ? autopilot.vehicleComponents : 0
SubMenuButton {
width: parent.width
text: modelData.name.toUpperCase()
imageResource: modelData.iconResource
setupComplete: modelData.setupComplete
exclusiveGroup: setupButtonGroup
onClicked: controller.setupButtonClicked(modelData)
}
}
SubMenuButton {
width: parent.width
text: "PARAMETERS"
setupIndicator: false
exclusiveGroup: setupButtonGroup
onClicked: controller.parametersButtonClicked()
}
}
}
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtGraphicalEffects 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
Rectangle {
id: topLevel
QGCPalette { id: palette; colorGroupEnabled: true }
color: palette.window
ExclusiveGroup { id: setupButtonGroup }
Column {
anchors.fill: parent
SubMenuButton {
id: firmwareButton; objectName: "firmwareButton"
width: parent.width
text: "FIRMWARE"
imageResource: "FirmwareUpgradeIcon.png"
setupIndicator: false
exclusiveGroup: setupButtonGroup
onClicked: controller.firmwareButtonClicked()
}
Item { width: parent.width; height: 10 } // spacer
QGCLabel {
width: parent.width
text: "Full setup options are only available when connected to vehicle and full parameter list has completed downloading."
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}
......@@ -25,6 +25,7 @@
#include "MultiVehicleManager.h"
#include "QGCMAVLink.h"
#include "QGCFileDialog.h"
#include "UAS.h"
#include <QSettings>
#include <QUrl>
......
......@@ -23,6 +23,7 @@
#include "ViewWidgetController.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
ViewWidgetController::ViewWidgetController(void) :
_autopilot(NULL),
......
/*=====================================================================
======================================================================*/
/**
* @file
* @brief Joystick interface
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
* @author Andreas Romer <mavteam@student.ethz.ch>
* @author Julian Oes <julian@oes.ch
*
*/
#include "JoystickInput.h"
#include <QDebug>
#include <limits.h>
#include "UAS.h"
#include "MultiVehicleManager.h"
#include "QGC.h"
#include <QMutexLocker>
#include <QSettings>
#include <math.h>
#include <limits>
using namespace std;
/**
* The coordinate frame of the joystick axis is the aeronautical frame like shown on this image:
* @image html http://pixhawk.ethz.ch/wiki/_media/standards/body-frame.png Aeronautical frame
*/
JoystickInput::JoystickInput() :
sdlJoystickMin(-32768.0f),
sdlJoystickMax(32767.0f),
isEnabled(false),
isCalibrating(false),
done(false),
uas(NULL),
autopilotType(0),
systemType(0),
uasCanReverse(false),
rollAxis(-1),
pitchAxis(-1),
yawAxis(-1),
throttleAxis(-1),
joystickName(""),
joystickID(-1),
joystickNumButtons(0)
{
// Make sure we initialize with the correct UAS.
_activeVehicleChanged(MultiVehicleManager::instance()->activeVehicle());
// Start this thread. This allows the Joystick Settings window to work correctly even w/o any UASes connected.
start();
}
JoystickInput::~JoystickInput()
{
storeGeneralSettings();
storeJoystickSettings();
done = true;
}
void JoystickInput::loadGeneralSettings()
{
// Load defaults from settings
QSettings settings;
// Deal with settings specific to the JoystickInput
settings.beginGroup("JOYSTICK_INPUT");
isEnabled = settings.value("ENABLED", false).toBool();
joystickName = settings.value("JOYSTICK_NAME", "").toString();
mode = (JOYSTICK_MODE)settings.value("JOYSTICK_MODE", JOYSTICK_MODE_ATTITUDE).toInt();
settings.endGroup();
}
/**
* @brief Restores settings for the current joystick from saved settings file.
* Assumes that both joystickName & joystickNumAxes are correct.
*/
void JoystickInput::loadJoystickSettings()
{
// Load defaults from settings
QSettings settings;
// Now for the current joystick
settings.beginGroup(joystickName);
rollAxis = (settings.value("ROLL_AXIS_MAPPING", -1).toInt());
pitchAxis = (settings.value("PITCH_AXIS_MAPPING", -1).toInt());
yawAxis = (settings.value("YAW_AXIS_MAPPING", -1).toInt());
throttleAxis = (settings.value("THROTTLE_AXIS_MAPPING", -1).toInt());
// Clear out and then restore the (AUTOPILOT, SYSTEM) mapping for joystick settings
joystickSettings.clear();
int autopilots = settings.beginReadArray("AUTOPILOTS");
for (int i = 0; i < autopilots; i++)
{
settings.setArrayIndex(i);
int autopilotType = settings.value("AUTOPILOT_TYPE", 0).toInt();
int systems = settings.beginReadArray("SYSTEMS");
for (int j = 0; j < systems; j++)
{
settings.setArrayIndex(j);
int systemType = settings.value("SYSTEM_TYPE", 0).toInt();
// Now that both the autopilot and system type are available, update some references.
QMap<int, bool>* joystickAxesInverted = &joystickSettings[autopilotType][systemType].axesInverted;
QMap<int, bool>* joystickAxesLimited = &joystickSettings[autopilotType][systemType].axesLimited;
QMap<int, float>* joystickAxesMinRange = &joystickSettings[autopilotType][systemType].axesMaxRange;
QMap<int, float>* joystickAxesMaxRange = &joystickSettings[autopilotType][systemType].axesMinRange;
QMap<int, int>* joystickButtonActions = &joystickSettings[autopilotType][systemType].buttonActions;
// Read back the joystickAxesInverted QList one element at a time.
int axesStored = settings.beginReadArray("AXES_INVERTED");
for (int k = 0; k < axesStored; k++)
{
settings.setArrayIndex(k);
int index = settings.value("INDEX", 0).toInt();
bool inverted = settings.value("INVERTED", false).toBool();
joystickAxesInverted->insert(index, inverted);
}
settings.endArray();
// Read back the joystickAxesLimited QList one element at a time.
axesStored = settings.beginReadArray("AXES_LIMITED");
for (int k = 0; k < axesStored; k++)
{
settings.setArrayIndex(k);
int index = settings.value("INDEX", 0).toInt();
bool limited = settings.value("LIMITED", false).toBool();
joystickAxesLimited->insert(index, limited);
}
settings.endArray();
// Read back the joystickAxesMinRange QList one element at a time.
axesStored = settings.beginReadArray("AXES_MIN_RANGE");
for (int k = 0; k < axesStored; k++)
{
settings.setArrayIndex(k);
int index = settings.value("INDEX", 0).toInt();
float min = settings.value("MIN_RANGE", false).toFloat();
joystickAxesMinRange->insert(index, min);
}
settings.endArray();
// Read back the joystickAxesMaxRange QList one element at a time.
axesStored = settings.beginReadArray("AXES_MAX_RANGE");
for (int k = 0; k < axesStored; k++)
{
settings.setArrayIndex(k);
int index = settings.value("INDEX", 0).toInt();
float max = settings.value("MAX_RANGE", false).toFloat();
joystickAxesMaxRange->insert(index, max);
}
settings.endArray();
// Read back the button->action mapping.
int buttonsStored = settings.beginReadArray("BUTTONS_ACTIONS");
for (int k = 0; k < buttonsStored; k++)
{
settings.setArrayIndex(k);
int index = settings.value("INDEX", 0).toInt();
int action = settings.value("ACTION", 0).toInt();
joystickButtonActions->insert(index, action);
}
settings.endArray();
}
settings.endArray();
}
settings.endArray();
settings.endGroup();
emit joystickSettingsChanged();
}
void JoystickInput::storeGeneralSettings() const
{
QSettings settings;
settings.beginGroup("JOYSTICK_INPUT");
settings.setValue("ENABLED", isEnabled);
settings.setValue("JOYSTICK_NAME", joystickName);
settings.setValue("JOYSTICK_MODE", mode);
settings.endGroup();
}
void JoystickInput::storeJoystickSettings() const
{
// Store settings
QSettings settings;
settings.beginGroup(joystickName);
settings.setValue("ROLL_AXIS_MAPPING", rollAxis);
settings.setValue("PITCH_AXIS_MAPPING", pitchAxis);
settings.setValue("YAW_AXIS_MAPPING", yawAxis);
settings.setValue("THROTTLE_AXIS_MAPPING", throttleAxis);
settings.beginWriteArray("AUTOPILOTS");
QMapIterator<int, QMap<int, JoystickSettings> > i(joystickSettings);
int autopilotIndex = 0;
while (i.hasNext())
{
i.next();
settings.setArrayIndex(autopilotIndex++);
int autopilotType = i.key();
settings.setValue("AUTOPILOT_TYPE", autopilotType);
settings.beginWriteArray("SYSTEMS");
QMapIterator<int, JoystickSettings> j(i.value());
int systemIndex = 0;
while (j.hasNext())
{
j.next();
settings.setArrayIndex(systemIndex++);
int systemType = j.key();
settings.setValue("SYSTEM_TYPE", systemType);
// Now that both the autopilot and system type are available, update some references.
QMapIterator<int, bool> joystickAxesInverted(joystickSettings[autopilotType][systemType].axesInverted);
QMapIterator<int, bool> joystickAxesLimited(joystickSettings[autopilotType][systemType].axesLimited);
QMapIterator<int, float> joystickAxesMinRange(joystickSettings[autopilotType][systemType].axesMaxRange);
QMapIterator<int, float> joystickAxesMaxRange(joystickSettings[autopilotType][systemType].axesMinRange);
QMapIterator<int, int> joystickButtonActions(joystickSettings[autopilotType][systemType].buttonActions);
settings.beginWriteArray("AXES_INVERTED");
int k = 0;
while (joystickAxesInverted.hasNext())
{
joystickAxesInverted.next();
int inverted = joystickAxesInverted.value();
// Only save this axes' inversion status if it's not the default
if (inverted)
{
settings.setArrayIndex(k++);
int index = joystickAxesInverted.key();
settings.setValue("INDEX", index);
settings.setValue("INVERTED", inverted);
}
}
settings.endArray();
settings.beginWriteArray("AXES_MIN_RANGE");
k = 0;
while (joystickAxesMinRange.hasNext())
{
joystickAxesMinRange.next();
float min = joystickAxesMinRange.value();
settings.setArrayIndex(k++);
int index = joystickAxesMinRange.key();
settings.setValue("INDEX", index);
settings.setValue("MIN_RANGE", min);
}
settings.endArray();
settings.beginWriteArray("AXES_MAX_RANGE");
k = 0;
while (joystickAxesMaxRange.hasNext())
{
joystickAxesMaxRange.next();
float max = joystickAxesMaxRange.value();
settings.setArrayIndex(k++);
int index = joystickAxesMaxRange.key();
settings.setValue("INDEX", index);
settings.setValue("MAX_RANGE", max);
}
settings.endArray();
settings.beginWriteArray("AXES_LIMITED");
k = 0;
while (joystickAxesLimited.hasNext())
{
joystickAxesLimited.next();
int limited = joystickAxesLimited.value();
if (limited)
{
settings.setArrayIndex(k++);
int index = joystickAxesLimited.key();
settings.setValue("INDEX", index);
settings.setValue("LIMITED", limited);
}
}
settings.endArray();
settings.beginWriteArray("BUTTONS_ACTIONS");
k = 0;
while (joystickButtonActions.hasNext())
{
joystickButtonActions.next();
int action = joystickButtonActions.value();
if (action != -1)
{
settings.setArrayIndex(k++);
int index = joystickButtonActions.key();
settings.setValue("INDEX", index);
settings.setValue("ACTION", action);
}
}
settings.endArray();
}
settings.endArray(); // SYSTEMS
}
settings.endArray(); // AUTOPILOTS
settings.endGroup();
}
void JoystickInput::_activeVehicleChanged(Vehicle* vehicle)
{
if (this->uas)
{
disconnect(this, SIGNAL(joystickChanged(float,float,float,float,qint8,qint8,quint16,quint8)), uas, SLOT(setExternalControlSetpoint(float,float,float,float,qint8,qint8,quint16,quint8)));
disconnect(this, SIGNAL(actionTriggered(int)), uas, SLOT(triggerAction(int)));
uasCanReverse = false;
uas = NULL;
}
// Save any settings for the last UAS
if (joystickID > -1)
{
storeJoystickSettings();
}
if (vehicle)
{
uas = vehicle->uas();
connect(this, SIGNAL(joystickChanged(float,float,float,float,qint8,qint8,quint16,quint8)), uas, SLOT(setExternalControlSetpoint(float,float,float,float,qint8,qint8,quint16,quint8)));
qDebug() << "connected joystick";
connect(this, SIGNAL(actionTriggered(int)), uas, SLOT(triggerAction(int)));
uasCanReverse = uas->systemCanReverse();
// Update the joystick settings for a new UAS.
autopilotType = uas->getAutopilotType();
systemType = uas->getSystemType();
}
// Make sure any UI elements know we've updated the UAS. The signal is re-emitted here so that UI elements know to
// update their UAS-specific UI.
emit activeVehicleChanged(vehicle);
// Load any joystick-specific settings now that the UAS has changed.
if (joystickID > -1)
{
loadJoystickSettings();
}
}
void JoystickInput::setEnabled(bool enabled)
{
this->isEnabled = enabled;
storeJoystickSettings();
}
void JoystickInput::init()
{
// Initialize SDL Joystick support and detect number of joysticks.
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) {
printf("Couldn't initialize SimpleDirectMediaLayer: %s\n", SDL_GetError());
}
// Enumerate joysticks and select one
numJoysticks = SDL_NumJoysticks();
// If no joysticks are connected, there's nothing we can do, so just keep
// going back to sleep every second unless the program quits.
while (!numJoysticks && !done)
{
QGC::SLEEP::sleep(1);
}
if (done)
{
return;
}
// Now that we've detected a joystick, load in the joystick-agnostic settings.
loadGeneralSettings();
// Enumerate all found devices
qDebug() << QString("%1 Input devices found:").arg(numJoysticks);
int activeJoystick = 0;
for(int i=0; i < numJoysticks; i++ )
{
QString name = SDL_JoystickName(i);
qDebug() << QString("\t%1").arg(name);
// If we've matched this joystick to what was last opened, note it.
// Note: The way this is implemented the LAST joystick of a given name will be opened.
if (name == joystickName)
{
activeJoystick = i;
}
SDL_Joystick* x = SDL_JoystickOpen(i);
qDebug() << QString("\tNumber of Axes: %1").arg(QString::number(SDL_JoystickNumAxes(x)));
qDebug() << QString("\tNumber of Buttons: %1").arg(QString::number(SDL_JoystickNumButtons(x)));
SDL_JoystickClose(x);
}
// Set the active joystick based on name, if a joystick was found in the saved settings, otherwise
// default to the first one.
setActiveJoystick(activeJoystick);
// Now make sure we know what the current UAS is and track changes to it.
_activeVehicleChanged(MultiVehicleManager::instance()->activeVehicle());
connect(MultiVehicleManager::instance(), &MultiVehicleManager::activeVehicleChanged, this, &JoystickInput::_activeVehicleChanged);
}
void JoystickInput::shutdown()
{
done = true;
}
/**
* @brief Execute the Joystick process
* Note that the SDL procedure is polled. This is because connecting and disconnecting while the event checker is running
* fails as of SDL 1.2. It is therefore much easier to just poll for the joystick we want to sample.
*/
void JoystickInput::run()
{
init();
forever
{
if (done)
{
done = false;
exit();
return;
}
// Poll the joystick for new values.
SDL_JoystickUpdate();
// Emit all necessary signals for all axes.
for (int i = 0; i < joystickNumAxes; i++)
{
// First emit the uncalibrated values for each axis based on their ID.
// This is generally not used for controlling a vehicle, but a UI representation, so it being slightly off is fine.
// Here we map the joystick axis value into the initial range of [0:1].
float axisValue = SDL_JoystickGetAxis(joystick, i);
// during calibration save min and max values
if (isCalibrating)
{
if (joystickSettings[autopilotType][systemType].axesMinRange.value(i) > axisValue)
{
joystickSettings[autopilotType][systemType].axesMinRange[i] = axisValue;
}
if (joystickSettings[autopilotType][systemType].axesMaxRange.value(i) < axisValue)
{
joystickSettings[autopilotType][systemType].axesMaxRange[i] = axisValue;
}
}
if (joystickSettings[autopilotType][systemType].axesInverted[i])
{
axisValue = (axisValue - joystickSettings[autopilotType][systemType].axesMinRange.value(i)) /
(joystickSettings[autopilotType][systemType].axesMaxRange.value(i) -
joystickSettings[autopilotType][systemType].axesMinRange.value(i));
}
else
{
axisValue = (axisValue - joystickSettings[autopilotType][systemType].axesMaxRange.value(i)) /
(joystickSettings[autopilotType][systemType].axesMinRange.value(i) -
joystickSettings[autopilotType][systemType].axesMaxRange.value(i));
}
axisValue = 1.0f - axisValue;
// For non-throttle axes or if the UAS can reverse, go ahead and convert this into the range [-1:1].
//if (uasCanReverse || throttleAxis != i)
// don't take into account if UAS can reverse. This means to reverse position but not throttle
// therefore deactivated for now
if (throttleAxis != i)
{
axisValue = axisValue * 2.0f - 1.0f;
}
// Otherwise if this vehicle can only go forward, scale it to [0:1].
else if (throttleAxis == i && joystickSettings[autopilotType][systemType].axesLimited.value(i))
{
if (axisValue < 0.0f)
{
axisValue = 0.0f;
}
}
// Bound rounding errors
if (axisValue > 1.0f) axisValue = 1.0f;
if (axisValue < -1.0f) axisValue = -1.0f;
if (joystickAxes[i] != axisValue)
{
joystickAxes[i] = axisValue;
emit axisValueChanged(i, axisValue);
}
}
// Build up vectors describing the hat position
int hatPosition = SDL_JoystickGetHat(joystick, 0);
qint8 newYHat = 0;
if ((SDL_HAT_UP & hatPosition) > 0) newYHat = 1;
if ((SDL_HAT_DOWN & hatPosition) > 0) newYHat = -1;
qint8 newXHat = 0;
if ((SDL_HAT_LEFT & hatPosition) > 0) newXHat = -1;
if ((SDL_HAT_RIGHT & hatPosition) > 0) newXHat = 1;
if (newYHat != yHat || newXHat != xHat)
{
xHat = newXHat;
yHat = newYHat;
emit hatDirectionChanged(newXHat, newYHat);
}
// Emit signals for each button individually
for (int i = 0; i < joystickNumButtons; i++)
{
// If the button was down, but now it's up, trigger a buttonPressed event
quint16 lastButtonState = joystickButtons & (1 << i);
if (SDL_JoystickGetButton(joystick, i) && !lastButtonState)
{
emit buttonPressed(i);
joystickButtons |= 1 << i;
}
else if (!SDL_JoystickGetButton(joystick, i) && lastButtonState)
{
emit buttonReleased(i);
if (isEnabled && joystickSettings[autopilotType][systemType].buttonActions.contains(i))
{
emit actionTriggered(joystickSettings[autopilotType][systemType].buttonActions.value(i));
}
joystickButtons &= ~(1 << i);
}
}
// Now signal an update for all UI together.
if (isEnabled)
{
float roll = rollAxis > -1?joystickAxes[rollAxis]:numeric_limits<float>::quiet_NaN();
float pitch = pitchAxis > -1?joystickAxes[pitchAxis]:numeric_limits<float>::quiet_NaN();
float yaw = yawAxis > -1?joystickAxes[yawAxis]:numeric_limits<float>::quiet_NaN();
float throttle = throttleAxis > -1?joystickAxes[throttleAxis]:numeric_limits<float>::quiet_NaN();
emit joystickChanged(roll, pitch, yaw, throttle, xHat, yHat, joystickButtons, mode);
}
// Sleep, update rate of joystick is approx. 25 Hz (1000 ms / 25 = 40 ms)
QGC::SLEEP::msleep(40);
}
}
void JoystickInput::setActiveJoystick(int id)
{
// If we already had a joystick, close that one before opening a new one.
if (joystick && SDL_JoystickOpened(joystickID))
{
storeJoystickSettings();
SDL_JoystickClose(joystick);
joystick = NULL;
joystickID = -1;
}
joystickID = id;
joystick = SDL_JoystickOpen(joystickID);
if (joystick && SDL_JoystickOpened(joystickID))
{
// Update joystick configuration.
joystickName = QString(SDL_JoystickName(joystickID));
joystickNumButtons = SDL_JoystickNumButtons(joystick);
joystickNumAxes = SDL_JoystickNumAxes(joystick);
// Restore saved settings for this joystick.
loadJoystickSettings();
// Update cached joystick axes values.
// Also emit any signals for currently-triggering events
joystickAxes.clear();
for (int i = 0; i < joystickNumAxes; i++)
{
joystickAxes.append(numeric_limits<float>::quiet_NaN());
}
// Update cached joystick button values.
// Emit signals for any button events.
joystickButtons = 0;
}
else
{
joystickNumButtons = 0;
joystickNumAxes = 0;
}
// Specify that a new joystick has been selected, so that any UI elements can update.
emit newJoystickSelected();
// And then trigger an update of this new UI.
emit joystickSettingsChanged();
}
void JoystickInput::setCalibrating(bool active)
{
if (active)
{
setEnabled(false);
isCalibrating = true;
// set range small so that limits can be re-found
for (int i = 0; i < joystickNumAxes; i++)
{
joystickSettings[autopilotType][systemType].axesMinRange[i] = -10.0f;
joystickSettings[autopilotType][systemType].axesMaxRange[i] = 10.0f;
}
} else {
// store calibration values
storeJoystickSettings();
qDebug() << "Calibration result:";
for (int i = 0; i < joystickNumAxes; i++)
{
qDebug() << i << ": " <<
joystickSettings[autopilotType][systemType].axesMinRange[i] <<
" - " <<
joystickSettings[autopilotType][systemType].axesMaxRange[i];
}
setEnabled(true);
isCalibrating = false;
}
}
void JoystickInput::setAxisMapping(int axis, JOYSTICK_INPUT_MAPPING newMapping)
{
switch (newMapping)
{
case JOYSTICK_INPUT_MAPPING_ROLL:
rollAxis = axis;
break;
case JOYSTICK_INPUT_MAPPING_PITCH:
pitchAxis = axis;
break;
case JOYSTICK_INPUT_MAPPING_YAW:
yawAxis = axis;
break;
case JOYSTICK_INPUT_MAPPING_THROTTLE:
throttleAxis = axis;
break;
case JOYSTICK_INPUT_MAPPING_NONE:
default:
if (rollAxis == axis)
{
rollAxis = -1;
}
if (pitchAxis == axis)
{
pitchAxis = -1;
}
if (yawAxis == axis)
{
yawAxis = -1;
}
if (throttleAxis == axis)
{
throttleAxis = -1;
joystickSettings[autopilotType][systemType].axesLimited.remove(axis);
}
break;
}
storeJoystickSettings();
}
void JoystickInput::setAxisInversion(int axis, bool inverted)
{
if (axis < joystickNumAxes)
{
joystickSettings[autopilotType][systemType].axesInverted[axis] = inverted;
storeJoystickSettings();
}
}
void JoystickInput::setAxisRangeLimit(int axis, bool limitRange)
{
if (axis < joystickNumAxes)
{
joystickSettings[autopilotType][systemType].axesLimited[axis] = limitRange;
storeJoystickSettings();
}
}
void JoystickInput::setAxisRangeLimitMin(int axis, float min)
{
if (axis < joystickNumAxes)
{
joystickSettings[autopilotType][systemType].axesMinRange[axis] = min;
storeJoystickSettings();
}
}
void JoystickInput::setAxisRangeLimitMax(int axis, float max)
{
if (axis < joystickNumAxes)
{
joystickSettings[autopilotType][systemType].axesMaxRange[axis] = max;
storeJoystickSettings();
}
}
void JoystickInput::setButtonAction(int button, int action)
{
if (button < joystickNumButtons)
{
joystickSettings[autopilotType][systemType].buttonActions[button] = action;
storeJoystickSettings();
}
}
float JoystickInput::getCurrentValueForAxis(int axis) const
{
if (axis < joystickNumAxes)
{
return joystickAxes.at(axis);
}
return 0.0f;
}
bool JoystickInput::getInvertedForAxis(int axis) const
{
if (axis < joystickNumAxes)
{
return joystickSettings[autopilotType][systemType].axesInverted.value(axis);
}
return false;
}
bool JoystickInput::getRangeLimitForAxis(int axis) const
{
if (axis < joystickNumAxes)
{
return joystickSettings[autopilotType][systemType].axesLimited.value(axis);
}
return false;
}
float JoystickInput::getAxisRangeLimitMinForAxis(int axis) const
{
if (axis < joystickNumAxes)
{
return joystickSettings[autopilotType][systemType].axesMinRange.value(axis);
}
return sdlJoystickMin;
}
float JoystickInput::getAxisRangeLimitMaxForAxis(int axis) const
{
if (axis < joystickNumAxes)
{
return joystickSettings[autopilotType][systemType].axesMaxRange.value(axis);
}
return sdlJoystickMax;
}
int JoystickInput::getActionForButton(int button) const
{
if (button < joystickNumButtons && joystickSettings[autopilotType][systemType].buttonActions.contains(button))
{
return joystickSettings[autopilotType][systemType].buttonActions.value(button);
}
return -1;
}
/*=====================================================================
PIXHAWK Micro Air Vehicle Flying Robotics Toolkit
(c) 2009, 2010 PIXHAWK PROJECT <http://pixhawk.ethz.ch>
This file is part of the PIXHAWK project
PIXHAWK 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.
PIXHAWK 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 PIXHAWK. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Definition of joystick interface
*
* This class defines a new thread to operate the reading of any joystick/controllers
* via the Simple Directmedia Library (libsdl.org). This relies on polling of the SDL,
* which is not their recommended method, instead they suggest use event checking. That
* does not seem to support switching joysticks after their internal event loop has started,
* so it was abandoned.
*
* All joystick-related functionality is done in this class, though the JoystickWidget provides
* a UI around modifying its settings. Additionally controller buttons can be mapped to
* actions defined by any UASInterface object through the `UASInterface::getActions()` function.
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
* @author Andreas Romer <mavteam@student.ethz.ch>
* @author Julian Oes <julian@oes.ch>
*/
#ifndef _JOYSTICKINPUT_H_
#define _JOYSTICKINPUT_H_
#include <QThread>
#include <QList>
#include <qmutex.h>
#ifdef Q_OS_MAC
#include <SDL.h>
#else
#include <SDL/SDL.h>
#endif
#include "UAS.h"
struct JoystickSettings {
QMap<int, bool> axesInverted; ///< Whether each axis should be used inverted from what was reported.
QMap<int, bool> axesLimited; ///< Whether each axis should be limited to only the positive range. Currently this only applies to the throttle axis, but is kept generic here to possibly support other axes.
QMap<int, float> axesMaxRange; ///< The maximum values per axis
QMap<int, float> axesMinRange; ///< The minimum values per axis
QMap<int, int> buttonActions; ///< The index of the action associated with every button.
};
Q_DECLARE_METATYPE(JoystickSettings)
/**
* @brief Joystick input
*/
class JoystickInput : public QThread
{
Q_OBJECT
public:
JoystickInput();
~JoystickInput();
void run();
void shutdown();
/**
* @brief The JOYSTICK_INPUT_MAPPING enum storing the values for each item in the mapping combobox.
* This should match the order of items in the mapping combobox in JoystickAxis.ui.
*/
enum JOYSTICK_INPUT_MAPPING
{
JOYSTICK_INPUT_MAPPING_NONE = 0,
JOYSTICK_INPUT_MAPPING_YAW = 1,
JOYSTICK_INPUT_MAPPING_PITCH = 2,
JOYSTICK_INPUT_MAPPING_ROLL = 3,
JOYSTICK_INPUT_MAPPING_THROTTLE = 4
};
/**
* @brief The JOYSTICK_MODE enum stores the values for each item in the mode combobox.
* This should match the order of items in the mode combobox in JoystickWidget.ui.
*/
enum JOYSTICK_MODE
{
JOYSTICK_MODE_ATTITUDE = 0,
JOYSTICK_MODE_POSITION = 1,
JOYSTICK_MODE_FORCE = 2,
JOYSTICK_MODE_VELOCITY = 3,
JOYSTICK_MODE_MANUAL = 4
};
/**
* @brief Load joystick-specific settings.
*/
void loadJoystickSettings();
/**
* @brief Load joystick-independent settings.
*/
void loadGeneralSettings();
/**
* @brief Store joystick-specific settings.
*/
void storeJoystickSettings() const;
/**
* @brief Store joystick-independent settings.
*/
void storeGeneralSettings() const;
bool enabled() const
{
return isEnabled;
}
bool calibrating() const
{
return isCalibrating;
}
int getMappingThrottleAxis() const
{
return throttleAxis;
}
int getMappingRollAxis() const
{
return rollAxis;
}
int getMappingPitchAxis() const
{
return pitchAxis;
}
int getMappingYawAxis() const
{
return yawAxis;
}
int getJoystickNumButtons() const
{
return joystickNumButtons;
}
int getJoystickNumAxes() const
{
return joystickNumAxes;
}
int getJoystickID() const
{
return joystickID;
}
const QString& getName() const
{
return joystickName;
}
int getNumJoysticks() const
{
return numJoysticks;
}
JOYSTICK_MODE getMode() const
{
return mode;
}
QString getJoystickNameById(int id) const
{
return QString(SDL_JoystickName(id));
}
float getCurrentValueForAxis(int axis) const;
bool getInvertedForAxis(int axis) const;
bool getRangeLimitForAxis(int axis) const;
float getAxisRangeLimitMinForAxis(int axis) const;
float getAxisRangeLimitMaxForAxis(int axis) const;
int getActionForButton(int button) const;
const double sdlJoystickMin;
const double sdlJoystickMax;
protected:
bool isEnabled; ///< Track whether the system should emit the higher-level signals: joystickChanged & actionTriggered.
bool isCalibrating; ///< Track if calibration in progress
bool done;
SDL_Joystick* joystick;
UAS* uas; ///< Track the current UAS.
int autopilotType; ///< Cache the autopilotType
int systemType; ///< Cache the systemType
bool uasCanReverse; ///< Track whether the connect UAS can drive a reverse speed.
// Store the mapping between axis numbers and the roll/pitch/yaw/throttle configuration.
// Value is one of JoystickAxis::JOYSTICK_INPUT_MAPPING.
int rollAxis;
int pitchAxis;
int yawAxis;
int throttleAxis;
// Cache information on the joystick instead of polling the SDL everytime.
int numJoysticks; ///< Total number of joysticks detected by the SDL.
QString joystickName;
int joystickID;
int joystickNumAxes;
int joystickNumButtons;
// mode of joystick (attitude, position, force, ... (see JOYSTICK_MODE enum))
JOYSTICK_MODE mode;
// Track axis/button settings based on a Joystick/AutopilotType/SystemType triplet.
// This is only a double-map, because settings are stored/loaded based on joystick
// name first, so only the settings for the current joystick need to be stored at any given time.
// Pointers are kept to the various settings field to reduce lookup times.
// Note that the mapping (0,0) corresponds to when no UAS is connected. Since this corresponds
// to a generic vehicle type and a generic autopilot, this is a pretty safe default.
QMap<int, QMap<int, JoystickSettings> > joystickSettings;
// Track the last state of the axes, buttons, and hats for only emitting change signals.
QList<float> joystickAxes; ///< The values of every axes during the last sample.
quint16 joystickButtons; ///< The state of every button. Bitfield supporting 16 buttons with 1s indicating that the button is down.
qint8 xHat, yHat; ///< The horizontal/vertical hat directions. Values are -1, 0, 1, with (-1,-1) indicating bottom-left.
/**
* @brief Called before main run() event loop starts. Waits for joysticks to be connected.
*/
void init();
signals:
/**
* @brief Signal containing all joystick raw positions
*
* @param roll forward / pitch / x axis, front: 1, center: 0, back: -1. If the roll axis isn't defined, NaN is transmit instead.
* @param pitch left / roll / y axis, left: -1, middle: 0, right: 1. If the roll axis isn't defined, NaN is transmit instead.
* @param yaw turn axis, left-turn: -1, centered: 0, right-turn: 1. If the roll axis isn't defined, NaN is transmit instead.
* @param throttle Throttle, -100%:-1.0, 0%: 0.0, 100%: 1.0. If the roll axis isn't defined, NaN is transmit instead.
* @param xHat hat vector in forward-backward direction, +1 forward, 0 center, -1 backward
* @param yHat hat vector in left-right direction, -1 left, 0 center, +1 right
* @param mode (setpoint type) see JOYSTICK_MODE enum
*/
void joystickChanged(float roll, float pitch, float yaw, float throttle, qint8 xHat, qint8 yHat, quint16 buttons, quint8 mode);
/**
* @brief Emit a new value for an axis
*
* @param value Value of the axis, between -1.0 and 1.0.
*/
void axisValueChanged(int axis, float value);
/**
* @brief Joystick button has changed state from unpressed to pressed.
* @param key index of the pressed key
*/
void buttonPressed(int key);
/**
* @brief Joystick button has changed state from pressed to unpressed.
*
* @param key index of the released key
*/
void buttonReleased(int key);
/**
* @brief A joystick button was pressed that had a corresponding action.
* @param action The index of the action to trigger. Dependent on UAS.
*/
void actionTriggered(int action);
/**
* @brief Hat (8-way switch on the top) has changed position
*
* Coordinate frame for joystick hat:
*
* y
* ^
* |
* |
* 0 ----> x
*
*
* @param x vector in left-right direction
* @param y vector in forward-backward direction
*/
void hatDirectionChanged(qint8 x, qint8 y);
/** @brief Signal that the UAS has been updated for this JoystickInput
* Note that any UI updates should NOT query this object for joystick details. That should be done in response to the joystickSettingsChanged signal.
*/
void activeVehicleChanged(Vehicle* vehicle);
/** @brief Signals that new joystick-specific settings were changed. Useful for triggering updates that at dependent on the current joystick. */
void joystickSettingsChanged();
/** @brief The JoystickInput has switched to a different joystick. UI should be adjusted accordingly. */
void newJoystickSelected();
public slots:
/** @brief Enable or disable emitting the high-level control signals from the joystick. */
void setEnabled(bool enable);
/** @brief Switch to a new joystick by ID number. Both buttons and axes are updated with the proper signals emitted. */
void setActiveJoystick(int id);
/** @brief Switch calibration mode active */
void setCalibrating(bool active);
/**
* @brief Change the control mapping for a given joystick axis.
* @param axisID The axis to modify (0-indexed)
* @param newMapping The mapping to use.
* @see JOYSTICK_INPUT_MAPPING
*/
void setAxisMapping(int axis, JoystickInput::JOYSTICK_INPUT_MAPPING newMapping);
/**
* @brief Specify if an axis should be inverted.
* @param axis The ID of the axis.
* @param inverted True indicates inverted from normal. Varies by controller.
*/
void setAxisInversion(int axis, bool inverted);
/**
* @brief Specify that an axis should only transmit the positive values. Useful for controlling throttle from auto-centering axes.
* @param axis Which axis has its range limited.
* @param limitRange If true only the positive half of this axis will be read.
*/
void setAxisRangeLimit(int axis, bool limitRange);
/**
* @brief Specify minimum value for axis.
* @param axis Which axis should be set.
* @param min Value to be set.
*/
void setAxisRangeLimitMin(int axis, float min);
/**
* @brief Specify maximum value for axis.
* @param axis Which axis should be set.
* @param max Value to be set.
*/
void setAxisRangeLimitMax(int axis, float max);
/**
* @brief Specify a button->action mapping for the given uas.
* This mapping is applied based on UAS autopilot type and UAS system type.
* Connects the buttonEmitted signal for the corresponding button to the corresponding action for the current UAS.
* @param button The numeric ID for the button
* @param action The numeric ID of the action for this UAS to map to.
*/
void setButtonAction(int button, int action);
/**
* @brief Specify which setpoints should be sent to the UAS when moving the joystick
* @param newMode the mode (setpoint type) see the JOYSTICK_MODE enum
*/
void setMode(int newMode)
{
mode = (JOYSTICK_MODE)newMode;
}
private slots:
void _activeVehicleChanged(Vehicle* vehicle);
};
#endif // _JOYSTICKINPUT_H_
......@@ -26,6 +26,7 @@
#include "FileManagerTest.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
//UT_REGISTER_TEST(FileManagerTest)
......
......@@ -37,6 +37,7 @@
#include "QGCMessageBox.h"
#include "QGCLoggingCategory.h"
#include "Vehicle.h"
#include "Joystick.h"
QGC_LOGGING_CATEGORY(UASLog, "UASLog")
......@@ -1372,7 +1373,7 @@ void UAS::receiveMessage(mavlink_message_t message)
*/
void UAS::setHomePosition(double lat, double lon, double alt)
{
if (blockHomePositionChanges)
if (!_vehicle || blockHomePositionChanges)
return;
QString uasName = (getUASName() == "")?
......@@ -1409,6 +1410,10 @@ void UAS::setHomePosition(double lat, double lon, double alt)
**/
void UAS::setLocalOriginAtCurrentGPSPosition()
{
if (!_vehicle) {
return;
}
QMessageBox::StandardButton button = QGCMessageBox::question(tr("Set the home position at the current GPS position?"),
tr("Do you want to set a new origin? Waypoints defined in the local frame will be shifted in their physical location"),
QMessageBox::Yes | QMessageBox::Cancel,
......@@ -1453,6 +1458,10 @@ void UAS::setLocalPositionOffset(float x, float y, float z, float yaw)
void UAS::startCalibration(UASInterface::StartCalibrationType calType)
{
if (!_vehicle) {
return;
}
int gyroCal = 0;
int magCal = 0;
int airspeedCal = 0;
......@@ -1510,6 +1519,10 @@ void UAS::startCalibration(UASInterface::StartCalibrationType calType)
void UAS::stopCalibration(void)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(),
mavlink->getComponentId(),
......@@ -1530,7 +1543,11 @@ void UAS::stopCalibration(void)
void UAS::startBusConfig(UASInterface::StartBusConfigType calType)
{
int actuatorCal = 0;
if (!_vehicle) {
return;
}
int actuatorCal = 0;
switch (calType) {
case StartBusConfigActuators:
......@@ -1558,6 +1575,10 @@ void UAS::startBusConfig(UASInterface::StartBusConfigType calType)
void UAS::stopBusConfig(void)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(),
mavlink->getComponentId(),
......@@ -1578,6 +1599,10 @@ void UAS::stopBusConfig(void)
void UAS::startDataRecording()
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 2, 0, 0, 0);
_vehicle->sendMessage(msg);
......@@ -1585,7 +1610,11 @@ void UAS::startDataRecording()
void UAS::stopDataRecording()
{
mavlink_message_t msg;
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 0, 0, 0, 0);
_vehicle->sendMessage(msg);
}
......@@ -1752,7 +1781,11 @@ void UAS::setMode(uint8_t newBaseMode, uint32_t newCustomMode)
*/
void UAS::setModeArm(uint8_t newBaseMode, uint32_t newCustomMode)
{
if (receivedMode)
if (!_vehicle) {
return;
}
if (receivedMode)
{
mavlink_message_t msg;
mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, (uint8_t)uasId, newBaseMode, newCustomMode);
......@@ -1882,7 +1915,11 @@ QImage UAS::getImage()
void UAS::requestImage()
{
qDebug() << "trying to get an image from the uas...";
if (!_vehicle) {
return;
}
qDebug() << "trying to get an image from the uas...";
// check if there is already an image transmission going on
if (imagePacketsArrived == 0)
......@@ -1944,6 +1981,10 @@ bool UAS::isFixedWing()
*/
void UAS::enableAllDataTransmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -1972,6 +2013,10 @@ void UAS::enableAllDataTransmission(int rate)
*/
void UAS::enableRawSensorDataTransmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -1996,6 +2041,10 @@ void UAS::enableRawSensorDataTransmission(int rate)
*/
void UAS::enableExtendedSystemStatusTransmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2020,6 +2069,10 @@ void UAS::enableExtendedSystemStatusTransmission(int rate)
*/
void UAS::enableRCChannelDataTransmission(int rate)
{
if (!_vehicle) {
return;
}
#if defined(MAVLINK_ENABLED_UALBERTA_MESSAGES)
mavlink_message_t msg;
mavlink_msg_request_rc_channels_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, enabled);
......@@ -2049,6 +2102,10 @@ void UAS::enableRCChannelDataTransmission(int rate)
*/
void UAS::enableRawControllerDataTransmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2095,6 +2152,10 @@ void UAS::enableRawControllerDataTransmission(int rate)
*/
void UAS::enablePositionTransmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2119,6 +2180,10 @@ void UAS::enablePositionTransmission(int rate)
*/
void UAS::enableExtra1Transmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2144,6 +2209,10 @@ void UAS::enableExtra1Transmission(int rate)
*/
void UAS::enableExtra2Transmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2169,6 +2238,10 @@ void UAS::enableExtra2Transmission(int rate)
*/
void UAS::enableExtra3Transmission(int rate)
{
if (!_vehicle) {
return;
}
// Buffers to write data to
mavlink_message_t msg;
mavlink_request_data_stream_t stream;
......@@ -2303,6 +2376,10 @@ void UAS::setUASName(const QString& name)
void UAS::executeCommand(MAV_CMD command)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_command_long_t cmd;
cmd.command = (uint16_t)command;
......@@ -2321,6 +2398,10 @@ void UAS::executeCommand(MAV_CMD command)
}
void UAS::executeCommandAck(int num, bool success)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_command_ack_t ack;
ack.command = num;
......@@ -2331,6 +2412,10 @@ void UAS::executeCommandAck(int num, bool success)
void UAS::executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_command_long_t cmd;
cmd.command = (uint16_t)command;
......@@ -2354,6 +2439,10 @@ void UAS::executeCommand(MAV_CMD command, int confirmation, float param1, float
*/
void UAS::launch()
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_NAV_TAKEOFF, 1, 0, 0, 0, 0, 0, 0, 0);
_vehicle->sendMessage(msg);
......@@ -2405,11 +2494,12 @@ void UAS::toggleAutonomy()
* This can only be done if the system has manual inputs enabled and is armed.
*/
#ifndef __mobile__
void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float thrust, qint8 xHat, qint8 yHat, quint16 buttons, quint8 joystickMode)
void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float thrust, quint16 buttons, int joystickMode)
{
Q_UNUSED(xHat);
Q_UNUSED(yHat);
if (!_vehicle) {
return;
}
// Store the previous manual commands
static float manualRollAngle = 0.0;
static float manualPitchAngle = 0.0;
......@@ -2447,7 +2537,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
mavlink_message_t message;
if (joystickMode == JoystickInput::JOYSTICK_MODE_ATTITUDE) {
if (joystickMode == Vehicle::JoystickModeAttitude) {
// send an external attitude setpoint command (rate control disabled)
float attitudeQuaternion[4];
mavlink_euler_to_quaternion(roll, pitch, yaw, attitudeQuaternion);
......@@ -2465,7 +2555,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
0,
thrust
);
} else if (joystickMode == JoystickInput::JOYSTICK_MODE_POSITION) {
} else if (joystickMode == Vehicle::JoystickModePosition) {
// Send the the local position setpoint (local pos sp external message)
static float px = 0;
static float py = 0;
......@@ -2495,7 +2585,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
yaw,
0
);
} else if (joystickMode == JoystickInput::JOYSTICK_MODE_FORCE) {
} else if (joystickMode == Vehicle::JoystickModeForce) {
// Send the the force setpoint (local pos sp external message)
float dcm[3][3];
mavlink_euler_to_dcm(roll, pitch, yaw, dcm);
......@@ -2523,7 +2613,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
0,
0
);
} else if (joystickMode == JoystickInput::JOYSTICK_MODE_VELOCITY) {
} else if (joystickMode == Vehicle::JoystickModeVelocity) {
// Send the the local velocity setpoint (local pos sp external message)
static float vx = 0;
static float vy = 0;
......@@ -2555,7 +2645,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
0,
yawrate
);
} else if (joystickMode == JoystickInput::JOYSTICK_MODE_MANUAL) {
} else if (joystickMode == Vehicle::JoystickModeRC) {
// Save the new manual control inputs
manualRollAngle = roll;
......@@ -2566,7 +2656,7 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
// Store scaling values for all 3 axes
const float axesScaling = 1.0 * 1000.0;
// Calculate the new commands for roll, pitch, yaw, and thrust
const float newRollCommand = roll * axesScaling;
// negate pitch value because pitch is negative for pitching forward but mavlink message argument is positive for forward
......@@ -2574,6 +2664,8 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
const float newYawCommand = yaw * axesScaling;
const float newThrustCommand = thrust * axesScaling;
//qDebug() << newRollCommand << newPitchCommand << newYawCommand << newThrustCommand;
// Send the MANUAL_COMMAND message
mavlink_msg_manual_control_pack(mavlink->getSystemId(), mavlink->getComponentId(), &message, this->uasId, newPitchCommand, newRollCommand, newThrustCommand, newYawCommand, buttons);
}
......@@ -2588,7 +2680,11 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t
#ifndef __mobile__
void UAS::setManual6DOFControlCommands(double x, double y, double z, double roll, double pitch, double yaw)
{
// If system has manual inputs enabled and is armed
if (!_vehicle) {
return;
}
// If system has manual inputs enabled and is armed
if(((base_mode & MAV_MODE_FLAG_DECODE_POSITION_MANUAL) && (base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY)) || (base_mode & MAV_MODE_FLAG_HIL_ENABLED))
{
mavlink_message_t message;
......@@ -2649,7 +2745,11 @@ bool UAS::isAirplane()
*/
void UAS::halt()
{
mavlink_message_t msg;
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_HOLD, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0);
_vehicle->sendMessage(msg);
}
......@@ -2659,7 +2759,11 @@ void UAS::halt()
*/
void UAS::go()
{
mavlink_message_t msg;
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_CONTINUE, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0);
_vehicle->sendMessage(msg);
}
......@@ -2669,6 +2773,10 @@ void UAS::go()
*/
void UAS::home()
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
double latitude = HomePositionManager::instance()->getHomeLatitude();
......@@ -2685,7 +2793,11 @@ void UAS::home()
*/
void UAS::land()
{
mavlink_message_t msg;
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_LAND, 1, 0, 0, 0, 0, 0, 0, 0);
_vehicle->sendMessage(msg);
......@@ -2696,6 +2808,10 @@ void UAS::land()
*/
void UAS::pairRX(int rxType, int rxSubType)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_START_RX_PAIR, 0, rxType, rxSubType, 0, 0, 0, 0, 0);
......@@ -2928,6 +3044,10 @@ void UAS::sendHilState(quint64 time_us, float roll, float pitch, float yaw, floa
float pitchspeed, float yawspeed, double lat, double lon, double alt,
float vx, float vy, float vz, float ind_airspeed, float true_airspeed, float xacc, float yacc, float zacc)
{
if (!_vehicle) {
return;
}
if (this->base_mode & MAV_MODE_FLAG_HIL_ENABLED)
{
float q[4];
......@@ -3006,6 +3126,10 @@ float UAS::addZeroMeanNoise(float truth_meas, float noise_var)
void UAS::sendHilSensors(quint64 time_us, float xacc, float yacc, float zacc, float rollspeed, float pitchspeed, float yawspeed,
float xmag, float ymag, float zmag, float abs_pressure, float diff_pressure, float pressure_alt, float temperature, quint32 fields_changed)
{
if (!_vehicle) {
return;
}
if (this->base_mode & MAV_MODE_FLAG_HIL_ENABLED)
{
float xacc_corrupt = addZeroMeanNoise(xacc, xacc_var);
......@@ -3043,6 +3167,10 @@ void UAS::sendHilSensors(quint64 time_us, float xacc, float yacc, float zacc, fl
void UAS::sendHilOpticalFlow(quint64 time_us, qint16 flow_x, qint16 flow_y, float flow_comp_m_x,
float flow_comp_m_y, quint8 quality, float ground_distance)
{
if (!_vehicle) {
return;
}
// FIXME: This needs to be updated for new mavlink_msg_hil_optical_flow_pack api
Q_UNUSED(time_us);
......@@ -3077,6 +3205,10 @@ void UAS::sendHilOpticalFlow(quint64 time_us, qint16 flow_x, qint16 flow_y, floa
#ifndef __mobile__
void UAS::sendHilGps(quint64 time_us, double lat, double lon, double alt, int fix_type, float eph, float epv, float vel, float vn, float ve, float vd, float cog, int satellites)
{
if (!_vehicle) {
return;
}
// Only send at 10 Hz max rate
if (QGC::groundTimeMilliseconds() - lastSendTimeGPS < 100)
return;
......@@ -3139,6 +3271,10 @@ void UAS::stopHil()
void UAS::shutdown()
{
if (!_vehicle) {
return;
}
QMessageBox::StandardButton button = QGCMessageBox::question(tr("Shutting down the UAS"),
tr("Do you want to shut down the onboard computer?"),
QMessageBox::Yes | QMessageBox::Cancel,
......@@ -3160,6 +3296,10 @@ void UAS::shutdown()
*/
void UAS::setTargetPosition(float x, float y, float z, float yaw)
{
if (!_vehicle) {
return;
}
mavlink_message_t msg;
mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_PATHPLANNING, 1, 1, 1, 0, yaw, x, y, z);
_vehicle->sendMessage(msg);
......@@ -3268,6 +3408,10 @@ void UAS::stopLowBattAlarm()
void UAS::sendMapRCToParam(QString param_id, float scale, float value0, quint8 param_rc_channel_index, float valueMin, float valueMax)
{
if (!_vehicle) {
return;
}
mavlink_message_t message;
char param_id_cstr[MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN] = {};
......@@ -3298,6 +3442,10 @@ void UAS::sendMapRCToParam(QString param_id, float scale, float value0, quint8 p
void UAS::unsetRCToParameterMap()
{
if (!_vehicle) {
return;
}
char param_id_cstr[MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN] = {};
for (int i = 0; i < 3; i++) {
......
......@@ -37,8 +37,9 @@ This file is part of the QGROUNDCONTROL project
#include <QVector3D>
#include "QGCMAVLink.h"
#include "FileManager.h"
#include "Vehicle.h"
#ifndef __mobile__
#include "JoystickInput.h"
#include "QGCHilLink.h"
#include "QGCFlightGearLink.h"
#include "QGCJSBSimLink.h"
......@@ -110,6 +111,8 @@ public:
Q_PROPERTY(double altitudeWGS84 READ getAltitudeWGS84 WRITE setAltitudeWGS84 NOTIFY altitudeWGS84Changed)
Q_PROPERTY(double altitudeRelative READ getAltitudeRelative WRITE setAltitudeRelative NOTIFY altitudeRelativeChanged)
void clearVehicle(void) { _vehicle = NULL; }
void setGroundSpeed(double val)
{
groundSpeed = val;
......@@ -847,7 +850,7 @@ public slots:
/** @brief Set the values for the manual control of the vehicle */
#ifndef __mobile__
void setExternalControlSetpoint(float roll, float pitch, float yaw, float thrust, qint8 xHat, qint8 yHat, quint16 buttons, quint8);
void setExternalControlSetpoint(float roll, float pitch, float yaw, float thrust, quint16 buttons, int joystickMode);
#endif
/** @brief Set the values for the 6dof manual control of the vehicle */
......
......@@ -30,6 +30,7 @@ This file is part of the QGROUNDCONTROL project
#include "QGCApplication.h"
#include "UASMessageHandler.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
UASMessage::UASMessage(int componentid, int severity, QString text)
{
......
......@@ -18,14 +18,17 @@
#include <QMouseEvent>
#include <QMenu>
#include <QSettings>
#include <QDebug>
#include <qmath.h>
#include "HDDisplay.h"
#include "ui_HDDisplay.h"
#include "MG.h"
#include "QGC.h"
#include "QGCApplication.h"
#include <QDebug>
#include "MultiVehicleManager.h"
#include "UAS.h"
HDDisplay::HDDisplay(const QStringList &plotList, QString title, QWidget *parent) :
QGraphicsView(parent),
......
......@@ -46,6 +46,7 @@ This file is part of the QGROUNDCONTROL project
#include <qmath.h>
#include "MAV2DIcon.h"
#include "QGCApplication.h"
#include "UAS.h"
HSIDisplay::HSIDisplay(QWidget *parent) :
HDDisplay(QStringList(), "HSI", parent),
......
#include "JoystickAxis.h"
#include "JoystickInput.h"
#include "ui_JoystickAxis.h"
#include "MultiVehicleManager.h"
#include <QString>
JoystickAxis::JoystickAxis(int id, QWidget *parent) :
QWidget(parent),
id(id),
ui(new Ui::JoystickAxis)
{
ui->setupUi(this);
mappingComboBoxChanged(JoystickInput::JOYSTICK_INPUT_MAPPING_NONE);
ui->label->setText(QString::number(id));
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(mappingComboBoxChanged(int)));
connect(ui->invertedCheckBox, SIGNAL(clicked(bool)), this, SLOT(inversionCheckBoxChanged(bool)));
connect(ui->rangeCheckBox, SIGNAL(clicked(bool)), this, SLOT(rangeCheckBoxChanged(bool)));
}
JoystickAxis::~JoystickAxis()
{
delete ui;
}
void JoystickAxis::setValue(float value)
{
ui->progressBar->setValue(100.0f * value);
}
void JoystickAxis::setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING newMapping)
{
ui->comboBox->setCurrentIndex(newMapping);
if (newMapping == JoystickInput::JOYSTICK_INPUT_MAPPING_THROTTLE)
{
ui->rangeCheckBox->show();
}
else
{
ui->rangeCheckBox->hide();
}
this->activeVehicleChanged(MultiVehicleManager::instance()->activeVehicle());
}
void JoystickAxis::setInverted(bool newValue)
{
ui->invertedCheckBox->setChecked(newValue);
}
void JoystickAxis::setRangeLimit(bool newValue)
{
ui->rangeCheckBox->setChecked(newValue);
}
void JoystickAxis::mappingComboBoxChanged(int newMapping)
{
JoystickInput::JOYSTICK_INPUT_MAPPING mapping = (JoystickInput::JOYSTICK_INPUT_MAPPING)newMapping;
emit mappingChanged(id, mapping);
updateUIBasedOnUAS(MultiVehicleManager::instance()->activeVehicle(), mapping);
}
void JoystickAxis::inversionCheckBoxChanged(bool inverted)
{
emit inversionChanged(id, inverted);
}
void JoystickAxis::rangeCheckBoxChanged(bool limited)
{
emit rangeChanged(id, limited);
}
void JoystickAxis::activeVehicleChanged(Vehicle* vehicle)
{
updateUIBasedOnUAS(vehicle, (JoystickInput::JOYSTICK_INPUT_MAPPING)ui->comboBox->currentIndex());
}
void JoystickAxis::updateUIBasedOnUAS(Vehicle* vehicle, JoystickInput::JOYSTICK_INPUT_MAPPING axisMapping)
{
UAS* uas = NULL;
if (vehicle) {
uas = vehicle->uas();
}
// Set the throttle display to only positive if:
// * This is the throttle axis AND
// * The current UAS can't reverse OR there is no current UAS
// This causes us to default to systems with no negative throttle.
if (((uas && !uas->systemCanReverse()) || !uas) && axisMapping == JoystickInput::JOYSTICK_INPUT_MAPPING_THROTTLE)
{
ui->progressBar->setRange(0, 100);
ui->rangeCheckBox->show();
}
else
{
ui->progressBar->setRange(-100, 100);
ui->rangeCheckBox->hide();
}
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* This class defines a UI element to represent a single controller axis.
* It is used by the JoystickWidget to simplify some of the logic in that class.
*/
#ifndef JOYSTICKAXIS_H
#define JOYSTICKAXIS_H
#include <QWidget>
#include "JoystickInput.h"
#include "Vehicle.h"
namespace Ui {
class JoystickAxis;
}
class JoystickAxis : public QWidget
{
Q_OBJECT
public:
explicit JoystickAxis(int id, QWidget *parent = 0);
~JoystickAxis();
void setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING newMapping);
void setInverted(bool newValue);
void setRangeLimit(bool newValue);
void setAxisRangeMin(float min);
void setAxisRangeMax(float max);
signals:
/** @brief Signal a change in this axis' yaw/pitch/roll mapping */
void mappingChanged(int id, JoystickInput::JOYSTICK_INPUT_MAPPING newMapping);
/** @brief Signal a change in this axis' inversion status */
void inversionChanged(int id, bool inversion);
/** @brief Signal a change in this axis' range setting. If limited is true then only the positive values should be read from this axis. */
void rangeChanged(int id, bool limited);
public slots:
/** @brief Update the displayed value of the included progressbar.
* @param value A value between -1.0 and 1.0.
*/
void setValue(float value);
/** @brief Specify the UAS that this axis should track for displaying throttle properly. */
void activeVehicleChanged(Vehicle* vehicle);
private:
int id; ///< The ID for this axis. Corresponds to the IDs used by JoystickInput.
Ui::JoystickAxis *ui;
/**
* @brief Update the UI based on both the current UAS and the current axis mapping.
* @param uas The currently-active UAS.
* @param axisMapping The new mapping for this axis.
*/
void updateUIBasedOnUAS(Vehicle* vehicle, JoystickInput::JOYSTICK_INPUT_MAPPING axisMapping);
private slots:
/** @brief Handle changes to the mapping dropdown bar. */
void mappingComboBoxChanged(int newMapping);
/** @brief Emit signal when the inversion checkbox is changed. */
void inversionCheckBoxChanged(bool inverted);
/** @brief Emit signal when the range checkbox is changed. */
void rangeCheckBoxChanged(bool inverted);
};
#endif // JOYSTICKAXIS_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JoystickAxis</class>
<widget class="QWidget" name="JoystickAxis">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>80</width>
<height>200</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Specify what property of the UAS this axis should command</string>
</property>
<item>
<property name="text">
<string>--</string>
</property>
</item>
<item>
<property name="text">
<string>Yaw</string>
</property>
</item>
<item>
<property name="text">
<string>Pitch</string>
</property>
</item>
<item>
<property name="text">
<string>Roll</string>
</property>
</item>
<item>
<property name="text">
<string>Throttle</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rangeCheckBox">
<property name="text">
<string>Half range</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="toolTip">
<string>Only use the positive values from this axis for control</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="textDirection">
<enum>QProgressBar::TopToBottom</enum>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="invertedCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="toolTip">
<string>Reverse the values for this axis</string>
</property>
<property name="text">
<string>Inverted</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
#include "JoystickButton.h"
#include "ui_JoystickButton.h"
#include "MultiVehicleManager.h"
JoystickButton::JoystickButton(int id, QWidget *parent) :
QWidget(parent),
id(id),
m_ui(new Ui::JoystickButton)
{
m_ui->setupUi(this);
m_ui->joystickButtonLabel->setText(QString::number(id));
this->activeVehicleChanged(MultiVehicleManager::instance()->activeVehicle());
connect(m_ui->joystickAction, SIGNAL(currentIndexChanged(int)), this, SLOT(actionComboBoxChanged(int)));
}
JoystickButton::~JoystickButton()
{
delete m_ui;
}
void JoystickButton::activeVehicleChanged(Vehicle* vehicle)
{
// Disable signals so that changes to joystickAction don't trigger JoystickInput updates.
disconnect(m_ui->joystickAction, SIGNAL(currentIndexChanged(int)), this, SLOT(actionComboBoxChanged(int)));
if (vehicle)
{
UAS* uas = vehicle->uas();
m_ui->joystickAction->setEnabled(true);
m_ui->joystickAction->clear();
m_ui->joystickAction->addItem("--");
QList<QAction*> actions = uas->getActions();
foreach (QAction* a, actions)
{
m_ui->joystickAction->addItem(a->text());
}
m_ui->joystickAction->setCurrentIndex(0);
} else {
m_ui->joystickAction->setEnabled(false);
m_ui->joystickAction->clear();
m_ui->joystickAction->addItem("--");
m_ui->joystickAction->setCurrentIndex(0);
}
connect(m_ui->joystickAction, SIGNAL(currentIndexChanged(int)), this, SLOT(actionComboBoxChanged(int)));
}
void JoystickButton::setAction(int index)
{
// Disable signals so that changes to joystickAction don't trigger JoystickInput updates.
disconnect(m_ui->joystickAction, SIGNAL(currentIndexChanged(int)), this, SLOT(actionComboBoxChanged(int)));
// Add one because the default no-action takes the 0-spot.
m_ui->joystickAction->setCurrentIndex(index + 1);
connect(m_ui->joystickAction, SIGNAL(currentIndexChanged(int)), this, SLOT(actionComboBoxChanged(int)));
}
void JoystickButton::actionComboBoxChanged(int index)
{
// Subtract one because the default no-action takes the 0-spot.
emit actionChanged(id, index - 1);
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* This class defines a UI element to represent a single controller axis.
* It is used by the JoystickWidget to simplify some of the logic in that class.
*/
#ifndef JOYSTICKBUTTON_H
#define JOYSTICKBUTTON_H
#include <QWidget>
#include "Vehicle.h"
namespace Ui
{
class JoystickButton;
}
class JoystickButton : public QWidget
{
Q_OBJECT
public:
explicit JoystickButton(int id, QWidget *parent = 0);
virtual ~JoystickButton();
public slots:
/** @brief Specify the UAS that this axis should track for displaying throttle properly. */
void activeVehicleChanged(Vehicle* vehicle);
/** @brieft Specify which action this button should correspond to.
* Values 0 and higher indicate a specific action, while -1 indicates no action. */
void setAction(int index);
signals:
/** @brief Signal a change in this buttons' action mapping */
void actionChanged(int id, int index);
private:
int id;
Ui::JoystickButton *m_ui;
private slots:
void actionComboBoxChanged(int index);
};
#endif // JOYSTICKBUTTON_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JoystickButton</class>
<widget class="QWidget" name="JoystickButton">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>125</width>
<height>29</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="joystickButtonLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="joystickAction">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
#include "JoystickWidget.h"
#include "QGCApplication.h"
#include "ui_JoystickWidget.h"
#include "JoystickButton.h"
#include "JoystickAxis.h"
#include <QDesktopWidget>
JoystickWidget::JoystickWidget(JoystickInput* joystick, QWidget *parent) :
QWidget(parent),
joystick(joystick),
m_ui(new Ui::JoystickWidget)
{
m_ui->setupUi(this);
// Center the window on the screen.
QRect position = frameGeometry();
position.moveCenter(QDesktopWidget().availableGeometry().center());
move(position.topLeft());
// Initialize the UI based on the current joystick
initUI();
// Watch for button, axis, and hat input events from the joystick.
connect(this->joystick, SIGNAL(buttonPressed(int)), this, SLOT(joystickButtonPressed(int)));
connect(this->joystick, SIGNAL(buttonReleased(int)), this, SLOT(joystickButtonReleased(int)));
connect(this->joystick, SIGNAL(axisValueChanged(int,float)), this, SLOT(updateAxisValue(int,float)));
connect(this->joystick, SIGNAL(hatDirectionChanged(qint8,qint8)), this, SLOT(setHat(qint8,qint8)));
// Also watch for when new settings were loaded for the current joystick to do a mass UI refresh.
connect(this->joystick, SIGNAL(joystickSettingsChanged()), this, SLOT(updateUI()));
// If the selected joystick is changed, update the JoystickInput.
connect(m_ui->joystickNameComboBox, SIGNAL(currentIndexChanged(int)), this->joystick, SLOT(setActiveJoystick(int)));
// Also wait for the JoystickInput to switch, then update our UI.
connect(this->joystick, SIGNAL(newJoystickSelected()), this, SLOT(createUIForJoystick()));
// Initialize the UI to the current JoystickInput state. Also make sure to listen for future changes
// so that the UI can be updated.
connect(m_ui->enableCheckBox, SIGNAL(toggled(bool)), m_ui->joystickFrame, SLOT(setEnabled(bool)));
m_ui->enableCheckBox->setChecked(this->joystick->enabled()); // Needs to be after connecting to the joystick frame and before watching for enabled events from JoystickInput.
connect(m_ui->enableCheckBox, SIGNAL(toggled(bool)), this->joystick, SLOT(setEnabled(bool)));
// Update the button label colors based on the current theme and watch for future theme changes.
styleChanged(qgcApp()->styleIsDark());
connect(qgcApp(), &QGCApplication::styleChanged, this, &JoystickWidget::styleChanged);
// change mode when mode combobox is changed
connect(m_ui->joystickModeComboBox, SIGNAL(currentIndexChanged(int)), this->joystick, SLOT(setMode(int)));
// Display the widget above all other windows.
this->raise();
this->show();
}
void JoystickWidget::initUI()
{
// Add the joysticks to the top combobox. They're indexed by their item number.
// And set the currently-selected combobox item to the current joystick.
int joysticks = joystick->getNumJoysticks();
if (joysticks)
{
for (int i = 0; i < joysticks; i++)
{
m_ui->joystickNameComboBox->addItem(joystick->getJoystickNameById(i));
}
m_ui->joystickNameComboBox->setCurrentIndex(joystick->getJoystickID());
// And if joystick support is enabled, show the UI.
if (m_ui->enableCheckBox->isChecked())
{
m_ui->joystickFrame->setEnabled(true);
}
// mode combo box
m_ui->joystickModeComboBox->addItem("Attitude");
m_ui->joystickModeComboBox->addItem("Position");
m_ui->joystickModeComboBox->addItem("Force");
m_ui->joystickModeComboBox->addItem("Velocity");
m_ui->joystickModeComboBox->addItem("Manual");
m_ui->joystickModeComboBox->setCurrentIndex(joystick->getMode());
// Create the initial UI.
createUIForJoystick();
}
// But if there're no joysticks, disable everything and hide empty UI.
else
{
m_ui->enableCheckBox->setEnabled(false);
m_ui->joystickNameComboBox->addItem(tr("No joysticks found. Connect and restart QGC to add one."));
m_ui->joystickNameComboBox->setEnabled(false);
m_ui->joystickFrame->hide();
}
}
void JoystickWidget::styleChanged(bool styleIsDark)
{
if (styleIsDark)
{
buttonLabelColor = QColor(0x14, 0xC6, 0x14);
}
else
{
buttonLabelColor = QColor(0x73, 0xD9, 0x5D);
}
}
JoystickWidget::~JoystickWidget()
{
delete m_ui;
}
void JoystickWidget::changeEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
break;
default:
break;
}
}
void JoystickWidget::updateUI()
{
// Update the actions for all of the buttons
for (int i = 0; i < buttons.size(); i++)
{
JoystickButton* button = buttons[i];
int action = joystick->getActionForButton(i);
button->setAction(action);
}
// Update the axis mappings
int rollAxis = joystick->getMappingRollAxis();
int pitchAxis = joystick->getMappingPitchAxis();
int yawAxis = joystick->getMappingYawAxis();
int throttleAxis = joystick->getMappingThrottleAxis();
for (int i = 0; i < axes.size(); i++)
{
JoystickAxis* axis = axes[i];
JoystickInput::JOYSTICK_INPUT_MAPPING mapping = JoystickInput::JOYSTICK_INPUT_MAPPING_NONE;
if (i == rollAxis)
{
mapping = JoystickInput::JOYSTICK_INPUT_MAPPING_ROLL;
}
else if (i == pitchAxis)
{
mapping = JoystickInput::JOYSTICK_INPUT_MAPPING_PITCH;
}
else if (i == yawAxis)
{
mapping = JoystickInput::JOYSTICK_INPUT_MAPPING_YAW;
}
else if (i == throttleAxis)
{
mapping = JoystickInput::JOYSTICK_INPUT_MAPPING_THROTTLE;
}
axis->setMapping(mapping);
bool inverted = joystick->getInvertedForAxis(i);
axis->setInverted(inverted);
bool limited = joystick->getRangeLimitForAxis(i);
axis->setRangeLimit(limited);
}
}
void JoystickWidget::createUIForJoystick()
{
// Delete all the old UI elements
foreach (JoystickButton* b, buttons)
{
delete b;
}
buttons.clear();
foreach (JoystickAxis* a, axes)
{
delete a;
}
axes.clear();
// And add the necessary button displays for this joystick.
int newButtons = joystick->getJoystickNumButtons();
if (newButtons)
{
m_ui->buttonBox->show();
for (int i = 0; i < newButtons; i++)
{
JoystickButton* button = new JoystickButton(i, m_ui->buttonBox);
button->setAction(joystick->getActionForButton(i));
connect(button, SIGNAL(actionChanged(int,int)), this->joystick, SLOT(setButtonAction(int,int)));
connect(this->joystick, &JoystickInput::activeVehicleChanged, button, &JoystickButton::activeVehicleChanged);
m_ui->buttonLayout->addWidget(button);
buttons.append(button);
}
}
else
{
m_ui->buttonBox->hide();
}
// Do the same for the axes supported by this joystick.
int rollAxis = joystick->getMappingRollAxis();
int pitchAxis = joystick->getMappingPitchAxis();
int yawAxis = joystick->getMappingYawAxis();
int throttleAxis = joystick->getMappingThrottleAxis();
int newAxes = joystick->getJoystickNumAxes();
if (newAxes)
{
for (int i = 0; i < newAxes; i++)
{
JoystickAxis* axis = new JoystickAxis(i, m_ui->axesBox);
axis->setValue(joystick->getCurrentValueForAxis(i));
if (i == rollAxis)
{
axis->setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING_ROLL);
}
else if (i == pitchAxis)
{
axis->setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING_PITCH);
}
else if (i == yawAxis)
{
axis->setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING_YAW);
}
else if (i == throttleAxis)
{
axis->setMapping(JoystickInput::JOYSTICK_INPUT_MAPPING_THROTTLE);
}
axis->setInverted(joystick->getInvertedForAxis(i));
axis->setRangeLimit(joystick->getRangeLimitForAxis(i));
connect(axis, SIGNAL(mappingChanged(int,JoystickInput::JOYSTICK_INPUT_MAPPING)), this->joystick, SLOT(setAxisMapping(int,JoystickInput::JOYSTICK_INPUT_MAPPING)));
connect(axis, SIGNAL(inversionChanged(int,bool)), this->joystick, SLOT(setAxisInversion(int,bool)));
connect(axis, SIGNAL(rangeChanged(int,bool)), this->joystick, SLOT(setAxisRangeLimit(int,bool)));
connect(this->joystick, &JoystickInput::activeVehicleChanged, axis, &JoystickAxis::activeVehicleChanged);
m_ui->axesLayout->addWidget(axis);
axes.append(axis);
}
}
else
{
m_ui->axesBox->hide();
}
connect(m_ui->calibrationButton, SIGNAL(clicked()), this, SLOT(cycleCalibrationButton()));
}
void JoystickWidget::updateAxisValue(int axis, float value)
{
if (axis < axes.size())
{
axes.at(axis)->setValue(value);
}
}
void JoystickWidget::setHat(qint8 x, qint8 y)
{
m_ui->statusLabel->setText(tr("Hat position: x: %1, y: %2").arg(x).arg(y));
}
void JoystickWidget::joystickButtonPressed(int key)
{
QString colorStyle = QString("QLabel { background-color: %1;}").arg(buttonLabelColor.name());
buttons.at(key)->setStyleSheet(colorStyle);
}
void JoystickWidget::joystickButtonReleased(int key)
{
buttons.at(key)->setStyleSheet("");
}
void JoystickWidget::cycleCalibrationButton()
{
if (this->joystick->calibrating()) {
this->joystick->setCalibrating(false);
m_ui->calibrationButton->setText("Calibrate range");
} else {
this->joystick->setCalibrating(true);
m_ui->calibrationButton->setText("End calibration");
}
}
/*=====================================================================
PIXHAWK Micro Air Vehicle Flying Robotics Toolkit
(c) 2009, 2010 PIXHAWK PROJECT <http://pixhawk.ethz.ch>
This file is part of the PIXHAWK project
PIXHAWK 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.
PIXHAWK 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 PIXHAWK. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Definition of joystick widget. Provides a UI for configuring the joystick settings.
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#ifndef JOYSTICKWIDGET_H
#define JOYSTICKWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QMap>
#include "JoystickInput.h"
#include "MainWindow.h"
#include "JoystickAxis.h"
#include "JoystickButton.h"
namespace Ui
{
class JoystickWidget;
}
class JoystickWidget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY(JoystickWidget)
public:
explicit JoystickWidget(JoystickInput* joystick, QWidget *parent = 0);
virtual ~JoystickWidget();
public slots:
/** @brief Update the UI for a new joystick based on SDL ID. */
void createUIForJoystick();
/**
* @brief Update a given axis with a new value
* @param axis The index of the axis to update.
* @param value The new value for the axis, [-1.0:1.0].
* @see JoystickInput:axisValueChanged
*/
void updateAxisValue(int axis, float value);
/** @brief Update the UI with new values for the hat.
* @see JoystickInput::hatDirectionChanged
*/
void setHat(qint8 x, qint8 y);
/** @brief Trigger a UI change based on a button being pressed */
void joystickButtonPressed(int key);
/** @brief Trigger a UI change based on a button being released */
void joystickButtonReleased(int key);
/** @brief Toggle the calibration button */
void cycleCalibrationButton();
/** @brief Update the UI color scheme when the MainWindow theme changes. */
void styleChanged(bool styleIsDark);
/** Update the UI assuming the joystick has stayed the same. */
void updateUI();
protected:
/** @brief UI change event */
virtual void changeEvent(QEvent *e);
JoystickInput* joystick; ///< Reference to the joystick
/** @brief a list of all button labels generated for this joystick. */
QList<JoystickButton*> buttons;
/** @brief a lit of all joystick axes generated for this joystick. */
QList<JoystickAxis*> axes;
/** @brief The color to use for button labels when their corresponding button is pressed */
QColor buttonLabelColor;
private:
Ui::JoystickWidget *m_ui;
/** @brief Initialize all dynamic UI elements (button list, joystick names, etc.).
* Only done once at startup.
*/
void initUI();
};
#endif // JOYSTICKWIDGET_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JoystickWidget</class>
<widget class="QWidget" name="JoystickWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>368</width>
<height>274</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>368</width>
<height>274</height>
</size>
</property>
<property name="windowTitle">
<string>Joystick Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>8</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<widget class="QCheckBox" name="enableCheckBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Enable controllers</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="joystickNameComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="joystickModeComboBox"/>
</item>
<item>
<widget class="QFrame" name="joystickFrame">
<property name="enabled">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Buttons</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="buttonLayout">
<property name="spacing">
<number>1</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="axesBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Axes</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QHBoxLayout" name="axesLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="calibrationButton">
<property name="text">
<string>Calibrate range</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
......@@ -48,9 +48,6 @@ This file is part of the QGROUNDCONTROL project
#include "MAVLinkProtocol.h"
#include "QGCWaypointListMulti.h"
#include "MainWindow.h"
#ifndef __mobile__
#include "JoystickWidget.h"
#endif
#include "GAudioOutput.h"
#include "QGCMAVLinkLogPlayer.h"
#include "SettingsDialog.h"
......@@ -200,10 +197,6 @@ MainWindow::MainWindow(QSplashScreen* splashScreen)
// Create actions
connectCommonActions();
// Connect user interface devices
emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
#ifndef __mobile__
joystick = new JoystickInput();
#endif
#ifdef QGC_MOUSE_ENABLED_WIN
emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
mouseInput = new Mouse3DInput(this);
......@@ -325,15 +318,6 @@ MainWindow::MainWindow(QSplashScreen* splashScreen)
MainWindow::~MainWindow()
{
#ifndef __mobile__
if (joystick)
{
joystick->shutdown();
joystick->wait(5000);
joystick->deleteLater();
joystick = NULL;
}
#endif
// Delete all UAS objects
for (int i=0;i<_commsWidgetList.size();i++)
{
......@@ -809,11 +793,7 @@ void MainWindow::showRoadMap()
void MainWindow::showSettings()
{
#ifndef __mobile__
SettingsDialog settings(joystick, this);
#else
SettingsDialog settings(this);
#endif
settings.exec();
}
......@@ -1057,11 +1037,7 @@ void MainWindow::hideSplashScreen(void)
void MainWindow::manageLinks()
{
#ifndef __mobile__
SettingsDialog settings(joystick, this, SettingsDialog::ShowCommLinks);
#else
SettingsDialog settings(this, SettingsDialog::ShowCommLinks);
#endif
settings.exec();
}
......
......@@ -46,9 +46,6 @@ This file is part of the QGROUNDCONTROL project
#include "WaypointList.h"
#include "CameraView.h"
#include "UASListWidget.h"
#ifndef __mobile__
#include "input/JoystickInput.h"
#endif
#if (defined QGC_MOUSE_ENABLED_WIN) | (defined QGC_MOUSE_ENABLED_LINUX)
#include "Mouse6dofInput.h"
#endif // QGC_MOUSE_ENABLED_WIN
......@@ -241,10 +238,6 @@ protected:
QPointer<QGCUASFileViewMulti> fileWidget;
#ifndef __mobile__
JoystickInput* joystick; ///< The joystick manager for QGC
#endif
#ifdef QGC_MOUSE_ENABLED_WIN
/** @brief 3d Mouse support (WIN only) */
Mouse3DInput* mouseInput; ///< 3dConnexion 3dMouse SDK
......
......@@ -3,6 +3,8 @@
#include "QGCMAVLink.h"
#include "QGCMAVLinkInspector.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
#include "ui_QGCMAVLinkInspector.h"
#include <QDebug>
......
#include "QGCWaypointListMulti.h"
#include "ui_QGCWaypointListMulti.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
#include "ui_QGCWaypointListMulti.h"
void* QGCWaypointListMulti::_offlineUAS = NULL;
......
......@@ -28,9 +28,6 @@
#include "MainWindow.h"
#include "ui_SettingsDialog.h"
#ifndef __mobile__
#include "JoystickWidget.h"
#endif
#include "LinkManager.h"
#include "MAVLinkProtocol.h"
#include "MAVLinkSettingsWidget.h"
......@@ -41,11 +38,7 @@
#include "QGCMessageBox.h"
#include "MainToolBar.h"
#ifndef __mobile__
SettingsDialog::SettingsDialog(JoystickInput *joystick, QWidget *parent, int showTab, Qt::WindowFlags flags) :
#else
SettingsDialog::SettingsDialog(QWidget *parent, int showTab, Qt::WindowFlags flags) :
#endif
QDialog(parent, flags),
_mainWindow(MainWindow::instance()),
_ui(new Ui::SettingsDialog)
......@@ -58,17 +51,10 @@ _ui(new Ui::SettingsDialog)
move(position.topLeft());
QGCLinkConfiguration* pLinkConf = new QGCLinkConfiguration(this);
#ifndef __mobile__
JoystickWidget* pJoystickConf = new JoystickWidget(joystick, this);
#endif
MAVLinkSettingsWidget* pMavsettings = new MAVLinkSettingsWidget(MAVLinkProtocol::instance(), this);
// Add the link settings pane
_ui->tabWidget->addTab(pLinkConf, "Comm Links");
#ifndef __mobile__
// Add the joystick settings pane
_ui->tabWidget->addTab(pJoystickConf, "Controllers");
#endif
// Add the MAVLink settings pane
_ui->tabWidget->addTab(pMavsettings, "MAVLink");
......@@ -113,11 +99,6 @@ _ui(new Ui::SettingsDialog)
case ShowCommLinks:
_ui->tabWidget->setCurrentWidget(pLinkConf);
break;
#ifndef __mobile__
case ShowControllers:
_ui->tabWidget->setCurrentWidget(pJoystickConf);
break;
#endif
case ShowMavlink:
_ui->tabWidget->setCurrentWidget(pMavsettings);
break;
......
......@@ -45,11 +45,7 @@ public:
ShowMavlink
};
#ifdef __mobile__
SettingsDialog(QWidget *parent = 0, int showTab = ShowDefault, Qt::WindowFlags flags = Qt::Sheet);
#else
SettingsDialog(JoystickInput *joystick, QWidget *parent = 0, int showTab = ShowDefault, Qt::WindowFlags flags = Qt::Sheet);
#endif
~SettingsDialog();
public slots:
......
......@@ -5,6 +5,7 @@
#include "QGC.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
MAV2DIcon::MAV2DIcon(mapcontrol::MapGraphicItem* map,mapcontrol::OPMapWidget* parent, UASInterface* uas, int radius, int type)
: UAVItem(map,parent),
......
......@@ -30,17 +30,18 @@ This file is part of the PIXHAWK project
*/
#include <QtGlobal>
#include <float.h>
#include <UASInfoWidget.h>
#include <MultiVehicleManager.h>
#include <QGC.h>
#include <QTimer>
#include <QDir>
#include <QDebug>
#include <float.h>
#include <cstdlib>
#include <cmath>
#include <QDebug>
#include "UASInfoWidget.h"
#include "MultiVehicleManager.h"
#include "QGC.h"
#include "UAS.h"
UASInfoWidget::UASInfoWidget(QWidget *parent, QString name) : QWidget(parent)
{
......
#include "UASQuickView.h"
#include <QMetaMethod>
#include <QDebug>
#include "UASQuickViewItemSelect.h"
#include "UASQuickViewTextItem.h"
#include "MultiVehicleManager.h"
#include "UAS.h"
#include <QMetaMethod>
#include <QDebug>
#include <QSettings>
#include <QInputDialog>
#include "MultiVehicleManager.h"
UASQuickView::UASQuickView(QWidget *parent) : QWidget(parent),
uas(NULL)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment