Joystick.cc 45.5 KB
Newer Older
1
2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4
5
6
7
8
9
 *
 * 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
Done    
Gus Grubba committed
26
const char* Joystick::_calibratedSettingsKey =          "Calibrated4"; // Increment number to force recalibration
Gus Grubba's avatar
WIP    
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
Done    
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
WIP    
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
Done    
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
WIP    
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
Done    
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
Done    
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
{
Gus Grubba's avatar
Gus Grubba committed
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

Gus Grubba's avatar
Gus Grubba committed
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
Done    
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

Gus Grubba's avatar
Gus Grubba committed
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

Gus Grubba's avatar
Gus Grubba committed
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

Gus Grubba's avatar
Gus Grubba committed
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
Done    
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
Done    
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
Done    
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
WIP    
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
WIP    
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
Done    
Gus Grubba committed
287
            ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool();
Gus Grubba's avatar
WIP    
Gus Grubba committed
288
            _buttonActionArray[button] = ap;
Gus Grubba's avatar
Done    
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
WIP    
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
Done    
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
Done    
Gus Grubba committed
322
        settings.setValue(_txModeSettingsKey,       _transmitterMode);
323

324
    settings.beginGroup(_name);
Gus Grubba's avatar
Done    
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);
Gus Grubba's avatar
Gus Grubba committed
333
    settings.setValue(_circleCorrectionSettingsKey, _circleCorrection);
334

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

Gus Grubba's avatar
Gus Grubba committed
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

Gus Grubba's avatar
Gus Grubba committed
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);
Gus Grubba's avatar
Gus Grubba committed
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) {
Gus Grubba's avatar
Gus Grubba committed
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
WIP    
Gus Grubba committed
460
    //-- Joystick thread
Gregory Dymarek's avatar
Gregory Dymarek committed
461
    _open();
Gus Grubba's avatar
WIP    
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
WIP    
Gus Grubba committed
470
471
472
473
474
475
476
        _update();
        _handleButtons();
        _handleAxis();
        QGC::SLEEP::msleep(20);
    }
    _close();
}
477

Gus Grubba's avatar
WIP    
Gus Grubba committed
478
479
void Joystick::_handleButtons()
{
Gus Grubba's avatar
Gus Grubba committed
480
    int lastBbuttonValues[256];
Gus Grubba's avatar
WIP    
Gus Grubba committed
481
482
483
    //-- Update button states
    for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
        bool newButtonValue = _getButton(buttonIndex);
Gus Grubba's avatar
Gus Grubba committed
484
485
        if(buttonIndex < 256)
            lastBbuttonValues[buttonIndex] = _rgButtonValues[buttonIndex];
Gus Grubba's avatar
WIP    
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
WIP    
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
WIP    
Gus Grubba committed
510
    }
Gus Grubba's avatar
Gus Grubba committed
511
    //-- Process button press/release
Gus Grubba's avatar
WIP    
Gus Grubba committed
512
    for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
Gus Grubba's avatar
Done    
Gus Grubba committed
513
        if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) {
Gus Grubba's avatar
WIP    
Gus Grubba committed
514
            if(_buttonActionArray[buttonIndex]) {
Gus Grubba's avatar
Done    
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
WIP    
Gus Grubba committed
522
                        qCDebug(JoystickLog) << "Single button triggered" << buttonIndex << buttonAction;
Gus Grubba's avatar
Gus Grubba committed
523
                        _executeButtonAction(buttonAction, true);
Gus Grubba's avatar
WIP    
Gus Grubba committed
524
525
526
                    }
                } else {
                    //-- Process repeat buttons
Gus Grubba's avatar
Done    
Gus Grubba committed
527
                    int buttonDelay = static_cast<int>(1000.0f / _buttonFrequency);
Gus Grubba's avatar
WIP    
Gus Grubba committed
528
529
                    if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
                        _buttonActionArray[buttonIndex]->buttonTime.start();
Gus Grubba's avatar
Done    
Gus Grubba committed
530
                        qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
Gus Grubba's avatar
Gus Grubba committed
531
                        _executeButtonAction(buttonAction, true);
Gus Grubba's avatar
WIP    
Gus Grubba committed
532
                    }
533
534
                }
            }
Gus Grubba's avatar
Done    
Gus Grubba committed
535
536
            //-- Flag it as processed
            _rgButtonValues[buttonIndex] = BUTTON_REPEAT;
Gus Grubba's avatar
Gus Grubba committed
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
WIP    
Gus Grubba committed
551
552
    }
}
553

Gus Grubba's avatar
WIP    
Gus Grubba committed
554
555
556
void Joystick::_handleAxis()
{
    //-- Get frequency
Gus Grubba's avatar
Done    
Gus Grubba committed
557
    int axisDelay = static_cast<int>(1000.0f / _axisFrequency);
Gus Grubba's avatar
WIP    
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
WIP    
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
        }
    }
}

Don Gagne's avatar
Don Gagne committed
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);
Don Gagne's avatar
Don Gagne committed
667
        }
668
        // Always set up the new vehicle
Don Gagne's avatar
Don Gagne committed
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()) {
Don Gagne's avatar
Don Gagne committed
695
696
        _exitThread = false;
        start();
697
698
699
700
701
    }
}

void Joystick::stopPolling(void)
{
Don Gagne's avatar
Don Gagne committed
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);
Don Gagne's avatar
Don Gagne committed
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
Done    
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
Done    
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
Done    
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
Done    
Gus Grubba committed
777
        return false;
Gus Grubba's avatar
Gus Grubba committed
778
    }
Gus Grubba's avatar
Done    
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
Done    
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
Done    
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
WIP    
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
WIP    
Gus Grubba committed
812
        }
Gus Grubba's avatar
Done    
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
WIP    
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
WIP    
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
WIP    
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
Done    
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
Done    
Gus Grubba committed
942
    _buttonFrequency = val;
943
    _saveSettings();
Gus Grubba's avatar
Done    
Gus Grubba committed
944
    emit buttonFrequencyChanged();
945
946
}

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

959

Gus Grubba's avatar
Gus Grubba committed
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) {
Gus Grubba's avatar
Gus Grubba committed
966
        if (buttonDown) emit setArmed(true);
967
    } else if (action == _buttonActionDisarm) {
Gus Grubba's avatar
Gus Grubba committed
968
        if (buttonDown) emit setArmed(false);
Gus Grubba's avatar
Gus Grubba committed
969
    } else if (action == _buttonActionToggleArm) {
Gus Grubba's avatar
Gus Grubba committed
970
        if (buttonDown) emit setArmed(!_activeVehicle->armed());
971
    } else if (action == _buttonActionVTOLFixedWing) {
Gus Grubba's avatar
Gus Grubba committed
972
        if (buttonDown) emit setVtolInFwdFlight(true);
973
    } else if (action == _buttonActionVTOLMultiRotor) {
Gus Grubba's avatar
Gus Grubba committed
974
        if (buttonDown) emit setVtolInFwdFlight(false);
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
975
    } else if (_activeVehicle->flightModes().contains(action) || _activeVehicle->extraJoystickFlightModes().contains(action)) {
Gus Grubba's avatar
Gus Grubba committed
976
        if (buttonDown) emit setFlightMode(action);
Gus Grubba's avatar
Gus Grubba committed
977
    } else if(action == _buttonActionContinuousZoomIn || action == _buttonActionContinuousZoomOut) {
Gus Grubba's avatar
Gus Grubba committed
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) {
Gus Grubba's avatar
Gus Grubba committed
984
        if (buttonDown) emit stepZoom(action == _buttonActionStepZoomIn ? 1 : -1);
985
    } else if(action == _buttonActionNextStream || action == _buttonActionPreviousStream) {
Gus Grubba's avatar
Gus Grubba committed
986
        if (buttonDown) emit stepStream(action == _buttonActionNextStream ? 1 : -1);
987
    } else if(action == _buttonActionNextCamera || action == _buttonActionPreviousCamera) {
Gus Grubba's avatar
Gus Grubba committed
988
        if (buttonDown) emit stepCamera(action == _buttonActionNextCamera ? 1 : -1);
Gus Grubba's avatar
Gus Grubba committed
989
    } else if(action == _buttonActionTriggerCamera) {
Gus Grubba's avatar
Gus Grubba committed
990
        if (buttonDown) emit triggerCamera();
Gus Grubba's avatar
Gus Grubba committed
991
    } else if(action == _buttonActionStartVideoRecord) {
Gus Grubba's avatar
Gus Grubba committed
992
        if (buttonDown) emit startVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
993
    } else if(action == _buttonActionStopVideoRecord) {
Gus Grubba's avatar
Gus Grubba committed
994
        if (buttonDown) emit stopVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
995
    } else if(action == _buttonActionToggleVideoRecord) {
Gus Grubba's avatar
Gus Grubba committed
996
        if (buttonDown) emit toggleVideoRecord();
Gus Grubba's avatar
Gus Grubba committed
997
    } else if(action == _buttonActionGimbalUp) {
Gus Grubba's avatar
Gus Grubba committed
998
        if (buttonDown) _pitchStep(1);
Gus Grubba's avatar
Gus Grubba committed
999
    } else if(action == _buttonActionGimbalDown) {
Gus Grubba's avatar
Gus Grubba committed
1000
        if (buttonDown) _pitchStep(-1);
Gus Grubba's avatar
Gus Grubba committed
1001
    } else if(action == _buttonActionGimbalLeft) {
Gus Grubba's avatar
Gus Grubba committed
1002
        if (buttonDown) _yawStep(-1);
Gus Grubba's avatar
Gus Grubba committed
1003
    } else if(action == _buttonActionGimbalRight) {
Gus Grubba's avatar
Gus Grubba committed
1004
        if (buttonDown) _yawStep(1);
Gus Grubba's avatar
Gus Grubba committed
1005
    } else if(action == _buttonActionGimbalCenter) {
Gus Grubba's avatar
Gus Grubba committed
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
Done    
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
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));
        }
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
1075
1076
1077
1078
        list = activeVehicle->extraJoystickFlightModes();
        foreach(auto mode, list) {
            _assignableButtonActions.append(new AssignableButtonAction(this, mode));
        }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
    }
    _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();
}