/* * Copyright (C) EdgeTX * * Based on code named * opentx - https://github.com/opentx/opentx * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program 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. */ #include "dataconstants.h" #include "gtests.h" #include "myeeprom.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" void setLogicalSwitch(int index, uint16_t _func, int16_t _v1, int16_t _v2, int16_t _v3 = 0, uint8_t _delay = 0, uint8_t _duration = 0, int8_t _andsw = 0) { g_model.logicalSw[index].func = _func; g_model.logicalSw[index].v1 = _v1; g_model.logicalSw[index].v2 = _v2; g_model.logicalSw[index].v3 = _v3; g_model.logicalSw[index].delay = _delay; g_model.logicalSw[index].duration = _duration; g_model.logicalSw[index].andsw = _andsw; } #define SWSRC_SW1 (SWSRC_FIRST_LOGICAL_SWITCH) #define SWSRC_SW2 (SWSRC_FIRST_LOGICAL_SWITCH + 1) TEST(getSwitch, OldTypeStickyCSW) { RADIO_RESET(); MODEL_RESET(); MIXER_RESET(); int sw = findHwSwitch(SWITCH_3POS); if (sw < 0) return; // no 3-pos switch on this target int swPos = (sw * 3) + SWSRC_FIRST_SWITCH; setLogicalSwitch(0, LS_FUNC_AND, swPos, SWSRC_NONE); setLogicalSwitch(1, LS_FUNC_OR, SWSRC_SW1, SWSRC_SW2); simuSetSwitch(sw, 0); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now trigger SA0, both switches should become true simuSetSwitch(sw, -1); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_TRUE(getSwitch(SWSRC_SW2)); // now release SA0 and SW2 should stay true simuSetSwitch(sw, 0); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_TRUE(getSwitch(SWSRC_SW2)); // now reset logical switches logicalSwitchesReset(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); } // Tick logical switches for N cycles. static void tickLogicalSwitches(int n) { for (int i = 0; i < n; i++) { logicalSwitchesTimerTick(); evalLogicalSwitches(); } } // Sticky LS latches at startup when the set-condition is already satisfied. // After logicalSwitchesReset(), lastValue starts at its CS_LAST_VALUE_INIT // sentinel, so the first logicalSwitchesTimerTick() sees a 0->1 edge on the // set-condition and latches immediately. TEST(getSwitch, StickyLatchesAtStartupWhenSetConditionTrue) { RADIO_RESET(); MODEL_RESET(); MIXER_RESET(); int sw = findHwSwitch(SWITCH_3POS); if (sw < 0) return; // no 3-pos switch on this target int swUp = (sw * 3) + SWSRC_FIRST_SWITCH; int swDn = swUp + 2; // Pre-set the switch to the "set" position BEFORE creating the LS simuSetSwitch(sw, -1); setLogicalSwitch(0, LS_FUNC_STICKY, swUp, swDn); // Simulate power-on logicalSwitchesReset(); // After reset + tick, the set-condition is already true so sticky latches tickLogicalSwitches(10); EXPECT_TRUE(getSwitch(SWSRC_SW1)) << "Sticky latches at startup when set-condition is already true"; // Reset via the reset-condition (swDn) simuSetSwitch(sw, 1); tickLogicalSwitches(5); EXPECT_FALSE(getSwitch(SWSRC_SW1)) << "Reset condition should unlatch sticky"; } // Sticky LS stays false at startup when the set-condition is NOT satisfied. TEST(getSwitch, StickyFalseAtStartupWhenSetConditionFalse) { RADIO_RESET(); MODEL_RESET(); MIXER_RESET(); int sw = findHwSwitch(SWITCH_3POS); if (sw < 0) return; // no 3-pos switch on this target int swUp = (sw * 3) + SWSRC_FIRST_SWITCH; int swDn = swUp + 2; // Switch in mid position - set-condition is false simuSetSwitch(sw, 0); setLogicalSwitch(0, LS_FUNC_STICKY, swUp, swDn); logicalSwitchesReset(); tickLogicalSwitches(10); EXPECT_FALSE(getSwitch(SWSRC_SW1)) << "Sticky should not latch when condition is false"; // Now trigger a rising edge -> should latch simuSetSwitch(sw, -1); tickLogicalSwitches(5); EXPECT_TRUE(getSwitch(SWSRC_SW1)) << "Sticky should latch on rising edge"; } TEST(getSwitch, nullSW) { MODEL_RESET(); EXPECT_TRUE(getSwitch(0)); } TEST(getSwitch, inputWithTrim) { MODEL_RESET(); setModelDefaults(); MIXER_RESET(); setLogicalSwitch(0, LS_FUNC_VPOS, MIXSRC_FIRST_INPUT, 0, 0); anaSetFiltered(0, 0); evalMixes(1); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); setTrimValue(0, 0, 32); evalMixes(1); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); } TEST(evalLogicalSwitches, playFile) { SYSTEM_RESET(); MODEL_RESET(); setModelDefaults(); MIXER_RESET(); extern BitField<(MAX_LOGICAL_SWITCHES * 2/*on, off*/)> sdAvailableLogicalSwitchAudioFiles; char filename[AUDIO_FILENAME_MAXLEN+1]; #define MODELNAME "MODEL01" sdAvailableLogicalSwitchAudioFiles.setBit(INDEX_LOGICAL_SWITCH_AUDIO_FILE(0,AUDIO_EVENT_OFF)); sdAvailableLogicalSwitchAudioFiles.setBit(INDEX_LOGICAL_SWITCH_AUDIO_FILE(0,AUDIO_EVENT_ON)); sdAvailableLogicalSwitchAudioFiles.setBit(INDEX_LOGICAL_SWITCH_AUDIO_FILE(31,AUDIO_EVENT_OFF)); sdAvailableLogicalSwitchAudioFiles.setBit(INDEX_LOGICAL_SWITCH_AUDIO_FILE(31,AUDIO_EVENT_ON)); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (0 << 16) + AUDIO_EVENT_OFF, filename); EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L1-off.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (0 << 16) + AUDIO_EVENT_ON, filename); EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L1-on.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_OFF, filename); EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L32-off.wav"), 0); isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_ON, filename); EXPECT_EQ(strcmp(filename, "/SOUNDS/en/" MODELNAME "/L32-on.wav"), 0); EXPECT_TRUE(isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (31 << 16) + AUDIO_EVENT_ON, filename)); EXPECT_FALSE(isAudioFileReferenced((LOGICAL_SWITCH_AUDIO_CATEGORY << 24) + (32 << 16) + AUDIO_EVENT_ON, filename)); #undef MODELNAME } TEST(getSwitch, edgeInstant) { int sw1 = findHwSwitch(SWITCH_3POS); int sw2 = findHwSwitch(SWITCH_3POS, sw1); if (sw1 < 0 || sw2 < 0) return; // needs two distinct 3-pos switches int sw1Pos = (sw1 * 3) + SWSRC_FIRST_SWITCH; int sw2Pos = (sw2 * 3) + SWSRC_FIRST_SWITCH; MODEL_RESET(); MIXER_RESET(); // LS1 setup: EDGE SD down (0:instant) // LS2 setup: (EDGE SD down (0:instant)) AND SA down setLogicalSwitch(0, LS_FUNC_EDGE, sw2Pos + 2, -129, -1); setLogicalSwitch(1, LS_FUNC_EDGE, sw2Pos + 2, -129, -1, 0, 0, sw1Pos + 2); simuSetSwitch(sw1, -1); //SA up simuSetSwitch(sw2, 0); //SD mid logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now trigger SD donw, LS1 should become true simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now release SD and LS1 should become false simuSetSwitch(sw2, 0); //SD mid logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now reset logical switches logicalSwitchesReset(); logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // second part with SA down simuSetSwitch(sw1, 1); //SA down simuSetSwitch(sw2, 0); //SD mid logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now trigger SD down, LS1 & LS2 should become true simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_TRUE(getSwitch(SWSRC_SW2)); // now release SA and LS1 & LS2 should stay false simuSetSwitch(sw2, 0); //SD mid logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now reset logical switches logicalSwitchesReset(); logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // now bug #2939 // SD is kept down and SA is toggled simuSetSwitch(sw1, -1); //SA up simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw1, 1); //SA down simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw1, -1); //SA up simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); //test what happens when EDGE condition is true and //logical switches are reset - the switch should fire again simuSetSwitch(sw1, 1); //SA down simuSetSwitch(sw2, 1); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); //switch will not trigger, because SF was already up EXPECT_FALSE(getSwitch(SWSRC_SW2)); logicalSwitchesReset(); logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_TRUE(getSwitch(SWSRC_SW2)); logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); } TEST(getSwitch, edgeRelease) { int sw1 = findHwSwitch(SWITCH_3POS); int sw2 = findHwSwitch(SWITCH_3POS, sw1); if (sw1 < 0 || sw2 < 0) return; // needs two distinct 3-pos switches int sw1Pos = (sw1 * 3) + SWSRC_FIRST_SWITCH; int sw2Pos = (sw2 * 3) + SWSRC_FIRST_SWITCH; MODEL_RESET(); MIXER_RESET(); // test for issue #2728 // LS1 setup: EDGE SDup (0:release) // LS2 setup: (EDGE SDup (0:release)) AND SAup setLogicalSwitch(0, LS_FUNC_EDGE, sw2Pos + 2, -129, 0); setLogicalSwitch(1, LS_FUNC_EDGE, sw2Pos + 2, -129, 0, 0, 0, sw1Pos + 2 ); simuSetSwitch(sw1, -1); //SA down simuSetSwitch(sw2, 0); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw2, 1); //SD up logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw2, 0); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); // second part with SAup simuSetSwitch(sw1, 1); //SA up simuSetSwitch(sw2, 0); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw2, 1); //SD up logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); simuSetSwitch(sw2, 0); //SD down logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_TRUE(getSwitch(SWSRC_SW1)); EXPECT_TRUE(getSwitch(SWSRC_SW2)); // with switches reset both should remain false logicalSwitchesReset(); logicalSwitchesTimerTick(); evalLogicalSwitches(); EXPECT_FALSE(getSwitch(SWSRC_SW1)); EXPECT_FALSE(getSwitch(SWSRC_SW2)); } uint8_t boardGetMaxSwitches(); TEST(FlexSwitches, switchGetPosition) { if (adcGetMaxInputs(ADC_INPUT_FLEX) == 0) return; if (MAX_FLEX_SWITCHES == 0) return; switchInit(); auto sw_idx = boardGetMaxSwitches(); auto sw_name = switchGetDefaultName(sw_idx); EXPECT_STREQ("FL1", sw_name); EXPECT_FALSE(switchIsFlexValid(sw_idx)); // Configure 1st FLEX input as switch g_eeGeneral.potsConfig = FLEX_SWITCH; switchConfigFlex(sw_idx, 0); EXPECT_TRUE(switchIsFlexValid(sw_idx)); auto offset = adcGetInputOffset(ADC_INPUT_FLEX); anaSetFiltered(offset, -1024); EXPECT_EQ(SWITCH_HW_UP, switchGetPosition(sw_idx)); anaSetFiltered(offset, 0); EXPECT_EQ(SWITCH_HW_MID, switchGetPosition(sw_idx)); anaSetFiltered(offset, +1024); EXPECT_EQ(SWITCH_HW_DOWN, switchGetPosition(sw_idx)); } TEST(FlexSwitches, getValue) { if (adcGetMaxInputs(ADC_INPUT_FLEX) == 0) return; if (MAX_FLEX_SWITCHES == 0) return; switchInit(); // Configure 1st FLEX input as switch g_eeGeneral.potsConfig = FLEX_SWITCH; auto sw_idx = boardGetMaxSwitches(); switchConfigFlex(sw_idx, 0); g_eeGeneral.switchSetType(sw_idx, SWITCH_3POS); EXPECT_EQ(SWITCH_3POS, g_model.getSwitchType(sw_idx)); auto offset = adcGetInputOffset(ADC_INPUT_FLEX); anaSetFiltered(offset, -1024); EXPECT_EQ(-1024, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); anaSetFiltered(offset, 0); EXPECT_EQ(0, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); anaSetFiltered(offset, +1024); EXPECT_EQ(+1024, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); g_eeGeneral.switchSetType(sw_idx, SWITCH_2POS); EXPECT_EQ(SWITCH_2POS, g_model.getSwitchType(sw_idx)); anaSetFiltered(offset, -1024); EXPECT_EQ(-1024, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); anaSetFiltered(offset, 0); EXPECT_EQ(+1024, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); anaSetFiltered(offset, +1024); EXPECT_EQ(+1024, getValue(MIXSRC_FIRST_SWITCH + sw_idx)); } TEST(FlexSwitches, getSwitch) { if (adcGetMaxInputs(ADC_INPUT_FLEX) == 0) return; if (MAX_FLEX_SWITCHES == 0) return; switchInit(); // Configure 1st FLEX input as switch g_eeGeneral.potsConfig = FLEX_SWITCH; auto sw_idx = boardGetMaxSwitches(); switchConfigFlex(sw_idx, 0); g_eeGeneral.switchSetType(sw_idx, SWITCH_3POS); EXPECT_EQ(SWITCH_3POS, g_model.getSwitchType(sw_idx)); auto offset = adcGetInputOffset(ADC_INPUT_FLEX); anaSetFiltered(offset, -1024); EXPECT_TRUE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3)); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 1)); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 2)); anaSetFiltered(offset, 0); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3)); EXPECT_TRUE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 1)); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 2)); anaSetFiltered(offset, +1024); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3)); EXPECT_FALSE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 1)); EXPECT_TRUE(getSwitch(SWSRC_FIRST_SWITCH + sw_idx * 3 + 2)); } #if defined(FUNCTION_SWITCHES) TEST(FunctionSwitches, holdIsNotRepeatedToggle) { MODEL_RESET(); setModelDefaults(); switchInit(); for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { if (!switchIsCustomSwitch(i)) continue; // Toggle type with no group, so a phantom "switch moved" event // flips the logical state and is observable g_model.cfsSetType(i, SWITCH_TOGGLE); g_model.cfsSetGroup(i, 0); simuSetSwitch(i, -1); } setFSStartupPosition(); evalFunctionSwitches(); // sync previous state with all switches released for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { if (!switchIsCustomSwitch(i)) continue; bool released = g_model.cfsState(i); simuSetSwitch(i, 1); evalFunctionSwitches(); bool pressed = g_model.cfsState(i); EXPECT_NE(released, pressed) << "press did not toggle switch index " << (int)i; // holding the switch must not generate further toggles evalFunctionSwitches(); EXPECT_EQ(pressed, g_model.cfsState(i)) << "hold re-toggled switch index " << (int)i; evalFunctionSwitches(); EXPECT_EQ(pressed, g_model.cfsState(i)) << "hold re-toggled switch index " << (int)i; simuSetSwitch(i, -1); evalFunctionSwitches(); } } #endif