1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-24 16:55:36 +03:00

Disconnect OSD stats display from storage bit position order

Allows reordering OSD post-flight statistics without affecting the storage bit position and requiring Configurator changes. Previously the display order was one and the same with the enabled flag bit position. Additionally this ordering was also used by the configurator. So if the ordering was changed then the user settings would become corrupted (different stats would be enabled/disabled). Also the Configurator had an internal representation that had to match the definition enumeration otherwise the flags returned when saving would also set the wrong bits.

Now the definition remains constant and unchanging. The bit positions for the enable flag will not be changed. A separate array defines the presentaion order of the permanent stats ID's.

Added the display order to MSP to allow the configurator to also present the stats in the same order displayed in the firmware.
This commit is contained in:
Bruce Luckcuck 2019-06-21 08:24:42 -04:00
parent 534938b943
commit 1b8fd99126
3 changed files with 256 additions and 174 deletions

View file

@ -822,6 +822,12 @@ static bool mspCommonProcessOutCommand(uint8_t cmdMSP, sbuf_t *dst, mspPostProce
sbufWriteU8(dst, 0);
#endif // USE_OSD_STICK_OVERLAY
// API >= 1.42
// Post-flight stats display order
for (int i = 0; i < OSD_STAT_COUNT; i++) {
sbufWriteU8(dst, osdStatsDisplayOrder[i]);
}
#endif // USE_OSD
break;
}

View file

@ -130,6 +130,39 @@ escSensorData_t *osdEscDataCombined;
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 6);
// Controls the display order of the OSD post-flight statistics.
// Adjust the ordering here to control how the post-flight stats are presented.
// Every entry in osd_stats_e should be represented. Any that are missing will not
// be shown on the the post-flight statistics page.
// If you reorder the stats it's likely that you'll need to make likewise updates
// to the unit tests.
const osd_stats_e osdStatsDisplayOrder[OSD_STAT_COUNT] = {
OSD_STAT_RTC_DATE_TIME,
OSD_STAT_TIMER_1,
OSD_STAT_TIMER_2,
OSD_STAT_MAX_ALTITUDE,
OSD_STAT_MAX_SPEED,
OSD_STAT_MAX_DISTANCE,
OSD_STAT_FLIGHT_DISTANCE,
OSD_STAT_MIN_BATTERY,
OSD_STAT_END_BATTERY,
OSD_STAT_BATTERY,
OSD_STAT_MIN_RSSI,
OSD_STAT_MAX_CURRENT,
OSD_STAT_USED_MAH,
OSD_STAT_BLACKBOX,
OSD_STAT_BLACKBOX_NUMBER,
OSD_STAT_MAX_G_FORCE,
OSD_STAT_MAX_ESC_TEMP,
OSD_STAT_MAX_ESC_RPM,
OSD_STAT_MIN_LINK_QUALITY,
OSD_STAT_MAX_FFT,
OSD_STAT_MIN_RSSI_DBM,
OSD_STAT_TOTAL_FLIGHTS,
OSD_STAT_TOTAL_TIME,
OSD_STAT_TOTAL_DIST,
};
void osdStatSetState(uint8_t statIndex, bool enabled)
{
if (enabled) {
@ -339,6 +372,7 @@ static void osdResetStats(void)
stats.max_current = 0;
stats.max_speed = 0;
stats.min_voltage = 5000;
stats.end_voltage = 0;
stats.min_rssi = 99; // percent
stats.max_altitude = 0;
stats.max_distance = 0;
@ -490,10 +524,207 @@ static bool isSomeStatEnabled(void)
// on the stats screen will have to be more beneficial than the hassle of not matching exactly to the
// configurator list.
static uint8_t osdShowStats(uint16_t endBatteryVoltage, int statsRowCount)
static bool osdDisplayStat(int statistic, uint8_t displayRow)
{
char buff[OSD_ELEMENT_BUFFER_LENGTH];
switch (statistic) {
case OSD_STAT_RTC_DATE_TIME: {
bool success = false;
#ifdef USE_RTC_TIME
success = osdFormatRtcDateTime(&buff[0]);
#endif
if (!success) {
tfp_sprintf(buff, "NO RTC");
}
displayWrite(osdDisplayPort, 2, displayRow, buff);
return true;
}
case OSD_STAT_TIMER_1:
osdFormatTimer(buff, false, (OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]) == OSD_TIMER_SRC_ON ? false : true), OSD_TIMER_1);
osdDisplayStatisticLabel(displayRow, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1])], buff);
return true;
case OSD_STAT_TIMER_2:
osdFormatTimer(buff, false, (OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]) == OSD_TIMER_SRC_ON ? false : true), OSD_TIMER_2);
osdDisplayStatisticLabel(displayRow, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2])], buff);
return true;
case OSD_STAT_MAX_ALTITUDE: {
const int alt = osdGetMetersToSelectedUnit(stats.max_altitude) / 10;
tfp_sprintf(buff, "%d.%d%c", alt / 10, alt % 10, osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(displayRow, "MAX ALTITUDE", buff);
return true;
}
#ifdef USE_GPS
case OSD_STAT_MAX_SPEED:
if (featureIsEnabled(FEATURE_GPS)) {
tfp_sprintf(buff, "%d%c", osdGetSpeedToSelectedUnit(stats.max_speed), osdGetSpeedToSelectedUnitSymbol());
osdDisplayStatisticLabel(displayRow, "MAX SPEED", buff);
return true;
}
break;
case OSD_STAT_MAX_DISTANCE:
if (featureIsEnabled(FEATURE_GPS)) {
tfp_sprintf(buff, "%d%c", osdGetMetersToSelectedUnit(stats.max_distance), osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(displayRow, "MAX DISTANCE", buff);
return true;
}
break;
case OSD_STAT_FLIGHT_DISTANCE:
if (featureIsEnabled(FEATURE_GPS)) {
const uint32_t distanceFlown = GPS_distanceFlownInCm / 100;
tfp_sprintf(buff, "%d%c", osdGetMetersToSelectedUnit(distanceFlown), osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(displayRow, "FLIGHT DISTANCE", buff);
return true;
}
break;
#endif
case OSD_STAT_MIN_BATTERY:
tfp_sprintf(buff, "%d.%02d%c", stats.min_voltage / 100, stats.min_voltage % 100, SYM_VOLT);
osdDisplayStatisticLabel(displayRow, "MIN BATTERY", buff);
return true;
case OSD_STAT_END_BATTERY:
tfp_sprintf(buff, "%d.%02d%c", stats.end_voltage / 100, stats.end_voltage % 100, SYM_VOLT);
osdDisplayStatisticLabel(displayRow, "END BATTERY", buff);
return true;
case OSD_STAT_BATTERY:
tfp_sprintf(buff, "%d.%02d%c", getBatteryVoltage() / 100, getBatteryVoltage() % 100, SYM_VOLT);
osdDisplayStatisticLabel(displayRow, "BATTERY", buff);
return true;
case OSD_STAT_MIN_RSSI:
itoa(stats.min_rssi, buff, 10);
strcat(buff, "%");
osdDisplayStatisticLabel(displayRow, "MIN RSSI", buff);
return true;
case OSD_STAT_MAX_CURRENT:
if (batteryConfig()->currentMeterSource != CURRENT_METER_NONE) {
tfp_sprintf(buff, "%d%c", stats.max_current, SYM_AMP);
osdDisplayStatisticLabel(displayRow, "MAX CURRENT", buff);
return true;
}
break;
case OSD_STAT_USED_MAH:
if (batteryConfig()->currentMeterSource != CURRENT_METER_NONE) {
tfp_sprintf(buff, "%d%c", getMAhDrawn(), SYM_MAH);
osdDisplayStatisticLabel(displayRow, "USED MAH", buff);
return true;
}
break;
#ifdef USE_BLACKBOX
case OSD_STAT_BLACKBOX:
if (blackboxConfig()->device && blackboxConfig()->device != BLACKBOX_DEVICE_SERIAL) {
osdGetBlackboxStatusString(buff);
osdDisplayStatisticLabel(displayRow, "BLACKBOX", buff);
return true;
}
break;
case OSD_STAT_BLACKBOX_NUMBER:
if (blackboxConfig()->device && blackboxConfig()->device != BLACKBOX_DEVICE_SERIAL) {
itoa(blackboxGetLogNumber(), buff, 10);
osdDisplayStatisticLabel(displayRow, "BB LOG NUM", buff);
return true;
}
break;
#endif
#if defined(USE_ACC)
case OSD_STAT_MAX_G_FORCE:
if (sensors(SENSOR_ACC)) {
const int gForce = lrintf(stats.max_g_force * 10);
tfp_sprintf(buff, "%01d.%01dG", gForce / 10, gForce % 10);
osdDisplayStatisticLabel(displayRow, "MAX G-FORCE", buff);
return true;
}
break;
#endif
#ifdef USE_ESC_SENSOR
case OSD_STAT_MAX_ESC_TEMP:
tfp_sprintf(buff, "%d%c", osdConvertTemperatureToSelectedUnit(stats.max_esc_temp), osdGetTemperatureSymbolForSelectedUnit());
osdDisplayStatisticLabel(displayRow, "MAX ESC TEMP", buff);
return true;
case OSD_STAT_MAX_ESC_RPM:
itoa(stats.max_esc_rpm, buff, 10);
osdDisplayStatisticLabel(displayRow, "MAX ESC RPM", buff);
return true;
#endif
#ifdef USE_RX_LINK_QUALITY_INFO
case OSD_STAT_MIN_LINK_QUALITY:
tfp_sprintf(buff, "%d", stats.min_link_quality);
strcat(buff, "%");
osdDisplayStatisticLabel(displayRow, "MIN LINK", buff);
return true;
#endif
#if defined(USE_GYRO_DATA_ANALYSE)
case OSD_STAT_MAX_FFT:
if (featureIsEnabled(FEATURE_DYNAMIC_FILTER)) {
int value = getMaxFFT();
if (value > 0) {
tfp_sprintf(buff, "%dHZ", value);
osdDisplayStatisticLabel(displayRow, "PEAK FFT", buff);
} else {
osdDisplayStatisticLabel(displayRow, "PEAK FFT", "THRT<20%");
}
return true;
}
break;
#endif
#ifdef USE_RX_RSSI_DBM
case OSD_STAT_MIN_RSSI_DBM:
tfp_sprintf(buff, "%3d", stats.min_rssi_dbm * -1);
osdDisplayStatisticLabel(displayRow, "MIN RSSI DBM", buff);
return true;
#endif
#ifdef USE_PERSISTENT_STATS
case OSD_STAT_TOTAL_FLIGHTS:
itoa(statsConfig()->stats_total_flights, buff, 10);
osdDisplayStatisticLabel(displayRow, "TOTAL FLIGHTS", buff);
return true;
case OSD_STAT_TOTAL_TIME: {
int minutes = statsConfig()->stats_total_time_s / 60;
tfp_sprintf(buff, "%d:%02dH", minutes / 60, minutes % 60);
osdDisplayStatisticLabel(displayRow, "TOTAL FLIGHT TIME", buff);
return true;
}
case OSD_STAT_TOTAL_DIST:
#define METERS_PER_KILOMETER 1000
#define METERS_PER_MILE 1609
if (osdConfig()->units == OSD_UNIT_IMPERIAL) {
tfp_sprintf(buff, "%dMI", statsConfig()->stats_total_dist_m / METERS_PER_MILE);
} else {
tfp_sprintf(buff, "%dKM", statsConfig()->stats_total_dist_m / METERS_PER_KILOMETER);
}
osdDisplayStatisticLabel(displayRow, "TOTAL DISTANCE", buff);
return true;
#endif
}
return false;
}
static uint8_t osdShowStats(int statsRowCount)
{
uint8_t top = 0;
char buff[OSD_ELEMENT_BUFFER_LENGTH];
bool displayLabel = false;
// if statsRowCount is 0 then we're running an initial analysis of the active stats items
@ -511,182 +742,28 @@ static uint8_t osdShowStats(uint16_t endBatteryVoltage, int statsRowCount)
displayWrite(osdDisplayPort, 2, top++, " --- STATS ---");
}
if (osdStatGetState(OSD_STAT_RTC_DATE_TIME)) {
bool success = false;
#ifdef USE_RTC_TIME
success = osdFormatRtcDateTime(&buff[0]);
#endif
if (!success) {
tfp_sprintf(buff, "NO RTC");
}
displayWrite(osdDisplayPort, 2, top++, buff);
}
if (osdStatGetState(OSD_STAT_TIMER_1)) {
osdFormatTimer(buff, false, (OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]) == OSD_TIMER_SRC_ON ? false : true), OSD_TIMER_1);
osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1])], buff);
}
if (osdStatGetState(OSD_STAT_TIMER_2)) {
osdFormatTimer(buff, false, (OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]) == OSD_TIMER_SRC_ON ? false : true), OSD_TIMER_2);
osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2])], buff);
}
if (osdStatGetState(OSD_STAT_MAX_ALTITUDE)) {
const int alt = osdGetMetersToSelectedUnit(stats.max_altitude) / 10;
tfp_sprintf(buff, "%d.%d%c", alt / 10, alt % 10, osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(top++, "MAX ALTITUDE", buff);
}
#ifdef USE_GPS
if (featureIsEnabled(FEATURE_GPS)) {
if (osdStatGetState(OSD_STAT_MAX_SPEED)) {
tfp_sprintf(buff, "%d%c", osdGetSpeedToSelectedUnit(stats.max_speed), osdGetSpeedToSelectedUnitSymbol());
osdDisplayStatisticLabel(top++, "MAX SPEED", buff);
}
if (osdStatGetState(OSD_STAT_MAX_DISTANCE)) {
tfp_sprintf(buff, "%d%c", osdGetMetersToSelectedUnit(stats.max_distance), osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(top++, "MAX DISTANCE", buff);
}
if (osdStatGetState(OSD_STAT_FLIGHT_DISTANCE)) {
const uint32_t distanceFlown = GPS_distanceFlownInCm / 100;
tfp_sprintf(buff, "%d%c", osdGetMetersToSelectedUnit(distanceFlown), osdGetMetersToSelectedUnitSymbol());
osdDisplayStatisticLabel(top++, "FLIGHT DISTANCE", buff);
for (int i = 0; i < OSD_STAT_COUNT; i++) {
if (osdStatGetState(osdStatsDisplayOrder[i])) {
if (osdDisplayStat(osdStatsDisplayOrder[i], top)) {
top++;
}
}
}
#endif
if (osdStatGetState(OSD_STAT_MIN_BATTERY)) {
tfp_sprintf(buff, "%d.%02d%c", stats.min_voltage / 100, stats.min_voltage % 100, SYM_VOLT);
osdDisplayStatisticLabel(top++, "MIN BATTERY", buff);
}
if (osdStatGetState(OSD_STAT_END_BATTERY)) {
tfp_sprintf(buff, "%d.%02d%c", endBatteryVoltage / 100, endBatteryVoltage % 100, SYM_VOLT);
osdDisplayStatisticLabel(top++, "END BATTERY", buff);
}
if (osdStatGetState(OSD_STAT_BATTERY)) {
tfp_sprintf(buff, "%d.%02d%c", getBatteryVoltage() / 100, getBatteryVoltage() % 100, SYM_VOLT);
osdDisplayStatisticLabel(top++, "BATTERY", buff);
}
if (osdStatGetState(OSD_STAT_MIN_RSSI)) {
itoa(stats.min_rssi, buff, 10);
strcat(buff, "%");
osdDisplayStatisticLabel(top++, "MIN RSSI", buff);
}
if (batteryConfig()->currentMeterSource != CURRENT_METER_NONE) {
if (osdStatGetState(OSD_STAT_MAX_CURRENT)) {
tfp_sprintf(buff, "%d%c", stats.max_current, SYM_AMP);
osdDisplayStatisticLabel(top++, "MAX CURRENT", buff);
}
if (osdStatGetState(OSD_STAT_USED_MAH)) {
tfp_sprintf(buff, "%d%c", getMAhDrawn(), SYM_MAH);
osdDisplayStatisticLabel(top++, "USED MAH", buff);
}
}
#ifdef USE_BLACKBOX
if (osdStatGetState(OSD_STAT_BLACKBOX) && blackboxConfig()->device && blackboxConfig()->device != BLACKBOX_DEVICE_SERIAL) {
osdGetBlackboxStatusString(buff);
osdDisplayStatisticLabel(top++, "BLACKBOX", buff);
}
if (osdStatGetState(OSD_STAT_BLACKBOX_NUMBER) && blackboxConfig()->device && blackboxConfig()->device != BLACKBOX_DEVICE_SERIAL) {
itoa(blackboxGetLogNumber(), buff, 10);
osdDisplayStatisticLabel(top++, "BB LOG NUM", buff);
}
#endif
#if defined(USE_ACC)
if (osdStatGetState(OSD_STAT_MAX_G_FORCE) && sensors(SENSOR_ACC)) {
const int gForce = lrintf(stats.max_g_force * 10);
tfp_sprintf(buff, "%01d.%01dG", gForce / 10, gForce % 10);
osdDisplayStatisticLabel(top++, "MAX G-FORCE", buff);
}
#endif
#ifdef USE_ESC_SENSOR
if (osdStatGetState(OSD_STAT_MAX_ESC_TEMP)) {
tfp_sprintf(buff, "%d%c", osdConvertTemperatureToSelectedUnit(stats.max_esc_temp), osdGetTemperatureSymbolForSelectedUnit());
osdDisplayStatisticLabel(top++, "MAX ESC TEMP", buff);
}
if (osdStatGetState(OSD_STAT_MAX_ESC_RPM)) {
itoa(stats.max_esc_rpm, buff, 10);
osdDisplayStatisticLabel(top++, "MAX ESC RPM", buff);
}
#endif
#ifdef USE_RX_LINK_QUALITY_INFO
if (osdStatGetState(OSD_STAT_MIN_LINK_QUALITY)) {
tfp_sprintf(buff, "%d", stats.min_link_quality);
strcat(buff, "%");
osdDisplayStatisticLabel(top++, "MIN LINK", buff);
}
#endif
#if defined(USE_GYRO_DATA_ANALYSE)
if (osdStatGetState(OSD_STAT_MAX_FFT) && featureIsEnabled(FEATURE_DYNAMIC_FILTER)) {
int value = getMaxFFT();
if (value > 0) {
tfp_sprintf(buff, "%dHZ", value);
osdDisplayStatisticLabel(top++, "PEAK FFT", buff);
} else {
osdDisplayStatisticLabel(top++, "PEAK FFT", "THRT<20%");
}
}
#endif
#ifdef USE_RX_RSSI_DBM
if (osdStatGetState(OSD_STAT_MIN_RSSI_DBM)) {
tfp_sprintf(buff, "%3d", stats.min_rssi_dbm * -1);
osdDisplayStatisticLabel(top++, "MIN RSSI DBM", buff);
}
#endif
#ifdef USE_PERSISTENT_STATS
if (osdStatGetState(OSD_STAT_TOTAL_FLIGHTS)) {
itoa(statsConfig()->stats_total_flights, buff, 10);
osdDisplayStatisticLabel(top++, "TOTAL FLIGHTS", buff);
}
if (osdStatGetState(OSD_STAT_TOTAL_TIME)) {
int minutes = statsConfig()->stats_total_time_s / 60;
tfp_sprintf(buff, "%d:%02dH", minutes / 60, minutes % 60);
osdDisplayStatisticLabel(top++, "TOTAL FLIGHT TIME", buff);
}
if (osdStatGetState(OSD_STAT_TOTAL_DIST)) {
#define METERS_PER_KILOMETER 1000
#define METERS_PER_MILE 1609
if (osdConfig()->units == OSD_UNIT_IMPERIAL) {
tfp_sprintf(buff, "%dMI", statsConfig()->stats_total_dist_m / METERS_PER_MILE);
} else {
tfp_sprintf(buff, "%dKM", statsConfig()->stats_total_dist_m / METERS_PER_KILOMETER);
}
osdDisplayStatisticLabel(top++, "TOTAL DISTANCE", buff);
}
#endif
return top;
}
static void osdRefreshStats(uint16_t endBatteryVoltage)
static void osdRefreshStats(void)
{
displayClearScreen(osdDisplayPort);
if (osdStatsRowCount == 0) {
// No stats row count has been set yet.
// Go through the logic one time to determine how many stats are actually displayed.
osdStatsRowCount = osdShowStats(endBatteryVoltage, 0);
osdStatsRowCount = osdShowStats(0);
// Then clear the screen and commence with normal stats display which will
// determine if the heading should be displayed and also center the content vertically.
displayClearScreen(osdDisplayPort);
}
osdShowStats(endBatteryVoltage, osdStatsRowCount);
osdShowStats(osdStatsRowCount);
}
static void osdShowArmed(void)
@ -701,7 +778,6 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
static bool osdStatsEnabled = false;
static bool osdStatsVisible = false;
static timeUs_t osdStatsRefreshTimeUs;
static uint16_t endBatteryVoltage;
// detect arm/disarm
if (armState != ARMING_FLAG(ARMED)) {
@ -717,7 +793,7 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
|| !VISIBLE(osdConfig()->item_pos[OSD_WARNINGS]))) { // suppress stats if runaway takeoff triggered disarm and WARNINGS element is visible
osdStatsEnabled = true;
resumeRefreshAt = currentTimeUs + (60 * REFRESH_1S);
endBatteryVoltage = getBatteryVoltage();
stats.end_voltage = getBatteryVoltage();
osdStatsRowCount = 0; // reset to 0 so it will be recalculated on the next stats refresh
}
@ -746,7 +822,7 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
}
if (currentTimeUs >= osdStatsRefreshTimeUs) {
osdStatsRefreshTimeUs = currentTimeUs + REFRESH_1S;
osdRefreshStats(endBatteryVoltage);
osdRefreshStats();
}
}
}

View file

@ -136,14 +136,12 @@ typedef enum {
} osd_items_e;
// *** IMPORTANT ***
// If the stats enumeration is reordered then the PR version must be incremented. Otherwise there
// is no indication that the stored config must be reset and the bitmapped values will be incorrect.
// DO NOT REORDER THE STATS ENUMERATION. The order here cooresponds to the enabled flag bit position
// storage and changing the order will corrupt user settings. Any new stats MUST be added to the end
// just before the OSD_STAT_COUNT entry. YOU MUST ALSO add the new stat to the
// osdStatsDisplayOrder array in osd.c.
//
// The stats display order was previously required to match the enumeration definition so it matched
// the order shown in the configurator. However, to allow reordering this screen without breaking the
// compatibility, this requirement has been relaxed to a best effort approach. Reordering the elements
// on the stats screen will have to be more beneficial than the hassle of not matching exactly to the
// configurator list.
// IF YOU WANT TO REORDER THE STATS DISPLAY, then adjust the ordering of the osdStatsDisplayOrder array
typedef enum {
OSD_STAT_RTC_DATE_TIME,
OSD_STAT_TIMER_1,
@ -231,6 +229,7 @@ STATIC_ASSERT(OSD_WARNING_COUNT <= 32, osdwarnings_overflow);
#define OSD_GPS_RESCUE_DISABLED_WARNING_DURATION_US 3000000 // 3 seconds
extern const uint16_t osdTimerDefault[OSD_TIMER_COUNT];
extern const osd_stats_e osdStatsDisplayOrder[OSD_STAT_COUNT];
typedef struct osdConfig_s {
uint16_t item_pos[OSD_ITEM_COUNT];
@ -266,6 +265,7 @@ typedef struct statistic_s {
timeUs_t armed_time;
int16_t max_speed;
int16_t min_voltage; // /100
uint16_t end_voltage;
int16_t max_current; // /10
uint8_t min_rssi;
int32_t max_altitude;