1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-24 00:35:18 +03:00

Fixes #2009: Timer Voice minute call not working for persistent timers

Timers moved to timer.cpp
Added timer overflow protection
Added timer gtests
THt mode now triggers on THR>10%
This commit is contained in:
Damjan Adamic 2015-01-29 19:26:54 +01:00
parent 76937ea566
commit afc41f082e
25 changed files with 4001 additions and 3563 deletions

View file

@ -15,13 +15,13 @@
*/
#include "stdio.h"
#include "inttypes.h"
#include "string.h"
#include "file.h"
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include "eeprominterface.h"
#include "file.h"
RleFile::RleFile():
eeprom(NULL),

View file

@ -71,6 +71,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"

View file

@ -72,6 +72,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"

View file

@ -75,6 +75,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"

View file

@ -83,6 +83,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"

View file

@ -87,6 +87,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"

View file

@ -88,6 +88,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"

View file

@ -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

View file

@ -35,9 +35,10 @@
*/
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include "opentx.h"
#include "inttypes.h"
#include "string.h"
#include "timers.h"
uint8_t s_eeDirtyMsk;
tmr10ms_t s_eeDirtyTime10ms;

View file

@ -35,9 +35,10 @@
*/
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#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<MAX_TIMERS; i++) {
if (g_model.timers[i].persistent) {
timersStates[i].val = g_model.timers[i].value;
}
}
restoreTimers();
resumeMixerCalculations();
// TODO pulses should be started after mixer calculations ...

View file

@ -34,9 +34,10 @@
*
*/
#include <inttypes.h>
#include <string.h>
#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<MAX_TIMERS; i++) {
if (g_model.timers[i].persistent) {
timersStates[i].val = g_model.timers[i].value;
}
}
#endif
restoreTimers();
#if defined(CPUARM)
for (int i=0; i<TELEM_VALUES_MAX; i++) {

View file

@ -35,6 +35,7 @@
*/
#include "opentx.h"
#include "timers.h"
CustomFunctionsContext modelFunctionsContext = { 0 };
@ -359,10 +360,7 @@ void evalFunctions()
#if defined(CPUARM)
case FUNC_SET_TIMER:
{
TimerState & timerState = timersStates[CFN_TIMER_INDEX(cfn)];
timerState.state = TMR_OFF; // is changed to RUNNING dep from mode
timerState.val = CFN_PARAM(cfn);
timerState.val_10ms = 0 ;
timerSet(CFN_TIMER_INDEX(cfn), CFN_PARAM(cfn));
break;
}
#endif

View file

@ -35,6 +35,7 @@
*/
#include "../../opentx.h"
#include "../../timers.h"
#define BIGSIZE DBLSIZE
#define LBOX_CENTERX (LCD_W/4 + 10)

View file

@ -35,6 +35,7 @@
*/
#include "../../opentx.h"
#include "../../timers.h"
void menuStatisticsView(uint8_t event)
{

View file

@ -35,6 +35,7 @@
*/
#include "opentx.h"
#include "timers.h"
#if defined(REVPLUS) && defined(LCD_DUAL_BUFFER)
display_t displayBuf1[DISPLAY_BUF_SIZE];

View file

@ -35,6 +35,7 @@
*/
#include "../../opentx.h"
#include "../../timers.h"
#define BIGSIZE MIDSIZE
#define LBOX_CENTERX (BOX_WIDTH/2 + 17)

View file

@ -35,6 +35,7 @@
*/
#include "../../opentx.h"
#include "../../timers.h"
void menuStatisticsView(uint8_t event)
{

View file

@ -39,6 +39,7 @@
#include "opentx.h"
#include "stamp-opentx.h"
#include "bin_allocator.h"
#include "timers.h"
#if !defined(SIMU)
extern "C" {

View file

@ -35,6 +35,7 @@
*/
#include "opentx.h"
#include "timers.h"
#if defined(PCBTARANIS)
int8_t virtualInputsTrims[NUM_INPUTS];

View file

@ -35,6 +35,7 @@
*/
#include "opentx.h"
#include "timers.h"
#if defined(COLORLCD)
#elif defined(PCBTARANIS)
@ -1520,14 +1521,6 @@ uint8_t trimsDisplayTimer = 0;
uint8_t trimsDisplayMask = 0;
#endif
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 ;
}
void flightReset()
{
// we don't reset the whole audio here (the tada.wav would be cut, if a prompt is queued before FlightReset, it should be played)
@ -1552,8 +1545,6 @@ void flightReset()
RESET_THR_TRACE();
}
TimerState timersStates[MAX_TIMERS] = { { 0 }, { 0 } };
#if defined(THRTRACE)
uint8_t s_traceBuf[MAXTRACE];
#if LCD_W >= 255
@ -1703,95 +1694,7 @@ void doMixerCalculations()
val >>= (RESX_SHIFT-4); // calibrate it
#endif
// Timers start
for (uint8_t i=0; i<MAX_TIMERS; i++) {
int8_t tm = g_model.timers[i].mode;
uint16_t tv = g_model.timers[i].start;
TimerState * timerState = &timersStates[i];
if (tm) {
if (timerState->state == 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; i<MAX_TIMERS; i++) {
if (g_model.timers[i].persistent) {
TimerState *timerState = &timersStates[i];
if (g_model.timers[i].value != (uint16_t)timerState->val) {
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)

View file

@ -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);

284
radio/src/tests/timers.cpp Normal file
View file

@ -0,0 +1,284 @@
/*
* Authors (alphabetical order)
* - Andre Bernet <bernet.andre@gmail.com>
* - Andreas Weitl
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
* - Cameron Weeks <th9xer@gmail.com>
* - Erez Raviv
* - Gabriel Birkus
* - Jean-Pierre Parisy
* - Karl Szmutny
* - Michael Blandford
* - Michal Hlavinka
* - Pat Mackenzie
* - Philip Moss
* - Rob Thomson
* - Romolo Manfredini <romolo.manfredini@gmail.com>
* - 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));
}

209
radio/src/timers.cpp Normal file
View file

@ -0,0 +1,209 @@
/*
* Authors (alphabetical order)
* - Andre Bernet <bernet.andre@gmail.com>
* - Andreas Weitl
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
* - Cameron Weeks <th9xer@gmail.com>
* - Erez Raviv
* - Gabriel Birkus
* - Jean-Pierre Parisy
* - Karl Szmutny
* - Michael Blandford
* - Michal Hlavinka
* - Pat Mackenzie
* - Philip Moss
* - Rob Thomson
* - Romolo Manfredini <romolo.manfredini@gmail.com>
* - 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; i<MAX_TIMERS; i++) {
if (g_model.timers[i].persistent) {
timersStates[i].val = g_model.timers[i].value;
}
}
}
void saveTimers()
{
for (uint8_t i=0; i<MAX_TIMERS; i++) {
if (g_model.timers[i].persistent) {
TimerState *timerState = &timersStates[i];
if (g_model.timers[i].value != (uint16_t)timerState->val) {
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; i<MAX_TIMERS; i++) {
int8_t tm = g_model.timers[i].mode;
uint16_t tv = g_model.timers[i].start;
TimerState * timerState = &timersStates[i];
if (tm) {
if ((timerState->state == 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);
}
}
}
}
}
}
}

74
radio/src/timers.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Authors (alphabetical order)
* - Andre Bernet <bernet.andre@gmail.com>
* - Andreas Weitl
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
* - Cameron Weeks <th9xer@gmail.com>
* - Erez Raviv
* - Gabriel Birkus
* - Jean-Pierre Parisy
* - Karl Szmutny
* - Michael Blandford
* - Michal Hlavinka
* - Pat Mackenzie
* - Philip Moss
* - Rob Thomson
* - Romolo Manfredini <romolo.manfredini@gmail.com>
* - 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