1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-20 06:45:16 +03:00

Merge pull request #3373 from DanNixon/generic_osd_timers

Generic OSD timers (plus high precision timer)
This commit is contained in:
Michael Keller 2017-07-08 10:21:39 +12:00 committed by GitHub
commit ea5a7cd998
8 changed files with 379 additions and 135 deletions

View file

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

View file

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

View file

@ -17,5 +17,4 @@
#pragma once
extern CMS_Menu cmsx_menuAlarms;
extern CMS_Menu cmsx_menuOsd;

View file

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

View file

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

View file

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

View file

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

View file

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