41 #define ASSERT_CONCAT_(a, b) a ## b
42 #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
45 #define STATIC_ASSERT(e,m) \
46 { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; }
52 #define STATIC_ASSERT(e,m) \
53 { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; }
57 #include "stm32f4xx_flash.h"
66 #include "onscreendisplaysettings.h"
67 #include "onscreendisplaypagesettings.h"
68 #include "onscreendisplaypagesettings2.h"
69 #include "onscreendisplaypagesettings3.h"
70 #include "onscreendisplaypagesettings4.h"
72 #include "modulesettings.h"
75 #include "physical_constants.h"
78 #include "airspeedactual.h"
79 #include "attitudeactual.h"
80 #include "baroaltitude.h"
81 #include "flightstatus.h"
82 #include "flightbatterystate.h"
83 #include "flightbatterysettings.h"
84 #include "flightstats.h"
85 #include "gpsposition.h"
86 #include "positionactual.h"
88 #include "gpssatellites.h"
89 #include "gpsvelocity.h"
90 #include "homelocation.h"
91 #include "magnetometer.h"
92 #include "manualcontrolcommand.h"
93 #include "modulesettings.h"
94 #include "stabilizationdesired.h"
95 #include "stabilizationsettings.h"
96 #include "stateestimation.h"
97 #include "systemalarms.h"
98 #include "systemstats.h"
99 #include "tabletinfo.h"
100 #include "taskinfo.h"
101 #include "velocityactual.h"
103 #include "waypoint.h"
104 #include "waypointactive.h"
123 #define STACK_SIZE_BYTES 2048
124 #define TASK_PRIORITY PIOS_THREAD_PRIO_LOW
125 #define BLINK_INTERVAL_FRAMES 12
126 #define BOOT_DISPLAY_TIME_MS (10*1000)
127 #define STATS_DELAY_MS 1500
171 #define MS_TO_KMH 3.6f
172 #define MS_TO_MPH 2.23694f
173 #define M_TO_FEET 3.28084f
192 const char digits[16] =
"0123456789abcdef";
202 static uint32_t in_ticks = 0;
203 static uint32_t out_ticks = 0;
204 static uint16_t in_time = 0;
205 static uint16_t out_time = 0;
209 void drawBattery(uint16_t x, uint16_t y, uint8_t battery, uint16_t size)
213 uint8_t charge_width = battery * (size - 2) / 100;
235 #define VERTICAL_SCALE_FILLED_NUMBER
236 #define VSCALE_FONT FONT8X10
245 int majtick_start = 0, majtick_end = 0, mintick_start = 0, mintick_end = 0, boundtick_start = 0, boundtick_end = 0;
251 majtick_end = x + majtick_len;
252 mintick_end = x + mintick_len;
253 boundtick_end = x + boundtick_len;
254 }
else if (halign == +1) {
255 majtick_end = x - majtick_len;
256 mintick_end = x - mintick_len;
257 boundtick_end = x - boundtick_len;
261 if (font_info == NULL)
263 int arrow_len = (font_info->
height / 2) + 1;
264 int text_x_spacing = (font_info->
width / 2);
265 int max_text_y = 0, text_length = 0;
266 int small_font_char_width = font_info->
width + 1;
268 int range_2 = range / 2;
269 int r = 0, rr = 0, rv = 0, ys = 0, style = 0;
271 for (r = -range_2; r <= +range_2; r++) {
273 rr = r + range_2 - v;
276 rr += majtick_step / 2;
278 if (rr % majtick_step == 0) {
280 }
else if (rr % mintick_step == 0) {
285 if (flags & HUD_VSCALE_FLAG_NO_NEGATIVE && rv < 0) {
290 ys = ((
long int)(r * height) / (
long int)range) + y;
294 memset(temp,
' ', 10);
296 text_length = (strlen(temp) + 1) * small_font_char_width;
297 if (text_length > max_text_y) {
298 max_text_y = text_length;
305 }
else if (style == 2) {
311 memset(temp,
' ', 10);
318 xx = majtick_end + text_x_spacing;
320 xx = majtick_end - text_x_spacing;
325 for (
i = 0;
i < arrow_len;
i++) {
329 #ifdef VERTICAL_SCALE_FILLED_NUMBER
339 #ifdef VERTICAL_SCALE_FILLED_NUMBER
351 write_vline_lm(xx + width - 1, y - arrow_len, y + arrow_len - 2, 1, 1);
355 write_vline_lm(xx - width - 1, y - arrow_len, y + arrow_len - 2, 1, 1);
363 #ifdef VERTICAL_SCALE_BRUTE_FORCE_BLANK_OUT
399 #define COMPASS_SMALL_NUMBER
406 int majtick_start = 0, majtick_end = 0, mintick_start = 0, mintick_end = 0, textoffset = 0;
409 majtick_end = y - majtick_len;
411 mintick_end = y - mintick_len;
413 int r, style, rr, xs;
414 int range_2 = range / 2;
415 bool home_drawn =
false;
416 for (r = -range_2; r <= +range_2; r++) {
418 rr = (v + r + 360) % 360;
420 if (rr % majtick_step == 0) {
422 }
else if (rr % mintick_step == 0) {
427 xs = ((
long int)(r * width) / (
long int)range) + x;
436 headingstr[0] =
'0' + (rr / 100);
437 headingstr[1] =
'0' + ((rr / 10) % 10);
462 }
else if (style == 2) {
468 if (rr == home_dir) {
469 xs = ((
long int)(r * width) / (
long int)range) + x;
476 if (home_dir > 0 && !home_drawn) {
477 if (((v > home_dir) && (v - home_dir < 180)) || ((v < home_dir) && (home_dir -v > 180)))
478 r = x - ((
long int)(range_2 * width) / (
long int)range);
480 r = x + ((
long int)(range_2 * width) / (
long int)range);
489 headingstr[0] =
'0' + (v / 100);
490 headingstr[1] =
'0' + ((v / 10) % 10);
491 headingstr[2] =
'0' + (v % 10);
494 if (font_info == NULL)
496 #ifdef COMPASS_SMALL_NUMBER
497 int rect_width = font_info->
width * 3;
498 #ifdef COMPASS_FILLED_NUMBER
506 int rect_width = (font_info->
width + 1) * 3 + 2;
507 #ifdef COMPASS_FILLED_NUMBER
517 #define CENTER_BODY 3
518 #define CENTER_WING 7
519 #define CENTER_RUDDER 5
520 #define PITCH_STEP 10
522 int16_t width, int16_t height, int8_t max_pitch,
523 uint8_t n_pitch_steps,
bool show_horizon,
524 OnScreenDisplayPageSettingsCenterMarkOptions center_mark)
528 StabilizationSettingsCameraTiltGet(&camera_tilt);
534 float sin_roll = sinf(roll * (
float)(M_PI / 180));
535 float cos_roll = cosf(roll * (
float)(M_PI / 180));
537 pitch += cos_roll * camera_tilt;
543 float modulo_pitch = pitch - pitch_step_offset * 10.0f;
546 int16_t pp_x = x + width * ((sin_roll * modulo_pitch) / (
float)max_pitch);
547 int16_t pp_y = y + height * ((cos_roll * modulo_pitch) / (
float)max_pitch);
552 d_x = cos_roll * width / 2;
553 d_y = sin_roll * height / 2;
560 int16_t d_x_10 = width * sin_roll *
PITCH_STEP / (float)max_pitch;
561 int16_t d_y_10 = height * cos_roll *
PITCH_STEP / (float)max_pitch;
563 int16_t d_x_2 = d_x_10 / 6;
564 int16_t d_y_2 = d_y_10 / 6;
566 for (
int i = (-max_pitch / 10)-1;
i<(max_pitch/10)+1;
i++) {
567 int angle = (pitch_step_offset +
i);
569 if (angle < -n_pitch_steps)
continue;
570 if (angle > n_pitch_steps)
continue;
577 }
else if (angle < -90) {
578 angle = -180 - angle;
581 int16_t pp_x2 = pp_x -
i * d_x_10;
582 int16_t pp_y2 = pp_y -
i * d_y_10;
590 write_line_outlined(pp_x2 - d_x2, pp_y2 + d_y2, pp_x2 - d_x2 - d_x_2, pp_y2 + d_y2 - d_y_2, 2, 2, 0, 1);
591 write_line_outlined(pp_x2 + d_x2, pp_y2 - d_y2, pp_x2 + d_x2 - d_x_2, pp_y2 - d_y2 - d_y_2, 2, 2, 0, 1);
595 }
else if (angle > 0) {
597 write_line_outlined(pp_x2 - d_x2, pp_y2 + d_y2, pp_x2 - d_x2 + d_x_2, pp_y2 + d_y2 + d_y_2, 2, 2, 0, 1);
598 write_line_outlined(pp_x2 + d_x2, pp_y2 - d_y2, pp_x2 + d_x2 + d_x_2, pp_y2 - d_y2 + d_y_2, 2, 2, 0, 1);
603 write_line_outlined(pp_x2 - d_x, pp_y2 + d_y, pp_x2 - d_x / 3, pp_y2 + d_y / 3, 2, 2, 0, 1);
604 write_line_outlined(pp_x2 + d_x / 3, pp_y2 - d_y / 3, pp_x2 + d_x, pp_y2 - d_y, 2, 2, 0, 1);
610 if ((center_mark == ONSCREENDISPLAYPAGESETTINGS_CENTERMARK_MIDDLE) ||
611 (center_mark == ONSCREENDISPLAYPAGESETTINGS_CENTERMARK_CAMERAPITCH)) {
612 if (center_mark == ONSCREENDISPLAYPAGESETTINGS_CENTERMARK_CAMERAPITCH) {
615 if (camera_tilt > max_pitch) {
616 camera_tilt = max_pitch;
619 camera_tilt /= max_pitch;
637 SharedDefsFlightModeOptions
mode;
638 FlightStatusFlightModeGet(&mode);
642 case SHAREDDEFS_FLIGHTMODE_MANUAL:
645 case SHAREDDEFS_FLIGHTMODE_ACRO:
646 write_string(
"ACRO", x, y, xs, ys, va, ha, flags, font);
648 case SHAREDDEFS_FLIGHTMODE_ACROPLUS:
649 write_string(
"ACROPLUS", x, y, xs, ys, va, ha, flags, font);
651 case SHAREDDEFS_FLIGHTMODE_ACRODYNE:
652 write_string(
"ACRODYNE", x, y, xs, ys, va, ha, flags, font);
654 case SHAREDDEFS_FLIGHTMODE_LEVELING:
655 write_string(
"LEVEL", x, y, xs, ys, va, ha, flags, font);
657 case SHAREDDEFS_FLIGHTMODE_HORIZON:
660 case SHAREDDEFS_FLIGHTMODE_AXISLOCK:
661 write_string(
"ALCK", x, y, xs, ys, va, ha, flags, font);
663 case SHAREDDEFS_FLIGHTMODE_VIRTUALBAR:
664 write_string(
"VBAR", x, y, xs, ys, va, ha, flags, font);
666 case SHAREDDEFS_FLIGHTMODE_STABILIZED1:
669 case SHAREDDEFS_FLIGHTMODE_STABILIZED2:
672 case SHAREDDEFS_FLIGHTMODE_STABILIZED3:
675 case SHAREDDEFS_FLIGHTMODE_AUTOTUNE:
676 write_string(
"TUNE", x, y, xs, ys, va, ha, flags, font);
678 case SHAREDDEFS_FLIGHTMODE_ALTITUDEHOLD:
679 write_string(
"AHLD", x, y, xs, ys, va, ha, flags, font);
681 case SHAREDDEFS_FLIGHTMODE_POSITIONHOLD:
682 write_string(
"PHLD", x, y, xs, ys, va, ha, flags, font);
684 case SHAREDDEFS_FLIGHTMODE_RETURNTOHOME:
687 case SHAREDDEFS_FLIGHTMODE_PATHPLANNER:
688 write_string(
"PLAN", x, y, xs, ys, va, ha, flags, font);
690 case SHAREDDEFS_FLIGHTMODE_FAILSAFE:
691 write_string(
"FAILSAFE", x, y, xs, ys, va, ha, flags, font);
693 case SHAREDDEFS_FLIGHTMODE_TABLETCONTROL:
695 TabletInfoTabletModeDesiredOptions tab_mode;
696 TabletInfoTabletModeDesiredGet(&tab_mode);
698 case TABLETINFO_TABLETMODEDESIRED_POSITIONHOLD:
699 write_string(
"TAB PH", x, y, xs, ys, va, ha, flags, font);
701 case TABLETINFO_TABLETMODEDESIRED_RETURNTOHOME:
702 write_string(
"TAB RTH", x, y, xs, ys, va, ha, flags, font);
704 case TABLETINFO_TABLETMODEDESIRED_RETURNTOTABLET:
705 write_string(
"TAB RTT", x, y, xs, ys, va, ha, flags, font);
707 case TABLETINFO_TABLETMODEDESIRED_PATHPLANNER:
708 write_string(
"TAB Path", x, y, xs, ys, va, ha, flags, font);
710 case TABLETINFO_TABLETMODEDESIRED_FOLLOWME:
711 write_string(
"TAB FollowMe", x, y, xs, ys, va, ha, flags, font);
713 case TABLETINFO_TABLETMODEDESIRED_LAND:
714 write_string(
"TAB Land", x, y, xs, ys, va, ha, flags, font);
716 case TABLETINFO_TABLETMODEDESIRED_CAMERAPOI:
717 write_string(
"TAB POI", x, y, xs, ys, va, ha, flags, font);
721 case SHAREDDEFS_FLIGHTMODE_LQG:
722 write_string(
"LQG-R", x, y, xs, ys, va, ha, flags, font);
724 case SHAREDDEFS_FLIGHTMODE_LQGLEVELING:
725 write_string(
"LQG-L", x, y, xs, ys, va, ha, flags, font);
727 case SHAREDDEFS_FLIGHTMODE_FLIPREVERSED:
728 write_string(
"FLIPOVER", x, y, xs, ys, va, ha, flags, font);
735 char buf[100] = { 0 };
736 SystemAlarmsData alarm;
739 SystemAlarmsGet(&alarm);
744 strncpy((
char*)buf, boot_reason,
sizeof(buf));
745 buf[
sizeof(buf) - 2] =
'\0';
755 int32_t len =
AlarmString(&alarm, buf + pos,
sizeof(buf) - pos,
769 void draw_map_home_center(
int width_px,
int height_px,
int width_m,
int height_m,
bool show_wp,
bool show_home,
bool show_tablet)
771 char tmp_str[10] = { 0 };
773 WaypointActiveData waypoint_active;
774 float scale_x, scale_y;
775 float p_north, p_east, p_north_draw, p_east_draw,
yaw, rot, aspect, aspect_pos;
779 scale_x = (float)width_px / width_m;
780 scale_y = (float)height_px / height_m;
783 if (show_wp && WaypointHandle() && WaypointActiveHandle()) {
785 WaypointActiveGet(&waypoint_active);
786 for (
int i = 0;
i < num_wp;
i++) {
787 WaypointInstGet(
i, &waypoint);
788 if ((num_wp == 1) && (waypoint.Position[WAYPOINT_POSITION_EAST] == 0.f) &&
789 (waypoint.Position[WAYPOINT_POSITION_NORTH] == 0.f)) {
793 if ((fabs(waypoint.Position[WAYPOINT_POSITION_EAST]) < width_m / 2) &&
794 (fabs(waypoint.Position[WAYPOINT_POSITION_NORTH]) < height_m / 2)) {
797 if (
i == waypoint_active.Index) {
813 TabletInfoData tabletInfo;
814 TabletInfoGet(&tabletInfo);
815 if (tabletInfo.Connected) {
817 lla_to_ned(tabletInfo.Latitude, tabletInfo.Longitude, tabletInfo.Altitude, NED);
819 if ((fabs(NED[1]) < width_m / 2) && (fabs(NED[0]) < height_m / 2)) {
828 PositionActualNorthGet(&p_north);
829 PositionActualEastGet(&p_east);
832 if ((2.0
f * (
float)fabs(p_north) > height_m) || (2.0
f * (
float)fabs(p_east) > width_m)) {
834 aspect = (float)width_m / (
float)height_m;
835 aspect_pos = p_north / p_east;
836 if ((
float)fabs(aspect_pos) < aspect) {
838 p_east_draw =
sign(p_east) * width_m / 2.f;
839 p_north_draw = p_east_draw * aspect_pos;
842 p_north_draw =
sign(p_north) * height_m / 2.f;
843 p_east_draw = p_north_draw / aspect_pos;
859 AttitudeActualYawGet(&yaw);
866 x = p_east_draw + 10.f * sinf(rot * (
float)(M_PI / 180));
867 y = p_north_draw - 10.f * cosf(rot * (
float)(M_PI / 180));
872 x = p_east_draw + 10 * sinf(rot * (
float)(M_PI / 180));
873 y = p_north_draw - 10 * cosf(rot * (
float)(M_PI / 180));
879 void draw_map_uav_center(
int width_px,
int height_px,
int width_m,
int height_m,
bool show_wp,
bool show_uav,
bool show_tablet)
881 char tmp_str[10] = { 0 };
883 WaypointActiveData waypoint_active;
884 float scale_x, scale_y;
885 float p_north, p_east, p_north_draw, p_east_draw, p_north_draw2, p_east_draw2,
yaw, aspect, aspect_pos, sin_yaw, cos_yaw;
887 bool draw_this_wp =
false;
890 scale_x = (float)width_px / (
float)width_m;
891 scale_y = (float)height_px / (
float)height_m;
892 aspect = (float)width_m / (
float)height_m;
895 AttitudeActualYawGet(&yaw);
896 PositionActualNorthGet(&p_north);
897 PositionActualEastGet(&p_east);
900 sin_yaw = sinf(yaw * (
float)(M_PI / 180));
901 cos_yaw = cosf(yaw * (
float)(M_PI / 180));
904 if (show_wp && WaypointHandle() && WaypointActiveHandle()) {
906 WaypointActiveGet(&waypoint_active);
907 for (
int i = 0;
i < num_wp;
i++) {
908 WaypointInstGet(
i, &waypoint);
909 if ((num_wp == 1) && (waypoint.Position[WAYPOINT_POSITION_EAST] == 0.f) &&
910 (waypoint.Position[WAYPOINT_POSITION_NORTH] == 0.f)) {
915 p_east_draw2 = waypoint.Position[WAYPOINT_POSITION_EAST] - p_east;
916 p_north_draw2 = waypoint.Position[WAYPOINT_POSITION_NORTH] - p_north;
919 p_east_draw = cos_yaw * p_east_draw2 - sin_yaw * p_north_draw2;
920 p_north_draw = sin_yaw * p_east_draw2 + cos_yaw * p_north_draw2;
922 draw_this_wp =
false;
923 if ((2.0
f * (
float)fabs(p_north_draw) > height_m) || (2.0
f * (
float)fabs(p_east_draw) > width_m)) {
925 aspect_pos = p_north_draw / p_east_draw;
926 if ((
float)fabs(aspect_pos) < aspect) {
928 p_east_draw =
sign(p_east_draw) * width_m / 2.f;
929 p_north_draw = p_east_draw * aspect_pos;
932 p_north_draw =
sign(p_north_draw) * height_m / 2.f;
933 p_east_draw = p_north_draw / aspect_pos;
944 if (
i == waypoint_active.Index) {
954 p_east_draw = cos_yaw * -1 * p_east + sin_yaw * p_north;
955 p_north_draw = sin_yaw * -1 * p_east - cos_yaw * p_north;
957 if ((2.0
f * (
float)fabs(p_north_draw) > height_m) || (2.0
f * (
float)fabs(p_east_draw) > width_m)) {
959 aspect_pos = p_north_draw / p_east_draw;
960 if ((
float)fabs(aspect_pos) < aspect) {
962 p_east_draw =
sign(p_east_draw) * width_m / 2.f;
963 p_north_draw = p_east_draw * aspect_pos;
966 p_north_draw =
sign(p_north_draw) * height_m / 2.f;
967 p_east_draw = p_north_draw / aspect_pos;
982 TabletInfoData tabletInfo;
983 TabletInfoGet(&tabletInfo);
984 if (tabletInfo.Connected) {
986 lla_to_ned(tabletInfo.Latitude, tabletInfo.Longitude, tabletInfo.Altitude, NED);
989 p_east_draw2 = NED[1] - p_east;
990 p_north_draw2 = NED[0] - p_north;
993 p_east_draw = cos_yaw * p_east_draw2 - sin_yaw * p_north_draw2;
994 p_north_draw = sin_yaw * p_east_draw2 + cos_yaw * p_north_draw2;
996 if ((2.0
f * (
float)fabs(p_north_draw) > height_m) || (2.0
f * (
float)fabs(p_east_draw) > width_m)) {
998 aspect_pos = p_north_draw / p_east_draw;
999 if ((
float)fabs(aspect_pos) < aspect) {
1001 p_east_draw =
sign(p_east_draw) * width_m / 2.f;
1002 p_north_draw = p_east_draw * aspect_pos;
1005 p_north_draw =
sign(p_north_draw) * height_m / 2.f;
1006 p_east_draw = p_north_draw / aspect_pos;
1030 #ifdef OSD_USE_BRAINFPV_LOGO
1048 char tmp[64] = { 0 };
1058 for (
int i = 0;
i < 26;
i++) {
1060 if (this_char != 0) {
1061 tmp[pos++] = this_char;
1069 for (
int i = 0;
i < 4;
i++) {
1071 tmp[pos++] =
digits[(this_char & 0xF0) >> 4];
1072 tmp[pos++] =
digits[(this_char & 0x0F)];
1089 char tmp_str[100] = { 0 };
1091 float home_dist = -1.f;
1095 uint32_t tmp_uint32;
1096 int tmp_int1, tmp_int2;
1102 if (
has_nav && (page->HomeDistance || page->CompassHomeDir) && PositionActualHandle() ) {
1103 PositionActualNorthGet(&tmp);
1104 PositionActualEastGet(&tmp1);
1106 if (page->HomeDistance)
1110 if (page->CompassHomeDir)
1111 home_dir = (int)(atan2f(tmp1, tmp) * RAD2DEG) + 180;
1115 if (
has_nav && page->Map && PositionActualHandle() ) {
1116 if (page->MapCenterMode == ONSCREENDISPLAYPAGESETTINGS_MAPCENTERMODE_UAV) {
1118 page->MapWidthMeters, page->MapHeightMeters,
1119 page->MapShowWp, page->MapShowUavHome,
1120 page->MapShowTablet);
1124 page->MapWidthMeters, page->MapHeightMeters,
1125 page->MapShowWp, page->MapShowUavHome,
1126 page->MapShowTablet);
1137 if (page->AltitudeScale) {
1138 bool valid_altitude =
false;
1139 if (page->AltitudeScaleSource == ONSCREENDISPLAYPAGESETTINGS_ALTITUDESCALESOURCE_BARO) {
1141 BaroAltitudeAltitudeGet(&tmp);
1143 valid_altitude =
true;
1145 }
else if (PositionActualHandle()) {
1146 PositionActualDownGet(&tmp);
1148 valid_altitude =
true;
1150 if (valid_altitude) {
1151 if (page->AltitudeScaleAlign == ONSCREENDISPLAYPAGESETTINGS_ALTITUDESCALEALIGN_LEFT)
1152 hud_draw_vertical_scale(tmp *
convert_distance, 100, -1, page->AltitudeScalePos,
GRAPHICS_Y_MIDDLE, 120, 10, 20, 5, 8,
1155 hud_draw_vertical_scale(tmp * convert_distance, 100, 1, page->AltitudeScalePos,
GRAPHICS_Y_MIDDLE, 120, 10, 20, 5, 8,
1161 if (page->AltitudeNumeric) {
1162 bool valid_altitude =
false;
1163 if (page->AltitudeNumericSource == ONSCREENDISPLAYPAGESETTINGS_ALTITUDENUMERICSOURCE_BARO) {
1165 BaroAltitudeAltitudeGet(&tmp);
1167 valid_altitude =
true;
1169 }
else if (PositionActualHandle()) {
1170 PositionActualDownGet(&tmp);
1172 valid_altitude =
true;
1174 if (valid_altitude) {
1176 write_string(tmp_str, page->AltitudeNumericPosX, page->AltitudeNumericPosY, 0, 0,
TEXT_VA_TOP, (
int)page->AltitudeNumericAlign,
1177 0, page->AltitudeNumericFont);
1182 if (page->ArmStatus) {
1183 FlightStatusArmedGet(&tmp_uint8);
1184 if (tmp_uint8 == FLIGHTSTATUS_ARMED_ARMED)
1185 write_string(
"ARMED", page->ArmStatusPosX, page->ArmStatusPosY, 0, 0,
TEXT_VA_TOP, (
int)page->ArmStatusAlign, 0,
1186 page->ArmStatusFont);
1187 else if (tmp_uint8 == FLIGHTSTATUS_ARMED_ARMING)
1188 write_string(
"arm..", page->ArmStatusPosX, page->ArmStatusPosY, 0, 0,
TEXT_VA_TOP, (
int)page->ArmStatusAlign, 0,
1189 page->ArmStatusFont);
1193 if (page->ArtificialHorizon || page->CenterMark) {
1194 AttitudeActualRollGet(&tmp);
1195 AttitudeActualPitchGet(&tmp1);
1197 page->ArtificialHorizonMaxPitch, page->ArtificialHorizonPitchSteps, page->ArtificialHorizon, page->CenterMark);
1202 if (page->BatteryVolt) {
1203 FlightBatteryStateVoltageGet(&tmp);
1204 sprintf(tmp_str,
"%0.1fV", (
double)tmp);
1205 write_string(tmp_str, page->BatteryVoltPosX, page->BatteryVoltPosY, 0, 0,
TEXT_VA_TOP, (
int)page->BatteryVoltAlign, 0,
1206 page->BatteryVoltFont);
1208 if (page->BatteryCurrent) {
1209 FlightBatteryStateCurrentGet(&tmp);
1210 sprintf(tmp_str,
"%0.1fA", (
double)tmp);
1212 (
int)page->BatteryCurrentAlign, 0, page->BatteryCurrentFont);
1214 if (page->BatteryConsumed) {
1215 FlightBatteryStateConsumedEnergyGet(&tmp);
1216 sprintf(tmp_str,
"%0.0fmAh", (
double)tmp);
1218 (
int)page->BatteryConsumedAlign, 0, page->BatteryConsumedFont);
1221 if (page->BatteryChargeState) {
1222 FlightBatteryStateConsumedEnergyGet(&tmp);
1223 FlightBatterySettingsCapacityGet(&tmp_uint32);
1224 drawBattery(page->BatteryChargeStatePosX, page->BatteryChargeStatePosY, 100 - 100 * tmp / tmp_uint32, 24);
1229 if (page->ClimbRate && VelocityActualHandle() &&
has_baro) {
1230 VelocityActualDownGet(&tmp);
1232 write_string(tmp_str, page->ClimbRatePosX, page->ClimbRatePosY, 0, 0,
TEXT_VA_TOP, (
int)page->ClimbRateAlign, 0,
1233 page->ClimbRateFont);
1237 if (page->Compass) {
1241 StateEstimationAttitudeFilterGet(&tmp_uint8);
1243 if (tmp_uint8 == STATEESTIMATION_ATTITUDEFILTER_COMPLEMENTARYVELCOMPASS) {
1249 AttitudeActualYawGet(&tmp);
1252 if (page->CompassHomeDir) {
1253 hud_draw_linear_compass(tmp, home_dir, 120, 180,
GRAPHICS_X_MIDDLE, (
int)page->CompassPos, 15, 30, 5, 8, 0);
1255 hud_draw_linear_compass(tmp, -1, 120, 180,
GRAPHICS_X_MIDDLE, (
int)page->CompassPos, 15, 30, 5, 8, 0);
1261 if (page->CustomText) {
1262 memcpy((
void *)tmp_str, (
void *)(
osd_settings.CustomText), ONSCREENDISPLAYSETTINGS_CUSTOMTEXT_NUMELEM);
1263 tmp_str[ONSCREENDISPLAYSETTINGS_CUSTOMTEXT_NUMELEM] = 0;
1264 write_string(tmp_str, page->CustomTextPosX, page->CustomTextPosY, 0, 0,
TEXT_VA_TOP, (
int)page->CustomTextAlign, 0,
1265 page->CustomTextFont);
1269 if (
has_nav && page->HomeArrow) {
1270 if (!page->Compass) {
1271 AttitudeActualYawGet(&tmp);
1273 tmp = fmodf(home_dir -tmp, 360.
f);
1274 draw_polygon(page->HomeArrowPosX, page->HomeArrowPosY, tmp, HOME_ARROW,
NELEMENTS(HOME_ARROW), 0, 1);
1279 SystemStatsCPULoadGet(&tmp_uint8);
1280 sprintf(tmp_str,
"CPU:%2d", tmp_uint8);
1281 write_string(tmp_str, page->CpuPosX, page->CpuPosY, 0, 0,
TEXT_VA_TOP, (
int)page->CpuAlign, 0, page->CpuFont);
1285 if (page->FlightMode) {
1287 page->FlightModeFont);
1293 AccelsGet(&accelsData);
1295 static AccelsData accelsDataAcc = { 0 };
1297 accelsDataAcc.x = 0.8f * accelsDataAcc.x + 0.2f * accelsData.x;
1298 accelsDataAcc.y = 0.8f * accelsDataAcc.y + 0.2f * accelsData.y;
1299 accelsDataAcc.z = 0.8f * accelsDataAcc.z + 0.2f * accelsData.z;
1301 tmp = sqrtf(powf(accelsDataAcc.x, 2.f) + powf(accelsDataAcc.y, 2.f) + powf(accelsDataAcc.z, 2.f)) / 9.81f;
1302 sprintf(tmp_str,
"%0.1fG", (
double)tmp);
1308 if (
has_gps && (page->GpsStatus || page->GpsLat || page->GpsLon || page->GpsMgrs)) {
1309 GPSPositionData gps_data;
1310 GPSPositionGet(&gps_data);
1314 uint8_t pdop_1 = gps_data.PDOP;
1315 uint8_t pdop_2 = roundf(10 * (gps_data.PDOP - pdop_1));
1317 if (page->GpsStatus) {
1318 switch (gps_data.Status)
1320 case GPSPOSITION_STATUS_NOFIX:
1323 case GPSPOSITION_STATUS_FIX2D:
1324 sprintf(tmp_str,
"2D %d %d.%d", (
int)gps_data.Satellites, (
int)pdop_1, pdop_2);
1326 case GPSPOSITION_STATUS_FIX3D:
1327 sprintf(tmp_str,
"3D %d %d.%d", (
int)gps_data.Satellites, (
int)pdop_1, pdop_2);
1329 case GPSPOSITION_STATUS_DIFF3D:
1330 sprintf(tmp_str,
"3D %d %d.%d", (
int)gps_data.Satellites, (
int)pdop_1, pdop_2);
1336 0, page->GpsStatusFont);
1340 sprintf(tmp_str,
"%0.5f", (
double)gps_data.Latitude / 10000000.0);
1346 sprintf(tmp_str,
"%0.5f", (
double)gps_data.Longitude / 10000000.0);
1352 if (page->GpsMgrs) {
1353 static char mgrs_str[20] = {0};
1358 (
double)gps_data.Longitude * (
double)DEG2RAD / 10000000.0, 5, mgrs_str);
1360 sprintf(mgrs_str,
"MGRS ERR: %d", tmp_int1);
1368 if (home_dist >= 0) {
1374 if (page->HomeDistanceShowIcon) {
1378 0, page->HomeDistanceFont);
1383 ManualControlCommandRssiGet(&tmp_int16);
1385 sprintf(tmp_str,
"%3d", tmp_int16);
1386 if (page->RssiShowIcon) {
1395 if (page->SpeedScale) {
1397 bool speed_valid =
false;
1398 switch (page->SpeedScaleSource)
1400 case ONSCREENDISPLAYPAGESETTINGS_SPEEDSCALESOURCE_NAV:
1401 if (VelocityActualHandle()) {
1402 VelocityActualNorthGet(&tmp);
1403 VelocityActualEastGet(&tmp1);
1404 tmp = sqrt(tmp * tmp + tmp1 * tmp1);
1407 sprintf(tmp_str,
"%s",
"GND");
1409 case ONSCREENDISPLAYPAGESETTINGS_SPEEDSCALESOURCE_GPS:
1410 if (
has_gps && GPSPositionHandle()) {
1411 GPSPositionGroundspeedGet(&tmp);
1413 GPSPositionStatusGet(&fix);
1414 speed_valid = fix != GPSPOSITION_STATUS_NOFIX;
1416 sprintf(tmp_str,
"%s",
"GND");
1418 case ONSCREENDISPLAYPAGESETTINGS_SPEEDSCALESOURCE_AIRSPEED:
1419 if (AirspeedActualHandle()) {
1420 AirspeedActualTrueAirspeedGet(&tmp);
1423 sprintf(tmp_str,
"%s",
"AIR");
1426 if (page->SpeedScaleAlign == ONSCREENDISPLAYPAGESETTINGS_SPEEDSCALEALIGN_LEFT) {
1427 hud_draw_vertical_scale(tmp *
convert_speed, 30, -1, page->SpeedScalePos,
GRAPHICS_Y_MIDDLE, 120, 10, 20, 5, 8, 11,
1431 hud_draw_vertical_scale(tmp *
convert_speed, 30, 1, page->SpeedScalePos,
GRAPHICS_Y_MIDDLE, 120, 10, 20, 5, 8, 11, 100,
1439 if (page->SpeedNumeric) {
1441 bool speed_valid =
false;
1442 switch (page->SpeedNumericSource)
1444 case ONSCREENDISPLAYPAGESETTINGS_SPEEDNUMERICSOURCE_NAV:
1445 if (VelocityActualHandle()) {
1446 VelocityActualNorthGet(&tmp);
1447 VelocityActualEastGet(&tmp1);
1450 tmp = sqrt(tmp * tmp + tmp1 * tmp1);
1452 case ONSCREENDISPLAYPAGESETTINGS_SPEEDNUMERICSOURCE_GPS:
1453 if (GPSVelocityHandle()) {
1454 GPSVelocityNorthGet(&tmp);
1455 GPSVelocityEastGet(&tmp1);
1456 tmp = sqrt(tmp * tmp + tmp1 * tmp1);
1460 case ONSCREENDISPLAYPAGESETTINGS_SPEEDNUMERICSOURCE_AIRSPEED:
1461 if (AirspeedActualHandle()) {
1462 AirspeedActualTrueAirspeedGet(&tmp);
1468 write_string(tmp_str, page->SpeedNumericPosX, page->SpeedNumericPosY, 0, 0, (
int)page->SpeedNumericAlign,
TEXT_HA_LEFT, 0,
1469 page->SpeedNumericFont);
1476 SystemStatsFlightTimeGet(&time);
1478 tmp_int16 = (time / 3600000);
1479 if (tmp_int16 == 0) {
1480 tmp_int1 = time / 60000;
1481 tmp_int2 = (time / 1000) - 60 * tmp_int1;
1482 sprintf(tmp_str,
"%02d:%02d", (
int)tmp_int1, (
int)tmp_int2);
1484 tmp_int1 = time / 60000 - 60 * tmp_int16;
1485 tmp_int2 = (time / 1000) - 60 * tmp_int1 - 3600 * tmp_int16;
1486 sprintf(tmp_str,
"%02d:%02d:%02d", (
int)tmp_int16, (
int)tmp_int1, (
int)tmp_int2);
1488 write_string(tmp_str, page->TimePosX, page->TimePosY, 0, 0,
TEXT_VA_TOP, (
int)page->TimeAlign, 0, page->TimeFont);
1492 if (page->Throttle) {
1493 StabilizationDesiredThrustGet(&tmp);
1495 int throttle = (100 * tmp + 0.5f);
1498 if (throttle < -99) {
1502 sprintf(tmp_str,
"%d", throttle);
1505 page->ThrottleFont);
1509 if (page->VTXFreq && VTXInfoHandle()) {
1511 VTXInfoFrequencyGet(&freq);
1512 if (page->VTXFreqShowUnit) {
1513 sprintf(tmp_str,
"%dMHz", freq);
1523 if (page->VTXPower && VTXInfoHandle()) {
1525 VTXInfoPowerGet(&power);
1526 if (page->VTXPowerShowUnit) {
1527 sprintf(tmp_str,
"%dmW", power);
1530 sprintf(tmp_str,
"%d", power);
1533 page->VTXPowerFont);
1537 #define STATS_LINE_SPACING 11
1538 #define STATS_LINE_Y 40
1539 #define STATS_LINE_X (GRAPHICS_LEFT + 10)
1540 #define STATS_FONT FONT8X10
1545 char tmp_str[100] = { 0 };
1547 FlightStatsData
stats;
1548 FlightStatsGet(&stats);
1594 sprintf(tmp_str,
"Maximum roll rate: %d deg/s", stats.MaxRollRate);
1598 sprintf(tmp_str,
"Maximum pitch rate: %d deg/s", stats.MaxPitchRate);
1602 sprintf(tmp_str,
"Maximum yaw rate: %d deg/s", stats.MaxYawRate);
1607 sprintf(tmp_str,
"Consumed energy: %d mAh", stats.ConsumedEnergy);
1611 sprintf(tmp_str,
"Initial battery voltage: %0.1f V", (
double)stats.InitialBatteryVoltage / 1000.);
1637 if (!onScreenDisplaySemaphore)
1645 #if defined(PIOS_INCLUDE_WDG) && defined(OSD_USE_WDG)
1660 if (OnScreenDisplaySettingsInitialize() == -1) {
1664 OnScreenDisplaySettingsOSDEnabledGet(&osd_state);
1666 if (osd_state == ONSCREENDISPLAYSETTINGS_OSDENABLED_ENABLED) {
1673 ModuleSettingsData module_settings;
1674 ModuleSettingsGet(&module_settings);
1677 has_battery = module_settings.AdminState[MODULESETTINGS_ADMINSTATE_BATTERY];
1680 if (StateEstimationHandle()) {
1682 StateEstimationNavigationFilterGet(&filter);
1683 if (filter != STATEESTIMATION_NAVIGATIONFILTER_NONE) {
1688 if (OnScreenDisplayPageSettingsInitialize() == -1 \
1689 || OnScreenDisplayPageSettings2Initialize() == -1 \
1690 || OnScreenDisplayPageSettings3Initialize() == -1 \
1691 || OnScreenDisplayPageSettings4Initialize() == -1 ) {
1716 if (idx >= MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM) {
1720 float accessories[MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM];
1722 ManualControlCommandAccessoryGet(accessories);
1724 return accessories[idx];
1730 #define BLANK_TIME 2000
1731 #define INTRO_TIME 5500
1734 OnScreenDisplayPageSettingsData osd_page_settings;
1739 uint32_t show_stats_start = 0;
1740 uint32_t show_stats_until = 0;
1742 uint8_t last_arm_status = FLIGHTSTATUS_ARMED_DISARMED;
1743 uint8_t current_page = 0;
1744 uint8_t last_page = -1;
1745 uint8_t page_when_stats_enabled = 0;
1774 case ONSCREENDISPLAYSETTINGS_THREEDMODE_SBS3D:
1802 if (BaroAltitudeHandle()) {
1803 BaroAltitudeAltitudeGet(&tmp);
1820 out_time = in_ticks - out_ticks;
1830 case ONSCREENDISPLAYSETTINGS_THREEDMODE_SBS3D:
1838 if (
osd_settings.Units == ONSCREENDISPLAYSETTINGS_UNITS_IMPERIAL) {
1858 if (video_system_act != video_system_last) {
1868 current_page = (uint8_t)roundf(((accessory + 1.0
f) / 2.0
f) * (
osd_settings.NumPages - 1));
1871 if (current_page != last_page)
1876 switch (current_page) {
1877 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM2:
1878 OnScreenDisplayPageSettings2Get((OnScreenDisplayPageSettings2Data*)&osd_page_settings);
1880 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM3:
1881 OnScreenDisplayPageSettings3Get((OnScreenDisplayPageSettings3Data*)&osd_page_settings);
1883 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM4:
1884 OnScreenDisplayPageSettings4Get((OnScreenDisplayPageSettings4Data*)&osd_page_settings);
1887 OnScreenDisplayPageSettingsGet(&osd_page_settings);
1893 FlightStatusArmedGet(&arm_status);
1894 if (arm_status == FLIGHTSTATUS_ARMED_DISARMED) {
1895 if (last_arm_status != FLIGHTSTATUS_ARMED_DISARMED) {
1897 case ONSCREENDISPLAYSETTINGS_STATSDISPLAYDURATION_OFF:
1898 show_stats_until = 0;
1900 case ONSCREENDISPLAYSETTINGS_STATSDISPLAYDURATION_10S:
1903 case ONSCREENDISPLAYSETTINGS_STATSDISPLAYDURATION_20S:
1906 case ONSCREENDISPLAYSETTINGS_STATSDISPLAYDURATION_30S:
1910 page_when_stats_enabled = current_page;
1913 if (show_stats_until > now) {
1914 if (
frame_counter % 5 == 0 && current_page != page_when_stats_enabled) {
1916 show_stats_until = 0;
1917 }
else if (now >= show_stats_start) {
1918 current_page = ONSCREENDISPLAYSETTINGS_PAGECONFIG_STATISTICS;
1925 switch (current_page) {
1926 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_OFF:
1928 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_STATISTICS:
1931 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_MENU:
1933 if ((arm_status == FLIGHTSTATUS_ARMED_DISARMED) ||
1934 (
osd_settings.DisableMenuWhenArmed == ONSCREENDISPLAYSETTINGS_DISABLEMENUWHENARMED_DISABLED)) {
1940 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM1:
1941 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM2:
1942 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM3:
1943 case ONSCREENDISPLAYSETTINGS_PAGECONFIG_CUSTOM4:
1952 last_page = current_page;
1953 last_arm_status = arm_status;
1954 video_system_last = video_system_act;
1957 in_time = out_ticks - in_ticks;
1959 sprintf(tmp_str,
"%03d %03d", (
int)in_time, (
int)out_time);
volatile bool video_active
#define BLINK_INTERVAL_FRAMES
MODULE_INITCALL(OnScreenDisplayInitialize, OnScreenDisplayStart)
static WaypointData waypoint
uint32_t PIOS_Thread_Systime(void)
void write_string(char *str, int x, int y, int xs, int ys, int va, int ha, int flags, int font)
static const struct Image image_rssi
void draw_polygon(int16_t x, int16_t y, float angle, const point_t *points, uint8_t n_points, int mode, int mmode)
static AccelsData accelsData
void write_vline_lm(int x, int y0, int y1, int lmode, int mmode)
void write_vline_outlined(int x, int y0, int y1, int endcap0, int endcap1, int mode, int mmode)
void PIOS_Video_SetXScale(uint8_t x_scale)
void hud_draw_linear_compass(int v, int home_dir, int range, int width, int x, int y, int mintick_step, int majtick_step, int mintick_len, int majtick_len, __attribute__((unused)) int flags)
int sprintf(char *out, const char *format,...)
void draw_image(uint16_t x, uint16_t y, const struct Image *image)
struct _msp_pid_item pitch
OSD gen module, handles OSD draw. Parts from CL-OSD and SUPEROSD projects.
int Convert_Geodetic_To_MGRS(double Latitude, double Longitude, int Precision, char *MGRS)
bool PIOS_Modules_IsEnabled(enum pios_modules module)
#define GRAPHICS_Y_MIDDLE
void drawBattery(uint16_t x, uint16_t y, uint8_t battery, uint16_t size)
uint8_t PIOS_Board_Revision(void)
enum pios_video_system PIOS_Video_GetSystem(void)
void write_rectangle_outlined(int x, int y, int width, int height, int mode, int mmode)
bool PIOS_WDG_RegisterFlag(uint16_t flag_requested)
Register a module against the watchdog.
struct _msp_pid_item roll
void write_line_outlined_dashed(int x0, int y0, int x1, int y1, __attribute__((unused)) int endcap0, __attribute__((unused)) int endcap1, int mode, int mmode, int dots)
void set_ntsc_pal_settings(enum pios_video_system video_system)
static volatile bool osd_settings_updated
static OnScreenDisplaySettingsData osd_settings
void UAVObjCbSetFlag(const UAVObjEvent *objEv, void *ctx, void *obj, int len)
static const struct Image image_brainfpv
#define sign(x)
This is but one definition of sign(.)
void write_line_outlined(int x0, int y0, int x1, int y1, __attribute__((unused)) int endcap0, __attribute__((unused)) int endcap1, int mode, int mmode)
static const struct Image image_home
void showVideoType(int16_t x, int16_t y)
void introText(int16_t x, int16_t y)
const struct pios_board_info pios_board_info_blob
void draw_map_uav_center(int width_px, int height_px, int width_m, int height_m, bool show_wp, bool show_uav, bool show_tablet)
const char METRIC_SPEED_UNIT[]
static const struct Image image_dronin
#define HUD_VSCALE_FLAG_NO_NEGATIVE
void PIOS_Video_SetYOffset(int8_t)
int32_t OnScreenDisplayInitialize(void)
void calc_text_dimensions(char *str, const struct FontEntry *font, int xs, int ys, struct FontDimensions *dim)
static const struct Image image_gps
void PIOS_Video_Set3DConfig(enum pios_video_3d_mode mode, uint8_t right_eye_x_shift)
uint8_t * draw_buffer_level
struct pios_semaphore * onScreenDisplaySemaphore
static struct pios_thread * taskHandle
static bool module_enabled
static const struct Image image_droninbig
bool PIOS_SENSORS_IsRegistered(enum pios_sensor_type type)
Checks if a sensor type is registered with the PIOS_SENSORS interface.
void introGraphics(int16_t x, int16_t y)
struct pios_semaphore * PIOS_Semaphore_Create(void)
Creates a binary semaphore.
#define BOOT_DISPLAY_TIME_MS
void draw_map_home_center(int width_px, int height_px, int width_m, int height_m, bool show_wp, bool show_home, bool show_tablet)
const char * dist_unit_short
void PIOS_Video_SetXOffset(int8_t)
void PIOS_Video_Init(const struct pios_video_cfg *cfg)
OSD gen module, handles OSD draw. Parts from CL-OSD and SUPEROSD projects.
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
static float get_accessorydesired(int idx)
#define GRAPHICS_X_MIDDLE
const char * dist_unit_long
void draw_alarms(int x, int y, int xs, int ys, int va, int ha, int flags, int font)
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
static volatile bool osd_page_updated
const point_t HOME_ARROW[]
static void onScreenDisplayTask(void *parameters)
const char IMPERIAL_SPEED_UNIT[]
const char * AlarmBootReason(uint8_t reason)
void hud_draw_vertical_scale(int v, int range, int halign, int x, int y, int height, int mintick_step, int majtick_step, int mintick_len, int majtick_len, int boundtick_len, __attribute__((unused)) int max_val, int flags)
uint8_t * draw_buffer_mask
void write_hline_outlined(int x0, int x1, int y, int endcap0, int endcap1, int mode, int mmode)
int32_t OnScreenDisplayStart(void)
void PIOS_Thread_Sleep(uint32_t time_ms)
const char METRIC_DIST_UNIT_SHORT[]
void simple_artificial_horizon(float roll, float pitch, int16_t x, int16_t y, int16_t width, int16_t height, int8_t max_pitch, uint8_t n_pitch_steps, bool show_horizon, OnScreenDisplayPageSettingsCenterMarkOptions center_mark)
void PIOS_Video_SetLevels(uint8_t, uint8_t)
float convert_distance_divider
bool PIOS_Semaphore_Take(struct pios_semaphore *sema, uint32_t timeout_ms)
Takes binary semaphore.
const struct FontEntry * get_font_info(int font)
Includes PiOS and core architecture components.
Include file of the WorldMagModel internal functionality.
uint16_t UAVObjGetNumInstances(UAVObjHandle obj)
void printFWVersion(int16_t x, int16_t y)
#define FONT_OUTLINED8X14
const char IMPERIAL_DIST_UNIT_LONG[]
Generic interface for sensors.
void write_hline_lm(int x0, int x1, int y, int lmode, int mmode)
int32_t AlarmString(SystemAlarmsData *alarm, char *buf, size_t buflen, bool blink, uint8_t *state)
void render_user_page(OnScreenDisplayPageSettingsData *page)
void write_filled_rectangle_lm(int x, int y, int width, int height, int lmode, int mmode)
void draw_flight_mode(int x, int y, int xs, int ys, int va, int ha, int flags, int font)
void lla_to_ned(int32_t lat, int32_t lon, float alt, float *NED)
const char METRIC_DIST_UNIT_LONG[]
void write_pixel_lm(int x, int y, int mmode, int lmode)
const char IMPERIAL_DIST_UNIT_SHORT[]
#define STATS_LINE_SPACING