Joystick.cc 43.6 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
    }
Gus Grubba's avatar
Gus Grubba committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    //-- Available Actions
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNone));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionArm));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionDisarm));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleArm));
    if (_activeVehicle) {
        QStringList list = _activeVehicle->flightModes();
        foreach(auto mode, list) {
            _assignableButtonActions.append(new AssignableButtonAction(this, mode));
        }
    }
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLFixedWing));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLMultiRotor));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomIn, true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomOut, true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomIn,  true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomOut, true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextStream));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousStream));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextCamera));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousCamera));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionTriggerCamera));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStartVideoRecord));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStopVideoRecord));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleVideoRecord));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalDown,    true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalUp,      true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalLeft,    true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalRight,   true));
    _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalCenter));
    for(int i = 0; i < _assignableButtonActions.count(); i++) {
        AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[i]);
        _availableActionTitles << p->action();
    }
Gus Grubba's avatar
Gus Grubba committed
145 146 147
    _updateTXModeSettingsKey(_multiVehicleManager->activeVehicle());
    _loadSettings();
    connect(_multiVehicleManager, &MultiVehicleManager::activeVehicleChanged, this, &Joystick::_activeVehicleChanged);
148 149 150 151
}

Joystick::~Joystick()
{
152 153 154
    // Crash out of the thread if it is still running
    terminate();
    wait();
Jacob Walser's avatar
Jacob Walser committed
155 156 157
    delete[] _rgAxisValues;
    delete[] _rgCalibration;
    delete[] _rgButtonValues;
Gus Grubba's avatar
Gus Grubba committed
158
    _assignableButtonActions.deleteListAndContents();
Gus Grubba's avatar
Gus Grubba committed
159 160 161 162 163
    for (int button = 0; button < _totalButtonCount; button++) {
        if(_buttonActionArray[button]) {
            _buttonActionArray[button]->deleteLater();
        }
    }
164 165
}

166
void Joystick::_setDefaultCalibration(void) {
Gus Grubba's avatar
Gus Grubba committed
167
    QSettings settings;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    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;

183 184 185 186 187
    // Default TX Mode 2 axis assignments for gamecontrollers
    _rgFunctionAxis[rollFunction]       = 2;
    _rgFunctionAxis[pitchFunction]      = 3;
    _rgFunctionAxis[yawFunction]        = 0;
    _rgFunctionAxis[throttleFunction]   = 1;
188

Gus Grubba's avatar
Gus Grubba committed
189 190 191 192 193 194
    _rgFunctionAxis[gimbalPitchFunction]= 4;
    _rgFunctionAxis[gimbalYawFunction]  = 5;

    _exponential    = 0;
    _accumulator    = false;
    _deadband       = false;
Gus Grubba's avatar
Gus Grubba committed
195 196
    _axisFrequency  = 25.0f;
    _buttonFrequency= 5.0f;
Gus Grubba's avatar
Gus Grubba committed
197 198
    _throttleMode   = ThrottleModeDownZero;
    _calibrated     = true;
199
    _circleCorrection = false;
200 201 202 203

    _saveSettings();
}

204
void Joystick::_updateTXModeSettingsKey(Vehicle* activeVehicle)
205 206 207 208 209 210 211 212 213 214 215 216 217
{
    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 {
218
            _txModeSettingsKey = nullptr;
219 220 221
            qWarning() << "No valid joystick TXmode settings key for selected vehicle";
            return;
        }
222
    } else {
223
        _txModeSettingsKey = nullptr;
224 225
    }
}
226

227 228 229 230
void Joystick::_activeVehicleChanged(Vehicle* activeVehicle)
{
    _updateTXModeSettingsKey(activeVehicle);
    if(activeVehicle) {
231 232 233 234 235 236 237
        QSettings settings;
        settings.beginGroup(_settingsGroup);
        int mode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt();
        setTXMode(mode);
    }
}

Gus Grubba's avatar
Gus Grubba committed
238
void Joystick::_loadSettings()
239
{
240
    QSettings settings;
241
    settings.beginGroup(_settingsGroup);
242 243 244 245
    Vehicle* activeVehicle = _multiVehicleManager->activeVehicle();

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

247
    settings.beginGroup(_name);
248

249 250
    bool badSettings = false;
    bool convertOk;
251

252
    qCDebug(JoystickLog) << "_loadSettings " << _name;
253

254 255 256 257
    _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
258 259
    _axisFrequency  = settings.value(_axisFrequencySettingsKey, 25.0f).toFloat();
    _buttonFrequency= settings.value(_buttonFrequencySettingsKey, 5.0f).toFloat();
260
    _circleCorrection = settings.value(_circleCorrectionSettingsKey, false).toBool();
Gus Grubba's avatar
Gus Grubba committed
261
    _gimbalEnabled  = settings.value(_gimbalSettingsKey, false).toBool();
262

263
    _throttleMode   = static_cast<ThrottleMode_t>(settings.value(_throttleModeSettingsKey, ThrottleModeDownZero).toInt(&convertOk));
264
    badSettings |= !convertOk;
265

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

268 269 270 271
    QString minTpl      ("Axis%1Min");
    QString maxTpl      ("Axis%1Max");
    QString trimTpl     ("Axis%1Trim");
    QString revTpl      ("Axis%1Rev");
272
    QString deadbndTpl  ("Axis%1Deadbnd");
273

274
    for (int axis = 0; axis < _axisCount; axis++) {
275
        Calibration_t* calibration = &_rgCalibration[axis];
276

277 278
        calibration->center = settings.value(trimTpl.arg(axis), 0).toInt(&convertOk);
        badSettings |= !convertOk;
279

280 281
        calibration->min = settings.value(minTpl.arg(axis), -32768).toInt(&convertOk);
        badSettings |= !convertOk;
282

283
        calibration->max = settings.value(maxTpl.arg(axis), 32767).toInt(&convertOk);
284
        badSettings |= !convertOk;
285

286 287 288
        calibration->deadband = settings.value(deadbndTpl.arg(axis), 0).toInt(&convertOk);
        badSettings |= !convertOk;

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

291
        qCDebug(JoystickLog) << "_loadSettings axis:min:max:trim:reversed:deadband:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings;
292
    }
293

Gus Grubba's avatar
Gus Grubba committed
294
    int workingAxis = 0;
Gus Grubba's avatar
Gus Grubba committed
295
    for (int function = 0; function < maxFunction; function++) {
296 297
        int functionAxis;
        functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk);
Gus Grubba's avatar
Gus Grubba committed
298 299 300 301
        badSettings |= !convertOk || (functionAxis >= _axisCount);
        if(functionAxis >= 0) {
            workingAxis++;
        }
Gus Grubba's avatar
Gus Grubba committed
302 303 304
        if(functionAxis < _axisCount) {
            _rgFunctionAxis[function] = functionAxis;
        }
305 306
        qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings;
    }
Gus Grubba's avatar
Gus Grubba committed
307
    badSettings |= workingAxis < 4;
308

309 310 311 312
    // 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
313 314
    for (int button = 0; button < _totalButtonCount; button++) {
        QString a = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString();
Gus Grubba's avatar
Gus Grubba committed
315
        if(!a.isEmpty() && _findAssignableButtonAction(a) >= 0 && a != _buttonActionNone) {
Gus Grubba's avatar
Gus Grubba committed
316 317 318
            if(_buttonActionArray[button]) {
                _buttonActionArray[button]->deleteLater();
            }
Gus Grubba's avatar
Gus Grubba committed
319
            AssignedButtonAction* ap = new AssignedButtonAction(this, a);
Gus Grubba's avatar
Gus Grubba committed
320
            ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool();
Gus Grubba's avatar
Gus Grubba committed
321
            _buttonActionArray[button] = ap;
Gus Grubba's avatar
Gus Grubba committed
322 323
            _buttonActionArray[button]->buttonTime.start();
            qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
Gus Grubba's avatar
Gus Grubba committed
324
        }
325
    }
326

327 328 329 330 331 332
    if (badSettings) {
        _calibrated = false;
        settings.setValue(_calibratedSettingsKey, false);
    }
}

Gus Grubba's avatar
Gus Grubba committed
333
void Joystick::_saveButtonSettings()
334 335
{
    QSettings settings;
Gus Grubba's avatar
Gus Grubba committed
336 337 338 339 340 341
    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
342
            qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button <<  _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
Gus Grubba's avatar
Gus Grubba committed
343 344 345
        }
    }
}
346

Gus Grubba's avatar
Gus Grubba committed
347 348 349
void Joystick::_saveSettings()
{
    QSettings settings;
350
    settings.beginGroup(_settingsGroup);
351 352 353

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

357
    settings.beginGroup(_name);
Gus Grubba's avatar
Gus Grubba committed
358 359 360 361 362 363 364 365
    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);
366
    settings.setValue(_circleCorrectionSettingsKey, _circleCorrection);
367

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

370 371 372 373
    QString minTpl      ("Axis%1Min");
    QString maxTpl      ("Axis%1Max");
    QString trimTpl     ("Axis%1Trim");
    QString revTpl      ("Axis%1Rev");
374
    QString deadbndTpl  ("Axis%1Deadbnd");
375

376
    for (int axis = 0; axis < _axisCount; axis++) {
377 378 379 380 381
        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);
382
        settings.setValue(deadbndTpl.arg(axis), calibration->deadband);
383
        qCDebug(JoystickLog) << "_saveSettings name:axis:min:max:trim:reversed:deadband"
384 385 386 387 388
                                << _name
                                << axis
                                << calibration->min
                                << calibration->max
                                << calibration->center
389 390
                                << calibration->reversed
                                << calibration->deadband;
391
    }
392

393 394 395 396
    // 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);
397
    for (int function = 0; function < maxFunction; function++) {
398
        settings.setValue(_rgFunctionSettingsKey[function], temp[function]);
399 400
        qCDebug(JoystickLog) << "_saveSettings name:function:axis" << _name << function << _rgFunctionSettingsKey[function];
    }
Gus Grubba's avatar
Gus Grubba committed
401
    _saveButtonSettings();
402 403
}

404 405
// Relative mappings of axis functions between different TX modes
int Joystick::_mapFunctionMode(int mode, int function) {
406 407 408 409 410
    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 }};
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    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;
    }
}

435
/// Adjust the raw axis value to the -1:1 range given calibration information
436
float Joystick::_adjustRange(int value, Calibration_t calibration, bool withDeadbands)
437 438 439 440
{
    float valueNormalized;
    float axisLength;
    float axisBasis;
441

442 443 444 445 446 447 448 449 450
    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;
    }
451

452 453
    float axisPercent;

454
    if (withDeadbands) {
455 456 457 458 459 460 461
        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;
        }
462 463 464
    }
    else {
        axisPercent = valueNormalized / axisLength;
465
    }
466

467
    float correctedValue = axisBasis * axisPercent;
468

469 470 471
    if (calibration.reversed) {
        correctedValue *= -1.0f;
    }
472

473
#if 0
474
    qCDebug(JoystickLog) << "_adjustRange corrected:value:min:max:center:reversed:deadband:basis:normalized:length"
475 476 477 478 479
                            << correctedValue
                            << value
                            << calibration.min
                            << calibration.max
                            << calibration.center
480
                            << calibration.reversed
481
                            << calibration.deadband
482 483 484 485 486
                            << axisBasis
                            << valueNormalized
                            << axisLength;
#endif

487
    return std::max(-1.0f, std::min(correctedValue, 1.0f));
488 489 490
}


Gus Grubba's avatar
Gus Grubba committed
491
void Joystick::run()
492
{
Gus Grubba's avatar
Gus Grubba committed
493
    //-- Joystick thread
Gregory Dymarek's avatar
Gregory Dymarek committed
494
    _open();
Gus Grubba's avatar
Gus Grubba committed
495 496 497 498 499 500 501
    //-- Reset timers
    _axisTime.start();
    for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
        if(_buttonActionArray[buttonIndex]) {
            _buttonActionArray[buttonIndex]->buttonTime.start();
        }
    }
502
    while (!_exitThread) {
Gus Grubba's avatar
Gus Grubba committed
503 504 505 506 507 508 509
        _update();
        _handleButtons();
        _handleAxis();
        QGC::SLEEP::msleep(20);
    }
    _close();
}
510

Gus Grubba's avatar
Gus Grubba committed
511 512 513 514 515 516 517 518 519 520 521
void Joystick::_handleButtons()
{
    //-- Update button states
    for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
        bool newButtonValue = _getButton(buttonIndex);
        if (newButtonValue && _rgButtonValues[buttonIndex] == BUTTON_UP) {
            _rgButtonValues[buttonIndex] = BUTTON_DOWN;
            emit rawButtonPressedChanged(buttonIndex, newButtonValue);
        } else if (!newButtonValue && _rgButtonValues[buttonIndex] != BUTTON_UP) {
            _rgButtonValues[buttonIndex] = BUTTON_UP;
            emit rawButtonPressedChanged(buttonIndex, newButtonValue);
522
        }
Gus Grubba's avatar
Gus Grubba committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
    }
    //-- 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);
538 539
            }
        }
Gus Grubba's avatar
Gus Grubba committed
540 541 542
    }
    //-- Process button press
    for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
Gus Grubba's avatar
Gus Grubba committed
543
        if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) {
Gus Grubba's avatar
Gus Grubba committed
544
            if(_buttonActionArray[buttonIndex]) {
Gus Grubba's avatar
Gus Grubba committed
545 546 547 548 549 550 551
                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
552
                        qCDebug(JoystickLog) << "Single button triggered" << buttonIndex << buttonAction;
Gus Grubba's avatar
Gus Grubba committed
553
                        _executeButtonAction(buttonAction);
Gus Grubba's avatar
Gus Grubba committed
554 555 556
                    }
                } else {
                    //-- Process repeat buttons
Gus Grubba's avatar
Gus Grubba committed
557
                    int buttonDelay = static_cast<int>(1000.0f / _buttonFrequency);
Gus Grubba's avatar
Gus Grubba committed
558 559
                    if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
                        _buttonActionArray[buttonIndex]->buttonTime.start();
Gus Grubba's avatar
Gus Grubba committed
560 561
                        qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
                        _executeButtonAction(buttonAction);
Gus Grubba's avatar
Gus Grubba committed
562
                    }
563 564
                }
            }
Gus Grubba's avatar
Gus Grubba committed
565 566
            //-- Flag it as processed
            _rgButtonValues[buttonIndex] = BUTTON_REPEAT;
567
        }
Gus Grubba's avatar
Gus Grubba committed
568 569
    }
}
570

Gus Grubba's avatar
Gus Grubba committed
571 572 573
void Joystick::_handleAxis()
{
    //-- Get frequency
Gus Grubba's avatar
Gus Grubba committed
574
    int axisDelay = static_cast<int>(1000.0f / _axisFrequency);
Gus Grubba's avatar
Gus Grubba committed
575 576 577 578 579 580 581 582 583 584
    //-- 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);
        }
585
        if (_activeVehicle->joystickEnabled() && !_calibrationMode && _calibrated) {
586
            int     axis = _rgFunctionAxis[rollFunction];
Gus Grubba's avatar
Gus Grubba committed
587
            float   roll = _adjustRange(_rgAxisValues[axis],    _rgCalibration[axis], _deadband);
588

589
                    axis = _rgFunctionAxis[pitchFunction];
Gus Grubba's avatar
Gus Grubba committed
590
            float   pitch = _adjustRange(_rgAxisValues[axis],   _rgCalibration[axis], _deadband);
591

592
                    axis = _rgFunctionAxis[yawFunction];
Gus Grubba's avatar
Gus Grubba committed
593
            float   yaw = _adjustRange(_rgAxisValues[axis],     _rgCalibration[axis],_deadband);
594

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

Gus Grubba's avatar
Gus Grubba committed
598 599
            float   gimbalPitch = 0.0f;
            float   gimbalYaw   = 0.0f;
Gus Grubba's avatar
Gus Grubba committed
600

Gus Grubba's avatar
Gus Grubba committed
601 602 603 604 605 606 607 608 609
            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
610

Gus Grubba's avatar
Gus Grubba committed
611
            if (_accumulator) {
612
                static float throttle_accu = 0.f;
Gus Grubba's avatar
Gus Grubba committed
613
                throttle_accu += throttle * (40 / 1000.f); //for throttle to change from min to max it will take 1000ms (40ms is a loop time)
614 615 616 617
                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
618
            if (_circleCorrection) {
Gus Grubba's avatar
Gus Grubba committed
619 620 621 622
                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)));
623 624

                // Map from unit circle to linear range and limit
Gus Grubba's avatar
Gus Grubba committed
625 626 627
                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));
628 629 630
                throttle =  std::max(-1.0f, std::min(tanf(asinf(throttle_limited)), 1.0f));
            }

Gus Grubba's avatar
Gus Grubba committed
631
            if ( _exponential < -0.01f) {
632
                // Exponential (0% to -50% range like most RC radios)
Gus Grubba's avatar
Gus Grubba committed
633
                // _exponential is set by a slider in joystickConfigAdvanced.qml
634
                // Calculate new RPY with exponential applied
Gus Grubba's avatar
Gus Grubba committed
635
                roll =      -_exponential*powf(roll, 3) + (1+_exponential)*roll;
636
                pitch =     -_exponential*powf(pitch,3) + (1+_exponential)*pitch;
Gus Grubba's avatar
Gus Grubba committed
637
                yaw =       -_exponential*powf(yaw,  3) + (1+_exponential)*yaw;
638
            }
639

640
            // Adjust throttle to 0:1 range
641
            if (_throttleMode == ThrottleModeCenterZero && _activeVehicle->supportsThrottleModeCenterZero()) {
642
                if (!_activeVehicle->supportsNegativeThrust() || !_negativeThrust) {
643 644
                    throttle = std::max(0.0f, throttle);
                }
645
            } else {
646 647
                throttle = (throttle + 1.0f) / 2.0f;
            }
Gus Grubba's avatar
Gus Grubba committed
648 649
            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
650 651 652
            // 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
653
                quint64 buttonBit = static_cast<quint64>(1LL << buttonIndex);
Gus Grubba's avatar
Gus Grubba committed
654
                if (_rgButtonValues[buttonIndex] != BUTTON_UP) {
655
                    // Mark the button as pressed as long as its pressed
656
                    buttonPressedBits |= buttonBit;
657 658
                }
            }
Gus Grubba's avatar
Gus Grubba committed
659 660 661 662 663 664 665
            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);
            }
666 667 668 669
        }
    }
}

670
void Joystick::startPolling(Vehicle* vehicle)
671
{
672 673 674 675 676
    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
677 678 679 680 681 682
            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);
683
            disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
684
        }
685
        // Always set up the new vehicle
686
        _activeVehicle = vehicle;
687 688 689 690
        // If joystick is not calibrated, disable it
        if ( !_calibrated ) {
            vehicle->setJoystickEnabled(false);
        }
Jacob Walser's avatar
Jacob Walser committed
691 692
        // Update qml in case of joystick transition
        emit calibratedChanged(_calibrated);
693 694 695 696 697
        // 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
698 699 700 701 702 703
            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);
704
            connect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
705 706 707 708 709
            // FIXME: ****
            //connect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction);
        }
    }
    if (!isRunning()) {
710 711
        _exitThread = false;
        start();
712 713 714 715 716
    }
}

void Joystick::stopPolling(void)
{
717
    if (isRunning()) {
718 719
        if (_activeVehicle && _activeVehicle->joystickEnabled()) {
            UAS* uas = _activeVehicle->uas();
Jacob Walser's avatar
Jacob Walser committed
720 721
            // Neutral attitude controls
            // emit manualControl(0, 0, 0, 0.5, 0, _activeVehicle->joystickMode());
Gus Grubba's avatar
Gus Grubba committed
722 723 724 725 726 727 728
            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);
729
            disconnect(this, &Joystick::gimbalControlValue, _activeVehicle, &Vehicle::gimbalControlValue);
730
        }
Don Gagne's avatar
Don Gagne committed
731 732
        // FIXME: ****
        //disconnect(this, &Joystick::buttonActionTriggered,  uas, &UAS::triggerAction);
733
        _exitThread = true;
Jacob Walser's avatar
Jacob Walser committed
734
    }
735 736 737 738
}

void Joystick::setCalibration(int axis, Calibration_t& calibration)
{
739
    if (!_validAxis(axis)) {
740 741 742 743 744 745 746 747 748 749
        return;
    }
    _calibrated = true;
    _rgCalibration[axis] = calibration;
    _saveSettings();
    emit calibratedChanged(_calibrated);
}

Joystick::Calibration_t Joystick::getCalibration(int axis)
{
750
    if (!_validAxis(axis)) {
Gus Grubba's avatar
Gus Grubba committed
751
        return Calibration_t();
752 753 754 755 756 757
    }
    return _rgCalibration[axis];
}

void Joystick::setFunctionAxis(AxisFunction_t function, int axis)
{
758
    if (!_validAxis(axis)) {
759 760 761 762 763 764 765 766 767 768
        return;
    }
    _calibrated = true;
    _rgFunctionAxis[function] = axis;
    _saveSettings();
    emit calibratedChanged(_calibrated);
}

int Joystick::getFunctionAxis(AxisFunction_t function)
{
Gus Grubba's avatar
Gus Grubba committed
769
    if (static_cast<int>(function) < 0 || function >= maxFunction) {
770 771 772 773 774
        qCWarning(JoystickLog) << "Invalid function" << function;
    }
    return _rgFunctionAxis[function];
}

Gus Grubba's avatar
Gus Grubba committed
775
void Joystick::setButtonRepeat(int button, bool repeat)
776
{
Gus Grubba's avatar
Gus Grubba committed
777 778 779 780
    if (!_validButton(button) || !_buttonActionArray[button]) {
        return;
    }
    _buttonActionArray[button]->repeat = repeat;
Gus Grubba's avatar
Gus Grubba committed
781 782 783 784 785 786
    _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
787 788
}

Gus Grubba's avatar
Gus Grubba committed
789
bool Joystick::getButtonRepeat(int button)
Gus Grubba's avatar
Gus Grubba committed
790 791
{
    if (!_validButton(button) || !_buttonActionArray[button]) {
Gus Grubba's avatar
Gus Grubba committed
792
        return false;
Gus Grubba's avatar
Gus Grubba committed
793
    }
Gus Grubba's avatar
Gus Grubba committed
794
    return _buttonActionArray[button]->repeat;
795 796
}

Don Gagne's avatar
Don Gagne committed
797
void Joystick::setButtonAction(int button, const QString& action)
798
{
799
    if (!_validButton(button)) {
800 801
        return;
    }
Gus Grubba's avatar
Gus Grubba committed
802
    qCWarning(JoystickLog) << "setButtonAction:" << button << action;
Gus Grubba's avatar
Gus Grubba committed
803 804 805
    QSettings settings;
    settings.beginGroup(_settingsGroup);
    settings.beginGroup(_name);
Gus Grubba's avatar
Gus Grubba committed
806 807 808 809
    if(action.isEmpty() || action == _buttonActionNone) {
        if(_buttonActionArray[button]) {
            _buttonActionArray[button]->deleteLater();
            _buttonActionArray[button] = nullptr;
Gus Grubba's avatar
Gus Grubba committed
810 811 812
            //-- Clear from settings
            settings.remove(QString(_buttonActionNameKey).arg(button));
            settings.remove(QString(_buttonActionRepeatKey).arg(button));
Gus Grubba's avatar
Gus Grubba committed
813
        }
Gus Grubba's avatar
Gus Grubba committed
814 815
    } else {
        if(!_buttonActionArray[button]) {
Gus Grubba's avatar
Gus Grubba committed
816 817 818 819 820 821 822 823 824 825 826
            _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
827
        }
Gus Grubba's avatar
Gus Grubba committed
828 829 830
        //-- 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
831
    }
Gus Grubba's avatar
Gus Grubba committed
832
    emit buttonActionsChanged();
833 834
}

Don Gagne's avatar
Don Gagne committed
835
QString Joystick::getButtonAction(int button)
836
{
Gus Grubba's avatar
Gus Grubba committed
837 838 839 840
    if (_validButton(button)) {
        if(_buttonActionArray[button]) {
            return _buttonActionArray[button]->action;
        }
Gus Grubba's avatar
Gus Grubba committed
841
    }
Gus Grubba's avatar
Gus Grubba committed
842
    return QString(_buttonActionNone);
843 844
}

Gus Grubba's avatar
Gus Grubba committed
845
QStringList Joystick::buttonActions()
846
{
Gus Grubba's avatar
Gus Grubba committed
847 848 849
    QStringList list;
    for (int button = 0; button < _totalButtonCount; button++) {
        list << getButtonAction(button);
850 851 852 853
    }
    return list;
}

Gus Grubba's avatar
Gus Grubba committed
854
int Joystick::throttleMode()
855 856 857 858 859 860 861 862 863 864
{
    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
865
    _throttleMode = static_cast<ThrottleMode_t>(mode);
866 867 868
    if (_throttleMode == ThrottleModeDownZero) {
        setAccumulator(false);
    }
869 870 871 872
    _saveSettings();
    emit throttleModeChanged(_throttleMode);
}

Gus Grubba's avatar
Gus Grubba committed
873
bool Joystick::negativeThrust()
874 875 876 877 878 879 880 881 882 883 884 885 886 887
{
    return _negativeThrust;
}

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

Gus Grubba's avatar
Gus Grubba committed
888
float Joystick::exponential()
889 890 891 892
{
    return _exponential;
}

893
void Joystick::setExponential(float expo)
894 895 896 897 898 899
{
    _exponential = expo;
    _saveSettings();
    emit exponentialChanged(_exponential);
}

Gus Grubba's avatar
Gus Grubba committed
900
bool Joystick::accumulator()
901 902 903 904 905 906 907 908 909 910 911
{
    return _accumulator;
}

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

Gus Grubba's avatar
Gus Grubba committed
912
bool Joystick::deadband()
Gregory Dymarek's avatar
Gregory Dymarek committed
913 914 915 916 917 918 919 920 921 922
{
    return _deadband;
}

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

Gus Grubba's avatar
Gus Grubba committed
923
bool Joystick::circleCorrection()
924 925 926 927 928 929 930 931 932 933 934
{
    return _circleCorrection;
}

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

Gus Grubba's avatar
Gus Grubba committed
935 936 937 938 939 940 941
void Joystick::setGimbalEnabled(bool set)
{
    _gimbalEnabled = set;
    _saveSettings();
    emit gimbalEnabledChanged();
}

Gus Grubba's avatar
Gus Grubba committed
942 943 944 945 946 947 948 949 950 951 952
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)
953 954
{
    //-- Arbitrary limits
Gus Grubba's avatar
Gus Grubba committed
955 956
    if(val < 0.25f) val = 0.25f;
    if(val > 50.0f) val = 50.0f;
Gus Grubba's avatar
Gus Grubba committed
957
    _buttonFrequency = val;
958
    _saveSettings();
Gus Grubba's avatar
Gus Grubba committed
959
    emit buttonFrequencyChanged();
960 961
}

962
void Joystick::setCalibrationMode(bool calibrating)
963
{
964 965
    _calibrationMode = calibrating;
    if (calibrating && !isRunning()) {
966
        _pollingStartedForCalibration = true;
967
        startPolling(_multiVehicleManager->activeVehicle());
968
    }
969 970
    else if (_pollingStartedForCalibration) {
        stopPolling();
Don Gagne's avatar
Don Gagne committed
971
    }
972 973
}

974

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

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
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);
}

1042 1043
bool Joystick::_validAxis(int axis)
{
Gus Grubba's avatar
Gus Grubba committed
1044 1045 1046 1047 1048
    if(axis >= 0 && axis < _axisCount) {
        return true;
    }
    qCWarning(JoystickLog) << "Invalid axis index" << axis;
    return false;
1049 1050 1051 1052
}

bool Joystick::_validButton(int button)
{
Gus Grubba's avatar
Gus Grubba committed
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
    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;
1067 1068
}