PX4RCCalibrationTest.cc 35.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*=====================================================================
 
 QGroundControl Open Source Ground Control Station
 
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 
 This file is part of the QGROUNDCONTROL project
 
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
 
 ======================================================================*/

#include "PX4RCCalibrationTest.h"
#include "UASManager.h"
#include "MockQGCUASParamManager.h"

/// @file
Don Gagne's avatar
Don Gagne committed
29
///     @brief QPX4RCCalibration Widget unit test
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
///
///     @author Don Gagne <don@thegagnes.com>

// This will check for the wizard buttons being enabled of disabled according to the mask you pass in.
// We use a macro instead of a method so that we get better line number reporting on failure.
#define CHK_BUTTONS(mask) \
{ \
    if (_nextButton->isEnabled() != !!((mask) & nextButtonMask) || \
        _skipButton->isEnabled() != !!((mask) & skipButtonMask) || \
        _cancelButton->isEnabled() != !!((mask) & cancelButtonMask) || \
        _tryAgainButton->isEnabled() != !!((mask) & tryAgainButtonMask)) { \
        qDebug() << _statusLabel->text(); \
    } \
    QCOMPARE(_nextButton->isEnabled(), !!((mask) & nextButtonMask)); \
    QCOMPARE(_skipButton->isEnabled(), !!((mask) & skipButtonMask)); \
    QCOMPARE(_cancelButton->isEnabled(), !!((mask) & cancelButtonMask)); \
    QCOMPARE(_tryAgainButton->isEnabled(), !!((mask) & tryAgainButtonMask)); \
}

// This allows you to write unit tests which will click the Cancel button the first time through, followed
// by the Next button on the second iteration.
#define NEXT_OR_CANCEL(cancelNum) \
{ \
Don Gagne's avatar
Don Gagne committed
53
    if (mode == testModeStandalone && tryCancel ## cancelNum) { \
54 55 56 57 58 59 60 61 62
        QTest::mouseClick(_cancelButton, Qt::LeftButton); \
        QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateChannelWait); \
        tryCancel ## cancelNum = false; \
        goto StartOver; \
    } else { \
        QTest::mouseClick(_nextButton, Qt::LeftButton); \
    } \
}

Don Gagne's avatar
Don Gagne committed
63 64 65 66 67
const int PX4RCCalibrationTest::_testMinValue = PX4RCCalibration::_rcCalPWMDefaultMinValue + 10;
const int PX4RCCalibrationTest::_testMaxValue = PX4RCCalibration::_rcCalPWMDefaultMaxValue - 10;
const int PX4RCCalibrationTest::_testTrimValue = PX4RCCalibration::_rcCalPWMDefaultTrimValue + 10;
const int PX4RCCalibrationTest::_testThrottleTrimValue = PX4RCCalibration::_rcCalPWMDefaultMinValue + 10;

68 69 70 71
/// @brief Maps from function index to channel index. -1 signals no mapping. Channel indices are offset 1 from function index
/// to catch bugs where function index is incorrectly used as channel index.
const int PX4RCCalibrationTest::_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionMax]= { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1 };

Don Gagne's avatar
Don Gagne committed
72
const struct PX4RCCalibrationTest::ChannelSettings PX4RCCalibrationTest::_rgChannelSettingsPreValidate[PX4RCCalibrationTest::_availableChannels] = {
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	// Function										Min Value									Max Value                                   Trim Value                                      Reversed    MinMaxShown MinValid MaxValid
	
	// Channel 0 : Not mapped to function, Simulate invalid Min/Max
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,		PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   false,  false },
	
    // Channels 1-10 are mapped to all available modes, except for Aux2 which is skipped
	{ PX4RCCalibration::rcCalFunctionRoll,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           true,		true,   true,   true },
    { PX4RCCalibration::rcCalFunctionPitch,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           false,      true,   true,   true },
    { PX4RCCalibration::rcCalFunctionYaw,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           true,		true,   true,   true },
    { PX4RCCalibration::rcCalFunctionThrottle,		PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testThrottleTrimValue,   false,      true,   true,   true },
    { PX4RCCalibration::rcCalFunctionModeSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionPosCtlSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionLoiterSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionReturnSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionFlaps,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionAux1,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	
	// Channel 11 : Not mapped to function, Simulate invalid Min/Max
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,		PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   false,  false },
	
    // Channel 12 : Not mapped to function, Simulate invalid Min, valid Max
    { PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,		PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   false,  true },
    
	// Channel 13 : Not mapped to function, Simulate valid Min, invalid Max
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMCenterPoint,				false,      true,   true,  false },
	
    // Channels 14-17: Not mapped to function, Simulate invalid Min/Max, since available channel Min/Max is still shown
    { PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,   PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,			false,      true,   false,  false },
    { PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,   PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,			false,      true,   false,  false },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,   PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,			false,      true,   false,  false },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMCenterPoint,   PX4RCCalibration::_rcCalPWMCenterPoint,     PX4RCCalibration::_rcCalPWMCenterPoint,			false,      true,   false,  false },
Don Gagne's avatar
Don Gagne committed
104 105 106
};

const struct PX4RCCalibrationTest::ChannelSettings PX4RCCalibrationTest::_rgChannelSettingsPostValidate[PX4RCCalibration::_chanMax] = {
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    // Function										Min Value									Max Value									Trim Value										Reversed    MinMaxShown MinValid MaxValid
	
	// Channel 0 is not mapped and should be defaulted
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	
	// Channels 1-10 are mapped to all available modes, except for Aux2 which is skipped
	{ PX4RCCalibration::rcCalFunctionRoll,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           true,		true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionPitch,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionYaw,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testTrimValue,           true,		true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionThrottle,		PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibrationTest::_testThrottleTrimValue,   false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionModeSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionPosCtlSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionLoiterSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionReturnSwitch,	PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionFlaps,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	{ PX4RCCalibration::rcCalFunctionAux1,			PX4RCCalibrationTest::_testMinValue,		PX4RCCalibrationTest::_testMaxValue,        PX4RCCalibration::_rcCalPWMCenterPoint,         false,      true,   true,   true },
	
	// Channels 11-17 are not mapped and should be set to defaults
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
	{ PX4RCCalibration::rcCalFunctionMax,			PX4RCCalibration::_rcCalPWMDefaultMinValue,	PX4RCCalibration::_rcCalPWMDefaultMaxValue,	PX4RCCalibration::_rcCalPWMDefaultTrimValue,	false,      true,   true,	true },
Don Gagne's avatar
Don Gagne committed
132 133
};

134 135 136 137
PX4RCCalibrationTest::PX4RCCalibrationTest(void) :
    _mockUASManager(NULL),
    _calWidget(NULL)
{
Don Gagne's avatar
Don Gagne committed
138 139 140 141 142
}

/// @brief Called one time before any test cases are run.
void PX4RCCalibrationTest::initTestCase(void)
{
143 144 145 146 147 148 149 150 151
	// Validate that our function to channel mapping is still correct.
	for (int function=0; function<PX4RCCalibration::rcCalFunctionMax; function++) {
		int chanIndex = _rgFunctionChannelMap[function];
		if (chanIndex != -1) {
			Q_ASSERT(_rgChannelSettingsPreValidate[chanIndex].function == function);
			Q_ASSERT(_rgChannelSettingsPostValidate[chanIndex].function == function);
		}
	}
	Q_ASSERT(_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionAux2] == -1);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
}

void PX4RCCalibrationTest::init(void)
{
    _mockUASManager = new MockUASManager();
    Q_ASSERT(_mockUASManager);
    
    UASManager::setMockUASManager(_mockUASManager);
    
    _mockUAS = new MockUAS();
    Q_CHECK_PTR(_mockUAS);
    
    // This will instatiate the widget with no active UAS set
    _calWidget = new PX4RCCalibration();
    Q_CHECK_PTR(_calWidget);
    
    _mockUASManager->setMockActiveUAS(_mockUAS);

    // Get pointers to the push buttons
    _cancelButton = _calWidget->findChild<QPushButton*>("rcCalCancel");
    _nextButton = _calWidget->findChild<QPushButton*>("rcCalNext");
    _skipButton = _calWidget->findChild<QPushButton*>("rcCalSkip");
    _tryAgainButton = _calWidget->findChild<QPushButton*>("rcCalTryAgain");
    
    Q_ASSERT(_cancelButton);
    Q_ASSERT(_nextButton);
    Q_ASSERT(_skipButton);
    Q_ASSERT(_tryAgainButton);
    
    _statusLabel = _calWidget->findChild<QLabel*>("rcCalStatus");
    Q_ASSERT(_statusLabel);
 
    for (size_t i=0; i<PX4RCCalibration::_chanMax; i++) {
        QString radioWidgetName("radio%1Widget");
        QString radioWidgetUserName("Radio %1");
        
Don Gagne's avatar
Don Gagne committed
188
        RCChannelWidget* radioWidget = _calWidget->findChild<RCChannelWidget*>(radioWidgetName.arg(i+1));
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
        Q_ASSERT(radioWidget);
        
        _rgRadioWidget[i] = radioWidget;
    }
}

void PX4RCCalibrationTest::cleanup(void)
{
    Q_ASSERT(_mockUAS);
    delete _mockUAS;
    
    UASManager::setMockUASManager(NULL);

    Q_ASSERT(_mockUASManager);
    delete _mockUASManager;
    
    Q_ASSERT(_calWidget);
    delete _calWidget;
}

/// @brief Tests for correct behavior when active UAS is set into widget.
void PX4RCCalibrationTest::_setUAS_test(void)
{
    // Widget is initialized with UAS, so it should be enabled
    QCOMPARE(_calWidget->isEnabled(), true);

    // Take away the UAS and widget should disable
    _mockUASManager->setMockActiveUAS(NULL);
    QCOMPARE(_calWidget->isEnabled(), false);
}

/// @brief Test for correct behavior in determining minimum numbers of channels for fligth.
void PX4RCCalibrationTest::_minRCChannels_test(void)
{
    // Next button won't be enabled until we see the minimum number of channels.
224 225
    for (int chan=0; chan<PX4RCCalibration::_chanMinimum; chan++) {
        _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint);
Don Gagne's avatar
Don Gagne committed
226 227
        
        // We use _chanCount internally so we should validate it
228
        QCOMPARE(_calWidget->_chanCount, chan+1);
Don Gagne's avatar
Don Gagne committed
229 230
        
        // Validate Next button state
231
        if (chan == PX4RCCalibration::_chanMinimum - 1) {
232 233 234 235 236 237
            // Last channel should trigger enable
            CHK_BUTTONS(nextButtonMask);
        } else {
            // Still less than the minimum channels
            CHK_BUTTONS(0);
        }
Don Gagne's avatar
Don Gagne committed
238 239 240 241

        // Only available channels should have enabled widget. A ui update cycle needs to have passed so we wait a little.
        QTest::qWait(PX4RCCalibration::_updateInterval * 2);
        for (int chanWidget=0; chanWidget<PX4RCCalibration::_chanMax; chanWidget++) {
242
            QCOMPARE(_rgRadioWidget[chanWidget]->isEnabled(), !!(chanWidget <= chan));
Don Gagne's avatar
Don Gagne committed
243
        }
244 245 246
    }
}

Don Gagne's avatar
Don Gagne committed
247
void PX4RCCalibrationTest::_beginState_worker(enum TestMode mode)
248 249 250 251
{
    bool tryCancel1 = true;
    
StartOver:
Don Gagne's avatar
Don Gagne committed
252 253
    if (mode == testModeStandalone || mode == testModePrerequisite) {
        _centerChannels();
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateBegin);
    }

    // Next button is always enabled in this state
    CHK_BUTTONS(nextButtonMask | cancelButtonMask);

    // Click the next button:
    // We should now be waiting for movement on a channel to identify the first RC function. The Next button will stay
    // disabled until the sticks are moved enough to identify the channel. For required functions the Skip button is
    // disabled.
    NEXT_OR_CANCEL(1);
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateIdentify);
    CHK_BUTTONS(cancelButtonMask);
}

void PX4RCCalibrationTest::_beginState_test(void)
{
Don Gagne's avatar
Don Gagne committed
272
    _beginState_worker(testModeStandalone);
273 274
}

Don Gagne's avatar
Don Gagne committed
275
void PX4RCCalibrationTest::_identifyState_worker(enum TestMode mode)
276
{
Don Gagne's avatar
Don Gagne committed
277
    bool tryCancel1 = true;
278 279
    
StartOver:
Don Gagne's avatar
Don Gagne committed
280 281 282
    if (mode == testModeStandalone || mode == testModePrerequisite)
    {
        _centerChannels();
283 284 285 286 287
        
        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateIdentify);
        
    }
    
288 289 290 291 292 293 294 295
    // Loop over all function identifying mapped channels
    for (int function=0; function<PX4RCCalibration::rcCalFunctionMax; function++) {
		int channelIndex = _rgFunctionChannelMap[function];
		
        // If this function is not mapped we skip it
		bool skipNonMapped = channelIndex == -1;
		bool skipButtonEnabled = !PX4RCCalibration::_rgFunctionInfo[function].required;
        int skipMask = skipButtonEnabled ? skipButtonMask : 0;
296 297 298 299 300 301
        
        // We should now be waiting for movement on a channel to identify the RC function. The Next button will stay
        // disabled until the sticks are moved enough to identify the channel. For required functions the Skip button is
        // disabled.
        CHK_BUTTONS(cancelButtonMask | skipMask);

302 303
        // Skip this mapping if specified by test case data
        if (skipNonMapped) {
Don Gagne's avatar
Don Gagne committed
304 305 306 307 308
            QTest::mouseClick(_skipButton, Qt::LeftButton);
            continue;
        }
        
        // Move channel less than delta to make sure function is not identified
309
        _mockUAS->emitRemoteControlChannelRawChanged(channelIndex, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta - 2.0f));
310 311
        CHK_BUTTONS(cancelButtonMask | skipMask);

312 313 314 315
        if (function != 0) {
            // Try to assign a function index 0 channel to more than one function. This is not allowed so Next button should not enable.
            _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[0], (float)PX4RCCalibration::_rcCalPWMValidMinValue);
            _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[0], (float)PX4RCCalibration::_rcCalPWMValidMaxValue);
316 317 318 319 320 321 322 323
            CHK_BUTTONS(cancelButtonMask | skipMask);
        }

        if (tryCancel1) {
            NEXT_OR_CANCEL(1);
        }
        
        // Move channel larger than delta to identify channel. We should now be sitting in a found state.
324
        _mockUAS->emitRemoteControlChannelRawChanged(channelIndex, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta + 2.0f));
325 326 327 328 329 330 331 332 333
        CHK_BUTTONS(cancelButtonMask | tryAgainButtonMask | nextButtonMask);

        NEXT_OR_CANCEL(1);
    }
    
    // We should now be waiting for min/max values.
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateMinMax);
    CHK_BUTTONS(nextButtonMask | cancelButtonMask);
    
Don Gagne's avatar
Don Gagne committed
334
    if (mode == testModeStandalone) {
335
        _calWidget->_writeCalibration(false /* !trimsOnly */);
Don Gagne's avatar
Don Gagne committed
336
        _validateParameters(validateMappingMask);
337 338 339 340 341
    }
}

void PX4RCCalibrationTest::_identifyState_test(void)
{
Don Gagne's avatar
Don Gagne committed
342
    _identifyState_worker(testModeStandalone);
343 344
}

Don Gagne's avatar
Don Gagne committed
345
void PX4RCCalibrationTest::_minMaxState_worker(enum TestMode mode)
346 347 348 349
{
    bool tryCancel1 = true;
    
StartOver:
Don Gagne's avatar
Don Gagne committed
350
    if (mode == testModeStandalone || mode == testModePrerequisite) {
351 352 353
        // The Min/Max calibration updates the radio channel ui widgets with the min/max values for all mapped channels.
        // In order for those adio channel ui widgets to be updated correctly those functions must be alread mapped to a
		// channel. So we have to run the _identifyState_test first to set up the internal state correctly.
Don Gagne's avatar
Don Gagne committed
354
        _identifyState_worker(testModePrerequisite);
355

Don Gagne's avatar
Don Gagne committed
356
        _centerChannels();
357 358 359 360 361 362
        
        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateMinMax);
        
        // We should now be waiting for min/max values.
        CHK_BUTTONS(nextButtonMask | cancelButtonMask);
    }
Don Gagne's avatar
Don Gagne committed
363 364 365 366 367 368 369 370 371 372 373 374 375
    
    // Before we start sending rc values the widgets should all have min/max as invalid
    for (int chan=0; chan<PX4RCCalibration::_chanMax; chan++) {
        QCOMPARE(_rgRadioWidget[chan]->isMinValid(), false);
        QCOMPARE(_rgRadioWidget[chan]->isMaxValid(), false);
    }
    
    // Try setting a min/max value that is below the threshold to make sure min/max doesn't go valid
    _mockUAS->emitRemoteControlChannelRawChanged(0, (float)(PX4RCCalibration::_rcCalPWMValidMinValue + 1));
    _mockUAS->emitRemoteControlChannelRawChanged(0, (float)(PX4RCCalibration::_rcCalPWMValidMaxValue - 1));
    QCOMPARE(_rgRadioWidget[0]->isMinValid(), false);
    QCOMPARE(_rgRadioWidget[0]->isMaxValid(), false);

376 377 378 379 380 381 382 383
    // Send min/max values for all channels
    for (int chan=0; chan<_availableChannels; chan++) {
		//qDebug() << chan << _rgChannelSettingsPreValidate[chan].rcMin << _rgChannelSettingsPreValidate[chan].rcMax;
		_mockUAS->emitRemoteControlChannelRawChanged(chan, (float)_rgChannelSettingsPreValidate[chan].rcMin);
		_mockUAS->emitRemoteControlChannelRawChanged(chan, (float)_rgChannelSettingsPreValidate[chan].rcMax);
		
		// Re-Center to not screw up next state
		_mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint);
384 385
    }

Don Gagne's avatar
Don Gagne committed
386 387
    _validateWidgets(validateMinMaxMask, _rgChannelSettingsPreValidate);
    
388
    // Make sure throttle is at min
389
    _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionThrottle], (float)PX4RCCalibration::_rcCalPWMValidMinValue);
390 391 392 393 394 395 396

    // Click the next button: We should now be waiting for center throttle in prep for inversion detection.
    // Throttle channel is at minimum so Next button should be disabled.
    NEXT_OR_CANCEL(1);
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateCenterThrottle);
    CHK_BUTTONS(cancelButtonMask);

Don Gagne's avatar
Don Gagne committed
397
    if (mode == testModeStandalone) {
398 399 400 401 402 403 404
        _calWidget->_writeCalibration(false /* !trimsOnly */);
        _validateParameters(validateMinMaxMask);
    }
}

void PX4RCCalibrationTest::_minMaxState_test(void)
{
Don Gagne's avatar
Don Gagne committed
405
    _minMaxState_worker(testModeStandalone);
406 407
}

Don Gagne's avatar
Don Gagne committed
408
void PX4RCCalibrationTest::_centerThrottleState_worker(enum TestMode mode)
409 410 411 412
{
    bool tryCancel1 = true;
    
StartOver:
Don Gagne's avatar
Don Gagne committed
413
    if (mode == testModeStandalone || mode == testModePrerequisite) {
414 415
        // In order to perform the center throttle state test the throttle channel has to have been identified.
        // So we have to run the _identifyState_test first to set up the internal state correctly.
Don Gagne's avatar
Don Gagne committed
416
        _identifyState_worker(testModePrerequisite);
417
        
Don Gagne's avatar
Don Gagne committed
418
        _centerChannels();
419
        _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionThrottle], (float)PX4RCCalibration::_rcCalPWMValidMinValue);
420 421 422 423 424 425 426 427 428
        
        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateCenterThrottle);
        
        // We should now be waiting for center throttle in prep for inversion detection.
        // Throttle channel is at minimum so Next button should be disabled.
        CHK_BUTTONS(cancelButtonMask);
    }

    // Move the throttle to just below rough center. Next should still be disabled
429
    _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionThrottle], PX4RCCalibration::_rcCalPWMCenterPoint - PX4RCCalibration::_rcCalRoughCenterDelta - 1);
430 431 432
    CHK_BUTTONS(cancelButtonMask);
    
    // Center the throttle and make sure Next button gets enabled
433
    _mockUAS->emitRemoteControlChannelRawChanged(_rgFunctionChannelMap[PX4RCCalibration::rcCalFunctionThrottle], PX4RCCalibration::_rcCalPWMCenterPoint);
434 435 436 437 438 439 440 441 442 443 444
    CHK_BUTTONS(cancelButtonMask | nextButtonMask);
    
    // Click the next button which should take us to our first channel inversion test. The Next button will stay disabled until
    // the stick for the specified channel is moved down.
    NEXT_OR_CANCEL(1);
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateDetectInversion);
    CHK_BUTTONS(cancelButtonMask);
}

void PX4RCCalibrationTest::_centerThrottleState_test(void)
{
Don Gagne's avatar
Don Gagne committed
445
    _centerThrottleState_worker(testModeStandalone);
446 447
}

Don Gagne's avatar
Don Gagne committed
448
void PX4RCCalibrationTest::_detectInversionState_worker(enum TestMode mode)
449 450 451 452 453
{
    bool tryCancel1 = true;
    bool tryCancel2 = true;
    
StartOver:
Don Gagne's avatar
Don Gagne committed
454
    if (mode == testModeStandalone || mode == testModePrerequisite) {
455 456
        // In order to perform the detect inversion test the roll/pitch/yaw/throttle functions must be mapped to a channel.
        // So we have to run the _identifyState_test first to set up the internal state correctly.
Don Gagne's avatar
Don Gagne committed
457
        _identifyState_worker(testModePrerequisite);
458
        
Don Gagne's avatar
Don Gagne committed
459
        _centerChannels();
460 461 462 463 464 465 466 467 468 469
        
        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateDetectInversion);
        
        // We should now be at the first channel inversion test. The Next button will stay disabled until the stick for the specified
        // channel is moved in the appropriate direction.
        CHK_BUTTONS(cancelButtonMask);
    }

    // Loop over Attitude Control Functions (roll/yaw/pitch/throttle) to detect inversion
    for (int chanFunction=PX4RCCalibration::rcCalFunctionFirstAttitudeFunction; chanFunction<=PX4RCCalibration::rcCalFunctionLastAttitudeFunction; chanFunction++) {
470 471
		int channelIndex = _rgFunctionChannelMap[chanFunction];
		
472 473 474 475 476 477 478
        if (chanFunction != 0) {
            // Click next to move to next inversion to identify
            NEXT_OR_CANCEL(1);
            CHK_BUTTONS(cancelButtonMask);
        }
        
        // Move all channels except for the one we are trying to detect to min and max value to make sure there is no effect.
Don Gagne's avatar
Don Gagne committed
479
        for (int chan=0; chan<_availableChannels; chan++) {
480
            if (channelIndex != chan) {
Don Gagne's avatar
Don Gagne committed
481 482
                _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta + 2.0f));
                _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint - (PX4RCCalibration::_rcCalMoveDelta + 2.0f));
483
                CHK_BUTTONS(cancelButtonMask);
Don Gagne's avatar
Don Gagne committed
484 485

                // Make sure to re-center for next inversion detect
486
				//qDebug() << "Inversion recenter" << chan << PX4RCCalibration::_rcCalPWMCenterPoint;
Don Gagne's avatar
Don Gagne committed
487
                _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint);
488 489 490
            }
        }
        
491
        // Move the channel we are detecting inversion on to the min or max value depending onn test case data.
492
        // This should put us in the found state and enable the Next button.
493 494 495 496 497 498 499 500 501
		float inversionValue = PX4RCCalibration::_rcCalPWMCenterPoint;
		float inversionDelta = (float)(PX4RCCalibration::_rcCalMoveDelta + 2.0f);
		if (_rgChannelSettingsPreValidate[channelIndex].reversed) {
			inversionValue += inversionDelta;
		} else {
			inversionValue -= inversionDelta;
		}
		//qDebug() << "Reverse set" << chanFunction << channelIndex << inversionValue;
        _mockUAS->emitRemoteControlChannelRawChanged(channelIndex, inversionValue);
502 503 504 505 506
        CHK_BUTTONS(cancelButtonMask | tryAgainButtonMask | nextButtonMask);
    }
    
    // Click the next button: We should now be waiting for low throttle in prep for trim detection.
    // Throttle channel is at minimum so Next button should be disabled.
Don Gagne's avatar
Don Gagne committed
507
    _centerChannels();
508 509 510 511
    NEXT_OR_CANCEL(2);
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateTrims);
    CHK_BUTTONS(cancelButtonMask);
    
Don Gagne's avatar
Don Gagne committed
512
    if (mode == testModeStandalone) {
513 514 515 516 517 518 519
        _calWidget->_writeCalibration(false /* !trimsOnly */);
        _validateParameters(validateMappingMask | validateReversedMask);
    }
}

void PX4RCCalibrationTest::_detectInversionState_test(void)
{
Don Gagne's avatar
Don Gagne committed
520
    _detectInversionState_worker(testModeStandalone);
521 522
}

Don Gagne's avatar
Don Gagne committed
523
void PX4RCCalibrationTest::_trimsState_worker(enum TestMode mode)
524 525 526 527
{
    bool tryCancel1 = true;
    
StartOver:
Don Gagne's avatar
Don Gagne committed
528
    if (mode == testModeStandalone || mode == testModePrerequisite) {
529
        // In order to perform the trim state test the functions must be mapped and the min/max values must be set.
Don Gagne's avatar
Don Gagne committed
530 531
        // So we have to run the _minMaxState_test first to set up the internal state correctly.
        _minMaxState_worker(testModePrerequisite);
532
        
Don Gagne's avatar
Don Gagne committed
533
        _centerChannels();
534 535 536 537 538 539 540
        
        _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateTrims);
        
        // We should now be waiting for low throttle.
        CHK_BUTTONS(cancelButtonMask);
    }

541 542 543 544
    // Send trim values to channels
	for (int chan=0; chan<_availableChannels; chan++) {
		//qDebug () << "Trim set" << chan << _rgChannelSettingsPreValidate[chan].rcTrim;
		_mockUAS->emitRemoteControlChannelRawChanged(chan, _rgChannelSettingsPreValidate[chan].rcTrim);
Don Gagne's avatar
Don Gagne committed
545 546 547 548 549
    }
    
    _validateWidgets(validateTrimsMask, _rgChannelSettingsPreValidate);
    
    // Throttle trim was set, so next should be enabled
550 551 552 553 554 555
    CHK_BUTTONS(cancelButtonMask | nextButtonMask);
    
    // Click the next button which should set Trims and take us the Save step.
    NEXT_OR_CANCEL(1);
    QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateSave);
    CHK_BUTTONS(cancelButtonMask | nextButtonMask);
Don Gagne's avatar
Don Gagne committed
556
    _validateWidgets(validateTrimsMask, _rgChannelSettingsPostValidate);
557

Don Gagne's avatar
Don Gagne committed
558
    if (mode == testModeStandalone) {
559 560 561 562 563 564 565
        _calWidget->_writeCalibration(false /* !trimsOnly */);
        _validateParameters(validateTrimsMask);
    }
}

void PX4RCCalibrationTest::_trimsState_test(void)
{
Don Gagne's avatar
Don Gagne committed
566
    _trimsState_worker(testModeStandalone);
567 568 569
}

void PX4RCCalibrationTest::_fullCalibration_test(void) {
Don Gagne's avatar
Don Gagne committed
570
    _centerChannels();
571 572
    QTest::mouseClick(_nextButton, Qt::LeftButton);

Don Gagne's avatar
Don Gagne committed
573 574 575 576 577 578
    _beginState_worker(testModeFullSequence);
    _identifyState_worker(testModeFullSequence);
    _minMaxState_worker(testModeFullSequence);
    _centerThrottleState_worker(testModeFullSequence);
    _detectInversionState_worker(testModeFullSequence);
    _trimsState_worker(testModeFullSequence);
579 580 581 582 583

    // One more click and the parameters should get saved
    QTest::mouseClick(_nextButton, Qt::LeftButton);
    
    _validateParameters(validateAllMask);
Don Gagne's avatar
Don Gagne committed
584
    _validateWidgets(validateAllMask, _rgChannelSettingsPostValidate);
585 586
}

Don Gagne's avatar
Don Gagne committed
587 588
/// @brief Sends RC center point values on minimum set of channels.
void PX4RCCalibrationTest::_centerChannels(void)
589
{
Don Gagne's avatar
Don Gagne committed
590 591 592
    // Initialize available channels them to center point. This should also set the channel count above the
    // minimum such that we can enter the idle state.
    for (int i=0; i<_availableChannels; i++) {
593 594 595 596
        _mockUAS->emitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint);
    }
}

Don Gagne's avatar
Don Gagne committed
597
void PX4RCCalibrationTest::_validateParameters(int validateMask)
598 599 600 601 602 603 604 605 606
{
    MockQGCUASParamManager* paramMgr = _mockUAS->getMockQGCUASParamManager();
    MockQGCUASParamManager::ParamMap_t mapParamsSet = paramMgr->getMockSetParameters();

    QString minTpl("RC%1_MIN");
    QString maxTpl("RC%1_MAX");
    QString trimTpl("RC%1_TRIM");
    QString revTpl("RC%1_REV");
    
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
	if (validateMask & validateMappingMask) {
		// Check mapping for all fuctions
		for (int chanFunction=0; chanFunction<PX4RCCalibration::rcCalFunctionMax; chanFunction++) {
			int chanIndex = _rgFunctionChannelMap[chanFunction];
			
			int expectedParameterValue;
			if (chanIndex == -1) {
				expectedParameterValue = 0;  // 0 signals no mapping
			} else {
				expectedParameterValue = chanIndex + 1; // 1-based parameter value
			}
			
			if (validateMask & validateMappingMask) {
				QCOMPARE(mapParamsSet.contains(PX4RCCalibration::_rgFunctionInfo[chanFunction].parameterName), true);
				QCOMPARE(mapParamsSet[PX4RCCalibration::_rgFunctionInfo[chanFunction].parameterName].toInt(), expectedParameterValue);
			}
		}
	}
Don Gagne's avatar
Don Gagne committed
625 626 627 628 629 630 631 632

    // Validate the channel settings. Note the channels are 1-based in parameter names.
    for (int chan = 0; chan<PX4RCCalibration::_chanMax; chan++) {
        int oneBasedChannel = chan + 1;
        int rcMinExpected, rcMaxExpected, rcTrimExpected;
        bool convertOk;
        
        // Required channels have min/max set on them. Remaining channels are left to default.
633 634 635
		rcMinExpected = _rgChannelSettingsPostValidate[chan].rcMin;
		rcMaxExpected = _rgChannelSettingsPostValidate[chan].rcMax;
		rcTrimExpected = _rgChannelSettingsPostValidate[chan].rcTrim;
Don Gagne's avatar
Don Gagne committed
636

637
        if (validateMask & validateMinMaxMask) {
Don Gagne's avatar
Don Gagne committed
638 639 640 641 642 643 644 645 646 647 648 649
            QCOMPARE(mapParamsSet.contains(minTpl.arg(oneBasedChannel)), true);
            QCOMPARE(mapParamsSet.contains(maxTpl.arg(oneBasedChannel)), true);
            QCOMPARE(mapParamsSet[minTpl.arg(oneBasedChannel)].toInt(&convertOk), rcMinExpected);
            QCOMPARE(convertOk, true);
            QCOMPARE(mapParamsSet[maxTpl.arg(oneBasedChannel)].toInt(&convertOk), rcMaxExpected);
            QCOMPARE(convertOk, true);
        }
        
        if (validateMask & validateTrimsMask) {
            QCOMPARE(mapParamsSet.contains(trimTpl.arg(oneBasedChannel)), true);
            QCOMPARE(mapParamsSet[trimTpl.arg(oneBasedChannel)].toInt(&convertOk), rcTrimExpected);
            QCOMPARE(convertOk, true);
650 651 652
        }

        if (validateMask & validateReversedMask) {
653 654 655
			float revValue = _rgChannelSettingsPostValidate[chan].reversed ? -1.0f : 1.0f;
			
			//qDebug() << "Reverse validate" << chan << mapParamsSet[revTpl.arg(oneBasedChannel)].toFloat() << revValue;
Don Gagne's avatar
Don Gagne committed
656
            QCOMPARE(mapParamsSet.contains(revTpl.arg(oneBasedChannel)), true);
657
            QCOMPARE(mapParamsSet[revTpl.arg(oneBasedChannel)].toFloat(&convertOk), revValue);
Don Gagne's avatar
Don Gagne committed
658
            QCOMPARE(convertOk, true);
659 660 661 662
        }
    }
    
    if (validateMask & validateMappingMask) {
Don Gagne's avatar
Don Gagne committed
663
        // Check mapping for all fuctions
664 665
        for (int chanFunction=0; chanFunction<PX4RCCalibration::rcCalFunctionMax; chanFunction++) {
            QCOMPARE(mapParamsSet.contains(PX4RCCalibration::_rgFunctionInfo[chanFunction].parameterName), true);
Don Gagne's avatar
Don Gagne committed
666

667
            int expectedValue;
668 669
            if (_rgFunctionChannelMap[chanFunction] == -1) {
				expectedValue = 0;  // 0 signals no mapping
Don Gagne's avatar
Don Gagne committed
670
            } else {
671
				expectedValue = _rgFunctionChannelMap[chanFunction] + 1; // 1-based
672
            }
673
			// qDebug() << chanFunction << expectedValue << mapParamsSet[PX4RCCalibration::_rgFunctionInfo[chanFunction].parameterName].toInt();
674 675 676
            QCOMPARE(mapParamsSet[PX4RCCalibration::_rgFunctionInfo[chanFunction].parameterName].toInt(), expectedValue);
        }
    }
Don Gagne's avatar
Don Gagne committed
677
}
678

Don Gagne's avatar
Don Gagne committed
679 680 681 682 683 684 685 686 687
void PX4RCCalibrationTest::_validateWidgets(int validateMask, const struct ChannelSettings* rgChannelSettings)
{
    // Radio channel widgets should be displaying the current min/max we just set. Wait a bit for ui to update before checking.
    
    QTest::qWait(PX4RCCalibration::_updateInterval * 2);
    
    for (int chan=0; chan<_availableChannels; chan++) {
        RCChannelWidget* radioWidget = _rgRadioWidget[chan];
        Q_ASSERT(radioWidget);
688
		
Don Gagne's avatar
Don Gagne committed
689
        if (validateMask & validateMinMaxMask) {
690
			//qDebug() << chan;
Don Gagne's avatar
Don Gagne committed
691 692 693 694 695 696 697 698
            QCOMPARE(radioWidget->isMinMaxShown(), rgChannelSettings[chan].isMinMaxShown);
            QCOMPARE(radioWidget->min(), rgChannelSettings[chan].rcMin);
            QCOMPARE(radioWidget->max(), rgChannelSettings[chan].rcMax);
            QCOMPARE(radioWidget->isMinValid(), rgChannelSettings[chan].isMinValid);
            QCOMPARE(radioWidget->isMaxValid(), rgChannelSettings[chan].isMaxValid);
        }
        
        if (validateMask & validateTrimsMask) {
699
			//qDebug() << "Trim validate widget" << chan;
Don Gagne's avatar
Don Gagne committed
700
            QCOMPARE(radioWidget->trim(), rgChannelSettings[chan].rcTrim);
701 702
        }
    }
Don Gagne's avatar
Don Gagne committed
703
    
Don Gagne's avatar
Don Gagne committed
704
    for (int chan=_availableChannels; chan<PX4RCCalibration::_chanMax; chan++) {
Don Gagne's avatar
Don Gagne committed
705 706 707
        QCOMPARE(_rgRadioWidget[chan]->isEnabled(), false);
    }
}