diff --git a/docs/OSD.md b/docs/OSD.md index b3526d0d761..d57da7e25f3 100644 --- a/docs/OSD.md +++ b/docs/OSD.md @@ -19,155 +19,156 @@ Not all OSDs are created equally. This table shows the differences between the d ## OSD Elements Here are the OSD Elements provided by INAV. -| ID | Element | Added | -|-----|--------------------------------------------------|--------| -| 0 | OSD_RSSI_VALUE | 1.0.0 | -| 1 | OSD_MAIN_BATT_VOLTAGE | 1.0.0 | -| 2 | OSD_CROSSHAIRS | 1.0.0 | -| 3 | OSD_ARTIFICIAL_HORIZON | 1.0.0 | -| 4 | OSD_HORIZON_SIDEBARS | 1.0.0 | -| 5 | OSD_ONTIME | 1.0.0 | -| 6 | OSD_FLYTIME | 1.0.0 | -| 7 | OSD_FLYMODE | 1.0.0 | -| 8 | OSD_CRAFT_NAME | 1.0.0 | -| 9 | OSD_THROTTLE_POS | 1.0.0 | -| 10 | OSD_VTX_CHANNEL | 1.0.0 | -| 11 | OSD_CURRENT_DRAW | 1.0.0 | -| 12 | OSD_MAH_DRAWN | 1.0.0 | -| 13 | OSD_GPS_SPEED | 1.0.0 | -| 14 | OSD_GPS_SATS | 1.0.0 | -| 15 | OSD_ALTITUDE | 1.0.0 | -| 16 | OSD_ROLL_PIDS | 1.6.0 | -| 17 | OSD_PITCH_PIDS | 1.6.0 | -| 18 | OSD_YAW_PIDS | 1.6.0 | -| 19 | OSD_POWER | 1.6.0 | -| 20 | OSD_GPS_LON | 1.6.0 | -| 21 | OSD_GPS_LAT | 1.6.0 | -| 22 | OSD_HOME_DIR | 1.6.0 | -| 23 | OSD_HOME_DIST | 1.6.0 | -| 24 | OSD_HEADING | 1.6.0 | -| 25 | OSD_VARIO | 1.6.0 | -| 26 | OSD_VARIO_NUM | 1.6.0 | -| 27 | OSD_AIR_SPEED | 1.7.3 | -| 28 | OSD_ONTIME_FLYTIME | 1.8.0 | -| 29 | OSD_RTC_TIME | 1.8.0 | -| 30 | OSD_MESSAGES | 1.8.0 | -| 31 | OSD_GPS_HDOP | 1.8.0 | -| 32 | OSD_MAIN_BATT_CELL_VOLTAGE | 1.8.0 | -| 33 | OSD_SCALED_THROTTLE_POS | 1.8.0 | -| 34 | OSD_HEADING_GRAPH | 1.8.0 | -| 35 | OSD_EFFICIENCY_MAH_PER_KM | 1.9.0 | -| 36 | OSD_WH_DRAWN | 1.9.0 | -| 37 | OSD_BATTERY_REMAINING_CAPACITY | 1.9.0 | -| 38 | OSD_BATTERY_REMAINING_PERCENT | 1.9.0 | -| 39 | OSD_EFFICIENCY_WH_PER_KM | 1.9.0 | -| 40 | OSD_TRIP_DIST | 1.9.1 | -| 41 | OSD_ATTITUDE_PITCH | 2.0.0 | -| 42 | OSD_ATTITUDE_ROLL | 2.0.0 | -| 43 | OSD_MAP_NORTH | 2.0.0 | -| 44 | OSD_MAP_TAKEOFF | 2.0.0 | -| 45 | OSD_RADAR | 2.0.0 | -| 46 | OSD_WIND_SPEED_HORIZONTAL | 2.0.0 | -| 47 | OSD_WIND_SPEED_VERTICAL | 2.0.0 | -| 48 | OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH | 2.0.0 | -| 49 | OSD_REMAINING_DISTANCE_BEFORE_RTH | 2.0.0 | -| 50 | OSD_HOME_HEADING_ERROR | 2.0.0 | -| 51 | OSD_COURSE_HOLD_ERROR | 2.0.0 | -| 52 | OSD_COURSE_HOLD_ADJUSTMENT | 2.0.0 | -| 53 | OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE | 2.0.0 | -| 54 | OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE | 2.0.0 | -| 55 | OSD_POWER_SUPPLY_IMPEDANCE | 2.0.0 | -| 56 | OSD_LEVEL_PIDS | 2.0.0 | -| 57 | OSD_POS_XY_PIDS | 2.0.0 | -| 58 | OSD_POS_Z_PIDS | 2.0.0 | -| 59 | OSD_VEL_XY_PIDS | 2.0.0 | -| 60 | OSD_VEL_Z_PIDS | 2.0.0 | -| 61 | OSD_HEADING_P | 2.0.0 | -| 62 | OSD_BOARD_ALIGN_ROLL | 2.0.0 | -| 63 | OSD_BOARD_ALIGN_PITCH | 2.0.0 | -| 64 | OSD_RC_EXPO | 2.0.0 | -| 65 | OSD_RC_YAW_EXPO | 2.0.0 | -| 66 | OSD_THROTTLE_EXPO | 2.0.0 | -| 67 | OSD_PITCH_RATE | 2.0.0 | -| 68 | OSD_ROLL_RATE | 2.0.0 | -| 69 | OSD_YAW_RATE | 2.0.0 | -| 70 | OSD_MANUAL_RC_EXPO | 2.0.0 | -| 71 | OSD_MANUAL_RC_YAW_EXPO | 2.0.0 | -| 72 | OSD_MANUAL_PITCH_RATE | 2.0.0 | -| 73 | OSD_MANUAL_ROLL_RATE | 2.0.0 | -| 74 | OSD_MANUAL_YAW_RATE | 2.0.0 | -| 75 | OSD_NAV_FW_CRUISE_THR | 2.0.0 | -| 76 | OSD_NAV_FW_PITCH2THR | 2.0.0 | -| 77 | OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE | 2.0.0 | -| 78 | OSD_DEBUG | 2.0.0 | -| 79 | OSD_FW_ALT_PID_OUTPUTS | 2.0.0 | -| 80 | OSD_FW_POS_PID_OUTPUTS | 2.0.0 | -| 81 | OSD_MC_VEL_X_PID_OUTPUTS | 2.0.0 | -| 82 | OSD_MC_VEL_Y_PID_OUTPUTS | 2.0.0 | -| 83 | OSD_MC_VEL_Z_PID_OUTPUTS | 2.0.0 | -| 84 | OSD_MC_POS_XYZ_P_OUTPUTS | 2.0.0 | -| 85 | OSD_3D_SPEED | 2.1.0 | -| 86 | OSD_IMU_TEMPERATURE | 2.1.0 | -| 87 | OSD_BARO_TEMPERATURE | 2.1.0 | -| 88 | OSD_TEMP_SENSOR_0_TEMPERATURE | 2.1.0 | -| 89 | OSD_TEMP_SENSOR_1_TEMPERATURE | 2.1.0 | -| 90 | OSD_TEMP_SENSOR_2_TEMPERATURE | 2.1.0 | -| 91 | OSD_TEMP_SENSOR_3_TEMPERATURE | 2.1.0 | -| 92 | OSD_TEMP_SENSOR_4_TEMPERATURE | 2.1.0 | -| 93 | OSD_TEMP_SENSOR_5_TEMPERATURE | 2.1.0 | -| 94 | OSD_TEMP_SENSOR_6_TEMPERATURE | 2.1.0 | -| 95 | OSD_TEMP_SENSOR_7_TEMPERATURE | 2.1.0 | -| 96 | OSD_ALTITUDE_MSL | 2.1.0 | -| 97 | OSD_PLUS_CODE | 2.1.0 | -| 98 | OSD_MAP_SCALE | 2.2.0 | -| 99 | OSD_MAP_REFERENCE | 2.2.0 | -| 100 | OSD_GFORCE | 2.2.0 | -| 101 | OSD_GFORCE_X | 2.2.0 | -| 102 | OSD_GFORCE_Y | 2.2.0 | -| 103 | OSD_GFORCE_Z | 2.2.0 | -| 104 | OSD_RC_SOURCE | 2.2.0 | -| 105 | OSD_VTX_POWER | 2.2.0 | -| 106 | OSD_ESC_RPM | 2.3.0 | -| 107 | OSD_ESC_TEMPERATURE | 2.5.0 | -| 108 | OSD_AZIMUTH | 2.6.0 | -| 109 | OSD_CRSF_RSSI_DBM | 2.6.0 | -| 110 | OSD_CRSF_LQ | 2.6.0 | -| 111 | OSD_CRSF_SNR_DB | 2.6.0 | -| 112 | OSD_CRSF_TX_POWER | 2.6.0 | -| 113 | OSD_GVAR_0 | 2.6.0 | -| 114 | OSD_GVAR_1 | 2.6.0 | -| 115 | OSD_GVAR_2 | 2.6.0 | -| 116 | OSD_GVAR_3 | 2.6.0 | -| 117 | OSD_TPA | 2.6.0 | -| 118 | OSD_NAV_FW_CONTROL_SMOOTHNESS | 2.6.0 | -| 119 | OSD_VERSION | 3.0.0 | -| 120 | OSD_RANGEFINDER | 3.0.0 | -| 121 | OSD_PLIMIT_REMAINING_BURST_TIME | 3.0.0 | -| 122 | OSD_PLIMIT_ACTIVE_CURRENT_LIMIT | 3.0.0 | -| 123 | OSD_PLIMIT_ACTIVE_POWER_LIMIT | 3.0.0 | -| 124 | OSD_GLIDESLOPE | 3.0.1 | -| 125 | OSD_GPS_MAX_SPEED | 4.0.0 | -| 126 | OSD_3D_MAX_SPEED | 4.0.0 | -| 127 | OSD_AIR_MAX_SPEED | 4.0.0 | -| 128 | OSD_ACTIVE_PROFILE | 4.0.0 | -| 129 | OSD_MISSION | 4.0.0 | -| 130 | OSD_SWITCH_INDICATOR_0 | 5.0.0 | -| 131 | OSD_SWITCH_INDICATOR_1 | 5.0.0 | -| 132 | OSD_SWITCH_INDICATOR_2 | 5.0.0 | -| 133 | OSD_SWITCH_INDICATOR_3 | 5.0.0 | -| 134 | OSD_TPA_TIME_CONSTANT | 5.0.0 | -| 135 | OSD_FW_LEVEL_TRIM | 5.0.0 | -| 136 | OSD_GLIDE_TIME_REMAINING | 6.0.0 | -| 137 | OSD_GLIDE_RANGE | 6.0.0 | -| 138 | OSD_CLIMB_EFFICIENCY | 6.0.0 | -| 139 | OSD_NAV_WP_MULTI_MISSION_INDEX | 6.0.0 | -| 140 | OSD_GROUND_COURSE | 6.0.0 | -| 141 | OSD_CROSS_TRACK_ERROR | 6.0.0 | -| 142 | OSD_PILOT_NAME | 6.0.0 | -| 143 | OSD_PAN_SERVO_CENTRED | 6.0.0 | -| 144 | OSD_MULTI_FUNCTION | 7.0.0 | -| 145 | OSD_ODOMETER | 7.0.0 | -| 146 | OSD_PILOT_LOGO | 7.0.0 | +| ID | Element | Added | Notes | +|-----|--------------------------------------------------|--------|-------| +| 0 | OSD_RSSI_VALUE | 1.0.0 | | +| 1 | OSD_MAIN_BATT_VOLTAGE | 1.0.0 | | +| 2 | OSD_CROSSHAIRS | 1.0.0 | | +| 3 | OSD_ARTIFICIAL_HORIZON | 1.0.0 | | +| 4 | OSD_HORIZON_SIDEBARS | 1.0.0 | | +| 5 | OSD_ONTIME | 1.0.0 | | +| 6 | OSD_FLYTIME | 1.0.0 | | +| 7 | OSD_FLYMODE | 1.0.0 | | +| 8 | OSD_CRAFT_NAME | 1.0.0 | | +| 9 | OSD_THROTTLE_POS | 1.0.0 | | +| 10 | OSD_VTX_CHANNEL | 1.0.0 | | +| 11 | OSD_CURRENT_DRAW | 1.0.0 | | +| 12 | OSD_MAH_DRAWN | 1.0.0 | | +| 13 | OSD_GPS_SPEED | 1.0.0 | | +| 14 | OSD_GPS_SATS | 1.0.0 | | +| 15 | OSD_ALTITUDE | 1.0.0 | | +| 16 | OSD_ROLL_PIDS | 1.6.0 | | +| 17 | OSD_PITCH_PIDS | 1.6.0 | | +| 18 | OSD_YAW_PIDS | 1.6.0 | | +| 19 | OSD_POWER | 1.6.0 | | +| 20 | OSD_GPS_LON | 1.6.0 | | +| 21 | OSD_GPS_LAT | 1.6.0 | | +| 22 | OSD_HOME_DIR | 1.6.0 | | +| 23 | OSD_HOME_DIST | 1.6.0 | | +| 24 | OSD_HEADING | 1.6.0 | | +| 25 | OSD_VARIO | 1.6.0 | | +| 26 | OSD_VARIO_NUM | 1.6.0 | | +| 27 | OSD_AIR_SPEED | 1.7.3 | | +| 28 | OSD_ONTIME_FLYTIME | 1.8.0 | | +| 29 | OSD_RTC_TIME | 1.8.0 | | +| 30 | OSD_MESSAGES | 1.8.0 | | +| 31 | OSD_GPS_HDOP | 1.8.0 | | +| 32 | OSD_MAIN_BATT_CELL_VOLTAGE | 1.8.0 | | +| 33 | OSD_SCALED_THROTTLE_POS | 1.8.0 | | +| 34 | OSD_HEADING_GRAPH | 1.8.0 | | +| 35 | OSD_EFFICIENCY_MAH_PER_KM | 1.9.0 | | +| 36 | OSD_WH_DRAWN | 1.9.0 | | +| 37 | OSD_BATTERY_REMAINING_CAPACITY | 1.9.0 | | +| 38 | OSD_BATTERY_REMAINING_PERCENT | 1.9.0 | | +| 39 | OSD_EFFICIENCY_WH_PER_KM | 1.9.0 | | +| 40 | OSD_TRIP_DIST | 1.9.1 | | +| 41 | OSD_ATTITUDE_PITCH | 2.0.0 | | +| 42 | OSD_ATTITUDE_ROLL | 2.0.0 | | +| 43 | OSD_MAP_NORTH | 2.0.0 | | +| 44 | OSD_MAP_TAKEOFF | 2.0.0 | | +| 45 | OSD_RADAR | 2.0.0 | | +| 46 | OSD_WIND_SPEED_HORIZONTAL | 2.0.0 | | +| 47 | OSD_WIND_SPEED_VERTICAL | 2.0.0 | | +| 48 | OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH | 2.0.0 | | +| 49 | OSD_REMAINING_DISTANCE_BEFORE_RTH | 2.0.0 | | +| 50 | OSD_HOME_HEADING_ERROR | 2.0.0 | | +| 51 | OSD_COURSE_HOLD_ERROR | 2.0.0 | | +| 52 | OSD_COURSE_HOLD_ADJUSTMENT | 2.0.0 | | +| 53 | OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE | 2.0.0 | | +| 54 | OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE | 2.0.0 | | +| 55 | OSD_POWER_SUPPLY_IMPEDANCE | 2.0.0 | | +| 56 | OSD_LEVEL_PIDS | 2.0.0 | | +| 57 | OSD_POS_XY_PIDS | 2.0.0 | | +| 58 | OSD_POS_Z_PIDS | 2.0.0 | | +| 59 | OSD_VEL_XY_PIDS | 2.0.0 | | +| 60 | OSD_VEL_Z_PIDS | 2.0.0 | | +| 61 | OSD_HEADING_P | 2.0.0 | | +| 62 | OSD_BOARD_ALIGN_ROLL | 2.0.0 | | +| 63 | OSD_BOARD_ALIGN_PITCH | 2.0.0 | | +| 64 | OSD_RC_EXPO | 2.0.0 | | +| 65 | OSD_RC_YAW_EXPO | 2.0.0 | | +| 66 | OSD_THROTTLE_EXPO | 2.0.0 | | +| 67 | OSD_PITCH_RATE | 2.0.0 | | +| 68 | OSD_ROLL_RATE | 2.0.0 | | +| 69 | OSD_YAW_RATE | 2.0.0 | | +| 70 | OSD_MANUAL_RC_EXPO | 2.0.0 | | +| 71 | OSD_MANUAL_RC_YAW_EXPO | 2.0.0 | | +| 72 | OSD_MANUAL_PITCH_RATE | 2.0.0 | | +| 73 | OSD_MANUAL_ROLL_RATE | 2.0.0 | | +| 74 | OSD_MANUAL_YAW_RATE | 2.0.0 | | +| 75 | OSD_NAV_FW_CRUISE_THR | 2.0.0 | | +| 76 | OSD_NAV_FW_PITCH2THR | 2.0.0 | | +| 77 | OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE | 2.0.0 | | +| 78 | OSD_DEBUG | 2.0.0 | | +| 79 | OSD_FW_ALT_PID_OUTPUTS | 2.0.0 | | +| 80 | OSD_FW_POS_PID_OUTPUTS | 2.0.0 | | +| 81 | OSD_MC_VEL_X_PID_OUTPUTS | 2.0.0 | | +| 82 | OSD_MC_VEL_Y_PID_OUTPUTS | 2.0.0 | | +| 83 | OSD_MC_VEL_Z_PID_OUTPUTS | 2.0.0 | | +| 84 | OSD_MC_POS_XYZ_P_OUTPUTS | 2.0.0 | | +| 85 | OSD_3D_SPEED | 2.1.0 | | +| 86 | OSD_IMU_TEMPERATURE | 2.1.0 | | +| 87 | OSD_BARO_TEMPERATURE | 2.1.0 | | +| 88 | OSD_TEMP_SENSOR_0_TEMPERATURE | 2.1.0 | | +| 89 | OSD_TEMP_SENSOR_1_TEMPERATURE | 2.1.0 | | +| 90 | OSD_TEMP_SENSOR_2_TEMPERATURE | 2.1.0 | | +| 91 | OSD_TEMP_SENSOR_3_TEMPERATURE | 2.1.0 | | +| 92 | OSD_TEMP_SENSOR_4_TEMPERATURE | 2.1.0 | | +| 93 | OSD_TEMP_SENSOR_5_TEMPERATURE | 2.1.0 | | +| 94 | OSD_TEMP_SENSOR_6_TEMPERATURE | 2.1.0 | | +| 95 | OSD_TEMP_SENSOR_7_TEMPERATURE | 2.1.0 | | +| 96 | OSD_ALTITUDE_MSL | 2.1.0 | | +| 97 | OSD_PLUS_CODE | 2.1.0 | | +| 98 | OSD_MAP_SCALE | 2.2.0 | | +| 99 | OSD_MAP_REFERENCE | 2.2.0 | | +| 100 | OSD_GFORCE | 2.2.0 | | +| 101 | OSD_GFORCE_X | 2.2.0 | | +| 102 | OSD_GFORCE_Y | 2.2.0 | | +| 103 | OSD_GFORCE_Z | 2.2.0 | | +| 104 | OSD_RC_SOURCE | 2.2.0 | | +| 105 | OSD_VTX_POWER | 2.2.0 | | +| 106 | OSD_ESC_RPM | 2.3.0 | | +| 107 | OSD_ESC_TEMPERATURE | 2.5.0 | | +| 108 | OSD_AZIMUTH | 2.6.0 | | +| 109 | OSD_CRSF_RSSI_DBM | 2.6.0 | | +| 110 | OSD_CRSF_LQ | 2.6.0 | | +| 111 | OSD_CRSF_SNR_DB | 2.6.0 | | +| 112 | OSD_CRSF_TX_POWER | 2.6.0 | | +| 113 | OSD_GVAR_0 | 2.6.0 | | +| 114 | OSD_GVAR_1 | 2.6.0 | | +| 115 | OSD_GVAR_2 | 2.6.0 | | +| 116 | OSD_GVAR_3 | 2.6.0 | | +| 117 | OSD_TPA | 2.6.0 | | +| 118 | OSD_NAV_FW_CONTROL_SMOOTHNESS | 2.6.0 | | +| 119 | OSD_VERSION | 3.0.0 | | +| 120 | OSD_RANGEFINDER | 3.0.0 | | +| 121 | OSD_PLIMIT_REMAINING_BURST_TIME | 3.0.0 | | +| 122 | OSD_PLIMIT_ACTIVE_CURRENT_LIMIT | 3.0.0 | | +| 123 | OSD_PLIMIT_ACTIVE_POWER_LIMIT | 3.0.0 | | +| 124 | OSD_GLIDESLOPE | 3.0.1 | | +| 125 | OSD_GPS_MAX_SPEED | 4.0.0 | | +| 126 | OSD_3D_MAX_SPEED | 4.0.0 | | +| 127 | OSD_AIR_MAX_SPEED | 4.0.0 | | +| 128 | OSD_ACTIVE_PROFILE | 4.0.0 | | +| 129 | OSD_MISSION | 4.0.0 | | +| 130 | OSD_SWITCH_INDICATOR_0 | 5.0.0 | | +| 131 | OSD_SWITCH_INDICATOR_1 | 5.0.0 | | +| 132 | OSD_SWITCH_INDICATOR_2 | 5.0.0 | | +| 133 | OSD_SWITCH_INDICATOR_3 | 5.0.0 | | +| 134 | OSD_TPA_TIME_CONSTANT | 5.0.0 | | +| 135 | OSD_FW_LEVEL_TRIM | 5.0.0 | | +| 136 | OSD_GLIDE_TIME_REMAINING | 6.0.0 | | +| 137 | OSD_GLIDE_RANGE | 6.0.0 | | +| 138 | OSD_CLIMB_EFFICIENCY | 6.0.0 | | +| 139 | OSD_NAV_WP_MULTI_MISSION_INDEX | 6.0.0 | | +| 140 | OSD_GROUND_COURSE | 6.0.0 | | +| 141 | OSD_CROSS_TRACK_ERROR | 6.0.0 | | +| 142 | OSD_PILOT_NAME | 6.0.0 | | +| 143 | OSD_PAN_SERVO_CENTRED | 6.0.0 | | +| 144 | OSD_MULTI_FUNCTION | 7.0.0 | | +| 145 | OSD_ODOMETER | 7.0.0 | For this to work correctly, stats must be enabled (`set stats=ON`). Otherwise, this will show the total flight distance. | +| 146 | OSD_PILOT_LOGO | 7.0.0 | | +| 147 | OSD_BLACKBOX | 8.0.0 | The element will be hidden unless blackbox recording is attempted. | # Pilot Logos @@ -192,3 +193,43 @@ This is an example of the arming screen with the pilot logo enabled. This is usi This is an example of setting the `osd_inav_to_pilot_logo_spacing` to 0. This will allow a larger, single logo. ![Power on screen example with 0 spacing between logos](https://user-images.githubusercontent.com/17590174/271817352-6206402c-9da4-4682-9d83-790cc2396b00.png) + +# Post Flight Statistics +The post flight statistcs are set in the firmware. Statistics are only hidden if the supporting hardware is not present. Due to size contraints. The post flight statistics are spread over 2 pages on analogue systems. + +## Statistics shown +| Statistic | Requirement | Page | | +|-------------------------------|-----------------------|-------|-| +| Flight Time | | 1 | The total time from arm to disarm. | +| Flight Distance | | 1 | | +| Maximum Distance From Home | GPS | 1 | | +| Maximum Speed | GPS | 1 | | +| Average Speed | GPS | 1 | | +| Maximum Altitude | Baro/GPS | 1 | | +| Minimum Average Cell Voltage | | 1 | | +| Minimum Pack Voltage | | 1 | | +| Maximum Current | Current Sensor | 1 | | +| Maximum Power | Current Sensor | 1 | | +| Energy Used (Flight) | Current Sensor | 1 | | +| Energy Used (Battery Total) | Current Sensor | 1 | This data is not reset on arming. | +| Average Efficiency | Current Sensor & GPS | 1 | | +| Minimum RSSI | | 2 | | +| Minimum LQ | CRSF | 2 | | +| Minmum dBm | CRSF | 2 | | +| Minimum Satellites | GPS | 2 | | +| Maximum Satellites | GPS | 2 | | +| Minimum ESC Temperature | ESC Telemetry | 2 | | +| Maximum ESC Temperature | ESC Telemetry | 2 | | +| Maximum G-Force | | 2 | | +| Minimum Z axis G-Force | | 2 | | +| Maximum Z axis G-Force | | 2 | | +| Blackbox file number | Blackbox recording | 2 | | +| Disarm method | | 1 & 2 | | +| Settings save status | | 1 & 2 | Shows a message if the settings are being saved or have been saved on disarm. | + +## Configuration +There are a couple of settings that allow you to adjust parts of the post flights statistics. + +- `osd_stats_page_auto_swap_time` allows you to specify how long each stats page is displayed [seconds]. Reverts to manual control when Roll stick used to change pages. Disabled when set to 0. +- `osd_stats_energy_unit` allows you to choose the unit used for the drawn energy in the OSD stats [MAH/WH] (milliAmpere hour/ Watt hour). Default is MAH. +- `osd_stats_show_metric_efficiency` if you use non-metric units on your OSD. Enabling this option will also show the efficiency in metric. diff --git a/docs/Settings.md b/docs/Settings.md index e3ea1c74489..a736bd4699d 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -4892,23 +4892,23 @@ Unit used for the drawn energy in the OSD stats [MAH/WH] (milliAmpere hour/ Watt --- -### osd_stats_min_voltage_unit +### osd_stats_page_auto_swap_time -Display minimum voltage of the `BATTERY` or the average per `CELL` in the OSD stats. +Auto swap display time interval between disarm stats pages (seconds). Reverts to manual control when Roll stick used to change pages. Disabled when set to 0. | Default | Min | Max | | --- | --- | --- | -| BATTERY | | | +| 3 | 0 | 10 | --- -### osd_stats_page_auto_swap_time +### osd_stats_show_metric_efficiency -Auto swap display time interval between disarm stats pages (seconds). Reverts to manual control when Roll stick used to change pages. Disabled when set to 0. +Enabling this option will show metric efficiency statistics on the post flight stats screen. In addition to the efficiency statistics in your chosen units. | Default | Min | Max | | --- | --- | --- | -| 3 | 0 | 10 | +| OFF | OFF | ON | --- diff --git a/src/main/blackbox/blackbox_io.c b/src/main/blackbox/blackbox_io.c index 7a6f04149d3..3e6adb719b7 100644 --- a/src/main/blackbox/blackbox_io.c +++ b/src/main/blackbox/blackbox_io.c @@ -35,6 +35,10 @@ #include "config/parameter_group.h" #include "config/parameter_group_ids.h" +#ifdef USE_SDCARD +#include "drivers/sdcard/sdcard.h" +#endif + #include "io/asyncfatfs/asyncfatfs.h" #include "io/flashfs.h" #include "io/serial.h" @@ -507,6 +511,36 @@ bool isBlackboxDeviceFull(void) } } +bool isBlackboxDeviceWorking(void) +{ + switch (blackboxConfig()->device) { + case BLACKBOX_DEVICE_SERIAL: + return blackboxPort != NULL; +#ifdef USE_SDCARD + case BLACKBOX_DEVICE_SDCARD: + return sdcard_isInserted() && sdcard_isFunctional() && (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_READY); +#endif +#ifdef USE_FLASHFS + case BLACKBOX_DEVICE_FLASH: + return flashfsIsReady(); +#endif + default: + return false; + } +} + +int32_t blackboxGetLogNumber(void) +{ + switch (blackboxConfig()->device) { +#ifdef USE_SDCARD + case BLACKBOX_DEVICE_SDCARD: + return blackboxSDCard.largestLogFileNumber; +#endif + default: + return -1; + } +} + /** * Call once every loop iteration in order to maintain the global blackboxHeaderBudget with the number of bytes we can * transmit this iteration. diff --git a/src/main/blackbox/blackbox_io.h b/src/main/blackbox/blackbox_io.h index e71fdadca30..d143b72098d 100644 --- a/src/main/blackbox/blackbox_io.h +++ b/src/main/blackbox/blackbox_io.h @@ -67,6 +67,8 @@ bool blackboxDeviceBeginLog(void); bool blackboxDeviceEndLog(bool retainLog); bool isBlackboxDeviceFull(void); +bool isBlackboxDeviceWorking(void); +int32_t blackboxGetLogNumber(void); void blackboxReplenishHeaderBudget(void); blackboxBufferReserveStatus_e blackboxDeviceReserveBufferSpace(int32_t bytes); diff --git a/src/main/common/maths.h b/src/main/common/maths.h index c681080500f..83c33b70c03 100644 --- a/src/main/common/maths.h +++ b/src/main/common/maths.h @@ -65,6 +65,8 @@ #define METERS_TO_MILES(m) (m / 1609.344f) #define METERS_TO_NAUTICALMILES(m) (m / 1852.00f) +#define MWH_TO_WH(mWh) (mWh / 1000.0f) + #define CMSEC_TO_CENTIMPH(cms) (cms * 2.2369363f) #define CMSEC_TO_CENTIKPH(cms) (cms * 3.6f) #define CMSEC_TO_CENTIKNOTS(cms) (cms * 1.943845f) diff --git a/src/main/drivers/osd_symbols.h b/src/main/drivers/osd_symbols.h index 2437b9d3c2c..91cdcbb5f6b 100644 --- a/src/main/drivers/osd_symbols.h +++ b/src/main/drivers/osd_symbols.h @@ -178,6 +178,8 @@ #define SYM_ALERT 0xDD // 221 General alert symbol #define SYM_TERRAIN_FOLLOWING 0xFB // 251 Terrain following (also Alt adjust) #define SYM_CROSS_TRACK_ERROR 0xFC // 252 Cross track error +#define SYM_ADSB 0xFD // 253 ADBS +#define SYM_BLACKBOX 0xFE // 254 Blackbox #define SYM_ADSB 0xFD // 253 ADSB diff --git a/src/main/fc/fc_core.c b/src/main/fc/fc_core.c index d3becdcecd3..bede8929fea 100644 --- a/src/main/fc/fc_core.c +++ b/src/main/fc/fc_core.c @@ -1012,6 +1012,10 @@ float getFlightTime(void) return US2S(flightTime); } +void resetFlightTime(void) { + flightTime = 0; +} + float getArmTime(void) { return US2S(armTime); diff --git a/src/main/fc/fc_core.h b/src/main/fc/fc_core.h index 835a3431a07..ee5764696d8 100644 --- a/src/main/fc/fc_core.h +++ b/src/main/fc/fc_core.h @@ -45,6 +45,7 @@ bool emergencyArmingUpdate(bool armingSwitchIsOn, bool forceArm); bool areSensorsCalibrating(void); float getFlightTime(void); +void resetFlightTime(void); float getArmTime(void); void fcReboot(bool bootLoader); \ No newline at end of file diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 27ddc19ceaa..ff33c295fcd 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -68,9 +68,6 @@ tables: - name: osd_stats_energy_unit values: ["MAH", "WH"] enum: osd_stats_energy_unit_e - - name: osd_stats_min_voltage_unit - values: ["BATTERY", "CELL"] - enum: osd_stats_min_voltage_unit_e - name: osd_video_system values: ["AUTO", "PAL", "NTSC", "HDZERO", "DJIWTF", "AVATAR", "BF43COMPAT", "BFHDCOMPAT"] enum: videoSystem_e @@ -1204,8 +1201,6 @@ groups: field: mixer_config.tailsitterOrientationOffset type: bool - - - name: PG_REVERSIBLE_MOTORS_CONFIG type: reversibleMotorsConfig_t members: @@ -3175,18 +3170,17 @@ groups: field: stats_energy_unit table: osd_stats_energy_unit type: uint8_t - - name: osd_stats_min_voltage_unit - description: "Display minimum voltage of the `BATTERY` or the average per `CELL` in the OSD stats." - default_value: "BATTERY" - field: stats_min_voltage_unit - table: osd_stats_min_voltage_unit - type: uint8_t - name: osd_stats_page_auto_swap_time description: "Auto swap display time interval between disarm stats pages (seconds). Reverts to manual control when Roll stick used to change pages. Disabled when set to 0." default_value: 3 field: stats_page_auto_swap_time min: 0 max: 10 + - name: osd_stats_show_metric_efficiency + description: "Enabling this option will show metric efficiency statistics on the post flight stats screen. In addition to the efficiency statistics in your chosen units." + default_value: OFF + type: bool + field: stats_show_metric_efficiency - name: osd_rssi_alarm description: "Value below which to make the OSD RSSI indicator blink" default_value: 20 diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 617c42420e4..4d2fb29d459 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "platform.h" @@ -114,6 +115,10 @@ #include "programming/logic_condition.h" #include "programming/global_variables.h" +#ifdef USE_BLACKBOX +#include "blackbox/blackbox_io.h" +#endif + #ifdef USE_HARDWARE_REVISION_DETECTION #include "hardware_revision.h" #endif @@ -180,6 +185,12 @@ typedef struct statistic_s { int16_t min_rssi_dbm; // for CRSF int32_t max_altitude; uint32_t max_distance; + uint8_t min_sats; + uint8_t max_sats; + int16_t max_esc_temp; + int16_t min_esc_temp; + int32_t flightStartMAh; + int32_t flightStartMWh; } statistic_t; static statistic_t stats; @@ -224,8 +235,6 @@ void osdShowEEPROMSavedNotification(void) { notify_settings_saved = millis() + 5000; } - - bool osdDisplayIsPAL(void) { return displayScreenSize(osdDisplayPort) == VIDEO_BUFFER_CHARS_PAL; @@ -240,11 +249,14 @@ bool osdDisplayIsHD(void) return false; } - +bool osdIsNotMetric(void) { + return !(osdConfig()->units == OSD_UNIT_METRIC || osdConfig()->units == OSD_UNIT_METRIC_MPH); +} /* * Aligns text to the left side. Adds spaces at the end to keep string length unchanged. */ +/* -- Currently unused -- static void osdLeftAlignString(char *buff) { uint8_t sp = 0, ch = 0; @@ -252,7 +264,7 @@ static void osdLeftAlignString(char *buff) while (buff[sp] == ' ') sp++; for (ch = 0; ch < (len - sp); ch++) buff[ch] = buff[ch + sp]; for (sp = ch; sp < len; sp++) buff[sp] = ' '; -} +}*/ /* * This is a simplified distance conversion code that does not use any scaling @@ -1989,6 +2001,27 @@ static bool osdDrawSingleElement(uint8_t item) osdFormatDistanceSymbol(buff + 1, getTotalTravelDistance(), 0); break; + case OSD_BLACKBOX: + { +#ifdef USE_BLACKBOX + if (IS_RC_MODE_ACTIVE(BOXBLACKBOX)) { + if (!isBlackboxDeviceWorking()) { + tfp_sprintf(buff, "%c%c", SYM_BLACKBOX, SYM_ALERT); + } else if (isBlackboxDeviceFull()) { + tfp_sprintf(buff, "%cFULL", SYM_BLACKBOX); + } else { + int32_t logNumber = blackboxGetLogNumber(); + if (logNumber >= 0) { + tfp_sprintf(buff, "%c%05" PRId32, SYM_BLACKBOX, logNumber); + } else { + tfp_sprintf(buff, "%c", SYM_BLACKBOX); + } + } + } +#endif // USE_BLACKBOX + } + break; + case OSD_ODOMETER: { displayWriteChar(osdDisplayPort, elemPosX, elemPosY, SYM_ODOMETER); @@ -2724,7 +2757,7 @@ static bool osdDrawSingleElement(uint8_t item) // mAh/foot if (efficiencyValid) { osdFormatCentiNumber(buff, (value * METERS_PER_FOOT), 1, 2, 2, 3, false); - tfp_sprintf(buff, "%s%c%c", buff, SYM_AH_V_FT_0, SYM_AH_V_FT_1); + tfp_sprintf(buff + strlen(buff), "%c%c", SYM_AH_V_FT_0, SYM_AH_V_FT_1); } else { buff[0] = buff[1] = buff[2] = '-'; buff[3] = SYM_AH_V_FT_0; @@ -2738,7 +2771,7 @@ static bool osdDrawSingleElement(uint8_t item) // mAh/metre if (efficiencyValid) { osdFormatCentiNumber(buff, value, 1, 2, 2, 3, false); - tfp_sprintf(buff, "%s%c%c", buff, SYM_AH_V_M_0, SYM_AH_V_M_1); + tfp_sprintf(buff + strlen(buff), "%c%c", SYM_AH_V_M_0, SYM_AH_V_M_1); } else { buff[0] = buff[1] = buff[2] = '-'; buff[3] = SYM_AH_V_M_0; @@ -3201,9 +3234,9 @@ static bool osdDrawSingleElement(uint8_t item) case OSD_UNIT_IMPERIAL: moreThanAh = osdFormatCentiNumber(buff, value * METERS_PER_MILE / 10, 1000, 0, 2, digits, false); if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_MI_0, SYM_MAH_MI_1); + tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_MI_0, SYM_MAH_MI_1); } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_MI); + tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_MI); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3215,9 +3248,9 @@ static bool osdDrawSingleElement(uint8_t item) case OSD_UNIT_GA: moreThanAh = osdFormatCentiNumber(buff, value * METERS_PER_NAUTICALMILE / 10, 1000, 0, 2, digits, false); if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_NM_0, SYM_MAH_NM_1); + tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_NM_0, SYM_MAH_NM_1); } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_NM); + tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_NM); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3231,9 +3264,9 @@ static bool osdDrawSingleElement(uint8_t item) case OSD_UNIT_METRIC: moreThanAh = osdFormatCentiNumber(buff, value * 100, 1000, 0, 2, digits, false); if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_KM_0, SYM_MAH_KM_1); + tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_KM_0, SYM_MAH_KM_1); } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_KM); + tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_KM); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3952,8 +3985,8 @@ PG_RESET_TEMPLATE(osdConfig_t, osdConfig, .force_grid = SETTING_OSD_FORCE_GRID_DEFAULT, .stats_energy_unit = SETTING_OSD_STATS_ENERGY_UNIT_DEFAULT, - .stats_min_voltage_unit = SETTING_OSD_STATS_MIN_VOLTAGE_UNIT_DEFAULT, - .stats_page_auto_swap_time = SETTING_OSD_STATS_PAGE_AUTO_SWAP_TIME_DEFAULT + .stats_page_auto_swap_time = SETTING_OSD_STATS_PAGE_AUTO_SWAP_TIME_DEFAULT, + .stats_show_metric_efficiency = SETTING_OSD_STATS_SHOW_METRIC_EFFICIENCY_DEFAULT ); void pgResetFn_osdLayoutsConfig(osdLayoutsConfig_t *osdLayoutsConfig) @@ -4119,6 +4152,10 @@ void pgResetFn_osdLayoutsConfig(osdLayoutsConfig_t *osdLayoutsConfig) osdLayoutsConfig->item_pos[0][OSD_PLIMIT_ACTIVE_POWER_LIMIT] = OSD_POS(3, 6); #endif +#ifdef USE_BLACKBOX + osdLayoutsConfig->item_pos[0][OSD_BLACKBOX] = OSD_POS(2, 10); +#endif + // Under OSD_FLYMODE. TODO: Might not be visible on NTSC? osdLayoutsConfig->item_pos[0][OSD_MESSAGES] = OSD_POS(1, 13) | OSD_VISIBLE_FLAG; @@ -4193,83 +4230,154 @@ uint8_t drawLogos(bool singular, uint8_t row) { return logoRow; } -uint8_t drawStats(uint8_t row) { #ifdef USE_STATS - char string_buffer[30]; - uint8_t statNameX = (osdDisplayPort->cols - 22) / 2; - uint8_t statValueX = statNameX + 21; +uint8_t drawStat_Stats(uint8_t statNameX, uint8_t row, uint8_t statValueX, bool isBootStats) +{ + uint8_t buffLen = 0; + char string_buffer[osdDisplayPort->cols - statValueX]; if (statsConfig()->stats_enabled) { - displayWrite(osdDisplayPort, statNameX, row, "ODOMETER:"); + if (isBootStats) + displayWrite(osdDisplayPort, statNameX, row, "ODOMETER:"); + else + displayWrite(osdDisplayPort, statNameX, row, "ODOMETER"); + switch (osdConfig()->units) { case OSD_UNIT_UK: FALLTHROUGH; case OSD_UNIT_IMPERIAL: - tfp_sprintf(string_buffer, "%5d", (int)(statsConfig()->stats_total_dist / METERS_PER_MILE)); - string_buffer[5] = SYM_MI; + if (isBootStats) { + tfp_sprintf(string_buffer, "%5d", (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_MILE)); + buffLen = 5; + } else { + uint16_t statTotalDist = (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_MILE); + tfp_sprintf(string_buffer, ": %d", statTotalDist); + buffLen = 3 + sizeof(statTotalDist); + } + + string_buffer[buffLen++] = SYM_MI; break; default: case OSD_UNIT_GA: - tfp_sprintf(string_buffer, "%5d", (int)(statsConfig()->stats_total_dist / METERS_PER_NAUTICALMILE)); - string_buffer[5] = SYM_NM; + if (isBootStats) { + tfp_sprintf(string_buffer, "%5d", (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_NAUTICALMILE)); + buffLen = 5; + } else { + uint16_t statTotalDist = (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_NAUTICALMILE); + tfp_sprintf(string_buffer, ": %d", statTotalDist); + buffLen = 3 + sizeof(statTotalDist); + } + + string_buffer[buffLen++] = SYM_NM; break; case OSD_UNIT_METRIC_MPH: FALLTHROUGH; case OSD_UNIT_METRIC: - tfp_sprintf(string_buffer, "%5d", (int)(statsConfig()->stats_total_dist / METERS_PER_KILOMETER)); - string_buffer[5] = SYM_KM; + if (isBootStats) { + tfp_sprintf(string_buffer, "%5d", (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_KILOMETER)); + buffLen = 5; + } else { + uint16_t statTotalDist = (uint16_t)(statsConfig()->stats_total_dist / METERS_PER_KILOMETER); + tfp_sprintf(string_buffer, ": %d", statTotalDist); + buffLen = 3 + sizeof(statTotalDist); + } + + string_buffer[buffLen++] = SYM_KM; break; } - string_buffer[6] = '\0'; - displayWrite(osdDisplayPort, statValueX-5, row, string_buffer); + string_buffer[buffLen] = '\0'; + displayWrite(osdDisplayPort, statValueX-(isBootStats ? 5 : 0), row, string_buffer); - displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL TIME:"); + if (isBootStats) + displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL TIME:"); + else + displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL TIME"); + uint32_t tot_mins = statsConfig()->stats_total_time / 60; - tfp_sprintf(string_buffer, "%2d:%02dH:M", (int)(tot_mins / 60), (int)(tot_mins % 60)); - displayWrite(osdDisplayPort, statValueX-7, row, string_buffer); + if (isBootStats) + tfp_sprintf(string_buffer, "%d:%02dH:M%c", (int)(tot_mins / 60), (int)(tot_mins % 60), '\0'); + else + tfp_sprintf(string_buffer, ": %d:%02d H:M%c", (int)(tot_mins / 60), (int)(tot_mins % 60), '\0'); + + displayWrite(osdDisplayPort, statValueX-(isBootStats ? 7 : 0), row, string_buffer); #ifdef USE_ADC - if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER)) { - displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL ENERGY:"); - osdFormatCentiNumber(string_buffer, statsConfig()->stats_total_energy / 10, 0, 2, 0, 4, false); - displayWrite(osdDisplayPort, statValueX-4, row, string_buffer); - displayWriteChar(osdDisplayPort, statValueX, row, SYM_WH); + if (feature(FEATURE_VBAT) && feature(FEATURE_CURRENT_METER) && statsConfig()->stats_total_energy) { + uint8_t buffOffset = 0; + if (isBootStats) + displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL ENERGY:"); + else { + displayWrite(osdDisplayPort, statNameX, ++row, "TOTAL ENERGY"); + string_buffer[0] = ':'; + buffOffset = 2; + } + + osdFormatCentiNumber(string_buffer + buffOffset, statsConfig()->stats_total_energy / 10, 0, 2, 0, 6, true); + displayWrite(osdDisplayPort, statValueX - (isBootStats ? 6 : 0), row, string_buffer); + displayWriteChar(osdDisplayPort, statValueX + (isBootStats ? 0 : 8), row, SYM_WH); + + char avgEffBuff[osdDisplayPort->cols - statValueX]; + + for (uint8_t i = 0; i < osdDisplayPort->cols - statValueX; i++) { + avgEffBuff[i] = '\0'; + string_buffer[i] = '\0'; + } - displayWrite(osdDisplayPort, statNameX, ++row, "AVG EFFICIENCY:"); if (statsConfig()->stats_total_dist) { - uint32_t avg_efficiency = statsConfig()->stats_total_energy / (statsConfig()->stats_total_dist / METERS_PER_KILOMETER); // mWh/km + if (isBootStats) + displayWrite(osdDisplayPort, statNameX, ++row, "AVG EFFICIENCY:"); + else { + displayWrite(osdDisplayPort, statNameX, ++row, "AVG EFFICIENCY"); + strcat(avgEffBuff, ": "); + } + + float_t avg_efficiency = MWH_TO_WH(statsConfig()->stats_total_energy) / METERS_TO_KILOMETERS(statsConfig()->stats_total_dist); // Wh/km switch (osdConfig()->units) { case OSD_UNIT_UK: FALLTHROUGH; case OSD_UNIT_IMPERIAL: - osdFormatCentiNumber(string_buffer, avg_efficiency / 10, 0, 2, 0, 3, false); - string_buffer[3] = SYM_WH_MI; + osdFormatCentiNumber(string_buffer, (int32_t)(avg_efficiency * METERS_PER_MILE / 10), 0, 2, 2, 4, false); + string_buffer[4] = SYM_WH_MI; break; case OSD_UNIT_GA: - osdFormatCentiNumber(string_buffer, avg_efficiency / 10, 0, 2, 0, 3, false); - string_buffer[3] = SYM_WH_NM; + osdFormatCentiNumber(string_buffer, (int32_t)(avg_efficiency * METERS_PER_NAUTICALMILE / 10), 0, 2, 2, 4, false); + string_buffer[4] = SYM_WH_NM; break; default: case OSD_UNIT_METRIC_MPH: FALLTHROUGH; case OSD_UNIT_METRIC: - osdFormatCentiNumber(string_buffer, avg_efficiency / 10000 * METERS_PER_MILE, 0, 2, 0, 3, false); - string_buffer[3] = SYM_WH_KM; + osdFormatCentiNumber(string_buffer, (int32_t)(avg_efficiency * 100), 0, 2, 2, 4, false); + string_buffer[4] = SYM_WH_KM; break; } + + if (isBootStats) + strcat(avgEffBuff, string_buffer); + else + strcat(avgEffBuff, osdFormatTrimWhiteSpace(string_buffer)); } else { - string_buffer[0] = string_buffer[1] = string_buffer[2] = '-'; + strcat(avgEffBuff, "----"); } - string_buffer[4] = '\0'; - displayWrite(osdDisplayPort, statValueX-3, row++, string_buffer); + + displayWrite(osdDisplayPort, statValueX-(isBootStats ? 4 : 0), row++, avgEffBuff); } #endif // USE_ADC } -#endif // USE_STATS return row; } -static void osdSetNextRefreshIn(uint32_t timeMs) { +uint8_t drawStats(uint8_t row) +{ + uint8_t statNameX = (osdDisplayPort->cols - 22) / 2; + uint8_t statValueX = statNameX + 21; + + return drawStat_Stats(statNameX, row, statValueX, true); +} +#endif // USE STATS + +static void osdSetNextRefreshIn(uint32_t timeMs) +{ resumeRefreshAt = micros() + timeMs * 1000; refreshWaitForResumeCmdRelease = true; } @@ -4356,16 +4464,29 @@ void osdInit(displayPort_t *osdDisplayPortToUse) static void osdResetStats(void) { + // Reset internal OSD stats + stats.max_distance = 0; stats.max_current = 0; stats.max_power = 0; stats.max_speed = 0; stats.max_3D_speed = 0; stats.max_air_speed = 0; - stats.min_voltage = 5000; + stats.min_voltage = 12000; stats.min_rssi = 99; stats.min_lq = 300; stats.min_rssi_dbm = 0; stats.max_altitude = 0; + stats.min_sats = 255; + stats.max_sats = 0; + stats.min_esc_temp = 300; + stats.max_esc_temp = 0; + stats.flightStartMAh = getMAhDrawn(); + stats.flightStartMWh = getMWhDrawn(); + + // Reset external stats + posControl.totalTripDistance = 0.0f; + resetFlightTime(); + resetGForceStats(); } static void osdUpdateStats(void) @@ -4387,7 +4508,27 @@ static void osdUpdateStats(void) if (stats.max_distance < GPS_distanceToHome) stats.max_distance = GPS_distanceToHome; + + if (stats.min_sats > gpsSol.numSat) + stats.min_sats = gpsSol.numSat; + + if (stats.max_sats < gpsSol.numSat) + stats.max_sats = gpsSol.numSat; } +#if defined(USE_ESC_SENSOR) + if (STATE(ESC_SENSOR_ENABLED)) { + escSensorData_t * escSensor = escSensorGetData(); + bool escTemperatureValid = escSensor && escSensor->dataAge <= ESC_DATA_MAX_AGE; + + if (escTemperatureValid) { + if (stats.min_esc_temp > escSensor->temperature) + stats.min_esc_temp = escSensor->temperature; + + if (stats.max_esc_temp < escSensor->temperature) + stats.max_esc_temp = escSensor->temperature; + } + } +#endif value = getBatteryVoltage(); if (stats.min_voltage > value) @@ -4419,265 +4560,545 @@ static void osdUpdateStats(void) stats.max_altitude = MAX(stats.max_altitude, osdGetAltitude()); } -static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page) +uint8_t drawStat_FlightTime(uint8_t col, uint8_t row, uint8_t statValX) { - //We keep "" for backward compatibility with the Blackbox explorer and other potential usages - const char * disarmReasonStr[DISARM_REASON_COUNT] = { "UNKNOWN", "TIMEOUT", "STICKS", "SWITCH", "SWITCH", "", "FAILSAFE", "NAV SYS", "LANDING"}; - uint8_t top = 1; // Start one line down leaving space at the top of the screen. - size_t multiValueLengthOffset = 0; + char buff[12]; + displayWrite(osdDisplayPort, col, row, "FLIGHT TIME"); + uint16_t flySeconds = getFlightTime(); + uint16_t flyMinutes = flySeconds / 60; + flySeconds %= 60; + uint16_t flyHours = flyMinutes / 60; + flyMinutes %= 60; + tfp_sprintf(buff, ": %02u:%02u:%02u", flyHours, flyMinutes, flySeconds); + displayWrite(osdDisplayPort, statValX, row++, buff); - const uint8_t statNameX = osdDisplayIsHD() ? 11 : 1; - const uint8_t statValuesX = osdDisplayIsHD() ? 30 : 20; - char buff[10]; + return row; +} - if (page > 1) - page = 0; +uint8_t drawStat_FlightDistance(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; - displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING); - displayClearScreen(osdDisplayPort); + displayWrite(osdDisplayPort, col, row, "FLIGHT DISTANCE"); + tfp_sprintf(buff, ": "); + osdFormatDistanceStr(buff + 2, getTotalTravelDistance()); + displayWrite(osdDisplayPort, statValX, row++, buff); - if (isSinglePageStatsCompatible) { - displayWrite(osdDisplayPort, statNameX, top++, "--- STATS ---"); - } else if (page == 0) { - displayWrite(osdDisplayPort, statNameX, top++, "--- STATS --- 1/2 ->"); - } else if (page == 1) { - displayWrite(osdDisplayPort, statNameX, top++, "--- STATS --- <- 2/2"); - } - - if (isSinglePageStatsCompatible || page == 0) { - if (feature(FEATURE_GPS)) { - if (isSinglePageStatsCompatible) { - displayWrite(osdDisplayPort, statNameX, top, "MAX/AVG SPEED :"); - osdFormatVelocityStr(buff, stats.max_3D_speed, true, false); - osdLeftAlignString(buff); - strcat(osdFormatTrimWhiteSpace(buff),"/"); - multiValueLengthOffset = strlen(buff); - displayWrite(osdDisplayPort, statValuesX, top, buff); - osdGenerateAverageVelocityStr(buff); - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff); - } else { - displayWrite(osdDisplayPort, statNameX, top, "MAX SPEED :"); - osdFormatVelocityStr(buff, stats.max_3D_speed, true, false); - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - - displayWrite(osdDisplayPort, statNameX, top, "AVG SPEED :"); - osdGenerateAverageVelocityStr(buff); - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - } + return row; +} - displayWrite(osdDisplayPort, statNameX, top, "MAX DISTANCE :"); - osdFormatDistanceStr(buff, stats.max_distance*100); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - - displayWrite(osdDisplayPort, statNameX, top, "TRAVELED DISTANCE:"); - osdFormatDistanceStr(buff, getTotalTravelDistance()); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - } - - displayWrite(osdDisplayPort, statNameX, top, "MAX ALTITUDE :"); - osdFormatAltitudeStr(buff, stats.max_altitude); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - - switch (rxConfig()->serialrx_provider) { - case SERIALRX_CRSF: - if (isSinglePageStatsCompatible) { - displayWrite(osdDisplayPort, statNameX, top, "MIN RSSI %/DBM :"); - itoa(stats.min_rssi, buff, 10); - osdLeftAlignString(buff); - strcat(osdFormatTrimWhiteSpace(buff), "%/"); - multiValueLengthOffset = strlen(buff); - displayWrite(osdDisplayPort, statValuesX, top, buff); - itoa(stats.min_rssi_dbm, buff, 10); - tfp_sprintf(buff, "%s%c", buff, SYM_DBM); - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff); - } else { - displayWrite(osdDisplayPort, statNameX, top, "MIN RSSI % :"); - itoa(stats.min_rssi, buff, 10); - strcat(buff, "%"); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - - displayWrite(osdDisplayPort, statNameX, top, "MIN RSSI DBM :"); - itoa(stats.min_rssi_dbm, buff, 10); - tfp_sprintf(buff, "%s%c", buff, SYM_DBM); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - } +uint8_t drawStat_MaxDistanceFromHome(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + uint8_t valueXOffset = 0; + if (!osdDisplayIsHD()) { + displayWrite(osdDisplayPort, col, row, "DISTANCE FROM "); + valueXOffset = 14; + } else { + displayWrite(osdDisplayPort, col, row, "MAX DISTANCE FROM "); + valueXOffset = 18; + } + displayWriteChar(osdDisplayPort, col + valueXOffset, row, SYM_HOME); + tfp_sprintf(buff, ": "); + osdFormatDistanceStr(buff + 2, stats.max_distance * 100); + displayWrite(osdDisplayPort, statValX, row++, buff); - displayWrite(osdDisplayPort, statNameX, top, "MIN LQ :"); - itoa(stats.min_lq, buff, 10); - strcat(buff, "%"); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - break; - default: - displayWrite(osdDisplayPort, statNameX, top, "MIN RSSI :"); - itoa(stats.min_rssi, buff, 10); - strcat(buff, "%"); - displayWrite(osdDisplayPort, statValuesX, top++, buff); - } + return row; +} - displayWrite(osdDisplayPort, statNameX, top, "FLY TIME :"); - uint16_t flySeconds = getFlightTime(); - uint16_t flyMinutes = flySeconds / 60; - flySeconds %= 60; - uint16_t flyHours = flyMinutes / 60; - flyMinutes %= 60; - tfp_sprintf(buff, "%02u:%02u:%02u", flyHours, flyMinutes, flySeconds); - displayWrite(osdDisplayPort, statValuesX, top++, buff); +uint8_t drawStat_Speed(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + char buff2[12]; + uint8_t multiValueXOffset = 0; - displayWrite(osdDisplayPort, statNameX, top, "DISARMED BY :"); - displayWrite(osdDisplayPort, statValuesX, top++, disarmReasonStr[getDisarmReason()]); - } + displayWrite(osdDisplayPort, col, row, "MAX/AVG SPEED"); + + osdFormatVelocityStr(buff2, stats.max_3D_speed, true, false); + tfp_sprintf(buff, ": %s/", osdFormatTrimWhiteSpace(buff2)); + multiValueXOffset = strlen(buff); + displayWrite(osdDisplayPort, statValX, row, buff); - if (isSinglePageStatsCompatible || page == 1) { - if (osdConfig()->stats_min_voltage_unit == OSD_STATS_MIN_VOLTAGE_UNIT_BATTERY) { - displayWrite(osdDisplayPort, statNameX, top, "MIN BATTERY VOLT :"); - osdFormatCentiNumber(buff, stats.min_voltage, 0, osdConfig()->main_voltage_decimals, 0, osdConfig()->main_voltage_decimals + 2, false); - } else { - displayWrite(osdDisplayPort, statNameX, top, "MIN CELL VOLTAGE :"); - osdFormatCentiNumber(buff, stats.min_voltage/getBatteryCellCount(), 0, 2, 0, 3, false); - } - tfp_sprintf(buff, "%s%c", buff, SYM_VOLT); - displayWrite(osdDisplayPort, statValuesX, top++, buff); + osdGenerateAverageVelocityStr(buff2); + displayWrite(osdDisplayPort, statValX + multiValueXOffset, row++, osdFormatTrimWhiteSpace(buff2)); - if (feature(FEATURE_CURRENT_METER)) { - displayWrite(osdDisplayPort, statNameX, top, "MAX CURRENT :"); - osdFormatCentiNumber(buff, stats.max_current, 0, 2, 0, 3, false); - tfp_sprintf(buff, "%s%c", buff, SYM_AMP); - displayWrite(osdDisplayPort, statValuesX, top++, buff); + return row; +} - displayWrite(osdDisplayPort, statNameX, top, "MAX POWER :"); - bool kiloWatt = osdFormatCentiNumber(buff, stats.max_power, 1000, 2, 2, 3, false); - buff[3] = kiloWatt ? SYM_KILOWATT : SYM_WATT; - buff[4] = '\0'; - displayWrite(osdDisplayPort, statValuesX, top++, buff); +uint8_t drawStat_MaximumAltitude(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + displayWrite(osdDisplayPort, col, row, "MAX ALTITUDE"); + tfp_sprintf(buff, ": "); + osdFormatAltitudeStr(buff + 2, stats.max_altitude); + displayWrite(osdDisplayPort, statValX, row++, buff); - displayWrite(osdDisplayPort, statNameX, top, "USED CAPACITY :"); - if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { - tfp_sprintf(buff, "%d%c", (int)getMAhDrawn(), SYM_MAH); - } else { - osdFormatCentiNumber(buff, getMWhDrawn() / 10, 0, 2, 0, 3, false); - tfp_sprintf(buff, "%s%c", buff, SYM_WH); - } - displayWrite(osdDisplayPort, statValuesX, top++, buff); + return row; +} - int32_t totalDistance = getTotalTravelDistance(); - bool moreThanAh = false; - bool efficiencyValid = totalDistance >= 10000; - if (feature(FEATURE_GPS)) { - displayWrite(osdDisplayPort, statNameX, top, "AVG EFFICIENCY :"); - uint8_t digits = 3U; // Total number of digits (including decimal point) - #ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values - if (isBfCompatibleVideoSystem(osdConfig())) { - // Add one digit so no switch to scaled decimal occurs above 99 - digits = 4U; - } - #endif - switch (osdConfig()->units) { - case OSD_UNIT_UK: - FALLTHROUGH; - case OSD_UNIT_IMPERIAL: - if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { - moreThanAh = osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000.0f * METERS_PER_MILE / totalDistance), 1000, 0, 2, digits, false); - if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_MI_0, SYM_MAH_MI_1); - } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_MI); - } - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - buff[3] = SYM_MAH_MI_0; - buff[4] = SYM_MAH_MI_1; - buff[5] = '\0'; - } - } else { - osdFormatCentiNumber(buff, (int32_t)(getMWhDrawn() * 10.0f * METERS_PER_MILE / totalDistance), 0, 2, 0, digits, false); - tfp_sprintf(buff, "%s%c", buff, SYM_WH_MI); - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - } +uint8_t drawStat_BatteryVoltage(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + uint8_t multiValueXOffset = 0; + if (!osdDisplayIsHD()) + displayWrite(osdDisplayPort, col, row, "MIN VOLTS P/C"); + else + displayWrite(osdDisplayPort, col, row, "MIN VOLTS PACK/CELL"); + + // Pack voltage + tfp_sprintf(buff, ": "); + osdFormatCentiNumber(buff + 2, stats.min_voltage, 0, osdConfig()->main_voltage_decimals, 0, osdConfig()->main_voltage_decimals + 2, false); + strcat(osdFormatTrimWhiteSpace(buff), "/"); + multiValueXOffset = strlen(buff); + // AverageCell + osdFormatCentiNumber(buff + multiValueXOffset, stats.min_voltage / getBatteryCellCount(), 0, 2, 0, 3, false); + tfp_sprintf(buff + strlen(buff), "%c", SYM_VOLT); + + displayWrite(osdDisplayPort, statValX, row++, buff); + + return row; +} + +uint8_t drawStat_MaximumPowerAndCurrent(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + char outBuff[12]; + tfp_sprintf(outBuff, ": "); + osdFormatCentiNumber(buff, stats.max_current, 0, 2, 0, 3, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + bool kiloWatt = osdFormatCentiNumber(buff, stats.max_power, 1000, 2, 2, 3, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + displayWrite(osdDisplayPort, statValX, row, outBuff); + + if (kiloWatt) + displayWrite(osdDisplayPort, col, row, "MAX AMPS/K WATTS"); + else + displayWrite(osdDisplayPort, col, row, "MAX AMPS/WATTS"); + + return ++row; +} + +uint8_t drawStat_UsedEnergy(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + + if (osdDisplayIsHD()) + displayWrite(osdDisplayPort, col, row, "USED ENERGY FLT/TOT"); + else + displayWrite(osdDisplayPort, col, row, "USED ENERGY F/T"); + tfp_sprintf(buff, ": "); + if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { + tfp_sprintf(buff + 2, "%d/%d%c", (int)(getMAhDrawn() - stats.flightStartMAh), (int)getMAhDrawn(), SYM_MAH); + } else { + char preBuff[12]; + osdFormatCentiNumber(preBuff, (getMWhDrawn() - stats.flightStartMWh) / 10, 0, 2, 0, 3, false); + strcat(buff, osdFormatTrimWhiteSpace(preBuff)); + strcat(buff, "/"); + osdFormatCentiNumber(preBuff, getMWhDrawn() / 10, 0, 2, 0, 3, false); + strcat(buff, osdFormatTrimWhiteSpace(preBuff)); + tfp_sprintf(buff + strlen(buff), "%s%c", buff, SYM_WH); + } + displayWrite(osdDisplayPort, statValX, row++, buff); + + return row; +} + +uint8_t drawStat_AverageEfficiency(uint8_t col, uint8_t row, uint8_t statValX, bool forceMetric) +{ + char buff[15]; + char outBuff[15]; + int32_t totalDistance = getTotalTravelDistance(); + bool moreThanAh = false; + bool efficiencyValid = totalDistance >= 10000; + + if (osdDisplayIsHD()) + displayWrite(osdDisplayPort, col, row, "AVG EFFICIENCY FLT/TOT"); + else + displayWrite(osdDisplayPort, col, row, "AV EFFICIENCY F/T"); + + tfp_sprintf(outBuff, ": "); + uint8_t digits = 3U; // Total number of digits (including decimal point) +#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values + if (isBfCompatibleVideoSystem(osdConfig())) { + // Add one digit so no switch to scaled decimal occurs above 99 + digits = 4U; + } +#endif + if (!forceMetric) { + switch (osdConfig()->units) { + case OSD_UNIT_UK: + FALLTHROUGH; + case OSD_UNIT_IMPERIAL: + if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { + if (efficiencyValid) { + moreThanAh = osdFormatCentiNumber(buff, (int32_t)((getMAhDrawn() - stats.flightStartMAh) * 10000.0f * METERS_PER_MILE / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + if (osdDisplayIsHD()) { + if (!moreThanAh) + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_MI_0, SYM_MAH_MI_1); + else + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_MI); + + moreThanAh = false; } - break; - case OSD_UNIT_GA: - if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { - moreThanAh = osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000.0f * METERS_PER_NAUTICALMILE / totalDistance), 1000, 0, 2, digits, false); - if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_NM_0, SYM_MAH_NM_1); - } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_NM); - } - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - buff[3] = SYM_MAH_NM_0; - buff[4] = SYM_MAH_NM_1; - buff[5] = '\0'; - } - } else { - osdFormatCentiNumber(buff, (int32_t)(getMWhDrawn() * 10.0f * METERS_PER_NAUTICALMILE / totalDistance), 0, 2, 0, digits, false); - tfp_sprintf(buff, "%s%c", buff, SYM_WH_NM); - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - } + + strcat(outBuff, "/"); + moreThanAh = moreThanAh || osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000.0f * METERS_PER_MILE / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + + if (!moreThanAh) + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_MI_0, SYM_MAH_MI_1); + else + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_MI); + } else { + tfp_sprintf(outBuff + strlen(outBuff), "---/---%c%c", SYM_MAH_MI_0, SYM_MAH_MI_1); + } + } else { + if (efficiencyValid) { + osdFormatCentiNumber(buff, (int32_t)((getMWhDrawn() - stats.flightStartMWh) * 10.0f * METERS_PER_MILE / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + osdFormatCentiNumber(buff + strlen(buff), (int32_t)(getMWhDrawn() * 10.0f * METERS_PER_MILE / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + } else { + strcat(outBuff, "---/---"); + } + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_WH_MI); + } + break; + case OSD_UNIT_GA: + if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { + if (efficiencyValid) { + moreThanAh = osdFormatCentiNumber(buff, (int32_t)((getMAhDrawn()-stats.flightStartMAh) * 10000.0f * METERS_PER_NAUTICALMILE / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + if (osdDisplayIsHD()) { + if (!moreThanAh) + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_NM_0, SYM_MAH_NM_1); + else + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_NM); + + moreThanAh = false; } - break; - case OSD_UNIT_METRIC_MPH: - FALLTHROUGH; - case OSD_UNIT_METRIC: - if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { - moreThanAh = osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000000.0f / totalDistance), 1000, 0, 2, digits, false); - if (!moreThanAh) { - tfp_sprintf(buff, "%s%c%c", buff, SYM_MAH_KM_0, SYM_MAH_KM_1); - } else { - tfp_sprintf(buff, "%s%c", buff, SYM_AH_KM); - } - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - buff[3] = SYM_MAH_KM_0; - buff[4] = SYM_MAH_KM_1; - buff[5] = '\0'; - } + + strcat(outBuff, "/"); + moreThanAh = moreThanAh || osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000.0f * METERS_PER_NAUTICALMILE / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + if (!moreThanAh) { + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_NM_0, SYM_MAH_NM_1); } else { - osdFormatCentiNumber(buff, (int32_t)(getMWhDrawn() * 10000.0f / totalDistance), 0, 2, 0, digits, false); - tfp_sprintf(buff, "%s%c", buff, SYM_WH_KM); - if (!efficiencyValid) { - buff[0] = buff[1] = buff[2] = '-'; - } + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_NM); } - break; + } else { + tfp_sprintf(outBuff + strlen(outBuff), "---/---%c%c", SYM_MAH_NM_0, SYM_MAH_NM_1); + } + } else { + if (efficiencyValid) { + osdFormatCentiNumber(buff, (int32_t)((getMWhDrawn()-stats.flightStartMWh) * 10.0f * METERS_PER_NAUTICALMILE / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + osdFormatCentiNumber(buff, (int32_t)(getMWhDrawn() * 10.0f * METERS_PER_NAUTICALMILE / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + } else { + strcat(outBuff, "---/---"); + } + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_WH_NM); } - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX, top++, buff); + break; + case OSD_UNIT_METRIC_MPH: + case OSD_UNIT_METRIC: + forceMetric = true; + break; + } + } + + if (forceMetric) { + if (osdConfig()->stats_energy_unit == OSD_STATS_ENERGY_UNIT_MAH) { + if (efficiencyValid) { + moreThanAh = osdFormatCentiNumber(buff, (int32_t)((getMAhDrawn() - stats.flightStartMAh) * 10000000.0f / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + if (osdDisplayIsHD()) { + if (!moreThanAh) + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_KM_0, SYM_MAH_KM_1); + else + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_KM); + + moreThanAh = false; + } + + strcat(outBuff, "/"); + moreThanAh = moreThanAh || osdFormatCentiNumber(buff, (int32_t)(getMAhDrawn() * 10000000.0f / totalDistance), 1000, 0, 2, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + if (!moreThanAh) { + tfp_sprintf(outBuff + strlen(outBuff), "%c%c", SYM_MAH_KM_0, SYM_MAH_KM_1); + } else { + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_AH_KM); + } + } else { + tfp_sprintf(outBuff + strlen(outBuff), "---/---%c%c", SYM_MAH_KM_0, SYM_MAH_KM_1); } + } else { + if (efficiencyValid) { + osdFormatCentiNumber(buff, (int32_t)((getMWhDrawn() - stats.flightStartMWh) * 10000.0f / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + osdFormatCentiNumber(buff, (int32_t)(getMWhDrawn() * 10000.0f / totalDistance), 0, 2, 0, digits, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + } else { + strcat(outBuff, "---/---"); + } + tfp_sprintf(outBuff + strlen(outBuff), "%c", SYM_WH_KM); + } + } + + tfp_sprintf(outBuff + strlen(outBuff), "%c", '\0'); + displayWrite(osdDisplayPort, statValX, row++, outBuff); + + return row; +} + +uint8_t drawStat_RXStats(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[20]; + uint8_t multiValueXOffset = 0; + + tfp_sprintf(buff, "MIN RSSI"); + if (rxConfig()->serialrx_provider == SERIALRX_CRSF) { + strcat(buff, "/LQ"); + + if (osdDisplayIsHD()) + strcat(buff, "/DBM"); + } + displayWrite(osdDisplayPort, col, row, buff); + + memset(buff, '\0', strlen(buff)); + tfp_sprintf(buff, ": "); + itoa(stats.min_rssi, buff + 2, 10); + strcat(osdFormatTrimWhiteSpace(buff), "%"); + + if (rxConfig()->serialrx_provider == SERIALRX_CRSF) { + strcat(osdFormatTrimWhiteSpace(buff), "/"); + multiValueXOffset = strlen(buff); + itoa(stats.min_lq, buff + multiValueXOffset, 10); + strcat(osdFormatTrimWhiteSpace(buff), "%"); + + if (osdDisplayIsHD()) { + strcat(osdFormatTrimWhiteSpace(buff), "/"); + itoa(stats.min_rssi_dbm, buff + 2, 10); + tfp_sprintf(buff + strlen(buff), "%c", SYM_DBM); + displayWrite(osdDisplayPort, statValX, row++, buff); + } + } + + displayWrite(osdDisplayPort, statValX, row++, buff); + + if (!osdDisplayIsHD() && rxConfig()->serialrx_provider == SERIALRX_CRSF) { + displayWrite(osdDisplayPort, col, row, "MIN RX DBM"); + memset(buff, '\0', strlen(buff)); + tfp_sprintf(buff, ": "); + itoa(stats.min_rssi_dbm, buff + 2, 10); + tfp_sprintf(buff + strlen(buff), "%c", SYM_DBM); + displayWrite(osdDisplayPort, statValX, row++, buff); + } + + return row; +} + +uint8_t drawStat_GPS(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + displayWrite(osdDisplayPort, col, row, "MIN/MAX GPS SATS"); + tfp_sprintf(buff, ": %u/%u", stats.min_sats, stats.max_sats); + displayWrite(osdDisplayPort, statValX, row++, buff); + + return row; +} + +uint8_t drawStat_ESCTemperature(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + displayWrite(osdDisplayPort, col, row, "MIN/MAX ESC TEMP"); + tfp_sprintf(buff, ": %3d/%3d%c", + ((osdConfig()->units == OSD_UNIT_IMPERIAL) ? (int16_t)(stats.min_esc_temp * 9 / 5.0f + 320) : stats.min_esc_temp), + ((osdConfig()->units == OSD_UNIT_IMPERIAL) ? (int16_t)(stats.max_esc_temp * 9 / 5.0f + 320) : stats.max_esc_temp), + ((osdConfig()->units == OSD_UNIT_IMPERIAL) ? SYM_TEMP_F : SYM_TEMP_C)); + displayWrite(osdDisplayPort, statValX, row++, buff); + + return row; +} + +uint8_t drawStat_GForce(uint8_t col, uint8_t row, uint8_t statValX) +{ + char buff[12]; + char outBuff[12]; + + const float max_gforce = accGetMeasuredMaxG(); + const acc_extremes_t *acc_extremes = accGetMeasuredExtremes(); + const float acc_extremes_min = acc_extremes[Z].min; + const float acc_extremes_max = acc_extremes[Z].max; + + if (!osdDisplayIsHD()) + displayWrite(osdDisplayPort, col, row, "MAX G-FORCE"); + else + displayWrite(osdDisplayPort, col, row, "MAX/MIN Z/MAX Z G-FORCE"); + + tfp_sprintf(outBuff, ": "); + osdFormatCentiNumber(buff, max_gforce * 100, 0, 2, 0, 3, false); + + if (!osdDisplayIsHD()) { + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + displayWrite(osdDisplayPort, statValX, row++, outBuff); + + displayWrite(osdDisplayPort, col, row, "MIN/MAX Z G-FORCE"); + memset(outBuff, '\0', strlen(outBuff)); + tfp_sprintf(outBuff, ": "); + } else { + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + } + osdFormatCentiNumber(buff, acc_extremes_min * 100, 0, 2, 0, 4, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + strcat(outBuff, "/"); + + osdFormatCentiNumber(buff, acc_extremes_max * 100, 0, 2, 0, 3, false); + strcat(outBuff, osdFormatTrimWhiteSpace(buff)); + displayWrite(osdDisplayPort, statValX, row++, outBuff); + + return row; +} + +uint8_t drawStat_DisarmMethod(uint8_t col, uint8_t row, uint8_t statValX) +{ + // We keep "" for backward compatibility with the Blackbox explorer and other potential usages + const char * disarmReasonStr[DISARM_REASON_COUNT] = { "UNKNOWN", "TIMEOUT", "STICKS", "SWITCH", "SWITCH", "", "FAILSAFE", "NAV SYS", "LANDING"}; + + displayWrite(osdDisplayPort, col, row, "DISARMED BY"); + displayWrite(osdDisplayPort, statValX, row, ": "); + displayWrite(osdDisplayPort, statValX + 2, row++, disarmReasonStr[getDisarmReason()]); + + return row; +} + +static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page) +{ + const char * statsHeader[2] = {"*** STATS 1/2 -> ***", "*** STATS <- 2/2 ***"}; + uint8_t row = 1; // Start one line down leaving space at the top of the screen. + + const uint8_t statNameX = (osdDisplayPort->cols - (osdDisplayIsHD() ? 41 : 28)) / 2; + const uint8_t statValuesX = osdDisplayPort->cols - statNameX - (osdDisplayIsHD() ? 15 : 11); + + if (page > 1) + page = 0; + + displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING); + displayClearScreen(osdDisplayPort); + + if (isSinglePageStatsCompatible) { + char buff[25]; + tfp_sprintf(buff, "*** STATS "); +#ifdef USE_BLACKBOX +#ifdef USE_SDCARD + if (feature(FEATURE_BLACKBOX)) { + int32_t logNumber = blackboxGetLogNumber(); + if (logNumber >= 0) + tfp_sprintf(buff + strlen(buff), " %c%05" PRId32 " ", SYM_BLACKBOX, logNumber); + else + tfp_sprintf(buff + strlen(buff), " %c ", SYM_BLACKBOX); } +#endif +#endif + strcat(buff, "***"); - const float max_gforce = accGetMeasuredMaxG(); - displayWrite(osdDisplayPort, statNameX, top, "MAX G-FORCE :"); - osdFormatCentiNumber(buff, max_gforce * 100, 0, 2, 0, 3, false); - displayWrite(osdDisplayPort, statValuesX, top++, buff); + displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(buff)) / 2, row++, buff); + } else + displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(statsHeader[page + 1])) / 2, row++, statsHeader[page]); - const acc_extremes_t *acc_extremes = accGetMeasuredExtremes(); - const float acc_extremes_min = acc_extremes[Z].min; - const float acc_extremes_max = acc_extremes[Z].max; - displayWrite(osdDisplayPort, statNameX, top, "MIN/MAX Z G-FORCE:"); - osdFormatCentiNumber(buff, acc_extremes_min * 100, 0, 2, 0, 4, false); - osdLeftAlignString(buff); - strcat(osdFormatTrimWhiteSpace(buff),"/"); - multiValueLengthOffset = strlen(buff); - displayWrite(osdDisplayPort, statValuesX, top, buff); - osdFormatCentiNumber(buff, acc_extremes_max * 100, 0, 2, 0, 3, false); - osdLeftAlignString(buff); - displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff); + if (isSinglePageStatsCompatible) { + // Top 15 rows for most important stats. Max 19 rows (WTF) + row = drawStat_FlightTime(statNameX, row, statValuesX); // 1 row + row = drawStat_FlightDistance(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_GPS)) row = drawStat_MaxDistanceFromHome(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_GPS)) row = drawStat_Speed(statNameX, row, statValuesX); // 1 row + row = drawStat_MaximumAltitude(statNameX, row, statValuesX); // 1 row + row = drawStat_BatteryVoltage(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER)) row = drawStat_MaximumPowerAndCurrent(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER)) row = drawStat_UsedEnergy(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER) && feature(FEATURE_GPS)) row = drawStat_AverageEfficiency(statNameX, row, statValuesX, false); // 1 row + if (osdConfig()->stats_show_metric_efficiency && osdIsNotMetric() && feature(FEATURE_CURRENT_METER) && feature(FEATURE_GPS)) row = drawStat_AverageEfficiency(statNameX, row, statValuesX, true); // 1 row + row = drawStat_RXStats(statNameX, row, statValuesX); // 1 row if non-CRSF else 2 rows + if (feature(FEATURE_GPS)) row = drawStat_GPS(statNameX, row, statValuesX); // 1 row + if (STATE(ESC_SENSOR_ENABLED)) row = drawStat_ESCTemperature(statNameX, row, statValuesX); // 1 row + + // Draw these if there is space space + if (row < (osdDisplayPort->cols-3)) row = drawStat_GForce(statNameX, row, statValuesX); // 1 row HD or 2 rows SD +#ifdef USE_STATS + if (row < (osdDisplayPort->cols-7) && statsConfig()->stats_enabled) row = drawStat_Stats(statNameX, row, statValuesX, false); // 4 rows +#endif + } else { + switch (page) { + case 0: + // Max 10 rows + row = drawStat_FlightTime(statNameX, row, statValuesX); // 1 row + row = drawStat_FlightDistance(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_GPS)) row = drawStat_MaxDistanceFromHome(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_GPS)) row = drawStat_Speed(statNameX, row, statValuesX); // 1 row + row = drawStat_MaximumAltitude(statNameX, row, statValuesX); // 1 row + row = drawStat_BatteryVoltage(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER)) row = drawStat_MaximumPowerAndCurrent(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER))row = drawStat_UsedEnergy(statNameX, row, statValuesX); // 1 row + if (feature(FEATURE_CURRENT_METER) && feature(FEATURE_GPS)) row = drawStat_AverageEfficiency(statNameX, row, statValuesX, false); // 1 row + if (feature(FEATURE_GPS))row = drawStat_GPS(statNameX, row, statValuesX); // 1 row + break; + case 1: + // Max 10 rows + row = drawStat_RXStats(statNameX, row, statValuesX); // 1 row if non-CRSF else 2 rows + if (STATE(ESC_SENSOR_ENABLED)) row = drawStat_ESCTemperature(statNameX, row, statValuesX); // 1 row + row = drawStat_GForce(statNameX, row, statValuesX); // 1 row HD or 2 rows SD + if (osdConfig()->stats_show_metric_efficiency && osdIsNotMetric() && feature(FEATURE_CURRENT_METER) && feature(FEATURE_GPS)) row = drawStat_AverageEfficiency(statNameX, row, statValuesX, true); // 1 row +#ifdef USE_BLACKBOX +#ifdef USE_SDCARD + if (feature(FEATURE_BLACKBOX)) { + char buff[12]; + displayWrite(osdDisplayPort, statNameX, row, "BLACKBOX FILE"); + + tfp_sprintf(buff, ": %u/%u", stats.min_sats, stats.max_sats); + + int32_t logNumber = blackboxGetLogNumber(); + if (logNumber >= 0) + tfp_sprintf(buff, ": %05ld ", logNumber); + else + strcat(buff, ": INVALID"); + + displayWrite(osdDisplayPort, statValuesX, row++, buff); // 1 row + } +#endif +#endif +#ifdef USE_STATS + if (row < (osdDisplayPort->cols-7) && statsConfig()->stats_enabled) row = drawStat_Stats(statNameX, row, statValuesX, false); // 4 rows +#endif + + break; + } } + + row = drawStat_DisarmMethod(statNameX, row, statValuesX); + + // The following has been commented out as it will be added in #9688 + // uint16_t rearmMs = (emergInflightRearmEnabled()) ? emergencyInFlightRearmTimeMS() : 0; if (savingSettings == true) { - displayWrite(osdDisplayPort, statNameX, top++, OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS)); + displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS))) / 2, row++, OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS)); + /*} else if (rearmMs > 0) { // Show rearming time if settings not actively being saved. Ignore the settings saved message if rearm available. + char emReArmMsg[23]; + tfp_sprintf(emReArmMsg, "** REARM PERIOD: "); + tfp_sprintf(emReArmMsg + strlen(emReArmMsg), "%02d", (uint8_t)MS2S(rearmMs)); + strcat(emReArmMsg, " **\0"); + displayWrite(osdDisplayPort, statNameX, top++, OSD_MESSAGE_STR(emReArmMsg));*/ } else if (notify_settings_saved > 0) { if (millis() > notify_settings_saved) { notify_settings_saved = 0; } else { - displayWrite(osdDisplayPort, statNameX, top++, OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED)); + displayWrite(osdDisplayPort, (osdDisplayPort->cols - strlen(OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED))) / 2, row++, OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED)); } } @@ -4981,16 +5402,16 @@ static void osdRefresh(timeUs_t currentTimeUs) if (ARMING_FLAG(ARMED)) { // Display the "Arming" screen statsDisplayed = false; - osdResetStats(); + if (!STATE(IN_FLIGHT_EMERG_REARM)) + osdResetStats(); + osdShowArmed(); uint16_t delay = osdConfig()->arm_screen_display_time; - if (STATE(IN_FLIGHT_EMERG_REARM)) { + if (STATE(IN_FLIGHT_EMERG_REARM)) delay = 500; - } #if defined(USE_SAFE_HOME) - else if (posControl.safehomeState.distance) { + else if (posControl.safehomeState.distance) delay += 3000; - } #endif osdSetNextRefreshIn(delay); } else { @@ -5159,7 +5580,7 @@ void osdUpdate(timeUs_t currentTimeUs) #define STATS_FREQ_DENOM 50 counter++; - if ((counter % STATS_FREQ_DENOM) == 0) { + if ((counter % STATS_FREQ_DENOM) == 0 && ARMING_FLAG(ARMED)) { osdUpdateStats(); } @@ -5418,8 +5839,17 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter /* Messages that are shown regardless of Arming state */ + // The following has been commented out as it will be added in #9688 + // uint16_t rearmMs = (emergInflightRearmEnabled()) ? emergencyInFlightRearmTimeMS() : 0; + if (savingSettings == true) { messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS); + /*} else if (rearmMs > 0) { // Show rearming time if settings not actively being saved. Ignore the settings saved message if rearm available. + char emReArmMsg[23]; + tfp_sprintf(emReArmMsg, "** REARM PERIOD: "); + tfp_sprintf(emReArmMsg + strlen(emReArmMsg), "%02d", (uint8_t)MS2S(rearmMs)); + strcat(emReArmMsg, " **\0"); + messages[messageCount++] = OSD_MESSAGE_STR(emReArmMsg);*/ } else if (notify_settings_saved > 0) { if (millis() > notify_settings_saved) { notify_settings_saved = 0; diff --git a/src/main/io/osd.h b/src/main/io/osd.h index 454b7160420..5e53a436252 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -285,6 +285,7 @@ typedef enum { OSD_CUSTOM_ELEMENT_3, OSD_ADSB_WARNING, OSD_ADSB_INFO, + OSD_BLACKBOX, OSD_ITEM_COUNT // MUST BE LAST } osd_items_e; @@ -303,11 +304,6 @@ typedef enum { OSD_STATS_ENERGY_UNIT_WH, } osd_stats_energy_unit_e; -typedef enum { - OSD_STATS_MIN_VOLTAGE_UNIT_BATTERY, - OSD_STATS_MIN_VOLTAGE_UNIT_CELL, -} osd_stats_min_voltage_unit_e; - typedef enum { OSD_CROSSHAIRS_STYLE_DEFAULT, OSD_CROSSHAIRS_STYLE_AIRCRAFT, @@ -417,8 +413,8 @@ typedef struct osdConfig_s { uint8_t units; // from osd_unit_e uint8_t stats_energy_unit; // from osd_stats_energy_unit_e - uint8_t stats_min_voltage_unit; // from osd_stats_min_voltage_unit_e uint8_t stats_page_auto_swap_time; // stats page auto swap interval time (seconds) + bool stats_show_metric_efficiency; // If true, show metric efficiency as well as for the selected units #ifdef USE_WIND_ESTIMATOR bool estimations_wind_compensation; // use wind compensation for estimated remaining flight/distance diff --git a/src/main/sensors/acceleration.c b/src/main/sensors/acceleration.c index 695f49b00b2..4e46f178843 100644 --- a/src/main/sensors/acceleration.c +++ b/src/main/sensors/acceleration.c @@ -529,6 +529,15 @@ float accGetMeasuredMaxG(void) return acc.maxG; } +void resetGForceStats(void) { + acc.maxG = 0.0f; + + for (int axis = 0; axis < XYZ_AXIS_COUNT; axis++) { + acc.extremes[axis].min = 100; + acc.extremes[axis].max = -100; + } +} + void accUpdate(void) { #ifdef USE_SIMULATOR diff --git a/src/main/sensors/acceleration.h b/src/main/sensors/acceleration.h index f3385edc6d5..a25063ec4fd 100644 --- a/src/main/sensors/acceleration.h +++ b/src/main/sensors/acceleration.h @@ -86,6 +86,7 @@ void accGetMeasuredAcceleration(fpVector3_t *measuredAcc); const acc_extremes_t* accGetMeasuredExtremes(void); float accGetMeasuredMaxG(void); void updateAccExtremes(void); +void resetGForceStats(void); void accGetVibrationLevels(fpVector3_t *accVibeLevels); float accGetVibrationLevel(void); uint32_t accGetClipCount(void);