diff --git a/src/main/cms/cms_menu_builtin.c b/src/main/cms/cms_menu_builtin.c index 43e8e5f6f8..2020db1058 100644 --- a/src/main/cms/cms_menu_builtin.c +++ b/src/main/cms/cms_menu_builtin.c @@ -134,7 +134,6 @@ static OSD_Entry menuMainEntries[] = {"FEATURES", OME_Submenu, cmsMenuChange, &menuFeatures, 0}, #ifdef OSD {"OSD", OME_Submenu, cmsMenuChange, &cmsx_menuOsd, 0}, - {"ALARMS", OME_Submenu, cmsMenuChange, &cmsx_menuAlarms, 0}, #endif {"FC&FW INFO", OME_Submenu, cmsMenuChange, &menuInfo, 0}, {"MISC", OME_Submenu, cmsMenuChange, &cmsx_menuMisc, 0}, diff --git a/src/main/cms/cms_menu_osd.c b/src/main/cms/cms_menu_osd.c index 2a811bc7b3..4219e6367a 100644 --- a/src/main/cms/cms_menu_osd.c +++ b/src/main/cms/cms_menu_osd.c @@ -39,51 +39,6 @@ #include "io/displayport_max7456.h" #include "io/osd.h" -static uint8_t osdConfig_rssi_alarm; -static uint16_t osdConfig_cap_alarm; -static uint16_t osdConfig_time_alarm; -static uint16_t osdConfig_alt_alarm; - -static long cmsx_menuAlarmsOnEnter(void) -{ - osdConfig_rssi_alarm = osdConfig()->rssi_alarm; - osdConfig_cap_alarm = osdConfig()->cap_alarm; - osdConfig_time_alarm = osdConfig()->time_alarm; - osdConfig_alt_alarm = osdConfig()->alt_alarm; - return 0; -} - -static long cmsx_menuAlarmsOnExit(const OSD_Entry *self) -{ - UNUSED(self); - - osdConfigMutable()->rssi_alarm = osdConfig_rssi_alarm; - osdConfigMutable()->cap_alarm = osdConfig_cap_alarm; - osdConfigMutable()->time_alarm = osdConfig_time_alarm; - osdConfigMutable()->alt_alarm = osdConfig_alt_alarm; - return 0; -} - -OSD_Entry cmsx_menuAlarmsEntries[] = -{ - {"--- ALARMS ---", OME_Label, NULL, NULL, 0}, - {"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0}, - {"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0}, - {"FLY TIME", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_time_alarm, 1, 200, 1}, 0}, - {"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0}, - {"BACK", OME_Back, NULL, NULL, 0}, - {NULL, OME_END, NULL, NULL, 0} -}; - -CMS_Menu cmsx_menuAlarms = { - .GUARD_text = "MENUALARMS", - .GUARD_type = OME_MENU, - .onEnter = cmsx_menuAlarmsOnEnter, - .onExit = cmsx_menuAlarmsOnExit, - .onGlobalExit = NULL, - .entries = cmsx_menuAlarmsEntries, -}; - #ifndef DISABLE_EXTENDED_CMS_OSD_MENU static uint16_t osdConfig_item_pos[OSD_ITEM_COUNT]; @@ -111,8 +66,8 @@ OSD_Entry menuOsdActiveElemsEntries[] = {"CROSSHAIRS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CROSSHAIRS], 0}, {"HORIZON", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARTIFICIAL_HORIZON], 0}, {"HORIZON SIDEBARS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HORIZON_SIDEBARS], 0}, - {"UPTIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ONTIME], 0}, - {"FLY TIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYTIME], 0}, + {"TIMER 1", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_TIMER_1], 0}, + {"TIMER 2", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_TIMER_2], 0}, {"FLY MODE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYMODE], 0}, {"NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CRAFT_NAME], 0}, {"THROTTLE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_THROTTLE_POS], 0}, @@ -141,7 +96,6 @@ OSD_Entry menuOsdActiveElemsEntries[] = {"DISARMED", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DISARMED], 0}, {"PIT ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PITCH_ANGLE], 0}, {"ROL ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ROLL_ANGLE], 0}, - {"ARMED TIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARMED_TIME], 0}, {"HEADING", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_HEADING], 0}, {"VARIO", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_VARIO], 0}, {"BACK", OME_Back, NULL, NULL, 0}, @@ -156,6 +110,100 @@ CMS_Menu menuOsdActiveElems = { .onGlobalExit = NULL, .entries = menuOsdActiveElemsEntries }; + +static uint8_t osdConfig_rssi_alarm; +static uint16_t osdConfig_cap_alarm; +static uint16_t osdConfig_alt_alarm; + +static long menuAlarmsOnEnter(void) +{ + osdConfig_rssi_alarm = osdConfig()->rssi_alarm; + osdConfig_cap_alarm = osdConfig()->cap_alarm; + osdConfig_alt_alarm = osdConfig()->alt_alarm; + + return 0; +} + +static long menuAlarmsOnExit(const OSD_Entry *self) +{ + UNUSED(self); + + osdConfigMutable()->rssi_alarm = osdConfig_rssi_alarm; + osdConfigMutable()->cap_alarm = osdConfig_cap_alarm; + osdConfigMutable()->alt_alarm = osdConfig_alt_alarm; + + return 0; +} + +OSD_Entry menuAlarmsEntries[] = +{ + {"--- ALARMS ---", OME_Label, NULL, NULL, 0}, + {"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0}, + {"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0}, + {"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0}, + {"BACK", OME_Back, NULL, NULL, 0}, + {NULL, OME_END, NULL, NULL, 0} +}; + +CMS_Menu menuAlarms = { + .GUARD_text = "MENUALARMS", + .GUARD_type = OME_MENU, + .onEnter = menuAlarmsOnEnter, + .onExit = menuAlarmsOnExit, + .onGlobalExit = NULL, + .entries = menuAlarmsEntries, +}; + +osd_timer_source_e timerSource[OSD_TIMER_COUNT]; +osd_timer_precision_e timerPrecision[OSD_TIMER_COUNT]; +uint8_t timerAlarm[OSD_TIMER_COUNT]; + +static long menuTimersOnEnter(void) +{ + for (int i = 0; i < OSD_TIMER_COUNT; i++) { + const uint16_t timer = osdConfig()->timers[i]; + timerSource[i] = OSD_TIMER_SRC(timer); + timerPrecision[i] = OSD_TIMER_PRECISION(timer); + timerAlarm[i] = OSD_TIMER_ALARM(timer); + } + + return 0; +} + +static long menuTimersOnExit(const OSD_Entry *self) +{ + UNUSED(self); + + for (int i = 0; i < OSD_TIMER_COUNT; i++) { + osdConfigMutable()->timers[i] = OSD_TIMER(timerSource[i], timerPrecision[i], timerAlarm[i]); + } + + return 0; +} + +static const char * osdTimerPrecisionNames[] = {"SCND", "HDTH"}; + +OSD_Entry menuTimersEntries[] = +{ + {"--- TIMERS ---", OME_Label, NULL, NULL, 0}, + {"1 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_1], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 }, + {"1 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_1], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0}, + {"1 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_1], 0, 0xFF, 1}, 0}, + {"2 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_2], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 }, + {"2 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_2], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0}, + {"2 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_2], 0, 0xFF, 1}, 0}, + {"BACK", OME_Back, NULL, NULL, 0}, + {NULL, OME_END, NULL, NULL, 0} +}; + +CMS_Menu menuTimers = { + .GUARD_text = "MENUTIMERS", + .GUARD_type = OME_MENU, + .onEnter = menuTimersOnEnter, + .onExit = menuTimersOnExit, + .onGlobalExit = NULL, + .entries = menuTimersEntries, +}; #endif /* DISABLE_EXTENDED_CMS_OSD_MENU */ #ifdef USE_MAX7456 @@ -193,6 +241,8 @@ OSD_Entry cmsx_menuOsdEntries[] = {"---OSD---", OME_Label, NULL, NULL, 0}, #ifndef DISABLE_EXTENDED_CMS_OSD_MENU {"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems, 0}, + {"TIMERS", OME_Submenu, cmsMenuChange, &menuTimers, 0}, + {"ALARMS", OME_Submenu, cmsMenuChange, &menuAlarms, 0}, #endif #ifdef USE_MAX7456 {"INVERT", OME_Bool, NULL, &displayPortProfileMax7456_invert, 0}, diff --git a/src/main/cms/cms_menu_osd.h b/src/main/cms/cms_menu_osd.h index c4e306a9ea..033d28c374 100644 --- a/src/main/cms/cms_menu_osd.h +++ b/src/main/cms/cms_menu_osd.h @@ -17,5 +17,4 @@ #pragma once -extern CMS_Menu cmsx_menuAlarms; extern CMS_Menu cmsx_menuOsd; diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index d4414d9751..ff3e813f93 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -840,17 +840,32 @@ static bool mspCommonProcessOutCommand(uint8_t cmdMSP, sbuf_t *dst, mspPostProce #ifdef OSD // OSD specific, not applicable to OSD slaves. + + // Configuration sbufWriteU8(dst, osdConfig()->units); + + // Alarms sbufWriteU8(dst, osdConfig()->rssi_alarm); sbufWriteU16(dst, osdConfig()->cap_alarm); - sbufWriteU16(dst, osdConfig()->time_alarm); + sbufWriteU16(dst, 0); sbufWriteU16(dst, osdConfig()->alt_alarm); + + // Element position and visibility for (int i = 0; i < OSD_ITEM_COUNT; i++) { sbufWriteU16(dst, osdConfig()->item_pos[i]); } + + // Post flight statistics + sbufWriteU8(dst, OSD_STAT_COUNT); for (int i = 0; i < OSD_STAT_COUNT; i++ ) { sbufWriteU8(dst, osdConfig()->enabled_stats[i]); } + + // Timers + sbufWriteU8(dst, OSD_TIMER_COUNT); + for (int i = 0; i < OSD_TIMER_COUNT; i++) { + sbufWriteU16(dst, osdConfig()->timers[i]); + } #endif break; } @@ -2154,11 +2169,23 @@ static mspResult_e mspCommonProcessInCommand(uint8_t cmdMSP, sbuf_t *src) #endif #if defined(OSD) osdConfigMutable()->units = sbufReadU8(src); + + // Alarms osdConfigMutable()->rssi_alarm = sbufReadU8(src); osdConfigMutable()->cap_alarm = sbufReadU16(src); - osdConfigMutable()->time_alarm = sbufReadU16(src); + sbufReadU16(src); // Skip unused (previously fly timer) osdConfigMutable()->alt_alarm = sbufReadU16(src); #endif + } else if ((int8_t)addr == -2) { +#if defined(OSD) + // Timers + uint8_t index = sbufReadU8(src); + if (index > OSD_TIMER_COUNT) { + return MSP_RESULT_ERROR; + } + osdConfigMutable()->timers[index] = sbufReadU16(src); +#endif + return MSP_RESULT_ERROR; } else { #if defined(OSD) const uint16_t value = sbufReadU16(src); @@ -2172,6 +2199,8 @@ static mspResult_e mspCommonProcessInCommand(uint8_t cmdMSP, sbuf_t *src) } else if (addr < OSD_ITEM_COUNT) { /* Set element positions */ osdConfigMutable()->item_pos[addr] = value; + } else { + return MSP_RESULT_ERROR; } #else return MSP_RESULT_ERROR; diff --git a/src/main/fc/settings.c b/src/main/fc/settings.c index 01527d3f16..b2742f3e89 100644 --- a/src/main/fc/settings.c +++ b/src/main/fc/settings.c @@ -634,13 +634,15 @@ const clivalue_t valueTable[] = { { "osd_rssi_alarm", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, 100 }, PG_OSD_CONFIG, offsetof(osdConfig_t, rssi_alarm) }, { "osd_cap_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 20000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, cap_alarm) }, - { "osd_time_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 60 }, PG_OSD_CONFIG, offsetof(osdConfig_t, time_alarm) }, { "osd_alt_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 10000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, alt_alarm) }, + { "osd_tim1", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, (uint16_t)0xFFFF }, PG_OSD_CONFIG, offsetof(osdConfig_t, timers[OSD_TIMER_1]) }, + { "osd_tim2", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, (uint16_t)0xFFFF }, PG_OSD_CONFIG, offsetof(osdConfig_t, timers[OSD_TIMER_2]) }, + { "osd_vbat_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_VOLTAGE]) }, { "osd_rssi_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_RSSI_VALUE]) }, - { "osd_flytimer_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_FLYTIME]) }, - { "osd_ontimer_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ONTIME]) }, + { "osd_tim_1_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ITEM_TIMER_1]) }, + { "osd_tim_2_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ITEM_TIMER_2]) }, { "osd_flymode_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_FLYMODE]) }, { "osd_throttle_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_THROTTLE_POS]) }, { "osd_vtx_channel_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_VTX_CHANNEL]) }, @@ -668,7 +670,6 @@ const clivalue_t valueTable[] = { { "osd_pit_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_PITCH_ANGLE]) }, { "osd_rol_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ROLL_ANGLE]) }, { "osd_battery_usage_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_USAGE]) }, - { "osd_arm_time_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ARMED_TIME]) }, { "osd_disarmed_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_DISARMED]) }, { "osd_nheading_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_HEADING]) }, { "osd_nvario_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_VARIO]) }, @@ -684,9 +685,9 @@ const clivalue_t valueTable[] = { { "osd_stat_max_alt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_MAX_ALTITUDE])}, { "osd_stat_bbox", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX])}, { "osd_stat_endbatt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_END_BATTERY])}, - { "osd_stat_flytime", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_FLYTIME])}, - { "osd_stat_armtime", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_ARMEDTIME])}, { "osd_stat_bb_no", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX_NUMBER])}, + { "osd_stat_tim_1", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_TIMER_1])}, + { "osd_stat_tim_2", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_TIMER_2])}, #endif // PG_SYSTEM_CONFIG diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 6a8b004595..f5bc83c018 100755 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -89,6 +89,12 @@ #define VIDEO_BUFFER_CHARS_PAL 480 +const char * const osdTimerSourceNames[] = { + "ON TIME ", + "TOTAL ARM", + "LAST ARM " +}; + // Blink control static bool blinkState = true; @@ -106,7 +112,7 @@ static uint32_t blinkBits[(OSD_ITEM_COUNT + 31)/32]; #define IS_LO(X) (rcData[X] < 1250) #define IS_MID(X) (rcData[X] > 1250 && rcData[X] < 1750) -static uint16_t flyTime = 0; +static timeUs_t flyTime = 0; static uint8_t statRssi; typedef struct statistic_s { @@ -116,7 +122,7 @@ typedef struct statistic_s { int16_t min_rssi; int16_t max_altitude; int16_t max_distance; - uint16_t armed_time; + timeUs_t armed_time; } statistic_t; static statistic_t stats; @@ -226,6 +232,65 @@ static uint8_t osdGetDirectionSymbolFromHeading(int heading) return SYM_ARROW_SOUTH + heading; } +static char osdGetTimerSymbol(osd_timer_source_e src) +{ + switch (src) { + case OSD_TIMER_SRC_ON: + return SYM_ON_M; + case OSD_TIMER_SRC_TOTAL_ARMED: + case OSD_TIMER_SRC_LAST_ARMED: + return SYM_FLY_M; + default: + return ' '; + } +} + +static timeUs_t osdGetTimerValue(osd_timer_source_e src) +{ + switch (src) { + case OSD_TIMER_SRC_ON: + return micros(); + case OSD_TIMER_SRC_TOTAL_ARMED: + return flyTime; + case OSD_TIMER_SRC_LAST_ARMED: + return stats.armed_time; + default: + return 0; + } +} + +STATIC_UNIT_TESTED void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time) +{ + int seconds = time / 1000000; + const int minutes = seconds / 60; + seconds = seconds % 60; + + switch (precision) { + case OSD_TIMER_PREC_SECOND: + default: + tfp_sprintf(buff, "%02d:%02d", minutes, seconds); + break; + case OSD_TIMER_PREC_HUNDREDTHS: + { + const int hundredths = (time / 10000) % 100; + tfp_sprintf(buff, "%02d:%02d.%02d", minutes, seconds, hundredths); + break; + } + } +} + +STATIC_UNIT_TESTED void osdFormatTimer(char *buff, bool showSymbol, int timerIndex) +{ + const uint16_t timer = osdConfig()->timers[timerIndex]; + const uint8_t src = OSD_TIMER_SRC(timer); + + if (showSymbol) { + *(buff++) = osdGetTimerSymbol(src); + } + + osdFormatTime(buff, OSD_TIMER_PRECISION(timer), osdGetTimerValue(src)); +} + static void osdDrawSingleElement(uint8_t item) { if (!VISIBLE(osdConfig()->item_pos[item]) || BLINK(item)) { @@ -235,7 +300,7 @@ static void osdDrawSingleElement(uint8_t item) uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]); uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]); uint8_t elemOffsetX = 0; - char buff[32]; + char buff[OSD_ELEMENT_BUFFER_LENGTH]; switch (item) { case OSD_RSSI_VALUE: @@ -350,24 +415,14 @@ static void osdDrawSingleElement(uint8_t item) break; } - case OSD_ONTIME: + case OSD_ITEM_TIMER_1: + case OSD_ITEM_TIMER_2: { - const uint32_t seconds = micros() / 1000000; - buff[0] = SYM_ON_M; - tfp_sprintf(buff + 1, "%02d:%02d", seconds / 60, seconds % 60); + const int timer = item - OSD_ITEM_TIMER_1; + osdFormatTimer(buff, true, timer); break; } - case OSD_FLYTIME: - buff[0] = SYM_FLY_M; - tfp_sprintf(buff + 1, "%02d:%02d", flyTime / 60, flyTime % 60); - break; - - case OSD_ARMED_TIME: - buff[0] = SYM_FLY_M; - tfp_sprintf(buff + 1, "%02d:%02d", stats.armed_time / 60, stats.armed_time % 60); - break; - case OSD_FLYMODE: { char *p = "ACRO"; @@ -658,8 +713,8 @@ static void osdDrawElements(void) osdDrawSingleElement(OSD_MAIN_BATT_VOLTAGE); osdDrawSingleElement(OSD_RSSI_VALUE); osdDrawSingleElement(OSD_CROSSHAIRS); - osdDrawSingleElement(OSD_FLYTIME); - osdDrawSingleElement(OSD_ONTIME); + osdDrawSingleElement(OSD_ITEM_TIMER_1); + osdDrawSingleElement(OSD_ITEM_TIMER_2); osdDrawSingleElement(OSD_FLYMODE); osdDrawSingleElement(OSD_THROTTLE_POS); osdDrawSingleElement(OSD_VTX_CHANNEL); @@ -678,7 +733,6 @@ static void osdDrawElements(void) osdDrawSingleElement(OSD_PITCH_ANGLE); osdDrawSingleElement(OSD_ROLL_ANGLE); osdDrawSingleElement(OSD_MAIN_BATT_USAGE); - osdDrawSingleElement(OSD_ARMED_TIME); osdDrawSingleElement(OSD_DISARMED); osdDrawSingleElement(OSD_NUMERICAL_HEADING); osdDrawSingleElement(OSD_NUMERICAL_VARIO); @@ -710,8 +764,8 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig) osdConfig->item_pos[OSD_CROSSHAIRS] = OSD_POS(8, 6) | VISIBLE_FLAG; osdConfig->item_pos[OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | VISIBLE_FLAG; osdConfig->item_pos[OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | VISIBLE_FLAG; - osdConfig->item_pos[OSD_ONTIME] = OSD_POS(22, 1) | VISIBLE_FLAG; - osdConfig->item_pos[OSD_FLYTIME] = OSD_POS(1, 1) | VISIBLE_FLAG; + osdConfig->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(22, 1) | VISIBLE_FLAG; + osdConfig->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | VISIBLE_FLAG; osdConfig->item_pos[OSD_FLYMODE] = OSD_POS(13, 10) | VISIBLE_FLAG; osdConfig->item_pos[OSD_CRAFT_NAME] = OSD_POS(10, 11) | VISIBLE_FLAG; osdConfig->item_pos[OSD_THROTTLE_POS] = OSD_POS(1, 7) | VISIBLE_FLAG; @@ -737,7 +791,6 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig) osdConfig->item_pos[OSD_HOME_DIR] = OSD_POS(14, 9) | VISIBLE_FLAG; osdConfig->item_pos[OSD_COMPASS_BAR] = OSD_POS(10, 8) | VISIBLE_FLAG; osdConfig->item_pos[OSD_MAIN_BATT_USAGE] = OSD_POS(8, 12) | VISIBLE_FLAG; - osdConfig->item_pos[OSD_ARMED_TIME] = OSD_POS(1, 2) | VISIBLE_FLAG; osdConfig->item_pos[OSD_DISARMED] = OSD_POS(10, 4) | VISIBLE_FLAG; osdConfig->item_pos[OSD_NUMERICAL_HEADING] = OSD_POS(23, 9) | VISIBLE_FLAG; osdConfig->item_pos[OSD_NUMERICAL_VARIO] = OSD_POS(23, 8) | VISIBLE_FLAG; @@ -752,16 +805,18 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig) osdConfig->enabled_stats[OSD_STAT_MAX_ALTITUDE] = false; osdConfig->enabled_stats[OSD_STAT_BLACKBOX] = true; osdConfig->enabled_stats[OSD_STAT_END_BATTERY] = false; - osdConfig->enabled_stats[OSD_STAT_FLYTIME] = false; - osdConfig->enabled_stats[OSD_STAT_ARMEDTIME] = true; osdConfig->enabled_stats[OSD_STAT_MAX_DISTANCE] = false; osdConfig->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = true; + osdConfig->enabled_stats[OSD_STAT_TIMER_1] = false; + osdConfig->enabled_stats[OSD_STAT_TIMER_2] = true; osdConfig->units = OSD_UNIT_METRIC; + osdConfig->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_SECOND, 10); + osdConfig->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 10); + osdConfig->rssi_alarm = 20; osdConfig->cap_alarm = 2200; - osdConfig->time_alarm = 10; // in minutes osdConfig->alt_alarm = 100; // meters or feet depend on configuration } @@ -837,10 +892,15 @@ void osdUpdateAlarms(void) else CLR_BLINK(OSD_GPS_SATS); - if (flyTime / 60 >= osdConfig()->time_alarm && ARMING_FLAG(ARMED)) - SET_BLINK(OSD_FLYTIME); - else - CLR_BLINK(OSD_FLYTIME); + for (int i = 0; i < OSD_TIMER_COUNT; i++) { + const uint16_t timer = osdConfig()->timers[i]; + const timeUs_t time = osdGetTimerValue(OSD_TIMER_SRC(timer)); + const timeUs_t alarmTime = OSD_TIMER_ALARM(timer) * 60000000; // convert from minutes to us + if (alarmTime != 0 && time >= alarmTime) + SET_BLINK(OSD_ITEM_TIMER_1 + i); + else + CLR_BLINK(OSD_ITEM_TIMER_1 + i); + } if (getMAhDrawn() >= osdConfig()->cap_alarm) { SET_BLINK(OSD_MAH_DRAWN); @@ -862,11 +922,12 @@ void osdResetAlarms(void) CLR_BLINK(OSD_MAIN_BATT_VOLTAGE); CLR_BLINK(OSD_WARNINGS); CLR_BLINK(OSD_GPS_SATS); - CLR_BLINK(OSD_FLYTIME); CLR_BLINK(OSD_MAH_DRAWN); CLR_BLINK(OSD_ALTITUDE); CLR_BLINK(OSD_AVG_CELL_VOLTAGE); CLR_BLINK(OSD_MAIN_BATT_USAGE); + CLR_BLINK(OSD_ITEM_TIMER_1); + CLR_BLINK(OSD_ITEM_TIMER_2); } static void osdResetStats(void) @@ -967,14 +1028,14 @@ static void osdShowStats(void) displayClearScreen(osdDisplayPort); displayWrite(osdDisplayPort, 2, top++, " --- STATS ---"); - if (osdConfig()->enabled_stats[OSD_STAT_ARMEDTIME]) { - tfp_sprintf(buff, "%02d:%02d", stats.armed_time / 60, stats.armed_time % 60); - osdDisplayStatisticLabel(top++, "ARMED TIME", buff); + if (osdConfig()->enabled_stats[OSD_STAT_TIMER_1]) { + osdFormatTimer(buff, false, OSD_TIMER_1); + osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1])], buff); } - if (osdConfig()->enabled_stats[OSD_STAT_FLYTIME]) { - tfp_sprintf(buff, "%02d:%02d", flyTime / 60, flyTime % 60); - osdDisplayStatisticLabel(top++, "FLY TIME", buff); + if (osdConfig()->enabled_stats[OSD_STAT_TIMER_2]) { + osdFormatTimer(buff, false, OSD_TIMER_2); + osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2])], buff); } if (osdConfig()->enabled_stats[OSD_STAT_MAX_SPEED] && STATE(GPS_FIX)) { @@ -1046,8 +1107,7 @@ static void osdShowArmed(void) STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs) { - static uint8_t lastSec = 0; - uint8_t sec; + static timeUs_t lastTimeUs = 0; // detect arm/disarm if (armState != ARMING_FLAG(ARMED)) { @@ -1067,13 +1127,12 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs) osdUpdateStats(); - sec = currentTimeUs / 1000000; - - if (ARMING_FLAG(ARMED) && sec != lastSec) { - flyTime++; - stats.armed_time++; - lastSec = sec; + if (ARMING_FLAG(ARMED)) { + timeUs_t deltaT = currentTimeUs - lastTimeUs; + flyTime += deltaT; + stats.armed_time += deltaT; } + lastTimeUs = currentTimeUs; if (resumeRefreshAt) { if (cmp32(currentTimeUs, resumeRefreshAt) < 0) { diff --git a/src/main/io/osd.h b/src/main/io/osd.h index b6cb82dc86..7f9ae54342 100755 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -21,27 +21,38 @@ #include "common/time.h" #include "config/parameter_group.h" +#define OSD_NUM_TIMER_TYPES 3 +extern const char * const osdTimerSourceNames[OSD_NUM_TIMER_TYPES]; + +#define OSD_ELEMENT_BUFFER_LENGTH 32 + #define VISIBLE_FLAG 0x0800 #define VISIBLE(x) (x & VISIBLE_FLAG) #define OSD_POS_MAX 0x3FF #define OSD_POSCFG_MAX (VISIBLE_FLAG|0x3FF) // For CLI values // Character coordinate - #define OSD_POSITION_BITS 5 // 5 bits gives a range 0-31 #define OSD_POSITION_XY_MASK ((1 << OSD_POSITION_BITS) - 1) #define OSD_POS(x,y) ((x & OSD_POSITION_XY_MASK) | ((y & OSD_POSITION_XY_MASK) << OSD_POSITION_BITS)) #define OSD_X(x) (x & OSD_POSITION_XY_MASK) #define OSD_Y(x) ((x >> OSD_POSITION_BITS) & OSD_POSITION_XY_MASK) +// Timer configuration +// Stored as 15[alarm:8][precision:4][source:4]0 +#define OSD_TIMER(src, prec, alarm) ((src & 0x0F) | ((prec & 0x0F) << 4) | ((alarm & 0xFF ) << 8)) +#define OSD_TIMER_SRC(timer) (timer & 0x0F) +#define OSD_TIMER_PRECISION(timer) ((timer >> 4) & 0x0F) +#define OSD_TIMER_ALARM(timer) ((timer >> 8) & 0xFF) + typedef enum { OSD_RSSI_VALUE, OSD_MAIN_BATT_VOLTAGE, OSD_CROSSHAIRS, OSD_ARTIFICIAL_HORIZON, OSD_HORIZON_SIDEBARS, - OSD_ONTIME, - OSD_FLYTIME, + OSD_ITEM_TIMER_1, + OSD_ITEM_TIMER_2, OSD_FLYMODE, OSD_CRAFT_NAME, OSD_THROTTLE_POS, @@ -64,7 +75,6 @@ typedef enum { OSD_PITCH_ANGLE, OSD_ROLL_ANGLE, OSD_MAIN_BATT_USAGE, - OSD_ARMED_TIME, OSD_DISARMED, OSD_HOME_DIR, OSD_HOME_DIST, @@ -85,8 +95,8 @@ typedef enum { OSD_STAT_MAX_ALTITUDE, OSD_STAT_BLACKBOX, OSD_STAT_END_BATTERY, - OSD_STAT_FLYTIME, - OSD_STAT_ARMEDTIME, + OSD_STAT_TIMER_1, + OSD_STAT_TIMER_2, OSD_STAT_MAX_DISTANCE, OSD_STAT_BLACKBOX_NUMBER, OSD_STAT_COUNT // MUST BE LAST @@ -97,6 +107,25 @@ typedef enum { OSD_UNIT_METRIC } osd_unit_e; +typedef enum { + OSD_TIMER_1, + OSD_TIMER_2, + OSD_TIMER_COUNT +} osd_timer_e; + +typedef enum { + OSD_TIMER_SRC_ON, + OSD_TIMER_SRC_TOTAL_ARMED, + OSD_TIMER_SRC_LAST_ARMED, + OSD_TIMER_SRC_COUNT +} osd_timer_source_e; + +typedef enum { + OSD_TIMER_PREC_SECOND, + OSD_TIMER_PREC_HUNDREDTHS, + OSD_TIMER_PREC_COUNT +} osd_timer_precision_e; + typedef struct osdConfig_s { uint16_t item_pos[OSD_ITEM_COUNT]; bool enabled_stats[OSD_STAT_COUNT]; @@ -104,10 +133,11 @@ typedef struct osdConfig_s { // Alarms uint8_t rssi_alarm; uint16_t cap_alarm; - uint16_t time_alarm; uint16_t alt_alarm; osd_unit_e units; + + uint16_t timers[OSD_TIMER_COUNT]; } osdConfig_t; extern uint32_t resumeRefreshAt; diff --git a/src/test/unit/osd_unittest.cc b/src/test/unit/osd_unittest.cc index 9e7da34940..c0f324f121 100644 --- a/src/test/unit/osd_unittest.cc +++ b/src/test/unit/osd_unittest.cc @@ -47,6 +47,8 @@ extern "C" { #include "rx/rx.h" void osdRefresh(timeUs_t currentTimeUs); + void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time); + void osdFormatTimer(char *buff, bool showSymbol, int timerIndex); uint16_t rssi; attitudeEulerAngles_t attitude; @@ -269,8 +271,8 @@ TEST(OsdTest, TestStatsImperial) osdConfigMutable()->enabled_stats[OSD_STAT_MAX_ALTITUDE] = true; osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX] = false; osdConfigMutable()->enabled_stats[OSD_STAT_END_BATTERY] = true; - osdConfigMutable()->enabled_stats[OSD_STAT_FLYTIME] = true; - osdConfigMutable()->enabled_stats[OSD_STAT_ARMEDTIME] = true; + osdConfigMutable()->enabled_stats[OSD_STAT_TIMER_1] = true; + osdConfigMutable()->enabled_stats[OSD_STAT_TIMER_2] = true; osdConfigMutable()->enabled_stats[OSD_STAT_MAX_DISTANCE] = true; osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = false; @@ -278,6 +280,14 @@ TEST(OsdTest, TestStatsImperial) // using imperial unit system osdConfigMutable()->units = OSD_UNIT_IMPERIAL; + // and + // this timer 1 configuration + osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0); + + // and + // this timer 2 configuration + osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0); + // and // a GPS fix is present stateFlags |= GPS_FIX | GPS_FIX_HOME; @@ -318,14 +328,15 @@ TEST(OsdTest, TestStatsImperial) // then // statistics screen should display the following - displayPortTestBufferSubstring(2, 3, "ARMED TIME : 00:04"); - displayPortTestBufferSubstring(2, 4, "FLY TIME : 00:07"); - displayPortTestBufferSubstring(2, 5, "MAX SPEED : 28"); - displayPortTestBufferSubstring(2, 6, "MAX DISTANCE : 328%c", SYM_FT); - displayPortTestBufferSubstring(2, 7, "MIN BATTERY : 14.7%c", SYM_VOLT); - displayPortTestBufferSubstring(2, 8, "END BATTERY : 15.2%c", SYM_VOLT); - displayPortTestBufferSubstring(2, 9, "MIN RSSI : 25%%"); - displayPortTestBufferSubstring(2, 10, "MAX ALTITUDE : 6.5%c", SYM_FT); + int row = 3; + displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:05.00"); + displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:03"); + displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28"); + displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 328%c", SYM_FT); + displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.7%c", SYM_VOLT); + displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.2%c", SYM_VOLT); + displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%"); + displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.5%c", SYM_FT); } /* @@ -367,14 +378,15 @@ TEST(OsdTest, TestStatsMetric) // then // statistics screen should display the following - displayPortTestBufferSubstring(2, 3, "ARMED TIME : 00:02"); - displayPortTestBufferSubstring(2, 4, "FLY TIME : 00:09"); - displayPortTestBufferSubstring(2, 5, "MAX SPEED : 28"); - displayPortTestBufferSubstring(2, 6, "MAX DISTANCE : 100%c", SYM_M); - displayPortTestBufferSubstring(2, 7, "MIN BATTERY : 14.7%c", SYM_VOLT); - displayPortTestBufferSubstring(2, 8, "END BATTERY : 15.2%c", SYM_VOLT); - displayPortTestBufferSubstring(2, 9, "MIN RSSI : 25%%"); - displayPortTestBufferSubstring(2, 10, "MAX ALTITUDE : 2.0%c", SYM_M); + int row = 3; + displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:07.50"); + displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:02"); + displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28"); + displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 100%c", SYM_M); + displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.7%c", SYM_VOLT); + displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.2%c", SYM_VOLT); + displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%"); + displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M); } /* @@ -390,16 +402,30 @@ TEST(OsdTest, TestAlarms) // the following OSD elements are visible osdConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | VISIBLE_FLAG; - osdConfigMutable()->item_pos[OSD_FLYTIME] = OSD_POS(1, 1) | VISIBLE_FLAG; + osdConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | VISIBLE_FLAG; + osdConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | VISIBLE_FLAG; // and // this set of alarm values osdConfigMutable()->rssi_alarm = 20; osdConfigMutable()->cap_alarm = 2200; - osdConfigMutable()->time_alarm = 1; // in minutes osdConfigMutable()->alt_alarm = 100; // meters + // and + // this timer 1 configuration + osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 2); + EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1])); + EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1])); + EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1])); + + // and + // this timer 2 configuration + osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 1); + EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2])); + EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2])); + EXPECT_EQ(1, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2])); + // and // using the metric unit system osdConfigMutable()->units = OSD_UNIT_METRIC; @@ -421,6 +447,7 @@ TEST(OsdTest, TestAlarms) displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI); displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT); displayPortTestBufferSubstring(1, 1, "%c00:", SYM_FLY_M); // only test the minute part of the timer + displayPortTestBufferSubstring(20, 1, "%c01:", SYM_ON_M); // only test the minute part of the timer displayPortTestBufferSubstring(23, 7, " 0.0%c", SYM_M); } @@ -430,11 +457,8 @@ TEST(OsdTest, TestAlarms) simulationBatteryState = BATTERY_CRITICAL; simulationBatteryVoltage = 135; simulationAltitude = 12000; - // Fly timer is incremented on periodic calls to osdRefresh, can't simply just increment the simulated system clock - for (int i = 0; i < 60; i++) { - simulationTime += 1e6; - osdRefresh(simulationTime); - } + simulationTime += 60e6; + osdRefresh(simulationTime); // then // elements showing values in alarm range should flash @@ -451,6 +475,7 @@ TEST(OsdTest, TestAlarms) displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI); displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT); displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer + displayPortTestBufferSubstring(20, 1, "%c02:", SYM_ON_M); // only test the minute part of the timer displayPortTestBufferSubstring(23, 7, " 120.0%c", SYM_M); } else { displayPortTestBufferIsEmpty(); @@ -492,6 +517,58 @@ TEST(OsdTest, TestElementRssi) displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI); } +/* + * Tests the time string formatting function with a series of precision settings and time values. + */ +TEST(OsdTest, TestFormatTimeString) +{ + char buff[OSD_ELEMENT_BUFFER_LENGTH]; + + /* Seconds precision, 0 us */ + osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0); + EXPECT_EQ(0, strcmp("00:00", buff)); + + /* Seconds precision, 0.9 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6); + EXPECT_EQ(0, strcmp("00:00", buff)); + + /* Seconds precision, 10 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6); + EXPECT_EQ(0, strcmp("00:10", buff)); + + /* Seconds precision, 1 minute */ + osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6); + EXPECT_EQ(0, strcmp("01:00", buff)); + + /* Seconds precision, 1 minute 59 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6); + EXPECT_EQ(0, strcmp("01:59", buff)); + + /* Hundredths precision, 0 us */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0); + EXPECT_EQ(0, strcmp("00:00.00", buff)); + + /* Hundredths precision, 10 milliseconds (one 100th of a second) */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3); + EXPECT_EQ(0, strcmp("00:00.01", buff)); + + /* Hundredths precision, 0.9 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6); + EXPECT_EQ(0, strcmp("00:00.90", buff)); + + /* Hundredths precision, 10 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6); + EXPECT_EQ(0, strcmp("00:10.00", buff)); + + /* Hundredths precision, 1 minute */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6); + EXPECT_EQ(0, strcmp("01:00.00", buff)); + + /* Hundredths precision, 1 minute 59 seconds */ + osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6); + EXPECT_EQ(0, strcmp("01:59.00", buff)); +} + // STUBS extern "C" {