From e39b779ad0e0f88d3929a66544b930df06b0ea96 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 25 Aug 2014 11:43:26 -0700 Subject: [PATCH] Updated validation rules Also uses new min/max/invalid widget display --- src/qgcunittest/PX4RCCalibrationTest.cc | 413 +++++++++++++------ src/qgcunittest/PX4RCCalibrationTest.h | 55 ++- src/ui/px4_configuration/PX4RCCalibration.cc | 108 +++-- src/ui/px4_configuration/PX4RCCalibration.h | 17 +- 4 files changed, 419 insertions(+), 174 deletions(-) diff --git a/src/qgcunittest/PX4RCCalibrationTest.cc b/src/qgcunittest/PX4RCCalibrationTest.cc index b296db6e5..13847a9e7 100644 --- a/src/qgcunittest/PX4RCCalibrationTest.cc +++ b/src/qgcunittest/PX4RCCalibrationTest.cc @@ -26,7 +26,7 @@ #include "MockQGCUASParamManager.h" /// @file -/// @brief QGCPX4RCCAlibration Widget unit test +/// @brief QPX4RCCalibration Widget unit test /// /// @author Don Gagne @@ -50,7 +50,7 @@ // by the Next button on the second iteration. #define NEXT_OR_CANCEL(cancelNum) \ { \ - if (standaloneTest && tryCancel ## cancelNum) { \ + if (mode == testModeStandalone && tryCancel ## cancelNum) { \ QTest::mouseClick(_cancelButton, Qt::LeftButton); \ QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateChannelWait); \ tryCancel ## cancelNum = false; \ @@ -60,11 +60,94 @@ } \ } +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; + +const struct PX4RCCalibrationTest::ChannelSettings PX4RCCalibrationTest::_rgChannelSettingsPreValidate[PX4RCCalibrationTest::_availableChannels] = { + //Min Value Max Value Trim Value Reversed MinMaxShown MinValid MaxValid + // Channel 0 : rcCalFunctionRoll + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 1 : rcCalFunctionPitch + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 2 : rcCalFunctionYaw + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 3 : rcCalFunctionThrottle + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testThrottleTrimValue, false, true, true, true }, + // Channel 4 : rcCalFunctionModeSwitch + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibration::_rcCalPWMCenterPoint, false, true, true, true }, + + // Channel 5 : Simulate invalid Min, valid Max + { PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMCenterPoint, false, true, false, true }, + + // Channels 6-7: Invalid Min/Max, since available channel Min/Max is still shown + { PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMCenterPoint, false, true, false, false }, + { PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMCenterPoint, PX4RCCalibration::_rcCalPWMCenterPoint, false, true, false, false }, +}; + +const struct PX4RCCalibrationTest::ChannelSettings PX4RCCalibrationTest::_rgChannelSettingsPostValidate[PX4RCCalibration::_chanMax] = { + // Min Value Max Value Trim Value Reversed MinMaxShown MinValid MaxValid + // Channel 0 : rcCalFunctionRoll + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 1 : rcCalFunctionPitch + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 2 : rcCalFunctionYaw + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testTrimValue, false, true, true, true }, + // Channel 3 : rcCalFunctionThrottle + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibrationTest::_testThrottleTrimValue, false, true, true, true }, + // Channel 4 : rcCalFunctionModeSwitch + { PX4RCCalibrationTest::_testMinValue, PX4RCCalibrationTest::_testMaxValue, PX4RCCalibration::_rcCalPWMCenterPoint, false, true, true, true }, + + // Channel 5 : Simulate invalid Min, valid Max, validation should switch back to defaults + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, true, true, true }, + + // Channels 6-7: Invalid Min/Max, since available channel Min/Max is still shown, validation will set to defaults + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, true, true, true }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, true, true, true }, + + // We are simulating an 8-channel radio, all other channel should be defaulted + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, + { PX4RCCalibration::_rcCalPWMDefaultMinValue, PX4RCCalibration::_rcCalPWMDefaultMaxValue, PX4RCCalibration::_rcCalPWMDefaultTrimValue, false, false, false, false }, +}; + PX4RCCalibrationTest::PX4RCCalibrationTest(void) : _mockUASManager(NULL), _calWidget(NULL) { + +} + +/// @brief Called one time before any test cases are run. +void PX4RCCalibrationTest::initTestCase(void) +{ + // The test case code makes the following assumptions about PX4RCCalibration class internals. + // Make sure these don't change out from under us. + + Q_ASSERT(PX4RCCalibration::rcCalFunctionRoll == 0); + Q_ASSERT(PX4RCCalibration::rcCalFunctionPitch == 1); + Q_ASSERT(PX4RCCalibration::rcCalFunctionYaw == 2); + Q_ASSERT(PX4RCCalibration::rcCalFunctionThrottle == 3); + Q_ASSERT(PX4RCCalibration::rcCalFunctionModeSwitch == 4); + // We only set min/max for required channels. Make sure the set of required channels doesn't change out from under us. + for (int chanFunction=0; chanFunctionemitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint); + + // We use _chanCount internally so we should validate it + QCOMPARE(_calWidget->_chanCount, i+1); + + // Validate Next button state if (i == PX4RCCalibration::_chanMinimum - 1) { // Last channel should trigger enable CHK_BUTTONS(nextButtonMask); @@ -146,6 +234,12 @@ void PX4RCCalibrationTest::_minRCChannels_test(void) // Still less than the minimum channels CHK_BUTTONS(0); } + + // 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; chanWidgetisEnabled(), !!(chanWidget <= i)); + } } } @@ -167,13 +261,13 @@ void PX4RCCalibrationTest::_liveRC_test(void) } #endif -void PX4RCCalibrationTest::_beginState_worker(bool standaloneTest) +void PX4RCCalibrationTest::_beginState_worker(enum TestMode mode) { bool tryCancel1 = true; StartOver: - if (standaloneTest) { - _centerAllChannels(); + if (mode == testModeStandalone || mode == testModePrerequisite) { + _centerChannels(); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateBegin); } @@ -192,34 +286,41 @@ StartOver: void PX4RCCalibrationTest::_beginState_test(void) { - _beginState_worker(true /* standalone test */); + _beginState_worker(testModeStandalone); } -void PX4RCCalibrationTest::_identifyState_worker(bool standaloneTest, bool skipOptional) +void PX4RCCalibrationTest::_identifyState_worker(enum TestMode mode) { - bool tryCancel1 = standaloneTest; + bool tryCancel1 = true; StartOver: - if (standaloneTest) { - _centerAllChannels(); + if (mode == testModeStandalone || mode == testModePrerequisite) + { + _centerChannels(); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateIdentify); } - // Loop over all functions setting them to a channel + // Loop over all function idenitfying required channels for (int i=0; iemitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint - (PX4RCCalibration::_rcCalMoveDelta - 2.0f)); + // Skip this mapping if allowed + if (skipNonRequired) { + QTest::mouseClick(_skipButton, Qt::LeftButton); + continue; + } + + // Move channel less than delta to make sure function is not identified + _mockUAS->emitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta - 2.0f)); CHK_BUTTONS(cancelButtonMask | skipMask); if (i != 0) { @@ -229,20 +330,12 @@ StartOver: CHK_BUTTONS(cancelButtonMask | skipMask); } - // Skip this mapping if allowed and requested - if (skipAllowed && skipOptional) { - QTest::mouseClick(_skipButton, Qt::LeftButton); - continue; - } else { - - } - if (tryCancel1) { NEXT_OR_CANCEL(1); } // Move channel larger than delta to identify channel. We should now be sitting in a found state. - _mockUAS->emitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMValidMinValue); + _mockUAS->emitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta + 2.0f)); CHK_BUTTONS(cancelButtonMask | tryAgainButtonMask | nextButtonMask); NEXT_OR_CANCEL(1); @@ -252,48 +345,58 @@ StartOver: QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateMinMax); CHK_BUTTONS(nextButtonMask | cancelButtonMask); - if (standaloneTest) { + if (mode == testModeStandalone) { _calWidget->_writeCalibration(false /* !trimsOnly */); - _validateParameters(validateMappingMask, skipOptional); + _validateParameters(validateMappingMask); } } void PX4RCCalibrationTest::_identifyState_test(void) { - _identifyState_worker(true /* standalone test */, false /* don't skip optional */); + _identifyState_worker(testModeStandalone); } -void PX4RCCalibrationTest::_identifyStateSkipOptional_test(void) -{ - _identifyState_worker(true /* standalone test */, true /* skip optional */); -} - -void PX4RCCalibrationTest::_minMaxState_worker(bool standaloneTest) +void PX4RCCalibrationTest::_minMaxState_worker(enum TestMode mode) { bool tryCancel1 = true; StartOver: - if (standaloneTest) { + if (mode == testModeStandalone || mode == testModePrerequisite) { // The Min/Max calibration updates the radio channel ui widgets with the min/max values as you move the sticks. - // In order for the roll/pitch/yaw/throttle radio channel ui widgets to be updated correctly those fucntions + // In order for the roll/pitch/yaw/throttle radio 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. - _identifyState_test(); + _identifyState_worker(testModePrerequisite); - _centerAllChannels(); + _centerChannels(); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateMinMax); // We should now be waiting for min/max values. CHK_BUTTONS(nextButtonMask | cancelButtonMask); } - - // Send min/max values for all channels - for (int i=0; iemitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMValidMinValue); - _mockUAS->emitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMValidMaxValue); + + // Before we start sending rc values the widgets should all have min/max as invalid + for (int chan=0; chanisMinValid(), 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); + + // Send min/max values + for (int chan=0; chan<_minMaxChannels; chan++) { + // Send Min/Max + _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)_rgChannelSettingsPreValidate[chan].rcMin); + _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)_rgChannelSettingsPreValidate[chan].rcMax); } + _validateWidgets(validateMinMaxMask, _rgChannelSettingsPreValidate); + // Make sure throttle is at min _mockUAS->emitRemoteControlChannelRawChanged(PX4RCCalibration::rcCalFunctionThrottle, (float)PX4RCCalibration::_rcCalPWMValidMinValue); @@ -303,18 +406,7 @@ StartOver: QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateCenterThrottle); CHK_BUTTONS(cancelButtonMask); - // Also at this point the radio channel widgets should be displaying the current min/max we just set. - - for (int i=0; iisMinMaxShown(), true); - QCOMPARE(radioWidget->min(), PX4RCCalibration::_rcCalPWMValidMinValue); - QCOMPARE(radioWidget->max(), PX4RCCalibration::_rcCalPWMValidMaxValue); - } - - if (standaloneTest) { + if (mode == testModeStandalone) { _calWidget->_writeCalibration(false /* !trimsOnly */); _validateParameters(validateMinMaxMask); } @@ -322,20 +414,20 @@ StartOver: void PX4RCCalibrationTest::_minMaxState_test(void) { - _minMaxState_worker(true /* standalone test */); + _minMaxState_worker(testModeStandalone); } -void PX4RCCalibrationTest::_centerThrottleState_worker(bool standaloneTest) +void PX4RCCalibrationTest::_centerThrottleState_worker(enum TestMode mode) { bool tryCancel1 = true; StartOver: - if (standaloneTest) { + if (mode == testModeStandalone || mode == testModePrerequisite) { // 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. - _identifyState_test(); + _identifyState_worker(testModePrerequisite); - _centerAllChannels(); + _centerChannels(); _mockUAS->emitRemoteControlChannelRawChanged(PX4RCCalibration::rcCalFunctionThrottle, (float)PX4RCCalibration::_rcCalPWMValidMinValue); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateCenterThrottle); @@ -362,21 +454,21 @@ StartOver: void PX4RCCalibrationTest::_centerThrottleState_test(void) { - _centerThrottleState_worker(true /* standalone test */); + _centerThrottleState_worker(testModeStandalone); } -void PX4RCCalibrationTest::_detectInversionState_worker(bool standaloneTest) +void PX4RCCalibrationTest::_detectInversionState_worker(enum TestMode mode) { bool tryCancel1 = true; bool tryCancel2 = true; StartOver: - if (standaloneTest) { + if (mode == testModeStandalone || mode == testModePrerequisite) { // 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. - _identifyState_test(); + _identifyState_worker(testModePrerequisite); - _centerAllChannels(); + _centerChannels(); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateDetectInversion); @@ -394,27 +486,31 @@ StartOver: } // Move all channels except for the one we are trying to detect to min and max value to make sure there is no effect. - for (int chan=0; chanemitRemoteControlChannelRawChanged(chan, PX4RCCalibration::_rcCalPWMValidMaxValue); + _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint + (PX4RCCalibration::_rcCalMoveDelta + 2.0f)); + _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint - (PX4RCCalibration::_rcCalMoveDelta + 2.0f)); CHK_BUTTONS(cancelButtonMask); + + // Make sure to re-center for next inversion detect + _mockUAS->emitRemoteControlChannelRawChanged(chan, (float)PX4RCCalibration::_rcCalPWMCenterPoint); } } // Move the channel we are detecting inversion on to the min value which should indicate no inversion. // This should put us in the found state and enable the Next button. - _mockUAS->emitRemoteControlChannelRawChanged(chanFunction, PX4RCCalibration::_rcCalPWMValidMinValue); + _mockUAS->emitRemoteControlChannelRawChanged(chanFunction, (float)PX4RCCalibration::_rcCalPWMCenterPoint - (PX4RCCalibration::_rcCalMoveDelta + 2.0f)); 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. - _centerAllChannels(); + _centerChannels(); NEXT_OR_CANCEL(2); QCOMPARE(_calWidget->_rcCalState, PX4RCCalibration::rcCalStateTrims); CHK_BUTTONS(cancelButtonMask); - if (standaloneTest) { + if (mode == testModeStandalone) { _calWidget->_writeCalibration(false /* !trimsOnly */); _validateParameters(validateMappingMask | validateReversedMask); } @@ -422,21 +518,20 @@ StartOver: void PX4RCCalibrationTest::_detectInversionState_test(void) { - _detectInversionState_worker(true /* standalone test */); + _detectInversionState_worker(testModeStandalone); } -void PX4RCCalibrationTest::_trimsState_worker(bool standaloneTest) +void PX4RCCalibrationTest::_trimsState_worker(enum TestMode mode) { bool tryCancel1 = true; StartOver: - if (standaloneTest) { + if (mode == testModeStandalone || mode == testModePrerequisite) { // In order to perform the trim state test the functions must be mapped and the min/max values must be set. - // So we have to run the _identifyState_test and _minMaxState_test first to set up the internal state correctly. - _identifyState_test(); - _minMaxState_test(); + // So we have to run the _minMaxState_test first to set up the internal state correctly. + _minMaxState_worker(testModePrerequisite); - _centerAllChannels(); + _centerChannels(); _calWidget->_unitTestForceCalState(PX4RCCalibration::rcCalStateTrims); @@ -444,16 +539,23 @@ StartOver: CHK_BUTTONS(cancelButtonMask); } - // Move the throttle to min. Next should enable - _mockUAS->emitRemoteControlChannelRawChanged(PX4RCCalibration::rcCalFunctionThrottle, PX4RCCalibration::_rcCalPWMValidMinValue); + // Send trim values to attitude control function channels + for (int chan=0; chan<_attitudeChannels; chan++) { + _mockUAS->emitRemoteControlChannelRawChanged(chan, _rgChannelSettingsPreValidate[chan].rcTrim); + } + + _validateWidgets(validateTrimsMask, _rgChannelSettingsPreValidate); + + // Throttle trim was set, so next should be enabled 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); + _validateWidgets(validateTrimsMask, _rgChannelSettingsPostValidate); - if (standaloneTest) { + if (mode == testModeStandalone) { _calWidget->_writeCalibration(false /* !trimsOnly */); _validateParameters(validateTrimsMask); } @@ -461,37 +563,38 @@ StartOver: void PX4RCCalibrationTest::_trimsState_test(void) { - _trimsState_worker(true /* standalone test */); + _trimsState_worker(testModeStandalone); } void PX4RCCalibrationTest::_fullCalibration_test(void) { - _centerAllChannels(); + _centerChannels(); QTest::mouseClick(_nextButton, Qt::LeftButton); - _beginState_worker(false); - _identifyState_worker(false, false); - _minMaxState_worker(false); - _centerThrottleState_worker(false); - _detectInversionState_worker(false); - _trimsState_worker(false); + _beginState_worker(testModeFullSequence); + _identifyState_worker(testModeFullSequence); + _minMaxState_worker(testModeFullSequence); + _centerThrottleState_worker(testModeFullSequence); + _detectInversionState_worker(testModeFullSequence); + _trimsState_worker(testModeFullSequence); // One more click and the parameters should get saved QTest::mouseClick(_nextButton, Qt::LeftButton); _validateParameters(validateAllMask); + _validateWidgets(validateAllMask, _rgChannelSettingsPostValidate); } -/// @brief Sends RC center point values on all channels. -void PX4RCCalibrationTest::_centerAllChannels(void) +/// @brief Sends RC center point values on minimum set of channels. +void PX4RCCalibrationTest::_centerChannels(void) { - // We use all channels for testing. Initialize to center point. This should set the channel count above the minimum - // such that we can enter the idle state. - for (int i=0; iemitRemoteControlChannelRawChanged(i, (float)PX4RCCalibration::_rcCalPWMCenterPoint); } } -void PX4RCCalibrationTest::_validateParameters(int validateMask, bool skipOptional) +void PX4RCCalibrationTest::_validateParameters(int validateMask) { MockQGCUASParamManager* paramMgr = _mockUAS->getMockQGCUASParamManager(); MockQGCUASParamManager::ParamMap_t mapParamsSet = paramMgr->getMockSetParameters(); @@ -501,51 +604,117 @@ void PX4RCCalibrationTest::_validateParameters(int validateMask, bool skipOption QString trimTpl("RC%1_TRIM"); QString revTpl("RC%1_REV"); - // Set Min/Max/Reversed parameter for all channels. Not that parameter name is 1-based. - for (int chan = 1; chan <= PX4RCCalibration::_chanMax; chan++) { + // Check mapping for all fuctions + for (int chanFunction=0; chanFunction= PX4RCCalibration::rcCalFunctionFirstAttitudeFunction && chan <= PX4RCCalibration::rcCalFunctionLastAttitudeFunction) { + if (chan == PX4RCCalibration::rcCalFunctionThrottle) { + rcTrimExpected = _testThrottleTrimValue; + } else { + rcTrimExpected = _testTrimValue; + } + } else { + rcTrimExpected = PX4RCCalibration::_rcCalPWMDefaultTrimValue; + } + if (validateMask & validateMinMaxMask) { - QCOMPARE(mapParamsSet.contains(minTpl.arg(chan)), true); - QCOMPARE(mapParamsSet[minTpl.arg(chan)].toInt(), PX4RCCalibration::_rcCalPWMValidMinValue); - QCOMPARE(mapParamsSet.contains(maxTpl.arg(chan)), true); - QCOMPARE(mapParamsSet[maxTpl.arg(chan)].toInt(), PX4RCCalibration::_rcCalPWMValidMaxValue); + 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); } + // No channels are reversed if (validateMask & validateReversedMask) { - QCOMPARE(mapParamsSet.contains(revTpl.arg(chan)), true); - QCOMPARE(mapParamsSet[revTpl.arg(chan)].toFloat(), 1.0f /* not reversed */); + QCOMPARE(mapParamsSet.contains(revTpl.arg(oneBasedChannel)), true); + QCOMPARE(mapParamsSet[revTpl.arg(oneBasedChannel)].toFloat(&convertOk), 1.0f /* not reversed */); + QCOMPARE(convertOk, true); } } if (validateMask & validateMappingMask) { + // Check mapping for all fuctions for (int chanFunction=0; chanFunctionisMinMaxShown(), 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) { + QCOMPARE(radioWidget->trim(), rgChannelSettings[chan].rcTrim); } } -} + + for (int chan=_availableChannels; chan<=PX4RCCalibration::_chanMax; chan++) { + QCOMPARE(_rgRadioWidget[chan]->isEnabled(), false); + } +} \ No newline at end of file diff --git a/src/qgcunittest/PX4RCCalibrationTest.h b/src/qgcunittest/PX4RCCalibrationTest.h index 1f466264c..3d1ad2538 100644 --- a/src/qgcunittest/PX4RCCalibrationTest.h +++ b/src/qgcunittest/PX4RCCalibrationTest.h @@ -30,10 +30,11 @@ #include "px4_configuration/PX4RCCalibration.h" /// @file -/// @brief QGCPX4RCCAlibration Widget unit test +/// @brief PX4RCCalibration Widget unit test /// /// @author Don Gagne +/// @brief PX4RCCalibration Widget unit test class PX4RCCalibrationTest : public QObject { Q_OBJECT @@ -42,6 +43,7 @@ public: PX4RCCalibrationTest(void); private slots: + void initTestCase(void); void init(void); void cleanup(void); @@ -50,7 +52,6 @@ private slots: //void _liveRC_test(void); void _beginState_test(void); void _identifyState_test(void); - void _identifyStateSkipOptional_test(void); void _minMaxState_test(void); void _centerThrottleState_test(void); void _detectInversionState_test(void); @@ -58,13 +59,20 @@ private slots: void _fullCalibration_test(void); private: - void _centerAllChannels(void); - void _beginState_worker(bool standaloneTest); - void _identifyState_worker(bool standaloneTest, bool skipOptional); - void _minMaxState_worker(bool standaloneTest); - void _centerThrottleState_worker(bool standaloneTest); - void _detectInversionState_worker(bool standaloneTest); - void _trimsState_worker(bool standaloneTest); + /// @brief Modes to run worker functions + enum TestMode { + testModeStandalone, ///< Perform standalone test of calibration state + testModePrerequisite, ///< Setup prequisites for subsequent calibration state + testModeFullSequence, ///< Run as full calibration sequence + }; + + void _centerChannels(void); + void _beginState_worker(enum TestMode mode); + void _identifyState_worker(enum TestMode mode); + void _minMaxState_worker(enum TestMode mode); + void _centerThrottleState_worker(enum TestMode mode); + void _detectInversionState_worker(enum TestMode mode); + void _trimsState_worker(enum TestMode mode); enum { validateMinMaxMask = 1 << 0, @@ -73,7 +81,19 @@ private: validateMappingMask = 1 << 3, validateAllMask = 0xFFFF }; - void _validateParameters(int validateMask, bool skipOptional = false); + + struct ChannelSettings { + int rcMin; + int rcMax; + int rcTrim; + int reversed; + bool isMinMaxShown; + bool isMinValid; + bool isMaxValid; + }; + + void _validateParameters(int validateMask); + void _validateWidgets(int validateMask, const struct ChannelSettings* rgChannelSettings); MockUASManager* _mockUASManager; MockUAS* _mockUAS; @@ -94,6 +114,21 @@ private: QLabel* _statusLabel; RCChannelWidget* _rgRadioWidget[PX4RCCalibration::_chanMax]; + + // When settings values into min/max/trim we set them slightly different than the defaults so that + // we can distinguish between the two values. + static const int _testMinValue; + static const int _testMaxValue; + static const int _testTrimValue; + static const int _testThrottleTrimValue; + + static const int _availableChannels = 8; ///< 8 channel RC Trasmitter + static const int _requiredChannels = 5; ///< Required channels are 0-4 + static const int _minMaxChannels = _requiredChannels + 1; ///< Send min/max to channels 0-5 + static const int _attitudeChannels = 4; ///< Attitude channels are 0-3 + + static const struct ChannelSettings _rgChannelSettingsPreValidate[_availableChannels]; + static const struct ChannelSettings _rgChannelSettingsPostValidate[PX4RCCalibration::_chanMax]; }; DECLARE_TEST(PX4RCCalibrationTest) diff --git a/src/ui/px4_configuration/PX4RCCalibration.cc b/src/ui/px4_configuration/PX4RCCalibration.cc index 4a5a1357c..538d9460b 100644 --- a/src/ui/px4_configuration/PX4RCCalibration.cc +++ b/src/ui/px4_configuration/PX4RCCalibration.cc @@ -31,13 +31,16 @@ #include "PX4RCCalibration.h" #include "UASManager.h" -const int PX4RCCalibration::_updateInterval = 150; ///< Interval for timer which updates radio channel widgets -const int PX4RCCalibration::_rcCalPWMValidMinValue = 1000; -const int PX4RCCalibration::_rcCalPWMValidMaxValue = 2000; +const int PX4RCCalibration::_updateInterval = 150; ///< Interval for timer which updates radio channel widgets const int PX4RCCalibration::_rcCalPWMCenterPoint = ((PX4RCCalibration::_rcCalPWMValidMaxValue - PX4RCCalibration::_rcCalPWMValidMinValue) / 2.0f) + PX4RCCalibration::_rcCalPWMValidMinValue; -const int PX4RCCalibration::_rcCalRoughCenterDelta = 200; ///< Delta around center point which is considered to be roughly centered -const float PX4RCCalibration::_rcCalMoveDelta = 300.0f; ///< Amount of delta which is considered stick movement -const float PX4RCCalibration::_rcCalMinDelta = 100.0f; ///< Amount of delta allowed around min value to consider channel at min +const int PX4RCCalibration::_rcCalPWMValidMinValue = 1300; ///< Largest valid minimum PWM Min range value +const int PX4RCCalibration::_rcCalPWMValidMaxValue = 1700; ///< Smallest valid maximum PWM Max range value +const int PX4RCCalibration::_rcCalPWMDefaultMinValue = 1000; ///< Default value for Min if not set +const int PX4RCCalibration::_rcCalPWMDefaultMaxValue = 2000; ///< Default value for Max if not set +const int PX4RCCalibration::_rcCalPWMDefaultTrimValue = PX4RCCalibration::_rcCalPWMCenterPoint; ///< Default value for Trim if not set +const int PX4RCCalibration::_rcCalRoughCenterDelta = 200; ///< Delta around center point which is considered to be roughly centered +const float PX4RCCalibration::_rcCalMoveDelta = 300.0f; ///< Amount of delta which is considered stick movement +const float PX4RCCalibration::_rcCalMinDelta = 100.0f; ///< Amount of delta allowed around min value to consider channel at min const struct PX4RCCalibration::FunctionInfo PX4RCCalibration::_rgFunctionInfo[PX4RCCalibration::rcCalFunctionMax] = { // Name Inversion Message Parameter required @@ -274,6 +277,31 @@ void PX4RCCalibration::_setActiveUAS(UASInterface* active) setEnabled(_mav ? true : false); } +/// @brief Validates the current settings against the calibration rules resetting values as necessary. +void PX4RCCalibration::_validateCalibration(void) +{ + for (int chan = 0; chan<_chanMax; chan++) { + struct ChannelInfo* info = &_rgChannelInfo[chan]; + + if (chan < _chanCount) { + // Validate Min/Max values. Although the channel appears as available we still may + // not have good min/max/trim values for it. Set to defaults if needed. + if (info->rcMin > _rcCalPWMValidMinValue || info->rcMax < _rcCalPWMValidMaxValue) { + info->rcMin = _rcCalPWMDefaultMinValue; + info->rcMax = _rcCalPWMDefaultMaxValue; + info->rcTrim = _rcCalPWMDefaultTrimValue; + } + } else { + // Unavailable channels are set to defaults + info->rcMin = _rcCalPWMDefaultMinValue; + info->rcMax = _rcCalPWMDefaultMaxValue; + info->rcTrim = _rcCalPWMDefaultTrimValue; + info->reversed = false; + } + } +} + + /// @brief Saves the rc calibration values to the board parameters. /// @param trimsOnly true: write only trim values, false: write all calibration values void PX4RCCalibration::_writeCalibration(bool trimsOnly) @@ -282,6 +310,8 @@ void PX4RCCalibration::_writeCalibration(bool trimsOnly) _mav->endRadioControlCalibration(); + _validateCalibration(); + QGCUASParamManagerInterface* paramMgr = _mav->getParamManager(); Q_ASSERT(paramMgr); @@ -290,17 +320,14 @@ void PX4RCCalibration::_writeCalibration(bool trimsOnly) QString trimTpl("RC%1_TRIM"); QString revTpl("RC%1_REV"); - // Do not write the RC type, as these values depend on this - // active onboard parameter - - for (int i = 0; i < _chanCount; ++i) { - struct ChannelInfo* info = &_rgChannelInfo[i]; - - paramMgr->setPendingParam(0, trimTpl.arg(i+1), info->rcTrim); + for (int chan = 0; chan<_chanMax; chan++) { + struct ChannelInfo* info = &_rgChannelInfo[chan]; + int oneBasedChannel = chan + 1; + paramMgr->setPendingParam(0, trimTpl.arg(oneBasedChannel), info->rcTrim); if (!trimsOnly) { - paramMgr->setPendingParam(0, minTpl.arg(i+1), info->rcMin); - paramMgr->setPendingParam(0, maxTpl.arg(i+1), info->rcMax); - paramMgr->setPendingParam(0, revTpl.arg(i+1), info->reversed ? -1.0f : 1.0f); + paramMgr->setPendingParam(0, minTpl.arg(oneBasedChannel), info->rcMin); + paramMgr->setPendingParam(0, maxTpl.arg(oneBasedChannel), info->rcMax); + paramMgr->setPendingParam(0, revTpl.arg(oneBasedChannel), info->reversed ? -1.0f : 1.0f); } } @@ -355,13 +382,7 @@ void PX4RCCalibration::_remoteControlChannelRawChanged(int chan, float fval) case rcCalStateIdentify: if (!_rcCalStateChannelComplete) { // If this channel is already used in a mapping we can't used it again - bool channelAlreadyMapped = false; - for (int chanFunction=0; chanFunction _rcCalMoveDelta) { @@ -383,10 +404,12 @@ void PX4RCCalibration::_remoteControlChannelRawChanged(int chan, float fval) break; case rcCalStateMinMax: - if (fval < _rgChannelInfo[chan].rcMin) { + if (fval < _rgChannelInfo[chan].rcMin && fval <= _rcCalPWMValidMinValue) { + _rgRadioWidget[chan]->setMinValid(true); _rgChannelInfo[chan].rcMin = fval; } - if (fval > _rgChannelInfo[chan].rcMax) { + if (fval > _rgChannelInfo[chan].rcMax && fval >= _rcCalPWMValidMaxValue) { + _rgRadioWidget[chan]->setMaxValid(true); _rgChannelInfo[chan].rcMax = fval; } break; @@ -572,7 +595,11 @@ void PX4RCCalibration::_rcCalChannelWait(bool firstTime) { _rcCalState = rcCalStateChannelWait; - _resetInternalCalibrationValues(); + if (firstTime) { + _resetInternalCalibrationValues(); + } else { + _setInternalCalibrationValuesFromParameters(); + } if (_chanCount == 0) { _ui->rcCalFound->setText(tr("Please turn on Radio")); @@ -741,18 +768,9 @@ void PX4RCCalibration::_rcCalTrims(void) _ui->rcCalSkip->setEnabled(false); _ui->rcCalCancel->setEnabled(true); - _initializeTrims(); _showTrimOnRadioWidgets(true); } -/// @brief Initializes the trim values based on current min/max. -void PX4RCCalibration::_initializeTrims(void) -{ - for (int chan=0; chan<=_chanMax; chan++) { - _rgChannelInfo[chan].rcTrim = ((_rgChannelInfo[chan].rcMax - _rgChannelInfo[chan].rcMin) / 2.0f) + _rgChannelInfo[chan].rcMin; - } -} - /// @brief Set up the Save state of calibration. void PX4RCCalibration::_rcCalSave(void) { @@ -766,6 +784,12 @@ void PX4RCCalibration::_rcCalSave(void) _ui->rcCalTryAgain->setEnabled(false); _ui->rcCalSkip->setEnabled(false); _ui->rcCalCancel->setEnabled(true); + + // This updates the internal values according to the validation rules. Then _updateView will tick and update ui + // such that the settings that will be written our are displayed. + _validateCalibration(); + + _showMinMaxOnRadioWidgets(true); } /// @brief This is used by unit test code to force the calibration state machine to the specified state. @@ -811,12 +835,26 @@ void PX4RCCalibration::_unitTestForceCalState(enum rcCalStates state) { /// @param show true: show the min/max values, false: hide the min/max values void PX4RCCalibration::_showMinMaxOnRadioWidgets(bool show) { + // Force a view update to widget have current values + _updateView(); + // Turn on min/max display for all radio widgets for (int i=0; i<_chanMax; i++) { RCChannelWidget* radioWidget = _rgRadioWidget[i]; Q_ASSERT(radioWidget); radioWidget->showMinMax(show); + if (show) { + if (radioWidget->min() <= _rcCalPWMValidMinValue) { + radioWidget->setMinValid(true); + } + if (radioWidget->max() >= _rcCalPWMValidMaxValue) { + radioWidget->setMaxValid(true); + } + } else { + radioWidget->setMinValid(false); + radioWidget->setMaxValid(false); + } } } diff --git a/src/ui/px4_configuration/PX4RCCalibration.h b/src/ui/px4_configuration/PX4RCCalibration.h index 68598283d..3cb49554b 100644 --- a/src/ui/px4_configuration/PX4RCCalibration.h +++ b/src/ui/px4_configuration/PX4RCCalibration.h @@ -124,10 +124,10 @@ private: // Methods - see source code for documentation + void _validateCalibration(void); void _writeCalibration(bool trimsOnly); void _resetInternalCalibrationValues(void); void _setInternalCalibrationValuesFromParameters(void); - void _initializeTrims(void); void _rcCalChannelWait(bool firstTime); void _rcCalBegin(void); @@ -164,12 +164,15 @@ private: int _rcCalStateIdentifyOldMapping; ///< Previous mapping for channel being currently identified int _rcCalStateReverseOldMapping; ///< Previous mapping for channel being currently used to detect inversion - static const int _rcCalPWMCenterPoint; ///< PWM center value; - static const int _rcCalPWMValidMinValue; ///< Valid minimum PWM value - static const int _rcCalPWMValidMaxValue; ///< Valid maximum PWM value - static const int _rcCalRoughCenterDelta; ///< Delta around center point which is considered to be roughly centered - static const float _rcCalMoveDelta; ///< Amount of delta which is considered stick movement - static const float _rcCalMinDelta; ///< Amount of delta allowed around min value to consider channel at min + static const int _rcCalPWMCenterPoint; + static const int _rcCalPWMValidMinValue; + static const int _rcCalPWMValidMaxValue; + static const int _rcCalPWMDefaultMinValue; + static const int _rcCalPWMDefaultMaxValue; + static const int _rcCalPWMDefaultTrimValue; + static const int _rcCalRoughCenterDelta; + static const float _rcCalMoveDelta; + static const float _rcCalMinDelta; float _rcValueSave[_chanMax]; ///< Saved values prior to detecting channel movement -- 2.22.0