Refactor sat count checks and GPS trust code
[betaflight.git] / src / test / unit / osd_unittest.cc
blob2f28bd6cfee7f8e3078feab0c6f345967200a07d
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdint.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <string.h>
23 extern "C" {
24 #include "platform.h"
26 #include "build/debug.h"
28 #include "blackbox/blackbox.h"
29 #include "blackbox/blackbox_io.h"
31 #include "common/time.h"
33 #include "config/config.h"
34 #include "config/feature.h"
36 #include "drivers/osd_symbols.h"
37 #include "drivers/persistent.h"
38 #include "drivers/serial.h"
40 #include "fc/core.h"
41 #include "fc/rc_controls.h"
42 #include "fc/rc_modes.h"
43 #include "fc/runtime_config.h"
45 #include "flight/gps_rescue.h"
46 #include "flight/imu.h"
47 #include "flight/mixer.h"
48 #include "flight/pid.h"
50 #include "io/beeper.h"
51 #include "io/gps.h"
53 #include "osd/osd.h"
54 #include "osd/osd_elements.h"
55 #include "osd/osd_warnings.h"
57 #include "pg/pg.h"
58 #include "pg/pg_ids.h"
59 #include "pg/rx.h"
61 #include "sensors/acceleration.h"
62 #include "sensors/battery.h"
64 #include "rx/rx.h"
66 void osdUpdate(timeUs_t currentTimeUs);
67 void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time);
68 int osdConvertTemperatureToSelectedUnit(int tempInDegreesCelcius);
70 uint16_t rssi;
71 attitudeEulerAngles_t attitude;
72 float rMat[3][3];
74 pidProfile_t *currentPidProfile;
75 int16_t debug[DEBUG16_VALUE_COUNT];
76 float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
77 uint8_t GPS_numSat;
78 uint16_t GPS_distanceToHome;
79 int16_t GPS_directionToHome;
80 uint32_t GPS_distanceFlownInCm;
81 int32_t GPS_coord[2];
82 gpsSolutionData_t gpsSol;
83 float motor[8];
85 linkQualitySource_e linkQualitySource;
87 acc_t acc;
89 PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0);
90 PG_REGISTER(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 0);
91 PG_REGISTER(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 0);
92 PG_REGISTER(pilotConfig_t, pilotConfig, PG_PILOT_CONFIG, 0);
93 PG_REGISTER(gpsRescueConfig_t, gpsRescueConfig, PG_GPS_RESCUE, 0);
94 PG_REGISTER(imuConfig_t, imuConfig, PG_IMU_CONFIG, 0);
95 PG_REGISTER(gpsConfig_t, gpsConfig, PG_GPS_CONFIG, 0);
97 timeUs_t simulationTime = 0;
98 batteryState_e simulationBatteryState;
99 uint8_t simulationBatteryCellCount;
100 uint16_t simulationBatteryVoltage;
101 uint32_t simulationBatteryAmperage;
102 uint32_t simulationMahDrawn;
103 int32_t simulationAltitude;
104 int32_t simulationVerticalSpeed;
105 uint16_t simulationCoreTemperature;
106 bool simulationGpsHealthy;
109 uint32_t simulationFeatureFlags = FEATURE_GPS;
111 /* #define DEBUG_OSD */
113 #include "unittest_macros.h"
114 #include "unittest_displayport.h"
115 #include "gtest/gtest.h"
117 void setDefaultSimulationState()
119 memset(osdElementConfigMutable(), 0, sizeof(osdElementConfig_t));
121 osdConfigMutable()->enabled_stats = 0;
122 osdConfigMutable()->framerate_hz = 12;
124 rssi = 1024;
126 simulationBatteryState = BATTERY_OK;
127 simulationBatteryCellCount = 4;
128 simulationBatteryVoltage = 1680;
129 simulationBatteryAmperage = 0;
130 simulationMahDrawn = 0;
131 simulationAltitude = 0;
132 simulationVerticalSpeed = 0;
133 simulationCoreTemperature = 0;
134 simulationGpsHealthy = false;
136 rcData[PITCH] = 1500;
138 osdFlyTime = 0;
140 DISABLE_ARMING_FLAG(ARMED);
143 void osdRefresh()
145 while (osdUpdateCheck(simulationTime, 0)) {
146 osdUpdate(simulationTime);
147 simulationTime += 10;
149 simulationTime += 0.1e6;
152 * Performs a test of the OSD actions on arming.
153 * (reused throughout the test suite)
155 void doTestArm(bool testEmpty = true)
157 // given
158 // craft has been armed
159 ENABLE_ARMING_FLAG(ARMED);
161 simulationTime += 5e6;
162 // when
163 // sufficient OSD updates have been called
164 osdRefresh();
166 // then
167 // arming alert displayed
168 displayPortTestBufferSubstring(12, 7, "ARMED");
170 // given
171 // armed alert times out (0.5 seconds)
172 simulationTime += 0.5e6;
174 // when
175 // sufficient OSD updates have been called
176 osdRefresh();
178 // then
179 // arming alert disappears
180 #ifdef DEBUG_OSD
181 displayPortTestPrint();
182 #endif
183 if (testEmpty) {
184 displayPortTestBufferIsEmpty();
189 * Auxiliary function. Test is there're stats that must be shown
191 bool isSomeStatEnabled(void) {
192 return (osdConfigMutable()->enabled_stats != 0);
196 * Performs a test of the OSD actions on disarming.
197 * (reused throughout the test suite)
199 void doTestDisarm()
201 // given
202 // craft is disarmed after having been armed
203 DISABLE_ARMING_FLAG(ARMED);
205 // when
206 // sufficient OSD updates have been called
207 osdRefresh();
209 // then
210 // post flight statistics displayed
211 if (isSomeStatEnabled()) {
212 unsigned enabledStats = osdConfigMutable()->enabled_stats;
213 unsigned count = 0;
214 while (enabledStats) {
215 count += enabledStats & 1;
216 enabledStats >>= 1;
219 displayPortTestBufferSubstring(2, 7 - count / 2, " --- STATS ---");
223 void setupStats(void)
225 // this set of enabled post flight statistics
226 osdStatSetState(OSD_STAT_MAX_SPEED, true);
227 osdStatSetState(OSD_STAT_MIN_BATTERY, true);
228 osdStatSetState(OSD_STAT_MIN_RSSI, true);
229 osdStatSetState(OSD_STAT_MAX_CURRENT, false);
230 osdStatSetState(OSD_STAT_USED_MAH, false);
231 osdStatSetState(OSD_STAT_MAX_ALTITUDE, true);
232 osdStatSetState(OSD_STAT_BLACKBOX, false);
233 osdStatSetState(OSD_STAT_END_BATTERY, true);
234 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
235 osdStatSetState(OSD_STAT_MAX_DISTANCE, true);
236 osdStatSetState(OSD_STAT_FLIGHT_DISTANCE, true);
237 osdStatSetState(OSD_STAT_BLACKBOX_NUMBER, false);
238 osdStatSetState(OSD_STAT_MAX_G_FORCE, false);
239 osdStatSetState(OSD_STAT_MAX_ESC_TEMP, false);
240 osdStatSetState(OSD_STAT_MAX_ESC_RPM, false);
243 void simulateFlight(void)
245 // these conditions occur during flight
246 rssi = 1024;
247 gpsSol.groundSpeed = 500;
248 GPS_distanceToHome = 20;
249 GPS_distanceFlownInCm = 2000;
250 simulationBatteryVoltage = 1580;
251 simulationAltitude = 100;
252 simulationTime += 1e6;
253 while (osdUpdateCheck(simulationTime, 0)) {
254 osdUpdate(simulationTime);
255 simulationTime += 10;
258 rssi = 512;
259 gpsSol.groundSpeed = 800;
260 GPS_distanceToHome = 50;
261 GPS_distanceFlownInCm = 10000;
262 simulationBatteryVoltage = 1470;
263 simulationAltitude = 150;
264 simulationTime += 1e6;
265 while (osdUpdateCheck(simulationTime, 0)) {
266 osdUpdate(simulationTime);
267 simulationTime += 10;
270 rssi = 256;
271 gpsSol.groundSpeed = 200;
272 GPS_distanceToHome = 100;
273 GPS_distanceFlownInCm = 20000;
274 simulationBatteryVoltage = 1520;
275 simulationAltitude = 200;
276 simulationTime += 1e6;
277 while (osdUpdateCheck(simulationTime, 0)) {
278 osdUpdate(simulationTime);
279 simulationTime += 10;
282 rssi = 256;
283 gpsSol.groundSpeed = 800;
284 GPS_distanceToHome = 100;
285 GPS_distanceFlownInCm = 10000;
286 simulationBatteryVoltage = 1470;
287 simulationAltitude = 200; // converts to 6.56168 feet which rounds to 6.6 in imperial units stats test
288 simulationTime += 1e6;
289 while (osdUpdateCheck(simulationTime, 0)) {
290 osdUpdate(simulationTime);
291 simulationTime += 10;
294 simulationBatteryVoltage = 1520;
295 simulationTime += 1e6;
296 while (osdUpdateCheck(simulationTime, 0)) {
297 osdUpdate(simulationTime);
298 simulationTime += 10;
301 rssi = 256;
302 gpsSol.groundSpeed = 800;
303 GPS_distanceToHome = 1150;
304 GPS_distanceFlownInCm = 1050000;
305 simulationBatteryVoltage = 1470;
306 simulationAltitude = 200;
307 simulationTime += 1e6;
308 while (osdUpdateCheck(simulationTime, 0)) {
309 osdUpdate(simulationTime);
310 simulationTime += 10;
313 simulationBatteryVoltage = 1520;
314 simulationTime += 1e6;
317 class OsdTest : public ::testing::Test
319 protected:
320 static void SetUpTestCase() {
321 displayPortTestInit();
324 virtual void SetUp() {
325 setDefaultSimulationState();
328 virtual void TearDown() {
329 // Clean up the armed state without showing stats at the end of a test
330 osdConfigMutable()->enabled_stats = 0;
332 doTestDisarm();
337 * Tests initialisation of the OSD and the power on splash screen.
339 TEST_F(OsdTest, TestInit)
341 // given
342 // display port is initialised
343 displayPortTestInit();
345 // and
346 // default state values are set
347 setDefaultSimulationState();
349 // and
350 // this battery configuration (used for battery voltage elements)
351 batteryConfigMutable()->vbatmincellvoltage = 330;
352 batteryConfigMutable()->vbatmaxcellvoltage = 430;
354 // when
355 // OSD is initialised
356 osdInit(&testDisplayPort, OSD_DISPLAYPORT_DEVICE_AUTO);
358 osdRefresh();
360 // then
361 // display buffer should contain splash screen
362 displayPortTestBufferSubstring(7, 8, "MENU:THR MID");
363 displayPortTestBufferSubstring(11, 9, "+ YAW LEFT");
364 displayPortTestBufferSubstring(11, 10, "+ PITCH UP");
366 // when
367 // splash screen timeout has elapsed
368 simulationTime += 4e6;
369 osdRefresh();
371 // then
372 // display buffer should be empty
373 #ifdef DEBUG_OSD
374 displayPortTestPrint();
375 #endif
376 displayPortTestBufferIsEmpty();
380 * Tests visibility of the ARMED notification after arming.
382 TEST_F(OsdTest, TestArm)
384 doTestArm();
388 * Tests display and timeout of the post flight statistics screen after disarming.
390 TEST_F(OsdTest, TestDisarm)
392 doTestArm();
394 doTestDisarm();
396 // given
397 // post flight stats times out (60 seconds)
398 simulationTime += 60e6;
400 // when
401 // sufficient OSD updates have been called
402 osdRefresh();
404 // then
405 // post flight stats screen disappears
406 #ifdef DEBUG_OSD
407 displayPortTestPrint();
408 #endif
409 displayPortTestBufferIsEmpty();
413 * Tests disarming and immediately rearming clears post flight stats and shows ARMED notification.
415 TEST_F(OsdTest, TestDisarmWithImmediateRearm)
417 doTestArm();
419 doTestDisarm();
421 doTestArm();
425 * Tests dismissing the statistics screen with pitch stick after disarming.
427 TEST_F(OsdTest, TestDisarmWithDismissStats)
429 doTestArm();
431 doTestDisarm();
433 // given
434 // sticks have been moved
435 rcData[PITCH] = 1800;
437 // when
438 // sufficient OSD updates have been called
439 osdRefresh();
441 // then
442 // post flight stats screen disappears
443 #ifdef DEBUG_OSD
444 displayPortTestPrint();
445 #endif
446 displayPortTestBufferIsEmpty();
450 * Tests the calculation of timing in statistics
452 TEST_F(OsdTest, TestStatsTiming)
454 // given
455 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
456 osdStatSetState(OSD_STAT_TIMER_1, true);
457 osdStatSetState(OSD_STAT_TIMER_2, true);
459 // and
460 // this timer 1 configuration
461 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0);
463 // and
464 // this timer 2 configuration
465 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0);
467 // and
468 // this RTC time
469 dateTime_t dateTime;
470 dateTime.year = 2017;
471 dateTime.month = 11;
472 dateTime.day = 19;
473 dateTime.hours = 10;
474 dateTime.minutes = 12;
475 dateTime.seconds = 0;
476 dateTime.millis = 0;
477 rtcSetDateTime(&dateTime);
479 // when
480 // the craft is armed
481 doTestArm();
483 // and
484 // these conditions occur during flight
485 simulationTime += 1e6;
486 osdRefresh();
488 // and
489 // the craft is disarmed
490 doTestDisarm();
492 // and
493 // the craft is armed again
494 doTestArm();
496 // and
497 // these conditions occur during flight
498 simulationTime += 1e6;
499 osdRefresh();
501 // and
502 // the craft is disarmed
503 doTestDisarm();
505 // then
506 // statistics screen should display the following
507 int row = 7;
508 displayPortTestBufferSubstring(2, row++, "2017-11-19 10:12:");
509 displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:13.61");
510 displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:01");
514 * Tests the calculation of statistics with imperial unit output.
516 TEST_F(OsdTest, TestStatsImperial)
518 // given
519 setupStats();
521 // and
522 // using imperial unit system
523 osdConfigMutable()->units = UNIT_IMPERIAL;
525 // and
526 // a GPS fix is present
527 stateFlags |= GPS_FIX | GPS_FIX_HOME;
529 // when
530 // the craft is armed
531 doTestArm();
533 // and
534 simulateFlight();
536 // and
537 // the craft is disarmed
538 doTestDisarm();
540 // then
541 // statistics screen should display the following
542 int row = 5;
543 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.6%c", SYM_FT);
544 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 17");
545 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 3772%c", SYM_FT);
546 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 6.52%c", SYM_MILES);
547 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
548 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
549 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
553 * Tests the calculation of statistics with metric unit output.
554 * (essentially an abridged version of the previous test
556 TEST_F(OsdTest, TestStatsMetric)
558 // given
559 setupStats();
561 // and
562 // using metric unit system
563 osdConfigMutable()->units = UNIT_METRIC;
565 // when
566 // the craft is armed
567 doTestArm();
569 // and
570 simulateFlight();
572 // and
573 // the craft is disarmed
574 doTestDisarm();
576 // then
577 // statistics screen should display the following
578 int row = 5;
579 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
580 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
581 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
582 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
583 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
584 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
585 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
589 * Tests the calculation of statistics with metric unit output.
590 * (essentially an abridged version of the previous test
592 TEST_F(OsdTest, TestStatsMetricDistanceUnits)
594 // given
595 setupStats();
597 // and
598 // using metric unit system
599 osdConfigMutable()->units = UNIT_METRIC;
601 // when
602 // the craft is armed
603 doTestArm();
605 // and
606 simulateFlight();
608 // and
609 // the craft is disarmed
610 doTestDisarm();
612 // then
613 // statistics screen should display the following
614 int row = 5;
615 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
616 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
617 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
618 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
619 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
620 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
621 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
625 * Tests activation of alarms and element flashing.
627 TEST_F(OsdTest, TestAlarms)
629 // given
630 sensorsSet(SENSOR_GPS);
632 // and
633 // the following OSD elements are visible
634 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
635 osdElementConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | OSD_PROFILE_1_FLAG;
636 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | OSD_PROFILE_1_FLAG;
637 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | OSD_PROFILE_1_FLAG;
638 osdElementConfigMutable()->item_pos[OSD_REMAINING_TIME_ESTIMATE] = OSD_POS(1, 2) | OSD_PROFILE_1_FLAG;
639 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
641 // and
642 // this set of alarm values
643 osdConfigMutable()->rssi_alarm = 20;
644 osdConfigMutable()->cap_alarm = 2200;
645 osdConfigMutable()->alt_alarm = 100; // meters
647 osdAnalyzeActiveElements();
649 // and
650 // this timer 1 configuration
651 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 5);
652 EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]));
653 EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1]));
654 EXPECT_EQ(5, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1]));
656 // and
657 // this timer 2 configuration
658 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 2);
659 EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]));
660 EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2]));
661 EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2]));
663 // and
664 // using the metric unit system
665 osdConfigMutable()->units = UNIT_METRIC;
667 // when
668 // time is passing by
669 simulationTime += 60e6;
670 osdRefresh();
672 // and
673 // the craft is armed
674 doTestArm(false);
676 simulationTime += 70e6;
677 osdRefresh();
679 // then
680 // no elements should flash as all values are out of alarm range
681 for (int i = 0; i < 30; i++) {
682 // Check for visibility every 100ms, elements should always be visible
683 simulationTime += 0.1e6;
684 osdRefresh();
686 #ifdef DEBUG_OSD
687 printf("%d\n", i);
688 #endif
689 displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer
690 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
691 displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT);
692 displayPortTestBufferSubstring(20, 1, "%c04:", SYM_ON_M); // only test the minute part of the timer
693 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
696 // when
697 // all values are out of range
698 rssi = 128;
699 simulationBatteryState = BATTERY_CRITICAL;
700 simulationBatteryVoltage = 1350;
701 simulationAltitude = 12000;
702 simulationMahDrawn = 999999;
704 simulationTime += 60e6;
705 osdRefresh();
707 // then
708 // elements showing values in alarm range should flash
709 simulationTime += 1000000;
710 simulationTime -= simulationTime % 1000000;
711 timeUs_t startTime = simulationTime;
712 for (int i = 0; i < 15; i++) {
713 // Blinking should happen at 2Hz
714 simulationTime = startTime + i*0.25e6;
715 osdRefresh();
717 #ifdef DEBUG_OSD
718 printf("%d\n", i);
719 displayPortTestPrint();
720 #endif
721 if (i % 2 == 1) {
722 displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI);
723 displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT);
724 displayPortTestBufferSubstring(1, 1, "%c02:", SYM_FLY_M); // only test the minute part of the timer
725 displayPortTestBufferSubstring(20, 1, "%c05:", SYM_ON_M); // only test the minute part of the timer
726 displayPortTestBufferSubstring(23, 7, "%c120.0%c", SYM_ALTITUDE, SYM_M);
727 } else {
728 displayPortTestBufferIsEmpty();
734 * Tests the RSSI OSD element.
736 TEST_F(OsdTest, TestElementRssi)
738 // given
739 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
740 osdConfigMutable()->rssi_alarm = 0;
742 osdAnalyzeActiveElements();
744 // when
745 rssi = 1024;
746 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
747 osdRefresh();
749 // then
750 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
752 // when
753 rssi = 0;
754 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
755 osdRefresh();
757 // then
758 displayPortTestBufferSubstring(8, 1, "%c 0", SYM_RSSI);
760 // when
761 rssi = 512;
762 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
763 osdRefresh();
765 // then
766 displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI);
770 * Tests the instantaneous battery current OSD element.
772 TEST_F(OsdTest, TestElementAmperage)
774 // given
775 osdElementConfigMutable()->item_pos[OSD_CURRENT_DRAW] = OSD_POS(1, 12) | OSD_PROFILE_1_FLAG;
777 osdAnalyzeActiveElements();
779 // when
780 simulationBatteryAmperage = 0;
781 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
782 osdRefresh();
784 // then
785 displayPortTestBufferSubstring(1, 12, " 0.00%c", SYM_AMP);
787 // when
788 simulationBatteryAmperage = 2156;
789 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
790 osdRefresh();
792 // then
793 displayPortTestBufferSubstring(1, 12, " 21.56%c", SYM_AMP);
795 // when
796 simulationBatteryAmperage = 12345;
797 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
798 osdRefresh();
800 // then
801 displayPortTestBufferSubstring(1, 12, "123.45%c", SYM_AMP);
805 * Tests the battery capacity drawn OSD element.
807 TEST_F(OsdTest, TestElementMahDrawn)
809 // given
810 osdElementConfigMutable()->item_pos[OSD_MAH_DRAWN] = OSD_POS(1, 11) | OSD_PROFILE_1_FLAG;
812 osdAnalyzeActiveElements();
814 // when
815 simulationMahDrawn = 0;
816 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
817 osdRefresh();
819 // then
820 displayPortTestBufferSubstring(1, 11, " 0%c", SYM_MAH);
822 // when
823 simulationMahDrawn = 4;
824 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
825 osdRefresh();
827 // then
828 displayPortTestBufferSubstring(1, 11, " 4%c", SYM_MAH);
830 // when
831 simulationMahDrawn = 15;
832 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
833 osdRefresh();
835 // then
836 displayPortTestBufferSubstring(1, 11, " 15%c", SYM_MAH);
838 // when
839 simulationMahDrawn = 246;
840 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
841 osdRefresh();
843 // then
844 displayPortTestBufferSubstring(1, 11, " 246%c", SYM_MAH);
846 // when
847 simulationMahDrawn = 1042;
848 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
849 osdRefresh();
851 // then
852 displayPortTestBufferSubstring(1, 11, "1042%c", SYM_MAH);
856 * Tests the instantaneous electrical power OSD element.
858 TEST_F(OsdTest, TestElementPower)
860 // given
861 osdElementConfigMutable()->item_pos[OSD_POWER] = OSD_POS(1, 10) | OSD_PROFILE_1_FLAG;
863 osdAnalyzeActiveElements();
865 // and
866 simulationBatteryVoltage = 1000; // 10V
868 // and
869 simulationBatteryAmperage = 0; // 0A
871 // when
872 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
873 osdRefresh();
875 // then
876 displayPortTestBufferSubstring(1, 10, " 0W");
878 // given
879 simulationBatteryAmperage = 10; // 0.1A
881 // when
882 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
883 osdRefresh();
885 // then
886 displayPortTestBufferSubstring(1, 10, " 1W");
888 // given
889 simulationBatteryAmperage = 120; // 1.2A
891 // when
892 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
893 osdRefresh();
895 // then
896 displayPortTestBufferSubstring(1, 10, " 12W");
898 // given
899 simulationBatteryAmperage = 1230; // 12.3A
901 // when
902 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
903 osdRefresh();
905 // then
906 displayPortTestBufferSubstring(1, 10, " 123W");
908 // given
909 simulationBatteryAmperage = 12340; // 123.4A
911 // when
912 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
913 osdRefresh();
915 // then
916 displayPortTestBufferSubstring(1, 10, "1234W");
920 * Tests the altitude OSD element.
922 TEST_F(OsdTest, TestElementAltitude)
924 // given
925 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
927 osdAnalyzeActiveElements();
929 // and
930 osdConfigMutable()->units = UNIT_METRIC;
931 sensorsClear(SENSOR_GPS);
933 // when
934 simulationAltitude = 0;
935 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
936 osdRefresh();
938 // then
939 displayPortTestBufferSubstring(23, 7, "%c-", SYM_ALTITUDE);
941 // when
942 sensorsSet(SENSOR_GPS);
943 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
944 osdRefresh();
946 // then
947 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
949 // when
950 simulationAltitude = 247; // rounds to 2.5m
951 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
952 osdRefresh();
954 // then
955 displayPortTestBufferSubstring(23, 7, "%c2.5%c", SYM_ALTITUDE, SYM_M);
957 // when
958 simulationAltitude = 4247; // rounds to 42.5m
959 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
960 osdRefresh();
962 // then
963 displayPortTestBufferSubstring(23, 7, "%c42.5%c", SYM_ALTITUDE, SYM_M);
965 // when
966 simulationAltitude = -247; // rounds to -2.5m
967 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
968 osdRefresh();
970 // then
971 displayPortTestBufferSubstring(23, 7, "%c-2.5%c", SYM_ALTITUDE, SYM_M);
973 // when
974 simulationAltitude = -70;
975 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
976 osdRefresh();
978 // then
979 displayPortTestBufferSubstring(23, 7, "%c-0.7%c", SYM_ALTITUDE, SYM_M);
984 * Tests the core temperature OSD element.
986 TEST_F(OsdTest, TestElementCoreTemperature)
988 // given
989 osdElementConfigMutable()->item_pos[OSD_CORE_TEMPERATURE] = OSD_POS(1, 8) | OSD_PROFILE_1_FLAG;
991 osdAnalyzeActiveElements();
993 // and
994 osdConfigMutable()->units = UNIT_METRIC;
996 // and
997 simulationCoreTemperature = 0;
999 // when
1000 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1001 osdRefresh();
1003 // then
1004 displayPortTestBufferSubstring(1, 8, "C%c 0%c", SYM_TEMPERATURE, SYM_C);
1006 // given
1007 simulationCoreTemperature = 33;
1009 // when
1010 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1011 osdRefresh();
1013 // then
1014 displayPortTestBufferSubstring(1, 8, "C%c 33%c", SYM_TEMPERATURE, SYM_C);
1016 // given
1017 osdConfigMutable()->units = UNIT_IMPERIAL;
1019 // when
1020 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1021 osdRefresh();
1023 // then
1024 displayPortTestBufferSubstring(1, 8, "C%c 91%c", SYM_TEMPERATURE, SYM_F);
1028 * Tests the battery notifications shown on the warnings OSD element.
1030 TEST_F(OsdTest, TestElementWarningsBattery)
1032 // given
1033 osdElementConfigMutable()->item_pos[OSD_WARNINGS] = OSD_POS(9, 10) | OSD_PROFILE_1_FLAG;
1034 osdConfigMutable()->enabledWarnings = 0; // disable all warnings
1035 osdWarnSetState(OSD_WARNING_BATTERY_WARNING, true);
1036 osdWarnSetState(OSD_WARNING_BATTERY_CRITICAL, true);
1037 osdWarnSetState(OSD_WARNING_BATTERY_NOT_FULL, true);
1039 osdAnalyzeActiveElements();
1041 // and
1042 batteryConfigMutable()->vbatfullcellvoltage = 410;
1044 // and
1045 // 4S battery
1046 simulationBatteryCellCount = 4;
1048 // and
1049 // used battery
1050 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount) - 1;
1051 simulationBatteryState = BATTERY_OK;
1053 // when
1054 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1055 // Delay as the warnings are flashing
1056 simulationTime += 1000000;
1057 simulationTime -= simulationTime % 1000000;
1058 osdRefresh();
1060 // then
1061 displayPortTestBufferSubstring(9, 10, "BATT < FULL");
1063 // given
1064 // full battery
1065 simulationBatteryVoltage = 1680;
1066 simulationBatteryState = BATTERY_OK;
1068 // when
1069 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1070 osdRefresh();
1072 // then
1073 displayPortTestBufferSubstring(9, 10, " ");
1075 // given
1076 // low battery
1077 simulationBatteryVoltage = 1400;
1078 simulationBatteryState = BATTERY_WARNING;
1080 // when
1081 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1082 // Delay as the warnings are flashing
1083 simulationTime += 1000000;
1084 simulationTime -= simulationTime % 1000000;
1085 simulationTime += 0.25e6;
1086 osdRefresh();
1088 // then
1089 displayPortTestBufferSubstring(9, 10, "LOW BATTERY ");
1091 // given
1092 // critical battery
1093 simulationBatteryVoltage = 1320;
1094 simulationBatteryState = BATTERY_CRITICAL;
1096 // when
1097 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1098 // Delay as the warnings are flashing
1099 simulationTime += 1000000;
1100 simulationTime -= simulationTime % 1000000;
1101 simulationTime += 0.25e6;
1102 osdRefresh();
1104 // then
1105 displayPortTestBufferSubstring(9, 10, " LAND NOW ");
1107 // given
1108 // full battery
1109 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount);
1110 simulationBatteryState = BATTERY_OK;
1112 // when
1113 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1114 osdRefresh();
1116 // then
1117 displayPortTestBufferSubstring(9, 10, " ");
1119 // TODO
1123 * Tests the time string formatting function with a series of precision settings and time values.
1125 TEST_F(OsdTest, TestFormatTimeString)
1127 char buff[OSD_ELEMENT_BUFFER_LENGTH];
1129 /* Seconds precision, 0 us */
1130 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0);
1131 EXPECT_EQ(0, strcmp("00:00", buff));
1133 /* Seconds precision, 0.9 seconds */
1134 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6);
1135 EXPECT_EQ(0, strcmp("00:00", buff));
1137 /* Seconds precision, 10 seconds */
1138 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6);
1139 EXPECT_EQ(0, strcmp("00:10", buff));
1141 /* Seconds precision, 1 minute */
1142 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6);
1143 EXPECT_EQ(0, strcmp("01:00", buff));
1145 /* Seconds precision, 1 minute 59 seconds */
1146 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6);
1147 EXPECT_EQ(0, strcmp("01:59", buff));
1149 /* Hundredths precision, 0 us */
1150 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0);
1151 EXPECT_EQ(0, strcmp("00:00.00", buff));
1153 /* Hundredths precision, 10 milliseconds (one 100th of a second) */
1154 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3);
1155 EXPECT_EQ(0, strcmp("00:00.01", buff));
1157 /* Hundredths precision, 0.9 seconds */
1158 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6);
1159 EXPECT_EQ(0, strcmp("00:00.90", buff));
1161 /* Hundredths precision, 10 seconds */
1162 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6);
1163 EXPECT_EQ(0, strcmp("00:10.00", buff));
1165 /* Hundredths precision, 1 minute */
1166 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6);
1167 EXPECT_EQ(0, strcmp("01:00.00", buff));
1169 /* Hundredths precision, 1 minute 59 seconds */
1170 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6);
1171 EXPECT_EQ(0, strcmp("01:59.00", buff));
1174 TEST_F(OsdTest, TestConvertTemperatureUnits)
1176 /* In Celsius */
1177 osdConfigMutable()->units = UNIT_METRIC;
1178 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 40);
1180 /* In Fahrenheit */
1181 osdConfigMutable()->units = UNIT_IMPERIAL;
1182 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 104);
1184 /* In Fahrenheit with rounding */
1185 osdConfigMutable()->units = UNIT_IMPERIAL;
1186 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(41), 106);
1189 TEST_F(OsdTest, TestGpsElements)
1191 // given
1192 osdElementConfigMutable()->item_pos[OSD_GPS_SATS] = OSD_POS(2, 4) | OSD_PROFILE_1_FLAG;
1193 gpsConfigMutable()->gpsMinimumSats = GPS_MINIMUM_SAT_COUNT;
1194 gpsConfigMutable()->gpsRequiredSats = GPS_REQUIRED_SAT_COUNT;
1196 sensorsSet(SENSOR_GPS);
1197 osdAnalyzeActiveElements();
1199 // when
1200 simulationGpsHealthy = false;
1201 gpsSol.numSat = 0;
1203 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1204 osdRefresh();
1206 // then
1207 // Sat indicator should blink and show "NC"
1208 simulationTime += 1000000;
1209 simulationTime -= simulationTime % 1000000;
1210 timeUs_t startTime = simulationTime;
1211 for (int i = 0; i < 15; i++) {
1212 // Blinking should happen at 2Hz
1213 simulationTime = startTime + i*0.25e6;
1214 osdRefresh();
1216 if (i % 2 == 1) {
1217 displayPortTestBufferSubstring(2, 4, "%c%cNC", SYM_SAT_L, SYM_SAT_R);
1218 } else {
1219 displayPortTestBufferIsEmpty();
1223 // when
1224 simulationGpsHealthy = true;
1225 gpsSol.numSat = 0;
1227 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1228 osdRefresh();
1230 // then
1231 // Sat indicator should blink and show "0"
1232 simulationTime += 1000000;
1233 simulationTime -= simulationTime % 1000000;
1234 startTime = simulationTime;
1235 for (int i = 0; i < 15; i++) {
1236 // Blinking should happen at 2Hz
1237 simulationTime = startTime + i*0.25e6;
1238 osdRefresh();
1240 if (i % 2 == 1) {
1241 displayPortTestBufferSubstring(2, 4, "%c%c 0", SYM_SAT_L, SYM_SAT_R);
1242 } else {
1243 displayPortTestBufferIsEmpty();
1247 // when
1248 simulationGpsHealthy = true;
1249 gpsSol.numSat = 10;
1251 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1252 osdRefresh();
1254 // then
1255 // Sat indicator should show "10" without flashing
1256 for (int i = 0; i < 15; i++) {
1257 // Blinking should happen at 2Hz
1258 simulationTime += 0.2e6;
1259 osdRefresh();
1261 displayPortTestBufferSubstring(2, 4, "%c%c10", SYM_SAT_L, SYM_SAT_R);
1265 // STUBS
1266 extern "C" {
1267 bool featureIsEnabled(uint32_t f) { return simulationFeatureFlags & f; }
1269 void beeperConfirmationBeeps(uint8_t) {}
1271 bool isModeActivationConditionPresent(boxId_e) {
1272 return false;
1275 bool IS_RC_MODE_ACTIVE(boxId_e) {
1276 return false;
1279 uint32_t micros() {
1280 return simulationTime;
1283 uint32_t millis() {
1284 return micros() / 1000;
1287 bool isBeeperOn() {
1288 return false;
1291 bool airmodeIsEnabled() {
1292 return false;
1295 uint8_t getCurrentPidProfileIndex() {
1296 return 0;
1299 uint8_t getCurrentControlRateProfileIndex() {
1300 return 0;
1303 batteryState_e getBatteryState() {
1304 return simulationBatteryState;
1307 uint8_t getBatteryCellCount() {
1308 return simulationBatteryCellCount;
1311 uint16_t getBatteryVoltage() {
1312 return simulationBatteryVoltage;
1315 uint16_t getBatteryAverageCellVoltage() {
1316 return simulationBatteryVoltage / simulationBatteryCellCount;
1319 int32_t getAmperage() {
1320 return simulationBatteryAmperage;
1323 int32_t getMAhDrawn() {
1324 return simulationMahDrawn;
1327 int32_t getEstimatedAltitudeCm() {
1328 return simulationAltitude;
1331 int32_t getEstimatedVario() {
1332 return simulationVerticalSpeed;
1335 int32_t blackboxGetLogNumber() {
1336 return 0;
1339 bool isBlackboxDeviceWorking() {
1340 return true;
1343 bool isBlackboxDeviceFull() {
1344 return false;
1347 bool isSerialTransmitBufferEmpty(const serialPort_t *) {
1348 return false;
1351 void serialWrite(serialPort_t *, uint8_t) {}
1353 bool cmsDisplayPortRegister(displayPort_t *) {
1354 return false;
1357 uint16_t getRssi(void) { return rssi; }
1359 uint8_t getRssiPercent(void) { return scaleRange(rssi, 0, RSSI_MAX_VALUE, 0, 100); }
1361 uint16_t rxGetLinkQuality(void) { return LINK_QUALITY_MAX_VALUE; }
1363 uint16_t getCoreTemperatureCelsius(void) { return simulationCoreTemperature; }
1365 bool isFlipOverAfterCrashActive(void) { return false; }
1367 float pidItermAccelerator(void) { return 1.0; }
1368 uint8_t getMotorCount(void){ return 4; }
1369 bool areMotorsRunning(void){ return true; }
1370 bool pidOsdAntiGravityActive(void) { return false; }
1371 bool failsafeIsActive(void) { return false; }
1372 bool gpsRescueIsConfigured(void) { return false; }
1373 bool gpsIsHealthy(void) { return simulationGpsHealthy; }
1374 int8_t calculateThrottlePercent(void) { return 0; }
1375 uint32_t persistentObjectRead(persistentObjectId_e) { return 0; }
1376 void persistentObjectWrite(persistentObjectId_e, uint32_t) {}
1377 bool isUpright(void) { return true; }
1378 float getMotorOutputLow(void) { return 1000.0; }
1379 float getMotorOutputHigh(void) { return 2047.0; }
1380 void schedulerIgnoreTaskStateTime(void) { }
1381 void schedulerIgnoreTaskExecRate(void) { }
1382 void schedulerIgnoreTaskExecTime(void) { }
1383 bool schedulerGetIgnoreTaskExecTime() { return false; }
1384 void schedulerSetNextStateTime(timeDelta_t) {}