Joystick.cc 45.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (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.
 *
 ****************************************************************************/

10 11 12 13 14

#include "Joystick.h"
#include "QGC.h"
#include "AutoPilotPlugin.h"
#include "UAS.h"
15 16 17 18
#include "QGCApplication.h"
#include "VideoManager.h"
#include "QGCCameraManager.h"
#include "QGCCameraControl.h"
19 20 21

#include <QSettings>

Gus Grubba's avatar
Gus Grubba committed
22
QGC_LOGGING_CATEGORY(JoystickLog,       "JoystickLog")
23
QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog")
24

25
const char* Joystick::_settingsGroup =                  "Joysticks";
Gus Grubba's avatar
Gus Grubba committed
26
const char* Joystick::_calibratedSettingsKey =          "Calibrated4"; // Increment number to force recalibration
Gus Grubba's avatar
Gus Grubba committed
27 28
const char* Joystick::_buttonActionNameKey =            "ButtonActionName%1";
const char* Joystick::_buttonActionRepeatKey =          "ButtonActionRepeat%1";
29 30 31 32
const char* Joystick::_throttleModeSettingsKey =        "ThrottleMode";
const char* Joystick::_exponentialSettingsKey =         "Exponential";
const char* Joystick::_accumulatorSettingsKey =         "Accumulator";
const char* Joystick::_deadbandSettingsKey =            "Deadband";
33
const char* Joystick::_circleCorrectionSettingsKey =    "Circle_Correction";
Gus Grubba's avatar
Gus Grubba committed
34 35
const char* Joystick::_axisFrequencySettingsKey =       "AxisFrequency";
const char* Joystick::_buttonFrequencySettingsKey =     "ButtonFrequency";
36
const char* Joystick::_txModeSettingsKey =              nullptr;
37 38 39 40 41
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";
Gus Grubba's avatar
Gus Grubba committed
42
const char* Joystick::_gimbalSettingsKey =              "GimbalEnabled";
43

Gus Grubba's avatar
Gus Grubba committed
44
const char* Joystick::_buttonActionNone =               QT_TR_NOOP("No Action");
45 46
const char* Joystick::_buttonActionArm =                QT_TR_NOOP("Arm");
const char* Joystick::_buttonActionDisarm =             QT_TR_NOOP("Disarm");
Gus Grubba's avatar
Gus Grubba committed
47
const char* Joystick::_buttonActionToggleArm =          QT_TR_NOOP("Toggle Arm");
48 49
const char* Joystick::_buttonActionVTOLFixedWing =      QT_TR_NOOP("VTOL: Fixed Wing");
const char* Joystick::_buttonActionVTOLMultiRotor =     QT_TR_NOOP("VTOL: Multi-Rotor");
Gus Grubba's avatar
Gus Grubba committed
50 51 52 53
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");
54 55 56 57
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");
Gus Grubba's avatar
Gus Grubba committed
58 59 60 61
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");
Gus Grubba's avatar
Gus Grubba committed
62 63 64 65 66
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");
67

68 69 70 71
const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = {
    "RollAxis",
    "PitchAxis",
    "YawAxis",
Gus Grubba's avatar
Gus Grubba committed
72 73 74
    "ThrottleAxis",
    "GimbalPitchAxis",
    "GimbalYawAxis"
75 76
};

77 78
int Joystick::_transmitterMode = 2;

Gus Grubba's avatar
Gus Grubba committed
79 80 81 82 83 84 85
AssignedButtonAction::AssignedButtonAction(QObject* parent, const QString name)
    : QObject(parent)
    , action(name)
{
}

AssignableButtonAction::AssignableButtonAction(QObject* parent, QString action_, bool canRepeat_)
Gus Grubba's avatar
Gus Grubba committed
86 87 88 89 90 91
    : QObject(parent)
    , _action(action_)
    , _repeat(canRepeat_)
{
}

92
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager)
Gus Grubba's avatar
Gus Grubba committed
93
    : _name(name)
94 95
    , _axisCount(axisCount)
    , _buttonCount(buttonCount)
96
    , _hatCount(hatCount)
Gus Grubba's avatar
Gus Grubba committed
97
    , _hatButtonCount(4 * hatCount)
98
    , _totalButtonCount(_buttonCount+_hatButtonCount)
99
    , _multiVehicleManager(multiVehicleManager)
100
{
Gus Grubba's avatar
Gus Grubba committed
101 102 103
    _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)];
Gus Grubba's avatar
Gus Grubba committed
104
    for (int i = 0; i < _axisCount; i++) {
105 106
        _rgAxisValues[i] = 0;
    }
Gus Grubba's avatar
Gus Grubba committed
107
    for (int i = 0; i < _totalButtonCount; i++) {
Gus Grubba's avatar
Gus Grubba committed
108
        _rgButtonValues[i] = BUTTON_UP;
Gus Grubba's avatar
Gus Grubba committed
109
        _buttonActionArray.append(nullptr);
110
    }
111
    _buildActionList(_multiVehicleManager->activeVehicle());
Gus Grubba's avatar
Gus Grubba committed
112 113 114
    _updateTXModeSettingsKey(_multiVehicleManager->activeVehicle());
    _loadSettings();
    connect(_multiVehicleManager, &MultiVehicleManager::activeVehicleChanged, this, &Joystick::_activeVehicleChanged);
115 116 117 118
}

Joystick::~Joystick()
{
119 120 121
    // Crash out of the thread if it is still running
    terminate();
    wait();
Jacob Walser's avatar
Jacob Walser committed
122 123 124
    delete[] _rgAxisValues;
    delete[] _rgCalibration;
    delete[] _rgButtonValues;
125
    _assignableButtonActions.clearAndDeleteContents();
Gus Grubba's avatar
Gus Grubba committed
126 127 128 129 130
    for (int button = 0; button < _totalButtonCount; button++) {
        if(_buttonActionArray[button]) {
            _buttonActionArray[button]->deleteLater();
        }
    }
131 132
}

133
void Joystick::_setDefaultCalibration(void) {
Gus Grubba's avatar
Gus Grubba committed
134
    QSettings settings;
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    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;

150 151 152 153 154
    // Default TX Mode 2 axis assignments for gamecontrollers
    _rgFunctionAxis[rollFunction]       = 2;
    _rgFunctionAxis[pitchFunction]      = 3;
    _rgFunctionAxis[yawFunction]        = 0;
    _rgFunctionAxis[throttleFunction]   = 1;
155

Gus Grubba's avatar
Gus Grubba committed
156 157 158 159 160 161
    _rgFunctionAxis[gimbalPitchFunction]= 4;
    _rgFunctionAxis[gimbalYawFunction]  = 5;

    _exponential    = 0;
    _accumulator    = false;
    _deadband       = false;
Gus Grubba's avatar
Gus Grubba committed
162 163
    _axisFrequency  = 25.0f;
    _buttonFrequency= 5.0f;
Gus Grubba's avatar
Gus Grubba committed
164 165
    _throttleMode   = ThrottleModeDownZero;
    _calibrated     = true;
166
    _circleCorrection = false;
167 168 169 170

    _saveSettings();
}

171
void Joystick::_updateTXModeSettingsKey(Vehicle* activeVehicle)
172 173 174 175 176 177 178 179 180 181 182 183 184
{
    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 {
185
            _txModeSettingsKey = nullptr;
186 187 188
            qWarning() << "No valid joystick TXmode settings key for selected vehicle";
            return;
        }
189
    } else {
190
        _txModeSettingsKey = nullptr;
191 192
    }
}
193

194 195 196 197
void Joystick::_activeVehicleChanged(Vehicle* activeVehicle)
{
    _updateTXModeSettingsKey(activeVehicle);
    if(activeVehicle) {
198 199 200 201 202 203 204
        QSettings settings;
        settings.beginGroup(_settingsGroup);
        int mode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt();
        setTXMode(mode);
    }
}

Gus Grubba's avatar
Gus Grubba committed
205
void Joystick::_loadSettings()
206
{
207
    QSettings settings;
208
    settings.beginGroup(_settingsGroup);
209 210 211 212
    Vehicle* activeVehicle = _multiVehicleManager->activeVehicle();

    if(_txModeSettingsKey && activeVehicle)
        _transmitterMode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt();
213

214
    settings.beginGroup(_name);
215

216 217
    bool badSettings = false;
    bool convertOk;
218

219
    qCDebug(JoystickLog) << "_loadSettings " << _name;
220

221 222 223 224
    _calibrated     = settings.value(_calibratedSettingsKey, false).toBool();
    _exponential    = settings.value(_exponentialSettingsKey, 0).toFloat();
    _accumulator    = settings.value(_accumulatorSettingsKey, false).toBool();
    _deadband       = settings.value(_deadbandSettingsKey, false).toBool();
Gus Grubba's avatar
Gus Grubba committed
225 226
    _axisFrequency  = settings.value(_axisFrequencySettingsKey, 25.0f).toFloat();
    _buttonFrequency= settings.value(_buttonFrequencySettingsKey, 5.0f).toFloat();
227
    _circleCorrection = settings.value(_circleCorrectionSettingsKey, false).toBool();
Gus Grubba's avatar
Gus Grubba committed
228
    _gimbalEnabled  = settings.value(_gimbalSettingsKey, false).toBool();
229

230
    _throttleMode   = static_cast<ThrottleMode_t>(settings.value(_throttleModeSettingsKey, ThrottleModeDownZero).toInt(&convertOk));
231
    badSettings |= !convertOk;
232

233
    qCDebug(JoystickLog) << "_loadSettings calibrated:txmode:throttlemode:exponential:deadband:badsettings" << _calibrated << _transmitterMode << _throttleMode << _exponential << _deadband << badSettings;
234

235 236 237 238
    QString minTpl      ("Axis%1Min");
    QString maxTpl      ("Axis%1Max");
    QString trimTpl     ("Axis%1Trim");
    QString revTpl      ("Axis%1Rev");
239
    QString deadbndTpl  ("Axis%1Deadbnd");
240

241
    for (int axis = 0; axis < _axisCount; axis++) {
242
        Calibration_t* calibration = &_rgCalibration[axis];
243

244 245
        calibration->center = settings.value(trimTpl.arg(axis), 0).toInt(&convertOk);
        badSettings |= !convertOk;
246

247 248
        calibration->min = settings.value(minTpl.arg(axis), -32768).toInt(&convertOk);
        badSettings |= !convertOk;
249

250
        calibration->max = settings.value(maxTpl.arg(axis), 32767).toInt(&convertOk);
251
        badSettings |= !convertOk;
252

253 254 255
        calibration->deadband = settings.value(deadbndTpl.arg(axis), 0).toInt(&convertOk);
        badSettings |= !convertOk;

256
        calibration->reversed = settings.value(revTpl.arg(axis), false).toBool();
257

258
        qCDebug(JoystickLog) << "_loadSettings axis:min:max:trim:reversed:deadband:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings;
259
    }
260

Gus Grubba's avatar
Gus Grubba committed
261
    int workingAxis = 0;
Gus Grubba's avatar
Gus Grubba committed
262
    for (int function = 0; function < maxFunction; function++) {
263 264
        int functionAxis;
        functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk);
Gus Grubba's avatar
Gus Grubba committed
265 266 267 268
        badSettings |= !convertOk || (functionAxis >= _axisCount);
        if(functionAxis >= 0) {
            workingAxis++;
        }
Gus Grubba's avatar
Gus Grubba committed
269 270 271
        if(functionAxis < _axisCount) {
            _rgFunctionAxis[function] = functionAxis;
        }
272 273
        qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings;
    }
Gus Grubba's avatar
Gus Grubba committed
274
    badSettings |= workingAxis < 4;
275

276 277 278 279
    // FunctionAxis mappings are always stored in TX mode 2
    // Remap to stored TX mode in settings
    _remapAxes(2, _transmitterMode, _rgFunctionAxis);

Gus Grubba's avatar
Gus Grubba committed
280 281
    for (int button = 0; button < _totalButtonCount; button++) {
        QString a = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString();
Gus Grubba's avatar
Gus Grubba committed
282
        if(!a.isEmpty() && _findAssignableButtonAction(a) >= 0 && a != _buttonActionNone) {
Gus Grubba's avatar
Gus Grubba committed
283 284 285
            if(_buttonActionArray[button]) {
                _buttonActionArray[button]->deleteLater();
            }
Gus Grubba's avatar
Gus Grubba committed
286
            AssignedButtonAction* ap = new AssignedButtonAction(this, a);
Gus Grubba's avatar
Gus Grubba committed
287
            ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool();
Gus Grubba's avatar
Gus Grubba committed
288
            _buttonActionArray[button] = ap;
Gus Grubba's avatar
Gus Grubba committed
289 290
            _buttonActionArray[button]->buttonTime.start();
            qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
Gus Grubba's avatar
Gus Grubba committed
291
        }
292
    }
293

294 295 296 297 298 299
    if (badSettings) {
        _calibrated = false;
        settings.setValue(_calibratedSettingsKey, false);
    }
}

Gus Grubba's avatar
Gus Grubba committed
300
void Joystick::_saveButtonSettings()
301 302
{
    QSettings settings;
Gus Grubba's avatar
Gus Grubba committed
303 304 305 306 307 308
    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);
Gus Grubba's avatar
Gus Grubba committed
309
            qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button <<  _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
Gus Grubba's avatar
Gus Grubba committed
310 311 312
        }
    }
}
313

Gus Grubba's avatar
Gus Grubba committed
314 315 316
void Joystick::_saveSettings()
{
    QSettings settings;
317
    settings.beginGroup(_settingsGroup);
318 319 320

    // Transmitter mode is static
    // Save the mode we are using
321
    if(_txModeSettingsKey)
Gus Grubba's avatar
Gus Grubba committed
322
        settings.setValue(_txModeSettingsKey,       _transmitterMode);
323

324
    settings.beginGroup(_name);
Gus Grubba's avatar
Gus Grubba committed
325 326 327 328 329 330 331 332
    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);
333
    settings.setValue(_circleCorrectionSettingsKey, _circleCorrection);
334

335
    qCDebug(JoystickLog) << "_saveSettings calibrated:throttlemode:deadband:txmode" << _calibrated << _throttleMode << _deadband << _circleCorrection << _transmitterMode;
336

337 338 339 340
    QString minTpl      ("Axis%1Min");
    QString maxTpl      ("Axis%1Max");
    QString trimTpl     ("Axis%1Trim");
    QString revTpl      ("Axis%1Rev");
341
    QString deadbndTpl  ("Axis%1Deadbnd");
342

343
    for (int axis = 0; axis < _axisCount; axis++) {
344 345 346 347 348
        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);
349
        settings.setValue(deadbndTpl.arg(axis), calibration->deadband);
350
        qCDebug(JoystickLog) << "_saveSettings name:axis:min:max:trim:reversed:deadband"
351 352 353 354 355
                                << _name
                                << axis
                                << calibration->min
                                << calibration->max
                                << calibration->center
356 357
                                << calibration->reversed
                                << calibration->deadband;
358
    }
359

360 361 362 363
    // 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);
364
    for (int function = 0; function < maxFunction; function++) {
365
        settings.setValue(_rgFunctionSettingsKey[function], temp[function]);
366 367
        qCDebug(JoystickLog) << "_saveSettings name:function:axis" << _name << function << _rgFunctionSettingsKey[function];
    }
Gus Grubba's avatar
Gus Grubba committed
368
    _saveButtonSettings();
369 370
}

371 372
// Relative mappings of axis functions between different TX modes
int Joystick::_mapFunctionMode(int mode, int function) {
373 374 375 376 377
    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 }};
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    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;
    }
}

402
/// Adjust the raw axis value to the -1:1 range given calibration information
403
float Joystick::_adjustRange(int value, Calibration_t calibration, bool withDeadbands)
404 405 406 407
{
    float valueNormalized;
    float axisLength;
    float axisBasis;
408

409 410 411 412 413 414 415 416 417
    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;
    }
418

419 420
    float axisPercent;

421
    if (withDeadbands) {
422 423 424 425 426 427 428
        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;
        }
429 430 431
    }
    else {
        axisPercent = valueNormalized / axisLength;
432
    }
433

434
    float correctedValue = axisBasis * axisPercent;
435

436 437 438
    if (calibration.reversed) {
        correctedValue *= -1.0f;
    }
439

440
#if 0
441
    qCDebug(JoystickLog) << "_adjustRange corrected:value:min:max:center:reversed:deadband:basis:normalized:length"
442 443 444 445 446
                            << correctedValue
                            << value
                            << calibration.min
                            << calibration.max
                            << calibration.center
447
                            << calibration.reversed
448
                            << calibration.deadband
449 450 451 452 453
                            << axisBasis
                            << valueNormalized
                            << axisLength;
#endif

454
    return std::max(-1.0f, std::min(correctedValue, 1.0f));
455 456 457
}


Gus Grubba's avatar
Gus Grubba committed
458
void Joystick::run()
459
{
Gus Grubba's avatar
Gus Grubba committed
460
    //-- Joystick thread
Gregory Dymarek's avatar
Gregory Dymarek committed
461
    _open();
Gus Grubba's avatar
Gus Grubba committed
462 463 464 465 466 467 468
    //-- Reset timers
    _axisTime.start();
    for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
        if(_buttonActionArray[buttonIndex]) {
            _buttonActionArray[buttonIndex]->buttonTime.start();
        }
    }
469
    while (!_exitThread) {
Gus Grubba's avatar
Gus Grubba committed
470 471 472 473 474 475 476
        _update();
        _handleButtons();
        _handleAxis();
        QGC::SLEEP::msleep(20);
    }
    _close();
}
477

Gus Grubba's avatar
Gus Grubba committed
478 479
void Joystick::_handleButtons()
{
480
    int lastBbuttonValues[256];
Gus Grubba's avatar
Gus Grubba committed
481 482 483
    //-- Update button states
    for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
        bool newButtonValue = _getButton(buttonIndex);
484 485
        if(buttonIndex < 256)
            lastBbuttonValues[buttonIndex] = _rgButtonValues[buttonIndex];
Gus Grubba's avatar
Gus Grubba committed
486 487 488 489 490 491
        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);
492
        }
Gus Grubba's avatar
Gus Grubba committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    }
    //-- 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);
508 509
            }
        }
Gus Grubba's avatar
Gus Grubba committed
510
    }
511
    //-- Process button press/release
Gus Grubba's avatar
Gus Grubba committed
512
    for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
Gus Grubba's avatar
Gus Grubba committed
513
        if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) {
Gus Grubba's avatar
Gus Grubba committed
514
            if(_buttonActionArray[buttonIndex]) {
Gus Grubba's avatar
Gus Grubba committed
515 516 517 518 519 520 521
                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) {
Gus Grubba's avatar
Gus Grubba committed
522
                        qCDebug(JoystickLog) << "Single button triggered" << buttonIndex << buttonAction;
523
                        _executeButtonAction(buttonAction, true);
Gus Grubba's avatar
Gus Grubba committed
524 525 526
                    }
                } else {
                    //-- Process repeat buttons
Gus Grubba's avatar
Gus Grubba committed
527
                    int buttonDelay = static_cast<int>(1000.0f / _buttonFrequency);
Gus Grubba's avatar
Gus Grubba committed
528 529
                    if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
                        _buttonActionArray[buttonIndex]->buttonTime.start();
Gus Grubba's avatar
Gus Grubba committed
530
                        qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
531
                        _executeButtonAction(buttonAction, true);
Gus Grubba's avatar
Gus Grubba committed
532
                    }
533 534
                }
            }
Gus Grubba's avatar
Gus Grubba committed
535 536
            //-- Flag it as processed
            _rgButtonValues[buttonIndex] = BUTTON_REPEAT;
537 538 539 540 541 542 543 544 545 546 547 548 549
        } 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);
                    }
                }
            }
550
        }
Gus Grubba's avatar
Gus Grubba committed
551 552
    }
}
553

Gus Grubba's avatar
Gus Grubba committed
554 555 556
void Joystick::_handleAxis()
{
    //-- Get frequency
Gus Grubba's avatar
Gus Grubba committed
557
    int axisDelay = static_cast<int>(1000.0f / _axisFrequency);
Gus Grubba's avatar
Gus Grubba committed
558 559 560 561 562 563 564 565 566 567
    //-- 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);
        }
568
        if (_activeVehicle->joystickEnabled() && !_calibrationMode && _calibrated) {
569
            int     axis = _rgFunctionAxis[rollFunction];
Gus Grubba's avatar
Gus Grubba committed
570
            float   roll = _adjustRange(_rgAxisValues[axis],    _rgCalibration[axis], _deadband);
571

572
                    axis = _rgFunctionAxis[pitchFunction];
Gus Grubba's avatar
Gus Grubba committed
573
            float   pitch = _adjustRange(_rgAxisValues[axis],   _rgCalibration[axis], _deadband);
574

575
                    axis = _rgFunctionAxis[yawFunction];
Gus Grubba's avatar
Gus Grubba committed
576
            float   yaw = _adjustRange(_rgAxisValues[axis],     _rgCalibration[axis],_deadband);
577

578
                    axis = _rgFunctionAxis[throttleFunction];
Gus Grubba's avatar
Gus Grubba committed
579
            float   throttle = _adjustRange(_rgAxisValues[axis],_rgCalibration[axis], _throttleMode==ThrottleModeDownZero?false:_deadband);
580

Gus Grubba's avatar
Gus Grubba committed
581 582
            float   gimbalPitch = 0.0f;
            float   gimbalYaw   = 0.0f;
Gus Grubba's avatar
Gus Grubba committed
583

Gus Grubba's avatar
Gus Grubba committed
584 585 586 587 588 589 590 591 592
            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);
            }
Gus Grubba's avatar
Gus Grubba committed
593

Gus Grubba's avatar
Gus Grubba committed
594
            if (_accumulator) {
595
                static float throttle_accu = 0.f;
Gus Grubba's avatar
Gus Grubba committed
596
                throttle_accu += throttle * (40 / 1000.f); //for throttle to change from min to max it will take 1000ms (40ms is a loop time)
597 598 599 600
                throttle_accu = std::max(static_cast<float>(-1.f), std::min(throttle_accu, static_cast<float>(1.f)));
                throttle = throttle_accu;
            }

Gus Grubba's avatar
Gus Grubba committed
601
            if (_circleCorrection) {
Gus Grubba's avatar
Gus Grubba committed
602 603 604 605
                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)));
606 607

                // Map from unit circle to linear range and limit
Gus Grubba's avatar
Gus Grubba committed
608 609 610
                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));
611 612 613
                throttle =  std::max(-1.0f, std::min(tanf(asinf(throttle_limited)), 1.0f));
            }

Gus Grubba's avatar
Gus Grubba committed
614
            if ( _exponential < -0.01f) {
615
                // Exponential (0% to -50% range like most RC radios)
Gus Grubba's avatar
Gus Grubba committed
616
                // _exponential is set by a slider in joystickConfigAdvanced.qml
617
                // Calculate new RPY with exponential applied
Gus Grubba's avatar
Gus Grubba committed
618
                roll =      -_exponential*powf(roll, 3) + (1+_exponential)*roll;
619
                pitch =     -_exponential*powf(pitch,3) + (1+_exponential)*pitch;
Gus Grubba's avatar
Gus Grubba committed
620
                yaw =       -_exponential*powf(yaw,  3) + (1+_exponential)*yaw;
621
            }
622

623
            // Adjust throttle to 0:1 range
624
            if (_throttleMode == ThrottleModeCenterZero && _activeVehicle->supportsThrottleModeCenterZero()) {
625
                if (!_activeVehicle->supportsNegativeThrust() || !_negativeThrust) {
626 627
                    throttle = std::max(0.0f, throttle);
                }
628
            } else {
629 630
                throttle = (throttle + 1.0f) / 2.0f;
            }
Gus Grubba's avatar
Gus Grubba committed
631 632
            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)
Gus Grubba's avatar
Gus Grubba committed
633 634 635
            // Set up button bitmap
            quint64 buttonPressedBits = 0;  // Buttons pressed for manualControl signal
            for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
Gus Grubba's avatar
Gus Grubba committed
636
                quint64 buttonBit = static_cast<quint64>(1LL << buttonIndex);
Gus Grubba's avatar
Gus Grubba committed
637
                if (_rgButtonValues[buttonIndex] != BUTTON_UP) {
638
                    // Mark the button as pressed as long as its pressed
639
                    buttonPressedBits |= buttonBit;
640 641
                }
            }
Gus Grubba's avatar
Gus Grubba committed
642 643 644 645 646 647 648
            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);
            }
649 650 651 652
        }
    }
}

653
void Joystick::startPolling(Vehicle* vehicle)
654
{
655 656 657 658 659
    if (vehicle) {
        // If a vehicle is connected, disconnect it
        if (_activeVehicle) {
            UAS* uas = _activeVehicle->uas();
            disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
Gus Grubba's avatar
Gus Grubba committed
660 661 662 663 664 665
            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);
666
            disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
667
        }
668
        // Always set up the new vehicle
669
        _activeVehicle = vehicle;
670 671 672 673
        // If joystick is not calibrated, disable it
        if ( !_calibrated ) {
            vehicle->setJoystickEnabled(false);
        }
Jacob Walser's avatar
Jacob Walser committed
674 675
        // Update qml in case of joystick transition
        emit calibratedChanged(_calibrated);
676 677
        // Build action list
        _buildActionList(vehicle);
678 679 680 681 682
        // 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);
Gus Grubba's avatar
Gus Grubba committed
683 684 685 686 687 688
            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);
689
            connect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
690 691 692 693 694
            // FIXME: ****
            //connect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
        }
    }
    if (!isRunning()) {
695 696
        _exitThread = false;
        start();
697 698 699 700 701
    }
}

void Joystick::stopPolling(void)
{
702
    if (isRunning()) {
703 704
        if (_activeVehicle && _activeVehicle->joystickEnabled()) {
            UAS* uas = _activeVehicle->uas();
Jacob Walser's avatar
Jacob Walser committed
705 706
            // Neutral attitude controls
            // emit manualControl(0, 0, 0, 0.5, 0, _activeVehicle->joystickMode());
Gus Grubba's avatar
Gus Grubba committed
707 708 709 710 711 712 713
            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);
714
            disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
715
        }
Don Gagne's avatar
Don Gagne committed
716 717
        // FIXME: ****
        //disconnect(this, &Joystick::buttonActionTriggered,  uas, &UAS::triggerAction);
718
        _exitThread = true;
Jacob Walser's avatar
Jacob Walser committed
719
    }
720 721 722 723
}

void Joystick::setCalibration(int axis, Calibration_t& calibration)
{
724
    if (!_validAxis(axis)) {
725 726 727 728 729 730 731 732 733 734
        return;
    }
    _calibrated = true;
    _rgCalibration[axis] = calibration;
    _saveSettings();
    emit calibratedChanged(_calibrated);
}

Joystick::Calibration_t Joystick::getCalibration(int axis)
{
735
    if (!_validAxis(axis)) {
Gus Grubba's avatar
Gus Grubba committed
736
        return Calibration_t();
737 738 739 740 741 742
    }
    return _rgCalibration[axis];
}

void Joystick::setFunctionAxis(AxisFunction_t function, int axis)
{
743
    if (!_validAxis(axis)) {
744 745 746 747 748 749 750 751 752 753
        return;
    }
    _calibrated = true;
    _rgFunctionAxis[function] = axis;
    _saveSettings();
    emit calibratedChanged(_calibrated);
}

int Joystick::getFunctionAxis(AxisFunction_t function)
{
Gus Grubba's avatar
Gus Grubba committed
754
    if (static_cast<int>(function) < 0 || function >= maxFunction) {
755 756 757 758 759
        qCWarning(JoystickLog) << "Invalid function" << function;
    }
    return _rgFunctionAxis[function];
}

Gus Grubba's avatar
Gus Grubba committed
760
void Joystick::setButtonRepeat(int button, bool repeat)
761
{
Gus Grubba's avatar
Gus Grubba committed
762 763 764 765
    if (!_validButton(button) || !_buttonActionArray[button]) {
        return;
    }
    _buttonActionArray[button]->repeat = repeat;
Gus Grubba's avatar
Gus Grubba committed
766 767 768 769 770 771
    _buttonActionArray[button]->buttonTime.start();
    //-- Save to settings
    QSettings settings;
    settings.beginGroup(_settingsGroup);
    settings.beginGroup(_name);
    settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
Gus Grubba's avatar
Gus Grubba committed
772 773
}

Gus Grubba's avatar
Gus Grubba committed
774
bool Joystick::getButtonRepeat(int button)
Gus Grubba's avatar
Gus Grubba committed
775 776
{
    if (!_validButton(button) || !_buttonActionArray[button]) {
Gus Grubba's avatar
Gus Grubba committed
777
        return false;
Gus Grubba's avatar
Gus Grubba committed
778
    }
Gus Grubba's avatar
Gus Grubba committed
779
    return _buttonActionArray[button]->repeat;
780 781
}

Don Gagne's avatar
Don Gagne committed
782
void Joystick::setButtonAction(int button, const QString& action)
783
{
784
    if (!_validButton(button)) {
785 786
        return;
    }
Gus Grubba's avatar
Gus Grubba committed
787
    qCWarning(JoystickLog) << "setButtonAction:" << button << action;
Gus Grubba's avatar
Gus Grubba committed
788 789 790
    QSettings settings;
    settings.beginGroup(_settingsGroup);
    settings.beginGroup(_name);
Gus Grubba's avatar
Gus Grubba committed
791 792 793 794
    if(action.isEmpty() || action == _buttonActionNone) {
        if(_buttonActionArray[button]) {
            _buttonActionArray[button]->deleteLater();
            _buttonActionArray[button] = nullptr;
Gus Grubba's avatar
Gus Grubba committed
795 796 797
            //-- Clear from settings
            settings.remove(QString(_buttonActionNameKey).arg(button));
            settings.remove(QString(_buttonActionRepeatKey).arg(button));
Gus Grubba's avatar
Gus Grubba committed
798
        }
Gus Grubba's avatar
Gus Grubba committed
799 800
    } else {
        if(!_buttonActionArray[button]) {
Gus Grubba's avatar
Gus Grubba committed
801 802 803 804 805 806 807 808 809 810 811
            _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;
                }
            }
Gus Grubba's avatar
Gus Grubba committed
812
        }
Gus Grubba's avatar
Gus Grubba committed
813 814 815
        //-- Save to settings
        settings.setValue(QString(_buttonActionNameKey).arg(button),   _buttonActionArray[button]->action);
        settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
Gus Grubba's avatar
Gus Grubba committed
816
    }
Gus Grubba's avatar
Gus Grubba committed
817
    emit buttonActionsChanged();
818 819
}

Don Gagne's avatar
Don Gagne committed
820
QString Joystick::getButtonAction(int button)
821
{
Gus Grubba's avatar
Gus Grubba committed
822 823 824 825
    if (_validButton(button)) {
        if(_buttonActionArray[button]) {
            return _buttonActionArray[button]->action;
        }
Gus Grubba's avatar
Gus Grubba committed
826
    }
Gus Grubba's avatar
Gus Grubba committed
827
    return QString(_buttonActionNone);
828 829
}

Gus Grubba's avatar
Gus Grubba committed
830
QStringList Joystick::buttonActions()
831
{
Gus Grubba's avatar
Gus Grubba committed
832 833 834
    QStringList list;
    for (int button = 0; button < _totalButtonCount; button++) {
        list << getButtonAction(button);
835 836 837 838
    }
    return list;
}

Gus Grubba's avatar
Gus Grubba committed
839
int Joystick::throttleMode()
840 841 842 843 844 845 846 847 848 849
{
    return _throttleMode;
}

void Joystick::setThrottleMode(int mode)
{
    if (mode < 0 || mode >= ThrottleModeMax) {
        qCWarning(JoystickLog) << "Invalid throttle mode" << mode;
        return;
    }
Gus Grubba's avatar
Gus Grubba committed
850
    _throttleMode = static_cast<ThrottleMode_t>(mode);
851 852 853
    if (_throttleMode == ThrottleModeDownZero) {
        setAccumulator(false);
    }
854 855 856 857
    _saveSettings();
    emit throttleModeChanged(_throttleMode);
}

Gus Grubba's avatar
Gus Grubba committed
858
bool Joystick::negativeThrust()
859 860 861 862 863 864 865 866 867 868 869 870 871 872
{
    return _negativeThrust;
}

void Joystick::setNegativeThrust(bool allowNegative)
{
    if (_negativeThrust == allowNegative) {
        return;
    }
    _negativeThrust = allowNegative;
    _saveSettings();
    emit negativeThrustChanged(_negativeThrust);
}

Gus Grubba's avatar
Gus Grubba committed
873
float Joystick::exponential()
874 875 876 877
{
    return _exponential;
}

878
void Joystick::setExponential(float expo)
879 880 881 882 883 884
{
    _exponential = expo;
    _saveSettings();
    emit exponentialChanged(_exponential);
}

Gus Grubba's avatar
Gus Grubba committed
885
bool Joystick::accumulator()
886 887 888 889 890 891 892 893 894 895 896
{
    return _accumulator;
}

void Joystick::setAccumulator(bool accu)
{
    _accumulator = accu;
    _saveSettings();
    emit accumulatorChanged(_accumulator);
}

Gus Grubba's avatar
Gus Grubba committed
897
bool Joystick::deadband()
Gregory Dymarek's avatar
Gregory Dymarek committed
898 899 900 901 902 903 904 905 906 907
{
    return _deadband;
}

void Joystick::setDeadband(bool deadband)
{
    _deadband = deadband;
    _saveSettings();
}

Gus Grubba's avatar
Gus Grubba committed
908
bool Joystick::circleCorrection()
909 910 911 912 913 914 915 916 917 918 919
{
    return _circleCorrection;
}

void Joystick::setCircleCorrection(bool circleCorrection)
{
    _circleCorrection = circleCorrection;
    _saveSettings();
    emit circleCorrectionChanged(_circleCorrection);
}

Gus Grubba's avatar
Gus Grubba committed
920 921 922 923 924 925 926
void Joystick::setGimbalEnabled(bool set)
{
    _gimbalEnabled = set;
    _saveSettings();
    emit gimbalEnabledChanged();
}

Gus Grubba's avatar
Gus Grubba committed
927 928 929 930 931 932 933 934 935 936 937
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)
938 939
{
    //-- Arbitrary limits
Gus Grubba's avatar
Gus Grubba committed
940 941
    if(val < 0.25f) val = 0.25f;
    if(val > 50.0f) val = 50.0f;
Gus Grubba's avatar
Gus Grubba committed
942
    _buttonFrequency = val;
943
    _saveSettings();
Gus Grubba's avatar
Gus Grubba committed
944
    emit buttonFrequencyChanged();
945 946
}

947
void Joystick::setCalibrationMode(bool calibrating)
948
{
949 950
    _calibrationMode = calibrating;
    if (calibrating && !isRunning()) {
951
        _pollingStartedForCalibration = true;
952
        startPolling(_multiVehicleManager->activeVehicle());
953
    }
954 955
    else if (_pollingStartedForCalibration) {
        stopPolling();
Don Gagne's avatar
Don Gagne committed
956
    }
957 958
}

959

960
void Joystick::_executeButtonAction(const QString& action, bool buttonDown)
Don Gagne's avatar
Don Gagne committed
961
{
Gus Grubba's avatar
Gus Grubba committed
962
    if (!_activeVehicle || !_activeVehicle->joystickEnabled() || action == _buttonActionNone) {
963 964
        return;
    }
965
    if (action == _buttonActionArm) {
966
        if (buttonDown) emit setArmed(true);
967
    } else if (action == _buttonActionDisarm) {
968
        if (buttonDown) emit setArmed(false);
Gus Grubba's avatar
Gus Grubba committed
969
    } else if (action == _buttonActionToggleArm) {
970
        if (buttonDown) emit setArmed(!_activeVehicle->armed());
971
    } else if (action == _buttonActionVTOLFixedWing) {
972
        if (buttonDown) emit setVtolInFwdFlight(true);
973
    } else if (action == _buttonActionVTOLMultiRotor) {
974
        if (buttonDown) emit setVtolInFwdFlight(false);
975
    } else if (_activeVehicle->flightModes().contains(action)) {
976
        if (buttonDown) emit setFlightMode(action);
Gus Grubba's avatar
Gus Grubba committed
977
    } else if(action == _buttonActionContinuousZoomIn || action == _buttonActionContinuousZoomOut) {
978 979 980 981 982
        if (buttonDown) {
            emit startContinuousZoom(action == _buttonActionContinuousZoomIn ? 1 : -1);
        } else {
            emit stopContinuousZoom();
        }
Gus Grubba's avatar
Gus Grubba committed
983
    } else if(action == _buttonActionStepZoomIn || action == _buttonActionStepZoomOut) {
984
        if (buttonDown) emit stepZoom(action == _buttonActionStepZoomIn ? 1 : -1);
985
    } else if(action == _buttonActionNextStream || action == _buttonActionPreviousStream) {
986
        if (buttonDown) emit stepStream(action == _buttonActionNextStream ? 1 : -1);
987
    } else if(action == _buttonActionNextCamera || action == _buttonActionPreviousCamera) {
988
        if (buttonDown) emit stepCamera(action == _buttonActionNextCamera ? 1 : -1);
Gus Grubba's avatar
Gus Grubba committed
989
    } else if(action == _buttonActionTriggerCamera) {
990
        if (buttonDown) emit triggerCamera();
Gus Grubba's avatar
Gus Grubba committed
991
    } else if(action == _buttonActionStartVideoRecord) {
992
        if (buttonDown) emit startVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
993
    } else if(action == _buttonActionStopVideoRecord) {
994
        if (buttonDown) emit stopVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
995
    } else if(action == _buttonActionToggleVideoRecord) {
996
        if (buttonDown) emit toggleVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
997
    } else if(action == _buttonActionGimbalUp) {
998
        if (buttonDown) _pitchStep(1);
Gus Grubba's avatar
Gus Grubba committed
999
    } else if(action == _buttonActionGimbalDown) {
1000
        if (buttonDown) _pitchStep(-1);
Gus Grubba's avatar
Gus Grubba committed
1001
    } else if(action == _buttonActionGimbalLeft) {
1002
        if (buttonDown) _yawStep(-1);
Gus Grubba's avatar
Gus Grubba committed
1003
    } else if(action == _buttonActionGimbalRight) {
1004
        if (buttonDown) _yawStep(1);
Gus Grubba's avatar
Gus Grubba committed
1005
    } else if(action == _buttonActionGimbalCenter) {
1006 1007 1008 1009 1010
        if (buttonDown) {
            _localPitch = 0.0;
            _localYaw   = 0.0;
            emit gimbalControlValue(0.0, 0.0);
        }
Don Gagne's avatar
Don Gagne committed
1011 1012 1013 1014 1015
    } else {
        qCDebug(JoystickLog) << "_buttonAction unknown action:" << action;
    }
}

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
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);
}

1033 1034
bool Joystick::_validAxis(int axis)
{
Gus Grubba's avatar
Gus Grubba committed
1035 1036 1037 1038 1039
    if(axis >= 0 && axis < _axisCount) {
        return true;
    }
    qCWarning(JoystickLog) << "Invalid axis index" << axis;
    return false;
1040 1041 1042 1043
}

bool Joystick::_validButton(int button)
{
Gus Grubba's avatar
Gus Grubba committed
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    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;
1058 1059
}

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();
}