JoystickConfigController.cc 34 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

#include "JoystickConfigController.h"
#include "JoystickManager.h"
13
#include "QGCApplication.h"
14 15 16 17 18 19 20

#include <QSettings>

QGC_LOGGING_CATEGORY(JoystickConfigControllerLog, "JoystickConfigControllerLog")

const int JoystickConfigController::_calCenterPoint =       0;
const int JoystickConfigController::_calValidMinValue =     -32768;     ///< Largest valid minimum axis value
21
const int JoystickConfigController::_calValidMaxValue =     32767;      ///< Smallest valid maximum axis value
22
const int JoystickConfigController::_calDefaultMinValue =   -32768;     ///< Default value for Min if not set
23
const int JoystickConfigController::_calDefaultMaxValue =   32767;      ///< Default value for Max if not set
24 25
const int JoystickConfigController::_calRoughCenterDelta =  500;        ///< Delta around center point which is considered to be roughly centered
const int JoystickConfigController::_calMoveDelta =         32768/2;    ///< Amount of delta past center which is considered stick movement
26
const int JoystickConfigController::_calSettleDelta =       600;        ///< Amount of delta which is considered no stick movement
27 28 29 30
const int JoystickConfigController::_calMinDelta =          1000;       ///< Amount of delta allowed around min value to consider channel at min

const int JoystickConfigController::_stickDetectSettleMSecs = 500;

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
static const JoystickConfigController::stateStickPositions stSticksCentered {
    0.25, 0.5, 0.75, 0.5
};

static const JoystickConfigController::stateStickPositions stLeftStickUp {
    0.25, 0.3084, 0.75, 0.5
};

static const JoystickConfigController::stateStickPositions stLeftStickDown {
    0.25, 0.6916, 0.75, 0.5
};

static const JoystickConfigController::stateStickPositions stLeftStickLeft {
    0.1542, 0.5, 0.75, 0.5
};

static const JoystickConfigController::stateStickPositions stLeftStickRight {
    0.3458, 0.5, 0.75, 0.5
};

static const JoystickConfigController::stateStickPositions stRightStickUp {
    0.25, 0.5, 0.75, 0.3084
};

static const JoystickConfigController::stateStickPositions stRightStickDown {
    0.25, 0.5, 0.75, 0.6916
};

static const JoystickConfigController::stateStickPositions stRightStickLeft {
    0.25, 0.5, 0.6542, 0.5
};

static const JoystickConfigController::stateStickPositions stRightStickRight {
    0.25, 0.5, 0.8423, 0.5
};

//-- Gimbal

static const JoystickConfigController::stateStickPositions stGimbalCentered {
    0.5, 0.5, 0.5, 0.3
};

static const JoystickConfigController::stateStickPositions stGimbalPitchDown {
    0.5, 0.6, 0.5, 0.3
};

static const JoystickConfigController::stateStickPositions stGimbalPitchUp {
    0.5, 0.4, 0.5, 0.3
};

static const JoystickConfigController::stateStickPositions stGimbalYawLeft {
    0.5, 0.5, 0.4, 0.3
};

static const JoystickConfigController::stateStickPositions stGimbalYawRight {
    0.5, 0.5, 0.6, 0.3
};

89
JoystickConfigController::JoystickConfigController(void)
Gus Grubba's avatar
Gus Grubba committed
90
    : _joystickManager(qgcApp()->toolbox()->joystickManager())
91 92
{
    
93 94
    connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &JoystickConfigController::_activeJoystickChanged);
    _activeJoystickChanged(_joystickManager->activeJoystick());
95
    _setStickPositions();
96
    _resetInternalCalibrationValues();
97 98
    _currentStickPositions  << _sticksCentered.leftX  << _sticksCentered.leftY  << _sticksCentered.rightX  << _sticksCentered.rightY;
    _currentGimbalPositions << stGimbalCentered.leftX << stGimbalCentered.leftY << stGimbalCentered.rightX << stGimbalCentered.rightY;
99 100 101 102 103 104 105
}

void JoystickConfigController::start(void)
{
    _stopCalibration();
}

106 107 108 109 110 111 112 113 114
void JoystickConfigController::setDeadbandValue(int axis, int value)
{
    _axisDeadbandChanged(axis,value);
    Joystick* joystick = _joystickManager->activeJoystick();
    Joystick::Calibration_t calibration = joystick->getCalibration(axis);
    calibration.deadband = value;
    joystick->setCalibration(axis,calibration);
}

115 116
JoystickConfigController::~JoystickConfigController()
{
Jacob Walser's avatar
Jacob Walser committed
117
    if(_activeJoystick) {
118
        _activeJoystick->setCalibrationMode(false);
Jacob Walser's avatar
Jacob Walser committed
119
    }
120 121 122 123 124
}

/// @brief Returns the state machine entry for the specified state.
const JoystickConfigController::stateMachineEntry* JoystickConfigController::_getStateMachineEntry(int step)
{
Gus Grubba's avatar
Gus Grubba committed
125
    static const char* msgBegin =               "Allow all sticks to center as shown in diagram.\nClick Next to continue";
Gus Grubba's avatar
Gus Grubba committed
126 127 128 129 130 131 132 133 134 135 136 137 138
    static const char* msgThrottleUp =          "Move the Throttle stick all the way up and hold it there...";
    static const char* msgThrottleDown =        "Move the Throttle stick all the way down and hold it there...";
    static const char* msgYawLeft =             "Move the Yaw stick all the way to the left and hold it there...";
    static const char* msgYawRight =            "Move the Yaw stick all the way to the right and hold it there...";
    static const char* msgRollLeft =            "Move the Roll stick all the way to the left and hold it there...";
    static const char* msgRollRight =           "Move the Roll stick all the way to the right and hold it there...";
    static const char* msgPitchDown =           "Move the Pitch stick all the way down and hold it there...";
    static const char* msgPitchUp =             "Move the Pitch stick all the way up and hold it there...";
    static const char* msgPitchCenter =         "Allow the Pitch stick to move back to center...";
    static const char* msgGimbalPitchDown =     "Move the Gimbal Pitch control all the way down and hold it there...";
    static const char* msgGimbalPitchUp =       "Move the Gimbal Pitch control all the way up and hold it there...";
    static const char* msgGimbalYawLeft =       "Move the Gimbal Yaw control all the way to the left and hold it there...";
    static const char* msgGimbalYawRight =      "Move the Gimbal Yaw control all the way to the right and hold it there...";
Gus Grubba's avatar
Gus Grubba committed
139
    static const char* msgComplete =            "All settings have been captured.\nClick Next to enable the joystick.";
140

141 142
    static const stateMachineEntry rgStateMachine[] = {
        //Function
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
        { Joystick::maxFunction,            msgBegin,           _sticksCentered,        stGimbalCentered,   &JoystickConfigController::_inputCenterWaitBegin,   &JoystickConfigController::_saveAllTrims,        nullptr, 0 },
        { Joystick::throttleFunction,       msgThrottleUp,      _sticksThrottleUp,      stGimbalCentered,   &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 0 },
        { Joystick::throttleFunction,       msgThrottleDown,    _sticksThrottleDown,    stGimbalCentered,   &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 0 },
        { Joystick::yawFunction,            msgYawRight,        _sticksYawRight,        stGimbalCentered,   &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 1 },
        { Joystick::yawFunction,            msgYawLeft,         _sticksYawLeft,         stGimbalCentered,   &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 1 },
        { Joystick::rollFunction,           msgRollRight,       _sticksRollRight,       stGimbalCentered,   &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 2 },
        { Joystick::rollFunction,           msgRollLeft,        _sticksRollLeft,        stGimbalCentered,   &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 2 },
        { Joystick::pitchFunction,          msgPitchUp,         _sticksPitchUp,         stGimbalCentered,   &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 3 },
        { Joystick::pitchFunction,          msgPitchDown,       _sticksPitchDown,       stGimbalCentered,   &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 3 },
        { Joystick::pitchFunction,          msgPitchCenter,     _sticksCentered,        stGimbalCentered,   &JoystickConfigController::_inputCenterWait,        nullptr,                                         nullptr, 3 },

        { Joystick::gimbalPitchFunction,    msgGimbalPitchUp,   _sticksCentered,        stGimbalPitchUp,    &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 4 },
        { Joystick::gimbalPitchFunction,    msgGimbalPitchDown, _sticksCentered,        stGimbalPitchDown,  &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 4 },
        { Joystick::gimbalYawFunction,      msgGimbalYawRight,  _sticksCentered,        stGimbalYawRight,   &JoystickConfigController::_inputStickDetect,       nullptr,                                         nullptr, 5 },
        { Joystick::gimbalYawFunction,      msgGimbalYawLeft,   _sticksCentered,        stGimbalYawLeft,    &JoystickConfigController::_inputStickMin,          nullptr,                                         nullptr, 5 },

        { Joystick::maxFunction,            msgComplete,        _sticksCentered,        stGimbalCentered,   nullptr,                                            &JoystickConfigController::_writeCalibration,    nullptr, -1 },
160 161
    };
    
162
    Q_ASSERT(step >= 0 && step < static_cast<int>((sizeof(rgStateMachine) / sizeof(rgStateMachine[0]))));
163 164 165
    return &rgStateMachine[step];
}

Gus Grubba's avatar
Gus Grubba committed
166
void JoystickConfigController::_advanceState()
167 168
{
    _currentStep++;
Gus Grubba's avatar
Gus Grubba committed
169 170 171 172 173
    const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
    while (state->channelID > _axisCount) {
        _currentStep++;
        state = _getStateMachineEntry(_currentStep);
    }
174 175 176
    _setupCurrentState();
}

Gus Grubba's avatar
Gus Grubba committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
bool JoystickConfigController::nextEnabled()
{
    if(_currentStep >= 0) {
        const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
        return state->nextFn != nullptr;
    }
    return false;
}

bool JoystickConfigController::skipEnabled()
{
    if(_currentStep >= 0) {
        const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
        return state->skipFn != nullptr;
    }
    return false;
}

195
/// @brief Sets up the state machine according to the current step from _currentStep.
Gus Grubba's avatar
Gus Grubba committed
196
void JoystickConfigController::_setupCurrentState()
197
{
Gus Grubba's avatar
Gus Grubba committed
198
    const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
199
    _setStatusText(state->instructions);
200
    _stickDetectAxis = _axisNoAxis;
201 202
    _stickDetectSettleStarted = false;
    _calSaveCurrentValues();
203 204 205 206 207 208
    _currentStickPositions.clear();
    _currentStickPositions << state->stickPositions.leftX << state->stickPositions.leftY << state->stickPositions.rightX << state->stickPositions.rightY;
    _currentGimbalPositions.clear();
    _currentGimbalPositions << state->gimbalPositions.leftX << state->gimbalPositions.leftY << state->gimbalPositions.rightX << state->gimbalPositions.rightY;
    emit stickPositionsChanged();
    emit gimbalPositionsChanged();
Gus Grubba's avatar
Gus Grubba committed
209 210
    emit nextEnabledChanged();
    emit skipEnabledChanged();
211 212 213 214
}

void JoystickConfigController::_axisValueChanged(int axis, int value)
{
215
    if (_validAxis(axis)) {
216 217 218 219 220
        // We always update raw values
        _axisRawValue[axis] = value;
        emit axisValueChanged(axis, _axisRawValue[axis]);
        if (_currentStep == -1) {
            // Track the axis count by keeping track of how many axes we see
Gus Grubba's avatar
Gus Grubba committed
221
            if (axis + 1 > static_cast<int>(_axisCount)) {
222
                _axisCount = axis + 1;
223
                emit hasGimbalChanged();
224 225 226 227 228 229 230 231 232 233 234 235
            }
        }
        if (_currentStep != -1) {
            const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
            Q_ASSERT(state);
            if (state->rcInputFn) {
                (this->*state->rcInputFn)(state->function, axis, value);
            }
        }
    }
}

Gus Grubba's avatar
Gus Grubba committed
236
void JoystickConfigController::nextButtonClicked()
237 238 239 240
{
    if (_currentStep == -1) {
        // Need to have enough channels
        if (_axisCount < _axisMinimum) {
241
            qgcApp()->showMessage(tr("Detected %1 joystick axes. To operate PX4, you need at least %2 axes.").arg(_axisCount).arg(_axisMinimum));
242 243 244 245 246 247 248 249 250 251 252
            return;
        }
        _startCalibration();
    } else {
        const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
        Q_ASSERT(state);
        Q_ASSERT(state->nextFn);
        (this->*state->nextFn)();
    }
}

Gus Grubba's avatar
Gus Grubba committed
253
void JoystickConfigController::skipButtonClicked()
254 255 256 257 258 259 260 261 262 263 264 265 266
{
    Q_ASSERT(_currentStep != -1);
    const stateMachineEntry* state = _getStateMachineEntry(_currentStep);
    Q_ASSERT(state);
    Q_ASSERT(state->skipFn);
    (this->*state->skipFn)();
}

void JoystickConfigController::cancelButtonClicked(void)
{
    _stopCalibration();
}

267 268 269 270 271 272
bool JoystickConfigController::getDeadbandToggle() {
    return _activeJoystick->deadband();
}

void JoystickConfigController::setDeadbandToggle(bool deadband) {
    _activeJoystick->setDeadband(deadband);
nanthony21's avatar
nanthony21 committed
273
    _signalAllAttitudeValueChanges();
274 275 276
    emit deadbandToggled(deadband);
}

Gus Grubba's avatar
Gus Grubba committed
277
void JoystickConfigController::_saveAllTrims()
278 279 280
{
    // We save all trims as the first step. At this point no axes are mapped but it should still
    // allow us to get good trims for the roll/pitch/yaw/throttle even though we don't know which
281
    // axis they are yet. As we continue through the process the other axes will get their
282
    // trims reset to correct values.
Gus Grubba's avatar
Gus Grubba committed
283
    for (int i = 0; i < _axisCount; i++) {
284 285 286 287 288 289
        qCDebug(JoystickConfigControllerLog) << "_saveAllTrims trim" << _axisRawValue[i];
        _rgAxisInfo[i].axisTrim = _axisRawValue[i];
    }
    _advanceState();
}

Gregory Dymarek's avatar
Gregory Dymarek committed
290 291 292
void JoystickConfigController::_axisDeadbandChanged(int axis, int value)
{
    value = abs(value)<_calValidMaxValue?abs(value):_calValidMaxValue;
293
    _rgAxisInfo[axis].deadband = value;
294
    emit axisDeadbandChanged(axis,value);
295
    qCDebug(JoystickConfigControllerLog) << "Axis:" << axis << "Deadband:" << _rgAxisInfo[axis].deadband;
Gregory Dymarek's avatar
Gregory Dymarek committed
296 297
}

298 299 300 301
/// @brief Waits for the sticks to be centered, enabling Next when done.
void JoystickConfigController::_inputCenterWaitBegin(Joystick::AxisFunction_t function, int axis, int value)
{
    Q_UNUSED(function);
Gregory Dymarek's avatar
Gregory Dymarek committed
302
    //sensing deadband
Gus Grubba's avatar
Gus Grubba committed
303 304
    if ((abs(value) * 1.1f > _rgAxisInfo[axis].deadband) && (_activeJoystick->deadband())) {   //add 10% on top of existing deadband
        _axisDeadbandChanged(axis, static_cast<int>(abs(value) * 1.1f));
Gregory Dymarek's avatar
Gregory Dymarek committed
305
    }
306
    // FIXME: Doesn't wait for center
307 308
}

Don Gagne's avatar
Don Gagne committed
309
bool JoystickConfigController::_stickSettleComplete(int axis, int value)
310
{
311 312 313 314 315
    if (!_validAxis(axis)) {
        qCWarning(JoystickConfigControllerLog) << "Invalid axis axis:_axisCount" << axis << _axisCount;
        return false;
    }

316 317 318 319
    // We are waiting for the stick to settle out to a max position
    
    if (abs(_stickDetectValue - value) > _calSettleDelta) {
        // Stick is moving too much to consider stopped
Don Gagne's avatar
Don Gagne committed
320
        qCDebug(JoystickConfigControllerLog) << "_stickSettleComplete still moving, axis:_stickDetectValue:value" << axis << _stickDetectValue << value;
321 322 323 324 325 326 327 328
        _stickDetectValue = value;
        _stickDetectSettleStarted = false;
    } else {
        // Stick is still positioned within the specified small range
        if (_stickDetectSettleStarted) {
            // We have already started waiting
            if (_stickDetectSettleElapsed.elapsed() > _stickDetectSettleMSecs) {
                // Stick has stayed positioned in one place long enough, detection is complete.
Don Gagne's avatar
Don Gagne committed
329
                qCDebug(JoystickConfigControllerLog) << "_stickSettleComplete detection complete, axis:_stickDetectValue:value" << axis << _stickDetectValue << value;
330 331 332 333
                return true;
            }
        } else {
            // Start waiting for the stick to stay settled for _stickDetectSettleWaitMSecs msecs
Don Gagne's avatar
Don Gagne committed
334
            qCDebug(JoystickConfigControllerLog) << "_stickSettleComplete starting settle timer, axis:_stickDetectValue:value" << axis << _stickDetectValue << value;
335 336 337 338 339 340 341 342 343 344 345 346
            _stickDetectSettleStarted = true;
            _stickDetectSettleElapsed.start();
        }
    }
    
    return false;
}

void JoystickConfigController::_inputStickDetect(Joystick::AxisFunction_t function, int axis, int value)
{
    qCDebug(JoystickConfigControllerLog) << "_inputStickDetect function:axis:value" << function << axis << value;
    
347 348 349 350 351
    if (!_validAxis(axis)) {
        qCWarning(JoystickConfigControllerLog) << "Invalid axis axis:_axisCount" << axis << _axisCount;
        return;
    }

352 353 354 355 356
    // If this axis is already used in a mapping we can't use it again
    if (_rgAxisInfo[axis].function != Joystick::maxFunction) {
        return;
    }
    
357
    if (_stickDetectAxis == _axisNoAxis) {
358 359 360 361 362 363 364 365 366 367
        // We have not detected enough movement on a axis yet
        if (abs(_axisValueSave[axis] - value) > _calMoveDelta) {
            // Stick has moved far enough to consider it as being selected for the function
            qCDebug(JoystickConfigControllerLog) << "_inputStickDetect starting settle wait, function:axis:value" << function << axis << value;
            // Setup up to detect stick being pegged to min or max value
            _stickDetectAxis = axis;
            _stickDetectInitialValue = value;
            _stickDetectValue = value;
        }
    } else if (axis == _stickDetectAxis) {
Don Gagne's avatar
Don Gagne committed
368
        if (_stickSettleComplete(axis, value)) {
369 370 371 372 373 374 375 376 377 378 379 380
            AxisInfo* info = &_rgAxisInfo[axis];
            // Stick detection is complete. Stick should be at max position.
            // Map the axis to the function
            _rgFunctionAxisMapping[function] = axis;
            info->function = function;
            // Axis should be at max value, if it is below initial set point the the axis is reversed.
            info->reversed = value < _axisValueSave[axis];
            if (info->reversed) {
                _rgAxisInfo[axis].axisMin = value;
            } else {
                _rgAxisInfo[axis].axisMax = value;
            }
Don Gagne's avatar
Don Gagne committed
381
            qCDebug(JoystickConfigControllerLog) << "_inputStickDetect saving values, function:axis:value:reversed:_axisValueSave" << function << axis << value << info->reversed << _axisValueSave[axis];
nanthony21's avatar
nanthony21 committed
382
            _signalAllAttitudeValueChanges();
383 384 385 386 387 388 389
            _advanceState();
        }
    }
}

void JoystickConfigController::_inputStickMin(Joystick::AxisFunction_t function, int axis, int value)
{
Don Gagne's avatar
Don Gagne committed
390
    qCDebug(JoystickConfigControllerLog) << "_inputStickMin function:axis:value" << function << axis << value;
391 392 393 394
    if (!_validAxis(axis)) {
        qCWarning(JoystickConfigControllerLog) << "Invalid axis axis:_axisCount" << axis << _axisCount;
        return;
    }
395 396 397 398
    // We only care about the axis mapped to the function we are working on
    if (_rgFunctionAxisMapping[function] != axis) {
        return;
    }
399
    if (_stickDetectAxis == _axisNoAxis) {
400 401 402 403 404 405
        // Setup up to detect stick being pegged to extreme position
        if (_rgAxisInfo[axis].reversed) {
            if (value > _calCenterPoint + _calMoveDelta) {
                _stickDetectAxis = axis;
                _stickDetectInitialValue = value;
                _stickDetectValue = value;
Don Gagne's avatar
Don Gagne committed
406
                qCDebug(JoystickConfigControllerLog) << "_inputStickMin detected movement _stickDetectAxis:_stickDetectInitialValue" << _stickDetectAxis << _stickDetectInitialValue;
407 408 409 410 411 412
            }
        } else {
            if (value < _calCenterPoint - _calMoveDelta) {
                _stickDetectAxis = axis;
                _stickDetectInitialValue = value;
                _stickDetectValue = value;
Don Gagne's avatar
Don Gagne committed
413
                qCDebug(JoystickConfigControllerLog) << "_inputStickMin detected movement _stickDetectAxis:_stickDetectInitialValue" << _stickDetectAxis << _stickDetectInitialValue;
414 415 416 417
            }
        }
    } else {
        // We are waiting for the selected axis to settle out
Don Gagne's avatar
Don Gagne committed
418
        if (_stickSettleComplete(axis, value)) {
419 420 421 422 423 424 425
            AxisInfo* info = &_rgAxisInfo[axis];
            // Stick detection is complete. Stick should be at min position.
            if (info->reversed) {
                _rgAxisInfo[axis].axisMax = value;
            } else {
                _rgAxisInfo[axis].axisMin = value;
            }
Don Gagne's avatar
Don Gagne committed
426
            qCDebug(JoystickConfigControllerLog) << "_inputStickMin saving values, function:axis:value:reversed" << function << axis << value << info->reversed;
427 428 429 430 431 432 433
            _advanceState();
        }
    }
}

void JoystickConfigController::_inputCenterWait(Joystick::AxisFunction_t function, int axis, int value)
{
Don Gagne's avatar
Don Gagne committed
434
    qCDebug(JoystickConfigControllerLog) << "_inputCenterWait function:axis:value" << function << axis << value;
435 436 437 438 439
    if (!_validAxis(axis)) {
        qCWarning(JoystickConfigControllerLog) << "Invalid axis axis:_axisCount" << axis << _axisCount;
        return;
    }

440 441 442 443 444
    // We only care about the axis mapped to the function we are working on
    if (_rgFunctionAxisMapping[function] != axis) {
        return;
    }
    
445
    if (_stickDetectAxis == _axisNoAxis) {
446
        // Sticks have not yet moved close enough to center
447
        int roughCenter = getDeadbandToggle() ? std::max(_rgAxisInfo[axis].deadband,_calRoughCenterDelta) : _calRoughCenterDelta;
448
        if (abs(_calCenterPoint - value) < roughCenter) {
449 450 451 452
            // Stick has moved close enough to center that we can start waiting for it to settle
            _stickDetectAxis = axis;
            _stickDetectInitialValue = value;
            _stickDetectValue = value;
Don Gagne's avatar
Don Gagne committed
453
            qCDebug(JoystickConfigControllerLog) << "_inputStickMin detected possible center _stickDetectAxis:_stickDetectInitialValue" << _stickDetectAxis << _stickDetectInitialValue;
454 455
        }
    } else {
Don Gagne's avatar
Don Gagne committed
456
        if (_stickSettleComplete(axis, value)) {
457 458 459 460 461 462
            _advanceState();
        }
    }
}

/// @brief Resets internal calibration values to their initial state in preparation for a new calibration sequence.
Gus Grubba's avatar
Gus Grubba committed
463
void JoystickConfigController::_resetInternalCalibrationValues()
464
{
Gus Grubba's avatar
Gus Grubba committed
465 466
    // Set all raw axis to not reversed and center point values
    for (int i = 0; i < _axisCount; i++) {
467 468 469
        struct AxisInfo* info = &_rgAxisInfo[i];
        info->function = Joystick::maxFunction;
        info->reversed = false;
470
        info->deadband = 0;
471
        emit axisDeadbandChanged(i,info->deadband);
Gus Grubba's avatar
Gus Grubba committed
472 473
        info->axisMin  = JoystickConfigController::_calCenterPoint;
        info->axisMax  = JoystickConfigController::_calCenterPoint;
474 475 476
        info->axisTrim = JoystickConfigController::_calCenterPoint;
    }
    // Initialize attitude function mapping to function axis not set
Gus Grubba's avatar
Gus Grubba committed
477
    for (size_t i = 0; i < Joystick::maxFunction; i++) {
478
        _rgFunctionAxisMapping[i] = _axisNoAxis;
479
    }
nanthony21's avatar
nanthony21 committed
480
    _signalAllAttitudeValueChanges();
481 482 483
}

/// @brief Sets internal calibration values from the stored settings
Gus Grubba's avatar
Gus Grubba committed
484
void JoystickConfigController::_setInternalCalibrationValuesFromSettings()
485
{
486
    Joystick* joystick = _joystickManager->activeJoystick();
487
    // Initialize all function mappings to not set
Gus Grubba's avatar
Gus Grubba committed
488
    for (int i = 0; i < _axisCount; i++) {
489 490 491 492
        struct AxisInfo* info = &_rgAxisInfo[i];
        info->function = Joystick::maxFunction;
    }
    
Gus Grubba's avatar
Gus Grubba committed
493
    for (size_t i = 0; i < Joystick::maxFunction; i++) {
494
        _rgFunctionAxisMapping[i] = _axisNoAxis;
495 496
    }
    
Gus Grubba's avatar
Gus Grubba committed
497
    for (int axis = 0; axis < _axisCount; axis++) {
498 499
        struct AxisInfo* info = &_rgAxisInfo[axis];
        Joystick::Calibration_t calibration = joystick->getCalibration(axis);
Gus Grubba's avatar
Gus Grubba committed
500 501 502 503 504
        info->axisTrim  = calibration.center;
        info->axisMin   = calibration.min;
        info->axisMax   = calibration.max;
        info->reversed  = calibration.reversed;
        info->deadband  = calibration.deadband;
505
        emit axisDeadbandChanged(axis,info->deadband);
506
        qCDebug(JoystickConfigControllerLog) << "Read settings name:axis:min:max:trim:reversed" << joystick->name() << axis << info->axisMin << info->axisMax << info->axisTrim << info->reversed;
507 508
    }
    
Gus Grubba's avatar
Gus Grubba committed
509
    for (int function = 0; function < Joystick::maxFunction; function++) {
510
        int paramAxis;
Gus Grubba's avatar
Gus Grubba committed
511 512
        paramAxis = joystick->getFunctionAxis(static_cast<Joystick::AxisFunction_t>(function));
        if(paramAxis >= 0 && paramAxis < _axisCount) {
Jacob Walser's avatar
Jacob Walser committed
513
            _rgFunctionAxisMapping[function] = paramAxis;
Gus Grubba's avatar
Gus Grubba committed
514
            _rgAxisInfo[paramAxis].function = static_cast<Joystick::AxisFunction_t>(function);
Jacob Walser's avatar
Jacob Walser committed
515
        }
516
    }
517 518

    _transmitterMode = joystick->getTXMode();
nanthony21's avatar
nanthony21 committed
519
    _signalAllAttitudeValueChanges();
520 521 522
}

/// @brief Validates the current settings against the calibration rules resetting values as necessary.
Gus Grubba's avatar
Gus Grubba committed
523
void JoystickConfigController::_validateCalibration()
524
{
Gus Grubba's avatar
Gus Grubba committed
525
    for (int chan = 0; chan < _axisCount; chan++) {
526
        struct AxisInfo* info = &_rgAxisInfo[chan];
527 528 529 530
        // Validate Min/Max values. Although the axis appears as available we still may
        // not have good min/max/trim values for it. Set to defaults if needed.
        if (info->axisMin < _calValidMinValue || info->axisMax > _calValidMaxValue) {
            qCDebug(JoystickConfigControllerLog) << "_validateCalibration resetting axis" << chan;
531 532 533
            info->axisMin = _calDefaultMinValue;
            info->axisMax = _calDefaultMaxValue;
            info->axisTrim = info->axisMin + ((info->axisMax - info->axisMin) / 2);
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
        }
        switch (_rgAxisInfo[chan].function) {
            case Joystick::throttleFunction:
            case Joystick::yawFunction:
            case Joystick::rollFunction:
            case Joystick::pitchFunction:
            case Joystick::gimbalPitchFunction:
            case Joystick::gimbalYawFunction:
                // Make sure trim is within min/max
                if (info->axisTrim < info->axisMin) {
                    info->axisTrim = info->axisMin;
                } else if (info->axisTrim > info->axisMax) {
                    info->axisTrim = info->axisMax;
                }
                break;
            default:
                // Non-attitude control axis have calculated trim
                info->axisTrim = info->axisMin + ((info->axisMax - info->axisMin) / 2);
                break;
553 554 555 556 557
        }
    }
}

/// @brief Saves the rc calibration values to the board parameters.
Gus Grubba's avatar
Gus Grubba committed
558
void JoystickConfigController::_writeCalibration()
559
{
560
    Joystick* joystick = _joystickManager->activeJoystick();
561 562
    _validateCalibration();
    
Gus Grubba's avatar
Gus Grubba committed
563
    for (int axis = 0; axis < _axisCount; axis++) {
564
        Joystick::Calibration_t calibration;
Gus Grubba's avatar
Gus Grubba committed
565 566 567 568 569 570
        struct AxisInfo* info   = &_rgAxisInfo[axis];
        calibration.center      = info->axisTrim;
        calibration.min         = info->axisMin;
        calibration.max         = info->axisMax;
        calibration.reversed    = info->reversed;
        calibration.deadband    = info->deadband;
571 572 573 574
        joystick->setCalibration(axis, calibration);
    }
    
    // Write function mapping parameters
Gus Grubba's avatar
Gus Grubba committed
575 576
    for (int function = 0; function < Joystick::maxFunction; function++) {
        joystick->setFunctionAxis(static_cast<Joystick::AxisFunction_t>(function), _rgFunctionAxisMapping[function]);
577 578 579 580
    }
    
    _stopCalibration();
    _setInternalCalibrationValuesFromSettings();
581 582 583 584 585

    Vehicle* vehicle = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle();
    if (vehicle) {
        vehicle->setJoystickEnabled(true);
    }
586 587 588
}

/// @brief Starts the calibration process
Gus Grubba's avatar
Gus Grubba committed
589
void JoystickConfigController::_startCalibration()
590
{
591
    _activeJoystick->setCalibrationMode(true);
592 593 594
    _resetInternalCalibrationValues();
    _currentStep = 0;
    _setupCurrentState();
595
    emit calibratingChanged();
596 597 598
}

/// @brief Cancels the calibration process, setting things back to initial state.
Gus Grubba's avatar
Gus Grubba committed
599
void JoystickConfigController::_stopCalibration()
600 601
{
    _currentStep = -1;
602
    _activeJoystick->setCalibrationMode(false);
603
    _setInternalCalibrationValuesFromSettings();
604
    _setStatusText("");
605
    emit calibratingChanged();
Gus Grubba's avatar
Gus Grubba committed
606 607 608 609 610 611
    _currentStickPositions.clear();
    _currentGimbalPositions.clear();
    _currentStickPositions  << _sticksCentered.leftX  << _sticksCentered.leftY  << _sticksCentered.rightX  << _sticksCentered.rightY;
    _currentGimbalPositions << stGimbalCentered.leftX << stGimbalCentered.leftY << stGimbalCentered.rightX << stGimbalCentered.rightY;
    emit stickPositionsChanged();
    emit gimbalPositionsChanged();
612 613 614
}

/// @brief Saves the current axis values, so that we can detect when the use moves an input.
Gus Grubba's avatar
Gus Grubba committed
615
void JoystickConfigController::_calSaveCurrentValues()
616 617
{
	qCDebug(JoystickConfigControllerLog) << "_calSaveCurrentValues";
618
    for (int i = 0; i < _axisCount; i++) {
619 620 621 622
        _axisValueSave[i] = _axisRawValue[i];
    }
}

623
void JoystickConfigController::_setStickPositions()
624
{
625
    _sticksCentered = stSticksCentered;
626 627
    switch(_transmitterMode) {
    case 1:
628 629 630 631 632 633 634 635
        _sticksThrottleUp   = stRightStickUp;
        _sticksThrottleDown = stRightStickDown;
        _sticksYawLeft      = stLeftStickLeft;
        _sticksYawRight     = stLeftStickRight;
        _sticksRollLeft     = stRightStickLeft;
        _sticksRollRight    = stRightStickRight;
        _sticksPitchUp      = stLeftStickDown;
        _sticksPitchDown    = stLeftStickUp;
636 637
        break;
    case 2:
638 639 640 641 642 643 644 645
        _sticksThrottleUp   = stLeftStickUp;
        _sticksThrottleDown = stLeftStickDown;
        _sticksYawLeft      = stLeftStickLeft;
        _sticksYawRight     = stLeftStickRight;
        _sticksRollLeft     = stRightStickLeft;
        _sticksRollRight    = stRightStickRight;
        _sticksPitchUp      = stRightStickDown;
        _sticksPitchDown    = stRightStickUp;
646 647
        break;
    case 3:
648 649 650 651 652 653 654 655
        _sticksThrottleUp   = stRightStickUp;
        _sticksThrottleDown = stRightStickDown;
        _sticksYawLeft      = stRightStickLeft;
        _sticksYawRight     = stRightStickRight;
        _sticksRollLeft     = stLeftStickLeft;
        _sticksRollRight    = stLeftStickRight;
        _sticksPitchUp      = stLeftStickDown;
        _sticksPitchDown    = stLeftStickUp;
656 657
        break;
    case 4:
658 659 660 661 662 663 664 665
        _sticksThrottleUp   = stLeftStickUp;
        _sticksThrottleDown = stLeftStickDown;
        _sticksYawLeft      = stRightStickLeft;
        _sticksYawRight     = stRightStickRight;
        _sticksRollLeft     = stLeftStickLeft;
        _sticksRollRight    = stLeftStickRight;
        _sticksPitchUp      = stRightStickDown;
        _sticksPitchDown    = stRightStickUp;
666 667 668 669
        break;
    default:
        Q_ASSERT(false);
    }
670 671
}

Gus Grubba's avatar
Gus Grubba committed
672
bool JoystickConfigController::rollAxisReversed()
673
{
674
    if (_rgFunctionAxisMapping[Joystick::rollFunction] != _axisNoAxis) {
675 676 677 678 679 680
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::rollFunction]].reversed;
    } else {
        return false;
    }
}

Gus Grubba's avatar
Gus Grubba committed
681
bool JoystickConfigController::pitchAxisReversed()
682
{
683
    if (_rgFunctionAxisMapping[Joystick::pitchFunction] != _axisNoAxis) {
684 685 686 687 688 689
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::pitchFunction]].reversed;
    } else {
        return false;
    }
}

Gus Grubba's avatar
Gus Grubba committed
690
bool JoystickConfigController::yawAxisReversed()
691
{
692
    if (_rgFunctionAxisMapping[Joystick::yawFunction] != _axisNoAxis) {
693 694 695 696 697 698
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::yawFunction]].reversed;
    } else {
        return false;
    }
}

Gus Grubba's avatar
Gus Grubba committed
699
bool JoystickConfigController::throttleAxisReversed()
700
{
701
    if (_rgFunctionAxisMapping[Joystick::throttleFunction] != _axisNoAxis) {
702 703 704 705 706 707
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::throttleFunction]].reversed;
    } else {
        return false;
    }
}

Gus Grubba's avatar
Gus Grubba committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
bool JoystickConfigController::gimbalPitchAxisReversed()
{
    if (_rgFunctionAxisMapping[Joystick::gimbalPitchFunction] != _axisNoAxis) {
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::gimbalPitchFunction]].reversed;
    } else {
        return false;
    }
}

bool JoystickConfigController::gimbalYawAxisReversed()
{
    if (_rgFunctionAxisMapping[Joystick::gimbalYawFunction] != _axisNoAxis) {
        return _rgAxisInfo[_rgFunctionAxisMapping[Joystick::gimbalYawFunction]].reversed;
    } else {
        return false;
    }
}

726 727
void JoystickConfigController::setTransmitterMode(int mode)
{
728 729
    // Mode selection is disabled during calibration
    if (mode > 0 && mode <= 4 && _currentStep == -1) {
730
        _transmitterMode = mode;
731 732 733
        _setStickPositions();
        _activeJoystick->setTXMode(mode);
        _setInternalCalibrationValuesFromSettings();
734 735 736
    }
}

Gus Grubba's avatar
Gus Grubba committed
737
void JoystickConfigController::_signalAllAttitudeValueChanges()
738 739 740 741 742
{
    emit rollAxisMappedChanged(rollAxisMapped());
    emit pitchAxisMappedChanged(pitchAxisMapped());
    emit yawAxisMappedChanged(yawAxisMapped());
    emit throttleAxisMappedChanged(throttleAxisMapped());
Gus Grubba's avatar
Gus Grubba committed
743 744 745
    emit gimbalPitchAxisMappedChanged(gimbalPitchAxisMapped());
    emit gimbalYawAxisMappedChanged(gimbalYawAxisMapped());

746 747 748 749
    emit rollAxisReversedChanged(rollAxisReversed());
    emit pitchAxisReversedChanged(pitchAxisReversed());
    emit yawAxisReversedChanged(yawAxisReversed());
    emit throttleAxisReversedChanged(throttleAxisReversed());
Gus Grubba's avatar
Gus Grubba committed
750 751
    emit gimbalPitchAxisReversedChanged(pitchAxisReversed());
    emit gimbalYawAxisReversedChanged(yawAxisReversed());
Gregory Dymarek's avatar
Gregory Dymarek committed
752

753
    emit transmitterModeChanged(_transmitterMode);
754
}
755 756 757

void JoystickConfigController::_activeJoystickChanged(Joystick* joystick)
{
758
    bool joystickTransition = false;
759
    if (_activeJoystick) {
760
        joystickTransition = true;
761
        disconnect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged);
Jacob Walser's avatar
Jacob Walser committed
762 763 764 765 766
        // This will reset _rgFunctionAxis values to -1 to prevent out-of-bounds accesses
        _resetInternalCalibrationValues();
        delete[] _rgAxisInfo;
        delete[] _axisValueSave;
        delete[] _axisRawValue;
767
        _axisCount = 0;
Gus Grubba's avatar
Gus Grubba committed
768
        _activeJoystick = nullptr;
769
        emit hasGimbalChanged();
770 771 772 773
    }
    
    if (joystick) {
        _activeJoystick = joystick;
774 775 776
        if (joystickTransition) {
            _stopCalibration();
        }
777
        _activeJoystick->setCalibrationMode(false);
Gus Grubba's avatar
Gus Grubba committed
778 779 780 781
        _axisCount      = _activeJoystick->axisCount();
        _rgAxisInfo     = new struct AxisInfo[_axisCount];
        _axisValueSave  = new int[_axisCount];
        _axisRawValue   = new int[_axisCount];
Jacob Walser's avatar
Jacob Walser committed
782
        _setInternalCalibrationValuesFromSettings();
783
        connect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged);
784
        emit hasGimbalChanged();
785 786
    }
}
787 788 789 790 791

bool JoystickConfigController::_validAxis(int axis)
{
    return axis >= 0 && axis < _axisCount;
}
792 793 794 795 796 797

void JoystickConfigController::_setStatusText(const QString& text)
{
    _statusText = text;
    emit statusTextChanged();
}