Newer
Older
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "Joystick.h"
#include "QGC.h"
#include "AutoPilotPlugin.h"
#include "UAS.h"
#include "QGCApplication.h"
#include "VideoManager.h"
#include "QGCCameraManager.h"
#include "QGCCameraControl.h"
QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog")
const char* Joystick::_settingsGroup = "Joysticks";
const char* Joystick::_calibratedSettingsKey = "Calibrated4"; // Increment number to force recalibration
const char* Joystick::_buttonActionNameKey = "ButtonActionName%1";
const char* Joystick::_buttonActionRepeatKey = "ButtonActionRepeat%1";
const char* Joystick::_throttleModeSettingsKey = "ThrottleMode";
const char* Joystick::_exponentialSettingsKey = "Exponential";
const char* Joystick::_accumulatorSettingsKey = "Accumulator";
const char* Joystick::_deadbandSettingsKey = "Deadband";
const char* Joystick::_circleCorrectionSettingsKey = "Circle_Correction";
const char* Joystick::_axisFrequencySettingsKey = "AxisFrequency";
const char* Joystick::_buttonFrequencySettingsKey = "ButtonFrequency";
const char* Joystick::_txModeSettingsKey = nullptr;
const char* Joystick::_fixedWingTXModeSettingsKey = "TXMode_FixedWing";
const char* Joystick::_multiRotorTXModeSettingsKey = "TXMode_MultiRotor";
const char* Joystick::_roverTXModeSettingsKey = "TXMode_Rover";
const char* Joystick::_vtolTXModeSettingsKey = "TXMode_VTOL";
const char* Joystick::_submarineTXModeSettingsKey = "TXMode_Submarine";
const char* Joystick::_gimbalSettingsKey = "GimbalEnabled";
const char* Joystick::_buttonActionNone = QT_TR_NOOP("No Action");
const char* Joystick::_buttonActionArm = QT_TR_NOOP("Arm");
const char* Joystick::_buttonActionDisarm = QT_TR_NOOP("Disarm");
const char* Joystick::_buttonActionToggleArm = QT_TR_NOOP("Toggle Arm");
const char* Joystick::_buttonActionVTOLFixedWing = QT_TR_NOOP("VTOL: Fixed Wing");
const char* Joystick::_buttonActionVTOLMultiRotor = QT_TR_NOOP("VTOL: Multi-Rotor");
const char* Joystick::_buttonActionContinuousZoomIn = QT_TR_NOOP("Continuous Zoom In");
const char* Joystick::_buttonActionContinuousZoomOut = QT_TR_NOOP("Continuous Zoom Out");
const char* Joystick::_buttonActionStepZoomIn = QT_TR_NOOP("Step Zoom In");
const char* Joystick::_buttonActionStepZoomOut = QT_TR_NOOP("Step Zoom Out");
const char* Joystick::_buttonActionNextStream = QT_TR_NOOP("Next Video Stream");
const char* Joystick::_buttonActionPreviousStream = QT_TR_NOOP("Previous Video Stream");
const char* Joystick::_buttonActionNextCamera = QT_TR_NOOP("Next Camera");
const char* Joystick::_buttonActionPreviousCamera = QT_TR_NOOP("Previous Camera");
const char* Joystick::_buttonActionTriggerCamera = QT_TR_NOOP("Trigger Camera");
const char* Joystick::_buttonActionStartVideoRecord = QT_TR_NOOP("Start Recording Video");
const char* Joystick::_buttonActionStopVideoRecord = QT_TR_NOOP("Stop Recording Video");
const char* Joystick::_buttonActionToggleVideoRecord = QT_TR_NOOP("Toggle Recording Video");
const char* Joystick::_buttonActionGimbalDown = QT_TR_NOOP("Gimbal Down");
const char* Joystick::_buttonActionGimbalUp = QT_TR_NOOP("Gimbal Up");
const char* Joystick::_buttonActionGimbalLeft = QT_TR_NOOP("Gimbal Left");
const char* Joystick::_buttonActionGimbalRight = QT_TR_NOOP("Gimbal Right");
const char* Joystick::_buttonActionGimbalCenter = QT_TR_NOOP("Gimbal Center");
const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = {
"RollAxis",
"PitchAxis",
"YawAxis",
int Joystick::_transmitterMode = 2;
AssignedButtonAction::AssignedButtonAction(QObject* parent, const QString name)
: QObject(parent)
, action(name)
{
}
AssignableButtonAction::AssignableButtonAction(QObject* parent, QString action_, bool canRepeat_)
: QObject(parent)
, _action(action_)
, _repeat(canRepeat_)
{
}
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager)
, _axisCount(axisCount)
, _buttonCount(buttonCount)
, _hatCount(hatCount)
, _totalButtonCount(_buttonCount+_hatButtonCount)
, _multiVehicleManager(multiVehicleManager)
_rgAxisValues = new int[static_cast<size_t>(_axisCount)];
_rgCalibration = new Calibration_t[static_cast<size_t>(_axisCount)];
_rgButtonValues = new uint8_t[static_cast<size_t>(_totalButtonCount)];
_rgAxisValues[i] = 0;
}
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//-- Available Actions
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNone));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionArm));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionDisarm));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleArm));
if (_activeVehicle) {
QStringList list = _activeVehicle->flightModes();
foreach(auto mode, list) {
_assignableButtonActions.append(new AssignableButtonAction(this, mode));
}
}
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLFixedWing));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLMultiRotor));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomIn, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomOut, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomIn, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomOut, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextStream));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousStream));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionTriggerCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStartVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStopVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalDown, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalUp, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalLeft, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalRight, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalCenter));
for(int i = 0; i < _assignableButtonActions.count(); i++) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[i]);
_availableActionTitles << p->action();
}
_updateTXModeSettingsKey(_multiVehicleManager->activeVehicle());
_loadSettings();
connect(_multiVehicleManager, &MultiVehicleManager::activeVehicleChanged, this, &Joystick::_activeVehicleChanged);
}
Joystick::~Joystick()
{
// Crash out of the thread if it is still running
terminate();
wait();
delete[] _rgAxisValues;
delete[] _rgCalibration;
delete[] _rgButtonValues;
for (int button = 0; button < _totalButtonCount; button++) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
}
}
void Joystick::_setDefaultCalibration(void) {
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
_calibrated = settings.value(_calibratedSettingsKey, false).toBool();
// Only set default calibrations if we do not have a calibration for this gamecontroller
if(_calibrated) return;
for(int axis = 0; axis < _axisCount; axis++) {
Joystick::Calibration_t calibration;
_rgCalibration[axis] = calibration;
}
_rgCalibration[1].reversed = true;
_rgCalibration[3].reversed = true;
// Default TX Mode 2 axis assignments for gamecontrollers
_rgFunctionAxis[rollFunction] = 2;
_rgFunctionAxis[pitchFunction] = 3;
_rgFunctionAxis[yawFunction] = 0;
_rgFunctionAxis[throttleFunction] = 1;
_rgFunctionAxis[gimbalPitchFunction]= 4;
_rgFunctionAxis[gimbalYawFunction] = 5;
_exponential = 0;
_accumulator = false;
_deadband = false;
_circleCorrection = false;
_saveSettings();
}
void Joystick::_updateTXModeSettingsKey(Vehicle* activeVehicle)
{
if(activeVehicle) {
if(activeVehicle->fixedWing()) {
_txModeSettingsKey = _fixedWingTXModeSettingsKey;
} else if(activeVehicle->multiRotor()) {
_txModeSettingsKey = _multiRotorTXModeSettingsKey;
} else if(activeVehicle->rover()) {
_txModeSettingsKey = _roverTXModeSettingsKey;
} else if(activeVehicle->vtol()) {
_txModeSettingsKey = _vtolTXModeSettingsKey;
} else if(activeVehicle->sub()) {
_txModeSettingsKey = _submarineTXModeSettingsKey;
} else {
_txModeSettingsKey = nullptr;
qWarning() << "No valid joystick TXmode settings key for selected vehicle";
return;
}
_txModeSettingsKey = nullptr;
void Joystick::_activeVehicleChanged(Vehicle* activeVehicle)
{
_updateTXModeSettingsKey(activeVehicle);
if(activeVehicle) {
QSettings settings;
settings.beginGroup(_settingsGroup);
int mode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt();
setTXMode(mode);
}
}
settings.beginGroup(_settingsGroup);
Vehicle* activeVehicle = _multiVehicleManager->activeVehicle();
if(_txModeSettingsKey && activeVehicle)
_transmitterMode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt();
settings.beginGroup(_name);
bool badSettings = false;
bool convertOk;
qCDebug(JoystickLog) << "_loadSettings " << _name;
_calibrated = settings.value(_calibratedSettingsKey, false).toBool();
_exponential = settings.value(_exponentialSettingsKey, 0).toFloat();
_accumulator = settings.value(_accumulatorSettingsKey, false).toBool();
_deadband = settings.value(_deadbandSettingsKey, false).toBool();
_axisFrequency = settings.value(_axisFrequencySettingsKey, 25.0f).toFloat();
_buttonFrequency= settings.value(_buttonFrequencySettingsKey, 5.0f).toFloat();
_circleCorrection = settings.value(_circleCorrectionSettingsKey, false).toBool();
_gimbalEnabled = settings.value(_gimbalSettingsKey, false).toBool();
_throttleMode = static_cast<ThrottleMode_t>(settings.value(_throttleModeSettingsKey, ThrottleModeDownZero).toInt(&convertOk));
badSettings |= !convertOk;
qCDebug(JoystickLog) << "_loadSettings calibrated:txmode:throttlemode:exponential:deadband:badsettings" << _calibrated << _transmitterMode << _throttleMode << _exponential << _deadband << badSettings;
QString minTpl ("Axis%1Min");
QString maxTpl ("Axis%1Max");
QString trimTpl ("Axis%1Trim");
QString revTpl ("Axis%1Rev");
QString deadbndTpl ("Axis%1Deadbnd");
for (int axis = 0; axis < _axisCount; 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), 32767).toInt(&convertOk);
badSettings |= !convertOk;
calibration->deadband = settings.value(deadbndTpl.arg(axis), 0).toInt(&convertOk);
badSettings |= !convertOk;
calibration->reversed = settings.value(revTpl.arg(axis), false).toBool();
qCDebug(JoystickLog) << "_loadSettings axis:min:max:trim:reversed:deadband:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings;
int functionAxis;
functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk);
badSettings |= !convertOk || (functionAxis >= _axisCount);
if(functionAxis >= 0) {
workingAxis++;
}
if(functionAxis < _axisCount) {
_rgFunctionAxis[function] = functionAxis;
}
qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings;
}
// FunctionAxis mappings are always stored in TX mode 2
// Remap to stored TX mode in settings
_remapAxes(2, _transmitterMode, _rgFunctionAxis);
for (int button = 0; button < _totalButtonCount; button++) {
QString a = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString();
if(!a.isEmpty() && _findAssignableButtonAction(a) >= 0 && a != _buttonActionNone) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
}
ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool();
_buttonActionArray[button]->buttonTime.start();
qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
if (badSettings) {
_calibrated = false;
settings.setValue(_calibratedSettingsKey, false);
}
}
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
for (int button = 0; button < _totalButtonCount; button++) {
if(_buttonActionArray[button]) {
settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
settings.beginGroup(_settingsGroup);
// Transmitter mode is static
// Save the mode we are using
settings.beginGroup(_name);
settings.setValue(_calibratedSettingsKey, _calibrated);
settings.setValue(_exponentialSettingsKey, _exponential);
settings.setValue(_accumulatorSettingsKey, _accumulator);
settings.setValue(_deadbandSettingsKey, _deadband);
settings.setValue(_axisFrequencySettingsKey, _axisFrequency);
settings.setValue(_buttonFrequencySettingsKey, _buttonFrequency);
settings.setValue(_throttleModeSettingsKey, _throttleMode);
settings.setValue(_gimbalSettingsKey, _gimbalEnabled);
settings.setValue(_circleCorrectionSettingsKey, _circleCorrection);
qCDebug(JoystickLog) << "_saveSettings calibrated:throttlemode:deadband:txmode" << _calibrated << _throttleMode << _deadband << _circleCorrection << _transmitterMode;
QString minTpl ("Axis%1Min");
QString maxTpl ("Axis%1Max");
QString trimTpl ("Axis%1Trim");
QString revTpl ("Axis%1Rev");
QString deadbndTpl ("Axis%1Deadbnd");
for (int axis = 0; axis < _axisCount; 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);
settings.setValue(deadbndTpl.arg(axis), calibration->deadband);
qCDebug(JoystickLog) << "_saveSettings name:axis:min:max:trim:reversed:deadband"
<< _name
<< axis
<< calibration->min
<< calibration->max
<< calibration->center
<< calibration->reversed
<< calibration->deadband;
// Always save function Axis mappings in TX Mode 2
// Write mode 2 mappings without changing mapping currently in use
int temp[maxFunction];
_remapAxes(_transmitterMode, 2, temp);
for (int function = 0; function < maxFunction; function++) {
settings.setValue(_rgFunctionSettingsKey[function], temp[function]);
qCDebug(JoystickLog) << "_saveSettings name:function:axis" << _name << function << _rgFunctionSettingsKey[function];
}
// Relative mappings of axis functions between different TX modes
int Joystick::_mapFunctionMode(int mode, int function) {
static const int mapping[][6] = {
{ yawFunction, pitchFunction, rollFunction, throttleFunction, gimbalPitchFunction, gimbalYawFunction },
{ yawFunction, throttleFunction, rollFunction, pitchFunction, gimbalPitchFunction, gimbalYawFunction },
{ rollFunction, pitchFunction, yawFunction, throttleFunction, gimbalPitchFunction, gimbalYawFunction },
{ rollFunction, throttleFunction, yawFunction, pitchFunction, gimbalPitchFunction, gimbalYawFunction }};
return mapping[mode-1][function];
}
// Remap current axis functions from current TX mode to new TX mode
void Joystick::_remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]) {
int temp[maxFunction];
for(int function = 0; function < maxFunction; function++) {
temp[_mapFunctionMode(newMode, function)] = _rgFunctionAxis[_mapFunctionMode(currentMode, function)];
}
for(int function = 0; function < maxFunction; function++) {
newMapping[function] = temp[function];
}
}
void Joystick::setTXMode(int mode) {
if(mode > 0 && mode <= 4) {
_remapAxes(_transmitterMode, mode, _rgFunctionAxis);
_transmitterMode = mode;
_saveSettings();
} else {
qCWarning(JoystickLog) << "Invalid mode:" << mode;
}
}
/// Adjust the raw axis value to the -1:1 range given calibration information
float Joystick::_adjustRange(int value, Calibration_t calibration, bool withDeadbands)
{
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;
if (withDeadbands) {
if (valueNormalized>calibration.deadband) {
axisPercent = (valueNormalized - calibration.deadband) / (axisLength - calibration.deadband);
} else if (valueNormalized<-calibration.deadband) {
axisPercent = (valueNormalized + calibration.deadband) / (axisLength - calibration.deadband);
} else {
axisPercent = 0.f;
}
}
else {
axisPercent = valueNormalized / axisLength;
float correctedValue = axisBasis * axisPercent;
if (calibration.reversed) {
correctedValue *= -1.0f;
}
qCDebug(JoystickLog) << "_adjustRange corrected:value:min:max:center:reversed:deadband:basis:normalized:length"
<< correctedValue
<< value
<< calibration.min
<< calibration.max
<< calibration.center
<< calibration.reversed
<< calibration.deadband
<< axisBasis
<< valueNormalized
<< axisLength;
#endif
return std::max(-1.0f, std::min(correctedValue, 1.0f));
//-- Reset timers
_axisTime.start();
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
if(_buttonActionArray[buttonIndex]) {
_buttonActionArray[buttonIndex]->buttonTime.start();
}
}
_update();
_handleButtons();
_handleAxis();
QGC::SLEEP::msleep(20);
}
_close();
}
void Joystick::_handleButtons()
{
//-- Update button states
for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
bool newButtonValue = _getButton(buttonIndex);
if (newButtonValue && _rgButtonValues[buttonIndex] == BUTTON_UP) {
_rgButtonValues[buttonIndex] = BUTTON_DOWN;
emit rawButtonPressedChanged(buttonIndex, newButtonValue);
} else if (!newButtonValue && _rgButtonValues[buttonIndex] != BUTTON_UP) {
_rgButtonValues[buttonIndex] = BUTTON_UP;
emit rawButtonPressedChanged(buttonIndex, newButtonValue);
}
//-- Update hat - append hat buttons to the end of the normal button list
int numHatButtons = 4;
for (int hatIndex = 0; hatIndex < _hatCount; hatIndex++) {
for (int hatButtonIndex = 0; hatButtonIndex<numHatButtons; hatButtonIndex++) {
// Create new index value that includes the normal button list
int rgButtonValueIndex = hatIndex*numHatButtons + hatButtonIndex + _buttonCount;
// Get hat value from joystick
bool newButtonValue = _getHat(hatIndex, hatButtonIndex);
if (newButtonValue && _rgButtonValues[rgButtonValueIndex] == BUTTON_UP) {
_rgButtonValues[rgButtonValueIndex] = BUTTON_DOWN;
emit rawButtonPressedChanged(rgButtonValueIndex, newButtonValue);
} else if (!newButtonValue && _rgButtonValues[rgButtonValueIndex] != BUTTON_UP) {
_rgButtonValues[rgButtonValueIndex] = BUTTON_UP;
emit rawButtonPressedChanged(rgButtonValueIndex, newButtonValue);
}
//-- Process button press
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) {
QString buttonAction = _buttonActionArray[buttonIndex]->action;
if(buttonAction.isEmpty() || buttonAction == _buttonActionNone)
continue;
//-- Process single button
if(!_buttonActionArray[buttonIndex]->repeat) {
//-- This button just went down
if(_rgButtonValues[buttonIndex] == BUTTON_DOWN) {
qCDebug(JoystickLog) << "Single button triggered" << buttonIndex << buttonAction;
if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
_buttonActionArray[buttonIndex]->buttonTime.start();
qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
_executeButtonAction(buttonAction);
//-- Flag it as processed
_rgButtonValues[buttonIndex] = BUTTON_REPEAT;
//-- Check elapsed time since last run
if(_axisTime.elapsed() > axisDelay) {
_axisTime.start();
//-- Update axis
for (int axisIndex = 0; axisIndex < _axisCount; axisIndex++) {
int newAxisValue = _getAxis(axisIndex);
// Calibration code requires signal to be emitted even if value hasn't changed
_rgAxisValues[axisIndex] = newAxisValue;
emit rawAxisValueChanged(axisIndex, newAxisValue);
}
if (_activeVehicle->joystickEnabled() && !_calibrationMode && _calibrated) {
int axis = _rgFunctionAxis[rollFunction];
float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband);
axis = _rgFunctionAxis[pitchFunction];
float pitch = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband);
axis = _rgFunctionAxis[yawFunction];
float yaw = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis],_deadband);
axis = _rgFunctionAxis[throttleFunction];
float throttle = _adjustRange(_rgAxisValues[axis],_rgCalibration[axis], _throttleMode==ThrottleModeDownZero?false:_deadband);
float gimbalPitch = 0.0f;
float gimbalYaw = 0.0f;
if(_axisCount > 4) {
axis = _rgFunctionAxis[gimbalPitchFunction];
gimbalPitch = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis],_deadband);
}
if(_axisCount > 5) {
axis = _rgFunctionAxis[gimbalYawFunction];
gimbalYaw = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis],_deadband);
}
static float throttle_accu = 0.f;
throttle_accu += throttle * (40 / 1000.f); //for throttle to change from min to max it will take 1000ms (40ms is a loop time)
throttle_accu = std::max(static_cast<float>(-1.f), std::min(throttle_accu, static_cast<float>(1.f)));
throttle = throttle_accu;
}
float roll_limited = std::max(static_cast<float>(-M_PI_4), std::min(roll, static_cast<float>(M_PI_4)));
float pitch_limited = std::max(static_cast<float>(-M_PI_4), std::min(pitch, static_cast<float>(M_PI_4)));
float yaw_limited = std::max(static_cast<float>(-M_PI_4), std::min(yaw, static_cast<float>(M_PI_4)));
float throttle_limited = std::max(static_cast<float>(-M_PI_4), std::min(throttle, static_cast<float>(M_PI_4)));
// Map from unit circle to linear range and limit
roll = std::max(-1.0f, std::min(tanf(asinf(roll_limited)), 1.0f));
pitch = std::max(-1.0f, std::min(tanf(asinf(pitch_limited)), 1.0f));
yaw = std::max(-1.0f, std::min(tanf(asinf(yaw_limited)), 1.0f));
throttle = std::max(-1.0f, std::min(tanf(asinf(throttle_limited)), 1.0f));
}
// Exponential (0% to -50% range like most RC radios)
// _exponential is set by a slider in joystickConfigAdvanced.qml
// Calculate new RPY with exponential applied
nanthony21
committed
pitch = -_exponential*powf(pitch,3) + (1+_exponential)*pitch;
// Adjust throttle to 0:1 range
if (_throttleMode == ThrottleModeCenterZero && _activeVehicle->supportsThrottleModeCenterZero()) {
if (!_activeVehicle->supportsNegativeThrust() || !_negativeThrust) {
throttle = std::max(0.0f, throttle);
}
throttle = (throttle + 1.0f) / 2.0f;
}
qCDebug(JoystickValuesLog) << "name:roll:pitch:yaw:throttle:gimbalPitch:gimbalYaw" << name() << roll << -pitch << yaw << throttle << gimbalPitch << gimbalYaw;
// NOTE: The buttonPressedBits going to MANUAL_CONTROL are currently used by ArduSub (and it only handles 16 bits)
// Set up button bitmap
quint64 buttonPressedBits = 0; // Buttons pressed for manualControl signal
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
quint64 buttonBit = static_cast<quint64>(1LL << buttonIndex);
// Mark the button as pressed as long as its pressed
Nate Weibley
committed
buttonPressedBits |= buttonBit;
uint16_t shortButtons = static_cast<uint16_t>(buttonPressedBits & 0xFFFF);
emit manualControl(roll, -pitch, yaw, throttle, shortButtons, _activeVehicle->joystickMode());
if(_activeVehicle && _axisCount > 4 && _gimbalEnabled) {
//-- TODO: There is nothing consuming this as there are no messages to handle gimbal
// the way MANUAL_CONTROL handles the other channels.
emit manualControlGimbal((gimbalPitch + 1.0f) / 2.0f * 90.0f, gimbalYaw * 180.0f);
}
void Joystick::startPolling(Vehicle* vehicle)
Lorenz Meier
committed
if (vehicle) {
// If a vehicle is connected, disconnect it
if (_activeVehicle) {
UAS* uas = _activeVehicle->uas();
disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
disconnect(this, &Joystick::setArmed, _activeVehicle, &Vehicle::setArmed);
disconnect(this, &Joystick::setVtolInFwdFlight, _activeVehicle, &Vehicle::setVtolInFwdFlight);
disconnect(this, &Joystick::setFlightMode, _activeVehicle, &Vehicle::setFlightMode);
disconnect(this, &Joystick::gimbalPitchStep, _activeVehicle, &Vehicle::gimbalPitchStep);
disconnect(this, &Joystick::gimbalYawStep, _activeVehicle, &Vehicle::gimbalYawStep);
disconnect(this, &Joystick::centerGimbal, _activeVehicle, &Vehicle::centerGimbal);
disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
Lorenz Meier
committed
// Always set up the new vehicle
// If joystick is not calibrated, disable it
if ( !_calibrated ) {
vehicle->setJoystickEnabled(false);
}
// Update qml in case of joystick transition
emit calibratedChanged(_calibrated);
Lorenz Meier
committed
// Only connect the new vehicle if it wants joystick data
if (vehicle->joystickEnabled()) {
_pollingStartedForCalibration = false;
UAS* uas = _activeVehicle->uas();
connect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
connect(this, &Joystick::setArmed, _activeVehicle, &Vehicle::setArmed);
connect(this, &Joystick::setVtolInFwdFlight, _activeVehicle, &Vehicle::setVtolInFwdFlight);
connect(this, &Joystick::setFlightMode, _activeVehicle, &Vehicle::setFlightMode);
connect(this, &Joystick::gimbalPitchStep, _activeVehicle, &Vehicle::gimbalPitchStep);
connect(this, &Joystick::gimbalYawStep, _activeVehicle, &Vehicle::gimbalYawStep);
connect(this, &Joystick::centerGimbal, _activeVehicle, &Vehicle::centerGimbal);
connect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
Lorenz Meier
committed
// FIXME: ****
//connect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
}
}
if (!isRunning()) {
}
}
void Joystick::stopPolling(void)
{
Lorenz Meier
committed
if (_activeVehicle && _activeVehicle->joystickEnabled()) {
UAS* uas = _activeVehicle->uas();
// Neutral attitude controls
// emit manualControl(0, 0, 0, 0.5, 0, _activeVehicle->joystickMode());
disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
disconnect(this, &Joystick::setArmed, _activeVehicle, &Vehicle::setArmed);
disconnect(this, &Joystick::setVtolInFwdFlight, _activeVehicle, &Vehicle::setVtolInFwdFlight);
disconnect(this, &Joystick::setFlightMode, _activeVehicle, &Vehicle::setFlightMode);
disconnect(this, &Joystick::gimbalPitchStep, _activeVehicle, &Vehicle::gimbalPitchStep);
disconnect(this, &Joystick::gimbalYawStep, _activeVehicle, &Vehicle::gimbalYawStep);
disconnect(this, &Joystick::centerGimbal, _activeVehicle, &Vehicle::centerGimbal);
disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
Lorenz Meier
committed
}
// FIXME: ****
//disconnect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
}
void Joystick::setCalibration(int axis, Calibration_t& calibration)
{
return;
}
_calibrated = true;
_rgCalibration[axis] = calibration;
_saveSettings();
emit calibratedChanged(_calibrated);
}
Joystick::Calibration_t Joystick::getCalibration(int axis)
{
}
return _rgCalibration[axis];
}
void Joystick::setFunctionAxis(AxisFunction_t function, int axis)
{
return;
}
_calibrated = true;
_rgFunctionAxis[function] = axis;
_saveSettings();
emit calibratedChanged(_calibrated);
}
int Joystick::getFunctionAxis(AxisFunction_t function)
{
if (static_cast<int>(function) < 0 || function >= maxFunction) {
qCWarning(JoystickLog) << "Invalid function" << function;
}
return _rgFunctionAxis[function];
}
if (!_validButton(button) || !_buttonActionArray[button]) {
return;
}
_buttonActionArray[button]->repeat = repeat;
_buttonActionArray[button]->buttonTime.start();
//-- Save to settings
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
{
if (!_validButton(button) || !_buttonActionArray[button]) {
void Joystick::setButtonAction(int button, const QString& action)
qCWarning(JoystickLog) << "setButtonAction:" << button << action;
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
if(action.isEmpty() || action == _buttonActionNone) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
_buttonActionArray[button] = nullptr;
//-- Clear from settings
settings.remove(QString(_buttonActionNameKey).arg(button));
settings.remove(QString(_buttonActionRepeatKey).arg(button));
_buttonActionArray[button] = new AssignedButtonAction(this, action);
} else {
_buttonActionArray[button]->action = action;
//-- Make sure repeat is off if this action doesn't support repeats
int idx = _findAssignableButtonAction(action);
if(idx >= 0) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[idx]);
if(!p->canRepeat()) {
_buttonActionArray[button]->repeat = false;
}
}
//-- Save to settings
settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
if (_validButton(button)) {
if(_buttonActionArray[button]) {
return _buttonActionArray[button]->action;
}
QStringList list;
for (int button = 0; button < _totalButtonCount; button++) {
list << getButtonAction(button);
}
return list;
}
{
return _throttleMode;
}
void Joystick::setThrottleMode(int mode)
{
if (mode < 0 || mode >= ThrottleModeMax) {
qCWarning(JoystickLog) << "Invalid throttle mode" << mode;
return;
}
if (_throttleMode == ThrottleModeDownZero) {
setAccumulator(false);
}
_saveSettings();
emit throttleModeChanged(_throttleMode);
}
{
return _negativeThrust;
}
void Joystick::setNegativeThrust(bool allowNegative)
{
if (_negativeThrust == allowNegative) {
return;
}
_negativeThrust = allowNegative;
_saveSettings();
emit negativeThrustChanged(_negativeThrust);
}
{
return _exponential;
}
nanthony21
committed
void Joystick::setExponential(float expo)
{
_exponential = expo;
_saveSettings();
emit exponentialChanged(_exponential);
}
{
return _accumulator;
}
void Joystick::setAccumulator(bool accu)
{
_accumulator = accu;
_saveSettings();
emit accumulatorChanged(_accumulator);
}
{
return _deadband;
}
void Joystick::setDeadband(bool deadband)
{
_deadband = deadband;
_saveSettings();
}
{
return _circleCorrection;
}
void Joystick::setCircleCorrection(bool circleCorrection)
{
_circleCorrection = circleCorrection;
_saveSettings();
emit circleCorrectionChanged(_circleCorrection);
}
void Joystick::setGimbalEnabled(bool set)
{
_gimbalEnabled = set;
_saveSettings();
emit gimbalEnabledChanged();
}
void Joystick::setAxisFrequency(float val)
{
//-- Arbitrary limits
if(val < 0.25f) val = 0.25f;
if(val > 50.0f) val = 50.0f;
_axisFrequency = val;
_saveSettings();
emit axisFrequencyChanged();
}
void Joystick::setButtonFrequency(float val)
void Joystick::setCalibrationMode(bool calibrating)
_calibrationMode = calibrating;
if (calibrating && !isRunning()) {
startPolling(_multiVehicleManager->activeVehicle());
else if (_pollingStartedForCalibration) {
stopPolling();
if (!_activeVehicle || !_activeVehicle->joystickEnabled() || action == _buttonActionNone) {
Lorenz Meier
committed
return;
}
if (action == _buttonActionArm) {
} else if (action == _buttonActionDisarm) {
} else if (action == _buttonActionVTOLFixedWing) {
} else if (action == _buttonActionVTOLMultiRotor) {
} else if (_activeVehicle->flightModes().contains(action)) {
} else if(action == _buttonActionContinuousZoomIn || action == _buttonActionContinuousZoomOut) {
emit startContinuousZoom(action == _buttonActionStepZoomIn ? 1 : -1);
} else if(action == _buttonActionStepZoomIn || action == _buttonActionStepZoomOut) {
emit stepZoom(action == _buttonActionStepZoomIn ? 1 : -1);
} else if(action == _buttonActionNextStream || action == _buttonActionPreviousStream) {
emit stepStream(action == _buttonActionNextStream ? 1 : -1);
} else if(action == _buttonActionNextCamera || action == _buttonActionPreviousCamera) {
emit stepCamera(action == _buttonActionNextCamera ? 1 : -1);