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;
}
_buildActionList(_multiVehicleManager->activeVehicle());
_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;
_assignableButtonActions.clearAndDeleteContents();
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();
}
//-- Update button states
for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
bool newButtonValue = _getButton(buttonIndex);
if(buttonIndex < 256)
lastBbuttonValues[buttonIndex] = _rgButtonValues[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);
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;
_executeButtonAction(buttonAction, true);
if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
_buttonActionArray[buttonIndex]->buttonTime.start();
qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
_executeButtonAction(buttonAction, true);
//-- Flag it as processed
_rgButtonValues[buttonIndex] = BUTTON_REPEAT;
} else if(_rgButtonValues[buttonIndex] == BUTTON_UP) {
//-- Button up transition
if(buttonIndex < 256) {
if(lastBbuttonValues[buttonIndex] == BUTTON_DOWN || lastBbuttonValues[buttonIndex] == BUTTON_REPEAT) {
if(_buttonActionArray[buttonIndex]) {
QString buttonAction = _buttonActionArray[buttonIndex]->action;
if(buttonAction.isEmpty() || buttonAction == _buttonActionNone)
continue;
qCDebug(JoystickLog) << "Button up" << buttonIndex << buttonAction;
_executeButtonAction(buttonAction, false);
}
}
}
//-- 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);
// Build action list
_buildActionList(vehicle);
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();
void Joystick::_executeButtonAction(const QString& action, bool buttonDown)
if (!_activeVehicle || !_activeVehicle->joystickEnabled() || action == _buttonActionNone) {
Lorenz Meier
committed
return;
}
if (action == _buttonActionArm) {
} else if (action == _buttonActionDisarm) {
if (buttonDown) emit setArmed(!_activeVehicle->armed());
} else if (action == _buttonActionVTOLFixedWing) {
if (buttonDown) emit setVtolInFwdFlight(true);
} else if (action == _buttonActionVTOLMultiRotor) {
if (buttonDown) emit setVtolInFwdFlight(false);
} else if (_activeVehicle->flightModes().contains(action)) {
if (buttonDown) emit setFlightMode(action);
} else if(action == _buttonActionContinuousZoomIn || action == _buttonActionContinuousZoomOut) {
if (buttonDown) {
emit startContinuousZoom(action == _buttonActionContinuousZoomIn ? 1 : -1);
} else {
emit stopContinuousZoom();
}
} else if(action == _buttonActionStepZoomIn || action == _buttonActionStepZoomOut) {
if (buttonDown) emit stepZoom(action == _buttonActionStepZoomIn ? 1 : -1);
} else if(action == _buttonActionNextStream || action == _buttonActionPreviousStream) {
if (buttonDown) emit stepStream(action == _buttonActionNextStream ? 1 : -1);
} else if(action == _buttonActionNextCamera || action == _buttonActionPreviousCamera) {
if (buttonDown) emit stepCamera(action == _buttonActionNextCamera ? 1 : -1);
if (buttonDown) emit toggleVideoRecord();
if (buttonDown) {
_localPitch = 0.0;
_localYaw = 0.0;
emit gimbalControlValue(0.0, 0.0);
}
} else {
qCDebug(JoystickLog) << "_buttonAction unknown action:" << action;
}
}
void Joystick::_pitchStep(int direction)
{
_localPitch += static_cast<double>(direction);
//-- Arbitrary range
if(_localPitch < -90.0) _localPitch = -90.0;
if(_localPitch > 35.0) _localPitch = 35.0;
emit gimbalControlValue(_localPitch, _localYaw);
}
void Joystick::_yawStep(int direction)
{
_localYaw += static_cast<double>(direction);
if(_localYaw < -180.0) _localYaw = -180.0;
if(_localYaw > 180.0) _localYaw = 180.0;
emit gimbalControlValue(_localPitch, _localYaw);
}
bool Joystick::_validAxis(int axis)
{
if(axis >= 0 && axis < _axisCount) {
return true;
}
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return false;
}
bool Joystick::_validButton(int button)
{
if(button >= 0 && button < _totalButtonCount)
return true;
qCWarning(JoystickLog) << "Invalid button index" << button;
return false;
}
int Joystick::_findAssignableButtonAction(const QString& action)
{
for(int i = 0; i < _assignableButtonActions.count(); i++) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[i]);
if(p->action() == action)
return i;
}
return -1;
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
void Joystick::_buildActionList(Vehicle* activeVehicle)
{
if(_assignableButtonActions.count())
_assignableButtonActions.clearAndDeleteContents();
_availableActionTitles.clear();
//-- 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();
}
emit assignableActionsChanged();
}