diff --git a/companion/src/file.cpp b/companion/src/file.cpp index f2cb1f980..a9f617d43 100644 --- a/companion/src/file.cpp +++ b/companion/src/file.cpp @@ -15,13 +15,13 @@ */ -#include "stdio.h" -#include "inttypes.h" -#include "string.h" -#include "file.h" +#include +#include +#include #include #include #include "eeprominterface.h" +#include "file.h" RleFile::RleFile(): eeprom(NULL), diff --git a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp index b7c7e6e95..a69cc626c 100644 --- a/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxGruvin9xsimulator.cpp @@ -70,6 +70,7 @@ namespace Open9xGruvin9x { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxM128simulator.cpp b/companion/src/firmwares/opentx/opentxM128simulator.cpp index 788448620..a1fffc85e 100644 --- a/companion/src/firmwares/opentx/opentxM128simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM128simulator.cpp @@ -71,6 +71,7 @@ namespace OpenTxM128 { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxM64simulator.cpp b/companion/src/firmwares/opentx/opentxM64simulator.cpp index c70beb867..09a1323a0 100644 --- a/companion/src/firmwares/opentx/opentxM64simulator.cpp +++ b/companion/src/firmwares/opentx/opentxM64simulator.cpp @@ -74,6 +74,7 @@ namespace OpenTxM64 { #include "radio/src/functions.cpp" #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/pulses/pulses_avr.cpp" #include "radio/src/stamp.cpp" #include "radio/src/maths.cpp" diff --git a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp index 83efaa962..47fdca92e 100644 --- a/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp +++ b/companion/src/firmwares/opentx/opentxSky9xsimulator.cpp @@ -82,6 +82,7 @@ namespace Open9xSky9x { #include "radio/src/switches.cpp" #include "radio/src/functions.cpp" #include "radio/src/mixer.cpp" +#include "radio/src/timers.cpp" #include "radio/src/curves.cpp" #include "radio/src/targets/sky9x/pulses_driver.cpp" #include "radio/src/pulses/pulses_arm.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp index 52e2bebe3..2edf1c335 100644 --- a/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisSimulator.cpp @@ -89,6 +89,7 @@ inline int geteepromsize() { #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" #include "radio/src/sdcard.cpp" +#include "radio/src/timers.cpp" #include "radio/src/targets/taranis/pulses_driver.cpp" #include "radio/src/targets/taranis/rtc_driver.cpp" #include "radio/src/targets/taranis/trainer_driver.cpp" diff --git a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp index 2adb13cba..6c50f39cc 100755 --- a/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp +++ b/companion/src/firmwares/opentx/opentxTaranisX9ESimulator.cpp @@ -90,6 +90,7 @@ inline int geteepromsize() { #include "radio/src/curves.cpp" #include "radio/src/mixer.cpp" #include "radio/src/sdcard.cpp" +#include "radio/src/timers.cpp" #include "radio/src/targets/taranis/pulses_driver.cpp" #include "radio/src/targets/taranis/rtc_driver.cpp" #include "radio/src/targets/taranis/rotenc_driver.cpp" diff --git a/radio/src/Makefile b/radio/src/Makefile index 4b916b110..70076591d 100644 --- a/radio/src/Makefile +++ b/radio/src/Makefile @@ -954,7 +954,7 @@ ifeq ($(GUI), YES) CPPDEFS += -DGUI endif -CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) +CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) timers.cpp ifeq ($(GUI), YES) GUISRC += gui/$(GUIDIRECTORY)/lcd.cpp gui/$(GUIDIRECTORY)/splash.cpp gui/$(GUIDIRECTORY)/fonts.cpp diff --git a/radio/src/debug.cpp b/radio/src/debug.cpp index b60631207..44a62a9e5 100644 --- a/radio/src/debug.cpp +++ b/radio/src/debug.cpp @@ -285,7 +285,7 @@ const struct TraceElement * getTraceElement(uint16_t idx) void dumpTraceBuffer() { - TRACE("Dump of Trace Buffer ("VERS_STR " " DATE_STR " " TIME_STR "):"); + TRACE("Dump of Trace Buffer (" VERS_STR " " DATE_STR " " TIME_STR "):"); TRACE("# Time Event Data"); for(int n = 0; n < TRACE_BUFFER_LEN; ++n) { struct gtm tp; diff --git a/radio/src/eeprom_common.cpp b/radio/src/eeprom_common.cpp index acf36ab30..ced8703a4 100644 --- a/radio/src/eeprom_common.cpp +++ b/radio/src/eeprom_common.cpp @@ -35,9 +35,10 @@ */ #include +#include +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" uint8_t s_eeDirtyMsk; tmr10ms_t s_eeDirtyTime10ms; diff --git a/radio/src/eeprom_raw.cpp b/radio/src/eeprom_raw.cpp index dafde85ef..c4a3664d8 100644 --- a/radio/src/eeprom_raw.cpp +++ b/radio/src/eeprom_raw.cpp @@ -35,9 +35,10 @@ */ #include +#include +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" volatile uint32_t Spi_complete; // TODO in the driver ? @@ -396,11 +397,7 @@ void eeLoadModel(uint8_t id) customFunctionsReset(); - for (uint8_t i=0; i +#include #include "opentx.h" -#include "inttypes.h" -#include "string.h" +#include "timers.h" uint8_t s_write_err = 0; // error reasons RlcFile theFile; //used for any file operation @@ -985,13 +986,7 @@ void eeLoadModel(uint8_t id) customFunctionsReset(); -#if !defined(PCBSTD) - for (uint8_t i=0; i= 255 @@ -1703,95 +1694,7 @@ void doMixerCalculations() val >>= (RESX_SHIFT-4); // calibrate it #endif - // Timers start - for (uint8_t i=0; istate == TMR_OFF) { - timerState->state = TMR_RUNNING; - timerState->cnt = 0; - timerState->sum = 0; - } - - if (tm == TMRMODE_THR_REL) { - timerState->cnt++; - timerState->sum+=val; - } - - if ((timerState->val_10ms += tick10ms) >= 100) { - timerState->val_10ms -= 100 ; - int16_t newTimerVal = timerState->val; - if (tv) newTimerVal = tv - newTimerVal; - - if (tm == TMRMODE_ABS) { - newTimerVal++; - } - else if (tm == TMRMODE_THR) { - if (val) newTimerVal++; - } - else if (tm == TMRMODE_THR_REL) { - // @@@ open.20.fsguruh: why so complicated? we have already a s_sum field; use it for the half seconds (not showable) as well - // check for s_cnt[i]==0 is not needed because we are shure it is at least 1 -#if defined(ACCURAT_THROTTLE_TIMER) - if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) - newTimerVal++; // add second used of throttle - timerState->sum -= 128*timerState->cnt; - } -#else - if ((timerState->sum/timerState->cnt) >= 32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) - newTimerVal++; // add second used of throttle - timerState->sum -= 32*timerState->cnt; - } -#endif - timerState->cnt=0; - } - else if (tm == TMRMODE_THR_TRG) { - if (val) { - timerState->state = TMR_TRIGGED; - } - if (timerState->state == TMR_TRIGGED) { - newTimerVal++; - } - } - else { - if (tm > 0) tm -= (TMRMODE_COUNT-1); - if (getSwitch(tm)) - newTimerVal++; - } - - switch (timerState->state) { - case TMR_RUNNING: - if (tv && newTimerVal>=(int16_t)tv) { - AUDIO_TIMER_00(g_model.timers[i].countdownBeep); - timerState->state = TMR_NEGATIVE; - } - break; - case TMR_NEGATIVE: - if (newTimerVal >= (int16_t)tv + MAX_ALERT_TIME) timerState->state = TMR_STOPPED; - break; - } - - if (tv) newTimerVal = tv - newTimerVal; // if counting backwards - display backwards - - if (newTimerVal != timerState->val) { - timerState->val = newTimerVal; - if (timerState->state == TMR_RUNNING) { - if (g_model.timers[i].countdownBeep && g_model.timers[i].start) { - if (newTimerVal==30) AUDIO_TIMER_30(); - if (newTimerVal==20) AUDIO_TIMER_20(); - if (newTimerVal<=10) AUDIO_TIMER_LT10(g_model.timers[i].countdownBeep, newTimerVal); - } - if (g_model.timers[i].minuteBeep && (newTimerVal % 60)==0) { - AUDIO_TIMER_MINUTE(newTimerVal); - } - } - } - } - } - } //endfor timer loop (only two) + evalTimers(val, tick10ms); static uint8_t s_cnt_100ms; static uint8_t s_cnt_1s; @@ -2455,29 +2358,6 @@ void moveTrimsToOffsets() // copy state of 3 primary to subtrim AUDIO_WARNING2(); } -#if defined(CPUARM) || defined(CPUM2560) -void saveTimers() -{ - for (uint8_t i=0; ival) { - g_model.timers[i].value = timerState->val; - eeDirty(EE_MODEL); - } - } - } - -#if defined(CPUARM) && !defined(REVA) - if (sessionTimer > 0) { - g_eeGeneral.globalTimer += sessionTimer; - eeDirty(EE_GENERAL); - sessionTimer = 0; - } -#endif -} -#endif - #if defined(ROTARY_ENCODERS) volatile rotenc_t g_rotenc[ROTARY_ENCODERS] = {0}; #elif defined(ROTARY_ENCODER_NAVIGATION) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 3a78b6914..6d3b8deda 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -270,6 +270,15 @@ #define convertSimuPath(x) (x) #endif +#if !defined(CPUM64) && !defined(ACCURAT_THROTTLE_TIMER) + // code cost is about 16 bytes for higher throttle accuracy for timer + // would not be noticable anyway, because all version up to this change had only 16 steps; + // now it has already 32 steps; this define would increase to 128 steps + #if !defined(ACCURAT_THROTTLE_TIMER) + #define ACCURAT_THROTTLE_TIMER + #endif +#endif + // RESX range is used for internal calculation; The menu says -100.0 to 100.0; internally it is -1024 to 1024 to allow some optimizations #define RESX_SHIFT 10 #define RESX 1024 @@ -867,16 +876,6 @@ extern uint16_t sessionTimer; extern uint16_t s_timeCumThr; extern uint16_t s_timeCum16ThrP; -struct TimerState { - uint16_t cnt; - uint16_t sum; - uint8_t state; - int16_t val; - uint8_t val_10ms; -}; - -extern TimerState timersStates[MAX_TIMERS]; - #if defined(OVERRIDE_CHANNEL_FUNCTION) #if defined(CPUARM) typedef int16_t safetych_t; @@ -895,12 +894,6 @@ extern uint8_t trimsDisplayTimer; extern uint8_t trimsDisplayMask; #endif -#define TMR_OFF 0 -#define TMR_RUNNING 1 -#define TMR_NEGATIVE 2 -#define TMR_STOPPED 3 -#define TMR_TRIGGED 4 -void timerReset(uint8_t idx); void flightReset(); extern uint8_t unexpectedShutdown; @@ -1068,12 +1061,6 @@ inline void resumeMixerCalculations() #define resumeMixerCalculations() #endif -#if defined(CPUARM) || defined(CPUM2560) -void saveTimers(); -#else -#define saveTimers() -#endif - void generalDefault(); void modelDefault(uint8_t id); diff --git a/radio/src/tests/timers.cpp b/radio/src/tests/timers.cpp new file mode 100644 index 000000000..ae1987409 --- /dev/null +++ b/radio/src/tests/timers.cpp @@ -0,0 +1,284 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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 "gtests.h" +#include "opentx.h" +#include "timers.h" + +/* +struct TimerState { + uint16_t cnt; + uint16_t sum; + uint8_t state; + int16_t val; + uint8_t val_10ms; +}; + +PACK(typedef struct t_TimerData { + int8_t mode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw + uint16_t start; + uint8_t countdownBeep:2; + uint8_t minuteBeep:1; + uint8_t persistent:2; // 0 off, 1 flight, 2 manual reset + uint8_t spare:3; + uint16_t value; +}) TimerData; + +enum TimerModes { + TMRMODE_NONE, + TMRMODE_ABS, + TMRMODE_THR, + TMRMODE_THR_REL, + TMRMODE_THR_TRG, + TMRMODE_COUNT +}; + +enum CountDownModes { + COUNTDOWN_SILENT, + COUNTDOWN_BEEPS, + COUNTDOWN_VOICE +}; + +*/ + +#if !defined(CPUARM) +#undef timerSet +void timerSet(int idx, int16_t val) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = val; + timerState.val_10ms = 0 ; +} +#endif // #if !defined(CPUARM) + +#if defined(ACCURAT_THROTTLE_TIMER) + #define THR_100 128 // approximately 10% full throttle + #define THR_50 64 // approximately 10% full throttle + #define THR_10 13 // approximately 10% full throttle +#else + #define THR_100 32 // approximately 10% full throttle + #define THR_50 16 // approximately 10% full throttle + #define THR_10 3 // approximately 10% full throttle +#endif + + +#define TEST_AB_EQUAL(a, b) if (a != b) { return ::testing::AssertionFailure() << \ + #a "= " << (uint32_t)a << ", " << #b "= " << (uint32_t)b; }; + +void initModelTimer(uint32_t idx, uint8_t mode, int16_t start = 0) +{ + g_model.timers[0].mode = mode; + g_model.timers[0].start = start; + g_model.timers[0].countdownBeep = COUNTDOWN_SILENT; + g_model.timers[0].minuteBeep = 0; + g_model.timers[0].spare = 0; +#if defined(CPUARM) || defined(CPUM2560) + g_model.timers[0].persistent = 0; + g_model.timers[0].value = 0; +#endif +} + +/* + Run timers for n seconds and test the end state +*/ +::testing::AssertionResult evalTimersForNSecondsAndTest(unsigned int n, uint8_t throttle, uint32_t idx, uint8_t state, int16_t value) +{ + unsigned int noLoops = n * 100; + while(noLoops--) + { + evalTimers(throttle, 1); + } + TEST_AB_EQUAL(timersStates[idx].state, state); + TEST_AB_EQUAL(timersStates[idx].val, value); + return ::testing::AssertionSuccess(); +} + +TEST(Timers, timerReset) +{ + initModelTimer(0, TMRMODE_THR_REL, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 200)); + + initModelTimer(1, TMRMODE_THR_REL, 0); + timerReset(1); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 0)); +} + +#if defined(CPUARM) +TEST(Timers, timerSet) +{ + timerSet(0, 500); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + + timerSet(1, 300); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 300)); +} +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) +TEST(Timers, saveRestoreTimers) +{ + g_model.timers[0].persistent = 1; + g_model.timers[1].persistent = 1; + timerSet(0, 500); + timerSet(1, 1500); + saveTimers(); + EXPECT_EQ(g_model.timers[0].value, 500); + EXPECT_EQ(g_model.timers[1].value, 1500); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 1500)); + + timerReset(0); + timerReset(1); + restoreTimers(); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 500)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 1, TMR_OFF, 1500)); +} +#endif + +TEST(Timers, timerOff) +{ + initModelTimer(0, TMRMODE_NONE, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(0, THR_100, 0, TMR_OFF, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(10, THR_100, 0, TMR_OFF, 0)); +} + +TEST(Timers, timerAbsolute) +{ + initModelTimer(0, TMRMODE_ABS, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 101)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 201)); + + // max timer value test + timerSet(0, TIMER_MAX-10); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, TIMER_MAX-9)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, TIMER_MAX)); + + // test down-running + g_model.timers[0].start = 200; + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED, -101)); + + // min timer value test + timerSet(0, TIMER_MIN+10); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, TIMER_MIN+9)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, TIMER_MIN)); +} + +TEST(Timers, timerThrottle) +{ + initModelTimer(0, TMRMODE_THR, 0); + timerReset(0); + + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 200)); + + // test down-running + g_model.timers[0].start = 200; + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-101)); +} + + + +TEST(Timers, timerThrottleRelative) +{ + initModelTimer(0, TMRMODE_THR_REL, 0); + + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_RUNNING, 150)); // 50% throttle == 50s +#if defined(ACCURAT_THROTTLE_TIMER) + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 160)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 160)); +#else + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_RUNNING, 159)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 159)); +#endif + + // test down-running + initModelTimer(0, TMRMODE_THR_REL, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, THR_100, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_RUNNING, 199)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 99)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(200, THR_50, 0, TMR_NEGATIVE, -1)); // 50% throttle == 100s +#if defined(ACCURAT_THROTTLE_TIMER) + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_NEGATIVE,-11)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-111)); +#else + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10, 0, TMR_NEGATIVE,-10)); // 10% throttle == 10s + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_STOPPED,-110)); +#endif +} + +TEST(Timers, timerThrottleTriggered) +{ + initModelTimer(0, TMRMODE_THR_TRG, 0); + + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_OFF, 0)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10-1, 0, TMR_OFF, 0)); // below threshold + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_50, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_RUNNING, 300)); + + // test down-running + initModelTimer(0, TMRMODE_THR_TRG, 200); + timerReset(0); + EXPECT_TRUE(evalTimersForNSecondsAndTest(1, 0, 0, TMR_OFF, 200)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_10-1, 0, TMR_OFF, 200)); // below threshold + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, THR_100, 0, TMR_RUNNING, 100)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(101, THR_50, 0, TMR_NEGATIVE, -1)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(10, 0, 0, TMR_NEGATIVE,-11)); + EXPECT_TRUE(evalTimersForNSecondsAndTest(100, 0, 0, TMR_STOPPED,-111)); +} diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp new file mode 100644 index 000000000..c24b429a5 --- /dev/null +++ b/radio/src/timers.cpp @@ -0,0 +1,209 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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 "opentx.h" +#include "timers.h" + +TimerState timersStates[MAX_TIMERS] = { 0 }; + + +void timerReset(uint8_t idx) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = g_model.timers[idx].start; + timerState.val_10ms = 0 ; +} + +#if defined(CPUARM) +void timerSet(int idx, int16_t val) +{ + TimerState & timerState = timersStates[idx]; + timerState.state = TMR_OFF; // is changed to RUNNING dep from mode + timerState.val = val; + timerState.val_10ms = 0 ; +} +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) +void restoreTimers() +{ + for (uint8_t i=0; ival) { + g_model.timers[i].value = timerState->val; + eeDirty(EE_MODEL); + } + } + } + +#if defined(CPUARM) && !defined(REVA) + if (sessionTimer > 0) { + g_eeGeneral.globalTimer += sessionTimer; + eeDirty(EE_GENERAL); + sessionTimer = 0; + } +#endif +} +#endif // #if defined(CPUARM) || defined(CPUM2560) + +#if defined(ACCURAT_THROTTLE_TIMER) + #define THR_TRG_TRESHOLD 13 // approximately 10% full throttle +#else + #define THR_TRG_TRESHOLD 3 // approximately 10% full throttle +#endif + +void evalTimers(int16_t throttle, uint8_t tick10ms) +{ + for (uint8_t i=0; istate == TMR_OFF) && (tm != TMRMODE_THR_TRG)) { + timerState->state = TMR_RUNNING; + timerState->cnt = 0; + timerState->sum = 0; + } + + if (tm == TMRMODE_THR_REL) { + timerState->cnt++; + timerState->sum += throttle; + } + + if ((timerState->val_10ms += tick10ms) >= 100) { + if (timerState->val == TIMER_MAX) break; + if (timerState->val == TIMER_MIN) break; + + timerState->val_10ms -= 100 ; + int16_t newTimerVal = timerState->val; + if (tv) newTimerVal = tv - newTimerVal; + + if (tm == TMRMODE_ABS) { + newTimerVal++; + } + else if (tm == TMRMODE_THR) { + if (throttle) newTimerVal++; + } + else if (tm == TMRMODE_THR_REL) { + // @@@ open.20.fsguruh: why so complicated? we have already a s_sum field; use it for the half seconds (not showable) as well + // check for s_cnt[i]==0 is not needed because we are shure it is at least 1 + #if defined(ACCURAT_THROTTLE_TIMER) + if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) + newTimerVal++; // add second used of throttle + timerState->sum -= 128*timerState->cnt; + } + #else + if ((timerState->sum/timerState->cnt) >= 32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) + newTimerVal++; // add second used of throttle + timerState->sum -= 32*timerState->cnt; + } + #endif + timerState->cnt=0; + } + else if (tm == TMRMODE_THR_TRG) { + // we can't rely on (throttle || newTimerVal > 0) as a detection if timer should be running + // because having persistent timer brakes this rule + if ((throttle > THR_TRG_TRESHOLD) && timerState->state == TMR_OFF) { + timerState->state = TMR_RUNNING; // start timer running + timerState->cnt = 0; + timerState->sum = 0; + TRACE("Timer[%d] THr triggered", i); + } + if (timerState->state != TMR_OFF) newTimerVal++; + } + else { + if (tm > 0) tm -= (TMRMODE_COUNT-1); + if (getSwitch(tm)) + newTimerVal++; + } + + switch (timerState->state) { + case TMR_RUNNING: + if (tv && newTimerVal>=(int16_t)tv) { + AUDIO_TIMER_00(g_model.timers[i].countdownBeep); + timerState->state = TMR_NEGATIVE; + TRACE("Timer[%d] negative", i); + } + break; + case TMR_NEGATIVE: + if (newTimerVal >= (int16_t)tv + MAX_ALERT_TIME) { + timerState->state = TMR_STOPPED; + TRACE("Timer[%d] stopped state at %d", i, newTimerVal); + } + break; + } + + if (tv) newTimerVal = tv - newTimerVal; // if counting backwards - display backwards + + if (newTimerVal != timerState->val) { + timerState->val = newTimerVal; + if (timerState->state == TMR_RUNNING) { + if (g_model.timers[i].countdownBeep && g_model.timers[i].start) { + if (newTimerVal==30) { + AUDIO_TIMER_30(); + TRACE("Timer[%d] 30s announcement", i); + } + if (newTimerVal==20) { + AUDIO_TIMER_20(); + TRACE("Timer[%d] 20s announcement", i); + } + if (newTimerVal<=10) { + AUDIO_TIMER_LT10(g_model.timers[i].countdownBeep, newTimerVal); + TRACE("Timer[%d] %ds announcement", i, newTimerVal); + } + } + if (g_model.timers[i].minuteBeep && (newTimerVal % 60)==0) { + AUDIO_TIMER_MINUTE(newTimerVal); + TRACE("Timer[%d] %d minute announcement", i, newTimerVal/60); + } + } + } + } + } + } +} diff --git a/radio/src/timers.h b/radio/src/timers.h new file mode 100644 index 000000000..a19ce1a57 --- /dev/null +++ b/radio/src/timers.h @@ -0,0 +1,74 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * 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. + * + */ + +#ifndef timers_h +#define timers_h + +#define TMR_OFF 0 +#define TMR_RUNNING 1 +#define TMR_NEGATIVE 2 +#define TMR_STOPPED 3 + +#define TIMER_MAX (32767) +#define TIMER_MIN (-32767-1) + +struct TimerState { + uint16_t cnt; + uint16_t sum; + uint8_t state; + int16_t val; + uint8_t val_10ms; +}; + +extern TimerState timersStates[MAX_TIMERS]; + +void timerReset(uint8_t idx); + +#if defined(CPUARM) +void timerSet(int idx, int16_t val); +#endif // #if defined(CPUARM) + +#if defined(CPUARM) || defined(CPUM2560) + void saveTimers(); + void restoreTimers(); +#else + #define saveTimers() + #define restoreTimers() +#endif + +void evalTimers(int16_t throttle, uint8_t tick10ms); + +#endif