Implement Stopwatch (#12623)
[betaflight.git] / src / test / unit / osd_unittest.cc
blobd23b29543982360ff1af80726c93604d84bb8055
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/gps_rescue.h"
58 #include "pg/pg.h"
59 #include "pg/pg_ids.h"
60 #include "pg/rx.h"
62 #include "sensors/acceleration.h"
63 #include "sensors/battery.h"
65 #include "rx/rx.h"
67 void osdUpdate(timeUs_t currentTimeUs);
68 void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time);
69 int osdConvertTemperatureToSelectedUnit(int tempInDegreesCelcius);
71 uint16_t rssi;
72 attitudeEulerAngles_t attitude;
73 float rMat[3][3];
75 pidProfile_t *currentPidProfile;
76 int16_t debug[DEBUG16_VALUE_COUNT];
77 float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
78 uint8_t GPS_numSat;
79 uint16_t GPS_distanceToHome;
80 int16_t GPS_directionToHome;
81 uint32_t GPS_distanceFlownInCm;
82 int32_t GPS_coord[2];
83 gpsSolutionData_t gpsSol;
84 float motor[8];
86 linkQualitySource_e linkQualitySource;
88 acc_t acc;
90 PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0);
91 PG_REGISTER(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 0);
92 PG_REGISTER(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 0);
93 PG_REGISTER(pilotConfig_t, pilotConfig, PG_PILOT_CONFIG, 0);
94 PG_REGISTER(gpsRescueConfig_t, gpsRescueConfig, PG_GPS_RESCUE, 0);
95 PG_REGISTER(imuConfig_t, imuConfig, PG_IMU_CONFIG, 0);
96 PG_REGISTER(gpsConfig_t, gpsConfig, PG_GPS_CONFIG, 0);
98 timeUs_t simulationTime = 0;
99 batteryState_e simulationBatteryState;
100 uint8_t simulationBatteryCellCount;
101 uint16_t simulationBatteryVoltage;
102 uint32_t simulationBatteryAmperage;
103 uint32_t simulationMahDrawn;
104 float simulationWhDrawn;
105 int32_t simulationAltitude;
106 int32_t simulationVerticalSpeed;
107 uint16_t simulationCoreTemperature;
108 bool simulationGpsHealthy;
111 uint32_t simulationFeatureFlags = FEATURE_GPS;
113 /* #define DEBUG_OSD */
115 #include "unittest_macros.h"
116 #include "unittest_displayport.h"
117 #include "gtest/gtest.h"
119 void setDefaultSimulationState()
121 memset(osdElementConfigMutable(), 0, sizeof(osdElementConfig_t));
123 osdConfigMutable()->enabled_stats = 0;
124 osdConfigMutable()->framerate_hz = 12;
126 rssi = 1024;
128 simulationBatteryState = BATTERY_OK;
129 simulationBatteryCellCount = 4;
130 simulationBatteryVoltage = 1680;
131 simulationBatteryAmperage = 0;
132 simulationMahDrawn = 0;
133 simulationWhDrawn = 0;
134 simulationAltitude = 0;
135 simulationVerticalSpeed = 0;
136 simulationCoreTemperature = 0;
137 simulationGpsHealthy = false;
139 rcData[PITCH] = 1500;
141 osdFlyTime = 0;
143 DISABLE_ARMING_FLAG(ARMED);
146 void osdRefresh()
148 while (osdUpdateCheck(simulationTime, 0)) {
149 osdUpdate(simulationTime);
150 simulationTime += 10;
152 simulationTime += 0.1e6;
155 * Performs a test of the OSD actions on arming.
156 * (reused throughout the test suite)
158 void doTestArm(bool testEmpty = true)
160 // given
161 // craft has been armed
162 ENABLE_ARMING_FLAG(ARMED);
164 simulationTime += 5e6;
165 // when
166 // sufficient OSD updates have been called
167 osdRefresh();
169 // then
170 // arming alert displayed
171 displayPortTestBufferSubstring(13, 8, "ARMED");
173 // given
174 // armed alert times out (0.5 seconds)
175 simulationTime += 0.5e6;
177 // when
178 // sufficient OSD updates have been called
179 osdRefresh();
181 // then
182 // arming alert disappears
183 #ifdef DEBUG_OSD
184 displayPortTestPrint();
185 #endif
186 if (testEmpty) {
187 displayPortTestBufferIsEmpty();
192 * Auxiliary function. Test is there're stats that must be shown
194 bool isSomeStatEnabled(void)
196 return (osdConfigMutable()->enabled_stats != 0);
200 * Performs a test of the OSD actions on disarming.
201 * (reused throughout the test suite)
203 void doTestDisarm()
205 // given
206 // craft is disarmed after having been armed
207 DISABLE_ARMING_FLAG(ARMED);
209 // when
210 // sufficient OSD updates have been called
211 osdRefresh();
213 // then
214 // post flight statistics displayed
215 if (isSomeStatEnabled()) {
216 unsigned enabledStats = osdConfigMutable()->enabled_stats;
217 unsigned count = 0;
218 while (enabledStats) {
219 count += enabledStats & 1;
220 enabledStats >>= 1;
223 displayPortTestBufferSubstring(9, 7 - count / 2, "--- STATS ---");
227 void setupStats(void)
229 // this set of enabled post flight statistics
230 osdStatSetState(OSD_STAT_MAX_SPEED, true);
231 osdStatSetState(OSD_STAT_MIN_BATTERY, true);
232 osdStatSetState(OSD_STAT_MIN_RSSI, true);
233 osdStatSetState(OSD_STAT_MAX_CURRENT, false);
234 osdStatSetState(OSD_STAT_USED_MAH, false);
235 osdStatSetState(OSD_STAT_MAX_ALTITUDE, true);
236 osdStatSetState(OSD_STAT_BLACKBOX, false);
237 osdStatSetState(OSD_STAT_END_BATTERY, true);
238 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
239 osdStatSetState(OSD_STAT_MAX_DISTANCE, true);
240 osdStatSetState(OSD_STAT_FLIGHT_DISTANCE, true);
241 osdStatSetState(OSD_STAT_BLACKBOX_NUMBER, false);
242 osdStatSetState(OSD_STAT_MAX_G_FORCE, false);
243 osdStatSetState(OSD_STAT_MAX_ESC_TEMP, false);
244 osdStatSetState(OSD_STAT_MAX_ESC_RPM, false);
247 void simulateFlight(void)
249 // these conditions occur during flight
250 rssi = 1024;
251 gpsSol.groundSpeed = 500;
252 GPS_distanceToHome = 20;
253 GPS_distanceFlownInCm = 2000;
254 simulationBatteryVoltage = 1580;
255 simulationAltitude = 100;
256 simulationTime += 1e6;
257 while (osdUpdateCheck(simulationTime, 0)) {
258 osdUpdate(simulationTime);
259 simulationTime += 10;
262 rssi = 512;
263 gpsSol.groundSpeed = 800;
264 GPS_distanceToHome = 50;
265 GPS_distanceFlownInCm = 10000;
266 simulationBatteryVoltage = 1470;
267 simulationAltitude = 150;
268 simulationTime += 1e6;
269 while (osdUpdateCheck(simulationTime, 0)) {
270 osdUpdate(simulationTime);
271 simulationTime += 10;
274 rssi = 256;
275 gpsSol.groundSpeed = 200;
276 GPS_distanceToHome = 100;
277 GPS_distanceFlownInCm = 20000;
278 simulationBatteryVoltage = 1520;
279 simulationAltitude = 200;
280 simulationTime += 1e6;
281 while (osdUpdateCheck(simulationTime, 0)) {
282 osdUpdate(simulationTime);
283 simulationTime += 10;
286 rssi = 256;
287 gpsSol.groundSpeed = 800;
288 GPS_distanceToHome = 100;
289 GPS_distanceFlownInCm = 10000;
290 simulationBatteryVoltage = 1470;
291 simulationAltitude = 200; // converts to 6.56168 feet which rounds to 6.6 in imperial units stats test
292 simulationTime += 1e6;
293 while (osdUpdateCheck(simulationTime, 0)) {
294 osdUpdate(simulationTime);
295 simulationTime += 10;
298 simulationBatteryVoltage = 1520;
299 simulationTime += 1e6;
300 while (osdUpdateCheck(simulationTime, 0)) {
301 osdUpdate(simulationTime);
302 simulationTime += 10;
305 rssi = 256;
306 gpsSol.groundSpeed = 800;
307 GPS_distanceToHome = 1150;
308 GPS_distanceFlownInCm = 1050000;
309 simulationBatteryVoltage = 1470;
310 simulationAltitude = 200;
311 simulationTime += 1e6;
312 while (osdUpdateCheck(simulationTime, 0)) {
313 osdUpdate(simulationTime);
314 simulationTime += 10;
317 simulationBatteryVoltage = 1520;
318 simulationTime += 1e6;
321 class OsdTest : public ::testing::Test
323 protected:
324 static void SetUpTestCase() {
325 displayPortTestInit();
328 virtual void SetUp() {
329 setDefaultSimulationState();
332 virtual void TearDown() {
333 // Clean up the armed state without showing stats at the end of a test
334 osdConfigMutable()->enabled_stats = 0;
336 doTestDisarm();
341 * Tests initialisation of the OSD and the power on splash screen.
343 TEST_F(OsdTest, TestInit)
345 // given
346 // display port is initialised
347 displayPortTestInit();
349 // and
350 // default state values are set
351 setDefaultSimulationState();
353 // and
354 // this battery configuration (used for battery voltage elements)
355 batteryConfigMutable()->vbatmincellvoltage = 330;
356 batteryConfigMutable()->vbatmaxcellvoltage = 430;
358 // when
359 // OSD is initialised
360 osdInit(&testDisplayPort, OSD_DISPLAYPORT_DEVICE_AUTO);
362 osdRefresh();
364 // then
365 // display buffer should contain splash screen
366 displayPortTestBufferSubstring(7, 10, "MENU:THR MID");
367 displayPortTestBufferSubstring(11, 11, "+ YAW LEFT");
368 displayPortTestBufferSubstring(11, 12, "+ PITCH UP");
370 // when
371 // splash screen timeout has elapsed
372 simulationTime += 4e6;
373 osdRefresh();
375 // then
376 // display buffer should be empty
377 #ifdef DEBUG_OSD
378 displayPortTestPrint();
379 #endif
380 displayPortTestBufferIsEmpty();
384 * Tests visibility of the ARMED notification after arming.
386 TEST_F(OsdTest, TestArm)
388 doTestArm();
392 * Tests display and timeout of the post flight statistics screen after disarming.
394 TEST_F(OsdTest, TestDisarm)
396 doTestArm();
398 doTestDisarm();
400 // given
401 // post flight stats times out (60 seconds)
402 simulationTime += 60e6;
404 // when
405 // sufficient OSD updates have been called
406 osdRefresh();
408 // then
409 // post flight stats screen disappears
410 #ifdef DEBUG_OSD
411 displayPortTestPrint();
412 #endif
413 displayPortTestBufferIsEmpty();
417 * Tests disarming and immediately rearming clears post flight stats and shows ARMED notification.
419 TEST_F(OsdTest, TestDisarmWithImmediateRearm)
421 doTestArm();
423 doTestDisarm();
425 doTestArm();
429 * Tests dismissing the statistics screen with pitch stick after disarming.
431 TEST_F(OsdTest, TestDisarmWithDismissStats)
433 doTestArm();
435 doTestDisarm();
437 // given
438 // sticks have been moved
439 rcData[PITCH] = 1800;
441 // when
442 // sufficient OSD updates have been called
443 osdRefresh();
445 // then
446 // post flight stats screen disappears
447 #ifdef DEBUG_OSD
448 displayPortTestPrint();
449 #endif
450 displayPortTestBufferIsEmpty();
454 * Tests the calculation of timing in statistics
456 TEST_F(OsdTest, TestStatsTiming)
458 // given
459 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
460 osdStatSetState(OSD_STAT_TIMER_1, true);
461 osdStatSetState(OSD_STAT_TIMER_2, true);
463 // and
464 // this timer 1 configuration
465 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0);
467 // and
468 // this timer 2 configuration
469 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0);
471 // and
472 // this RTC time
473 dateTime_t dateTime;
474 dateTime.year = 2017;
475 dateTime.month = 11;
476 dateTime.day = 19;
477 dateTime.hours = 10;
478 dateTime.minutes = 12;
479 dateTime.seconds = 0;
480 dateTime.millis = 0;
481 rtcSetDateTime(&dateTime);
483 // when
484 // the craft is armed
485 doTestArm();
487 // and
488 // these conditions occur during flight
489 simulationTime += 1e6;
490 osdRefresh();
492 // and
493 // the craft is disarmed
494 doTestDisarm();
496 // and
497 // the craft is armed again
498 doTestArm();
500 // and
501 // these conditions occur during flight
502 simulationTime += 1e6;
503 osdRefresh();
505 // and
506 // the craft is disarmed
507 doTestDisarm();
509 // then
510 // statistics screen should display the following
511 int row = 7;
512 displayPortTestBufferSubstring(2, row++, "2017-11-19 10:12:");
513 displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:13.61");
514 displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:01");
518 * Tests the calculation of statistics with imperial unit output.
520 TEST_F(OsdTest, TestStatsImperial)
522 // given
523 setupStats();
525 // and
526 // using imperial unit system
527 osdConfigMutable()->units = UNIT_IMPERIAL;
529 // and
530 // a GPS fix is present
531 stateFlags |= GPS_FIX | GPS_FIX_HOME;
533 // when
534 // the craft is armed
535 doTestArm();
537 // and
538 simulateFlight();
540 // and
541 // the craft is disarmed
542 doTestDisarm();
544 // then
545 // statistics screen should display the following
546 int row = 5;
547 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.6%c", SYM_FT);
548 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 17");
549 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 3772%c", SYM_FT);
550 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 6.52%c", SYM_MILES);
551 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
552 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
553 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
557 * Tests the calculation of statistics with metric unit output.
558 * (essentially an abridged version of the previous test
560 TEST_F(OsdTest, TestStatsMetric)
562 // given
563 setupStats();
565 // and
566 // using metric unit system
567 osdConfigMutable()->units = UNIT_METRIC;
569 // when
570 // the craft is armed
571 doTestArm();
573 // and
574 simulateFlight();
576 // and
577 // the craft is disarmed
578 doTestDisarm();
580 // then
581 // statistics screen should display the following
582 int row = 5;
583 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
584 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
585 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
586 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
587 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
588 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
589 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
593 * Tests the calculation of statistics with metric unit output.
594 * (essentially an abridged version of the previous test
596 TEST_F(OsdTest, TestStatsMetricDistanceUnits)
598 // given
599 setupStats();
601 // and
602 // using metric unit system
603 osdConfigMutable()->units = UNIT_METRIC;
605 // when
606 // the craft is armed
607 doTestArm();
609 // and
610 simulateFlight();
612 // and
613 // the craft is disarmed
614 doTestDisarm();
616 // then
617 // statistics screen should display the following
618 int row = 5;
619 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
620 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
621 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
622 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
623 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
624 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
625 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
629 * Tests activation of alarms and element flashing.
631 TEST_F(OsdTest, TestAlarms)
633 // given
634 sensorsSet(SENSOR_GPS);
636 // and
637 // the following OSD elements are visible
638 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
639 osdElementConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | OSD_PROFILE_1_FLAG;
640 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | OSD_PROFILE_1_FLAG;
641 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | OSD_PROFILE_1_FLAG;
642 osdElementConfigMutable()->item_pos[OSD_REMAINING_TIME_ESTIMATE] = OSD_POS(1, 2) | OSD_PROFILE_1_FLAG;
643 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
645 // and
646 // this set of alarm values
647 osdConfigMutable()->rssi_alarm = 20;
648 osdConfigMutable()->cap_alarm = 2200;
649 osdConfigMutable()->alt_alarm = 100; // meters
651 osdAnalyzeActiveElements();
653 // and
654 // this timer 1 configuration
655 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 5);
656 EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]));
657 EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1]));
658 EXPECT_EQ(5, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1]));
660 // and
661 // this timer 2 configuration
662 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 2);
663 EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]));
664 EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2]));
665 EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2]));
667 // and
668 // using the metric unit system
669 osdConfigMutable()->units = UNIT_METRIC;
671 // when
672 // time is passing by
673 simulationTime += 60e6;
674 osdRefresh();
676 // and
677 // the craft is armed
678 doTestArm(false);
680 simulationTime += 70e6;
681 osdRefresh();
683 // then
684 // no elements should flash as all values are out of alarm range
685 for (int i = 0; i < 30; i++) {
686 // Check for visibility every 100ms, elements should always be visible
687 simulationTime += 0.1e6;
688 osdRefresh();
690 #ifdef DEBUG_OSD
691 printf("%d\n", i);
692 #endif
693 displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer
694 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
695 displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT);
696 displayPortTestBufferSubstring(20, 1, "%c04:", SYM_ON_M); // only test the minute part of the timer
697 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
700 // when
701 // all values are out of range
702 rssi = 128;
703 simulationBatteryState = BATTERY_CRITICAL;
704 simulationBatteryVoltage = 1350;
705 simulationAltitude = 12000;
706 simulationMahDrawn = 999999;
708 simulationTime += 60e6;
709 osdRefresh();
711 // then
712 // elements showing values in alarm range should flash
713 simulationTime += 1000000;
714 simulationTime -= simulationTime % 1000000;
715 timeUs_t startTime = simulationTime;
716 for (int i = 0; i < 15; i++) {
717 // Blinking should happen at 2Hz
718 simulationTime = startTime + i*0.25e6;
719 osdRefresh();
721 #ifdef DEBUG_OSD
722 printf("%d\n", i);
723 displayPortTestPrint();
724 #endif
725 if (i % 2 == 1) {
726 displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI);
727 displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT);
728 displayPortTestBufferSubstring(1, 1, "%c02:", SYM_FLY_M); // only test the minute part of the timer
729 displayPortTestBufferSubstring(20, 1, "%c05:", SYM_ON_M); // only test the minute part of the timer
730 displayPortTestBufferSubstring(23, 7, "%c120.0%c", SYM_ALTITUDE, SYM_M);
731 } else {
732 displayPortTestBufferIsEmpty();
738 * Tests the RSSI OSD element.
740 TEST_F(OsdTest, TestElementRssi)
742 // given
743 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
744 osdConfigMutable()->rssi_alarm = 0;
746 osdAnalyzeActiveElements();
748 // when
749 rssi = 1024;
750 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
751 osdRefresh();
753 // then
754 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
756 // when
757 rssi = 0;
758 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
759 osdRefresh();
761 // then
762 displayPortTestBufferSubstring(8, 1, "%c 0", SYM_RSSI);
764 // when
765 rssi = 512;
766 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
767 osdRefresh();
769 // then
770 displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI);
774 * Tests the instantaneous battery current OSD element.
776 TEST_F(OsdTest, TestElementAmperage)
778 // given
779 osdElementConfigMutable()->item_pos[OSD_CURRENT_DRAW] = OSD_POS(1, 12) | OSD_PROFILE_1_FLAG;
781 osdAnalyzeActiveElements();
783 // when
784 simulationBatteryAmperage = 0;
785 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
786 osdRefresh();
788 // then
789 displayPortTestBufferSubstring(1, 12, " 0.00%c", SYM_AMP);
791 // when
792 simulationBatteryAmperage = 2156;
793 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
794 osdRefresh();
796 // then
797 displayPortTestBufferSubstring(1, 12, " 21.56%c", SYM_AMP);
799 // when
800 simulationBatteryAmperage = 12345;
801 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
802 osdRefresh();
804 // then
805 displayPortTestBufferSubstring(1, 12, "123.45%c", SYM_AMP);
809 * Tests the battery capacity drawn OSD element.
811 TEST_F(OsdTest, TestElementMahDrawn)
813 // given
814 osdElementConfigMutable()->item_pos[OSD_MAH_DRAWN] = OSD_POS(1, 11) | OSD_PROFILE_1_FLAG;
816 osdAnalyzeActiveElements();
818 // when
819 simulationMahDrawn = 0;
820 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
821 osdRefresh();
823 // then
824 displayPortTestBufferSubstring(1, 11, " 0%c", SYM_MAH);
826 // when
827 simulationMahDrawn = 4;
828 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
829 osdRefresh();
831 // then
832 displayPortTestBufferSubstring(1, 11, " 4%c", SYM_MAH);
834 // when
835 simulationMahDrawn = 15;
836 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
837 osdRefresh();
839 // then
840 displayPortTestBufferSubstring(1, 11, " 15%c", SYM_MAH);
842 // when
843 simulationMahDrawn = 246;
844 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
845 osdRefresh();
847 // then
848 displayPortTestBufferSubstring(1, 11, " 246%c", SYM_MAH);
850 // when
851 simulationMahDrawn = 1042;
852 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
853 osdRefresh();
855 // then
856 displayPortTestBufferSubstring(1, 11, "1042%c", SYM_MAH);
860 * Tests the instantaneous electrical power OSD element.
862 TEST_F(OsdTest, TestElementPower)
864 // given
865 osdElementConfigMutable()->item_pos[OSD_POWER] = OSD_POS(1, 10) | OSD_PROFILE_1_FLAG;
867 osdAnalyzeActiveElements();
869 // and
870 simulationBatteryVoltage = 1000; // 10V
872 // and
873 simulationBatteryAmperage = 0; // 0A
875 // when
876 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
877 osdRefresh();
879 // then
880 displayPortTestBufferSubstring(1, 10, " 0W");
882 // given
883 simulationBatteryAmperage = 10; // 0.1A
885 // when
886 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
887 osdRefresh();
889 // then
890 displayPortTestBufferSubstring(1, 10, " 1W");
892 // given
893 simulationBatteryAmperage = 120; // 1.2A
895 // when
896 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
897 osdRefresh();
899 // then
900 displayPortTestBufferSubstring(1, 10, " 12W");
902 // given
903 simulationBatteryAmperage = 1230; // 12.3A
905 // when
906 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
907 osdRefresh();
909 // then
910 displayPortTestBufferSubstring(1, 10, " 123W");
912 // given
913 simulationBatteryAmperage = 12340; // 123.4A
915 // when
916 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
917 osdRefresh();
919 // then
920 displayPortTestBufferSubstring(1, 10, "1234W");
924 * Tests the altitude OSD element.
926 TEST_F(OsdTest, TestElementAltitude)
928 // given
929 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
931 osdAnalyzeActiveElements();
933 // and
934 osdConfigMutable()->units = UNIT_METRIC;
935 sensorsClear(SENSOR_GPS);
937 // when
938 simulationAltitude = 0;
939 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
940 osdRefresh();
942 // then
943 displayPortTestBufferSubstring(23, 7, "%c-", SYM_ALTITUDE);
945 // when
946 sensorsSet(SENSOR_GPS);
947 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
948 osdRefresh();
950 // then
951 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
953 // when
954 simulationAltitude = 247; // rounds to 2.5m
955 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
956 osdRefresh();
958 // then
959 displayPortTestBufferSubstring(23, 7, "%c2.5%c", SYM_ALTITUDE, SYM_M);
961 // when
962 simulationAltitude = 4247; // rounds to 42.5m
963 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
964 osdRefresh();
966 // then
967 displayPortTestBufferSubstring(23, 7, "%c42.5%c", SYM_ALTITUDE, SYM_M);
969 // when
970 simulationAltitude = -247; // rounds to -2.5m
971 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
972 osdRefresh();
974 // then
975 displayPortTestBufferSubstring(23, 7, "%c-2.5%c", SYM_ALTITUDE, SYM_M);
977 // when
978 simulationAltitude = -70;
979 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
980 osdRefresh();
982 // then
983 displayPortTestBufferSubstring(23, 7, "%c-0.7%c", SYM_ALTITUDE, SYM_M);
988 * Tests the core temperature OSD element.
990 TEST_F(OsdTest, TestElementCoreTemperature)
992 // given
993 osdElementConfigMutable()->item_pos[OSD_CORE_TEMPERATURE] = OSD_POS(1, 8) | OSD_PROFILE_1_FLAG;
995 osdAnalyzeActiveElements();
997 // and
998 osdConfigMutable()->units = UNIT_METRIC;
1000 // and
1001 simulationCoreTemperature = 0;
1003 // when
1004 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1005 osdRefresh();
1007 // then
1008 displayPortTestBufferSubstring(1, 8, "C%c 0%c", SYM_TEMPERATURE, SYM_C);
1010 // given
1011 simulationCoreTemperature = 33;
1013 // when
1014 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1015 osdRefresh();
1017 // then
1018 displayPortTestBufferSubstring(1, 8, "C%c 33%c", SYM_TEMPERATURE, SYM_C);
1020 // given
1021 osdConfigMutable()->units = UNIT_IMPERIAL;
1023 // when
1024 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1025 osdRefresh();
1027 // then
1028 displayPortTestBufferSubstring(1, 8, "C%c 91%c", SYM_TEMPERATURE, SYM_F);
1032 * Tests the battery notifications shown on the warnings OSD element.
1034 TEST_F(OsdTest, TestElementWarningsBattery)
1036 // given
1037 osdElementConfigMutable()->item_pos[OSD_WARNINGS] = OSD_POS(9, 10) | OSD_PROFILE_1_FLAG;
1038 osdConfigMutable()->enabledWarnings = 0; // disable all warnings
1039 osdWarnSetState(OSD_WARNING_BATTERY_WARNING, true);
1040 osdWarnSetState(OSD_WARNING_BATTERY_CRITICAL, true);
1041 osdWarnSetState(OSD_WARNING_BATTERY_NOT_FULL, true);
1043 osdAnalyzeActiveElements();
1045 // and
1046 batteryConfigMutable()->vbatfullcellvoltage = 410;
1048 // and
1049 // 4S battery
1050 simulationBatteryCellCount = 4;
1052 // and
1053 // used battery
1054 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount) - 1;
1055 simulationBatteryState = BATTERY_OK;
1057 // when
1058 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1059 // Delay as the warnings are flashing
1060 simulationTime += 1000000;
1061 simulationTime -= simulationTime % 1000000;
1062 osdRefresh();
1064 // then
1065 displayPortTestBufferSubstring(9, 10, "BATT < FULL");
1067 // given
1068 // full battery
1069 simulationBatteryVoltage = 1680;
1070 simulationBatteryState = BATTERY_OK;
1072 // when
1073 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1074 osdRefresh();
1076 // then
1077 displayPortTestBufferSubstring(9, 10, " ");
1079 // given
1080 // low battery
1081 simulationBatteryVoltage = 1400;
1082 simulationBatteryState = BATTERY_WARNING;
1084 // when
1085 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1086 // Delay as the warnings are flashing
1087 simulationTime += 1000000;
1088 simulationTime -= simulationTime % 1000000;
1089 simulationTime += 0.25e6;
1090 osdRefresh();
1092 // then
1093 displayPortTestBufferSubstring(9, 10, "LOW BATTERY ");
1095 // given
1096 // critical battery
1097 simulationBatteryVoltage = 1320;
1098 simulationBatteryState = BATTERY_CRITICAL;
1100 // when
1101 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1102 // Delay as the warnings are flashing
1103 simulationTime += 1000000;
1104 simulationTime -= simulationTime % 1000000;
1105 simulationTime += 0.25e6;
1106 osdRefresh();
1108 // then
1109 displayPortTestBufferSubstring(9, 10, " LAND NOW ");
1111 // given
1112 // full battery
1113 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount);
1114 simulationBatteryState = BATTERY_OK;
1116 // when
1117 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1118 osdRefresh();
1120 // then
1121 displayPortTestBufferSubstring(9, 10, " ");
1123 // TODO
1127 * Tests the time string formatting function with a series of precision settings and time values.
1129 TEST_F(OsdTest, TestFormatTimeString)
1131 char buff[OSD_ELEMENT_BUFFER_LENGTH];
1133 /* Seconds precision, 0 us */
1134 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0);
1135 EXPECT_EQ(0, strcmp("00:00", buff));
1137 /* Seconds precision, 0.9 seconds */
1138 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6);
1139 EXPECT_EQ(0, strcmp("00:00", buff));
1141 /* Seconds precision, 10 seconds */
1142 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6);
1143 EXPECT_EQ(0, strcmp("00:10", buff));
1145 /* Seconds precision, 1 minute */
1146 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6);
1147 EXPECT_EQ(0, strcmp("01:00", buff));
1149 /* Seconds precision, 1 minute 59 seconds */
1150 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6);
1151 EXPECT_EQ(0, strcmp("01:59", buff));
1153 /* Hundredths precision, 0 us */
1154 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0);
1155 EXPECT_EQ(0, strcmp("00:00.00", buff));
1157 /* Hundredths precision, 10 milliseconds (one 100th of a second) */
1158 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3);
1159 EXPECT_EQ(0, strcmp("00:00.01", buff));
1161 /* Hundredths precision, 0.9 seconds */
1162 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6);
1163 EXPECT_EQ(0, strcmp("00:00.90", buff));
1165 /* Hundredths precision, 10 seconds */
1166 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6);
1167 EXPECT_EQ(0, strcmp("00:10.00", buff));
1169 /* Hundredths precision, 1 minute */
1170 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6);
1171 EXPECT_EQ(0, strcmp("01:00.00", buff));
1173 /* Hundredths precision, 1 minute 59 seconds */
1174 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6);
1175 EXPECT_EQ(0, strcmp("01:59.00", buff));
1178 TEST_F(OsdTest, TestConvertTemperatureUnits)
1180 /* In Celsius */
1181 osdConfigMutable()->units = UNIT_METRIC;
1182 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 40);
1184 /* In Fahrenheit */
1185 osdConfigMutable()->units = UNIT_IMPERIAL;
1186 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 104);
1188 /* In Fahrenheit with rounding */
1189 osdConfigMutable()->units = UNIT_IMPERIAL;
1190 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(41), 106);
1193 TEST_F(OsdTest, TestGpsElements)
1195 // given
1196 osdElementConfigMutable()->item_pos[OSD_GPS_SATS] = OSD_POS(2, 4) | OSD_PROFILE_1_FLAG;
1198 sensorsSet(SENSOR_GPS);
1199 osdAnalyzeActiveElements();
1201 // when
1202 simulationGpsHealthy = false;
1203 gpsSol.numSat = 0;
1205 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1206 osdRefresh();
1208 // then
1209 // Sat indicator should blink and show "NC"
1210 simulationTime += 1000000;
1211 simulationTime -= simulationTime % 1000000;
1212 timeUs_t startTime = simulationTime;
1213 for (int i = 0; i < 15; i++) {
1214 // Blinking should happen at 2Hz
1215 simulationTime = startTime + i*0.25e6;
1216 osdRefresh();
1218 if (i % 2 == 1) {
1219 displayPortTestBufferSubstring(2, 4, "%c%cNC", SYM_SAT_L, SYM_SAT_R);
1220 } else {
1221 displayPortTestBufferIsEmpty();
1225 // when
1226 simulationGpsHealthy = true;
1227 gpsSol.numSat = 0;
1229 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1230 osdRefresh();
1232 // then
1233 // Sat indicator should blink and show "0"
1234 simulationTime += 1000000;
1235 simulationTime -= simulationTime % 1000000;
1236 startTime = simulationTime;
1237 for (int i = 0; i < 15; i++) {
1238 // Blinking should happen at 2Hz
1239 simulationTime = startTime + i*0.25e6;
1240 osdRefresh();
1242 if (i % 2 == 1) {
1243 displayPortTestBufferSubstring(2, 4, "%c%c 0", SYM_SAT_L, SYM_SAT_R);
1244 } else {
1245 displayPortTestBufferIsEmpty();
1249 // when
1250 simulationGpsHealthy = true;
1251 gpsSol.numSat = 10;
1253 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1254 osdRefresh();
1256 // then
1257 // Sat indicator should show "10" without flashing
1258 for (int i = 0; i < 15; i++) {
1259 // Blinking should happen at 2Hz
1260 simulationTime += 0.2e6;
1261 osdRefresh();
1263 displayPortTestBufferSubstring(2, 4, "%c%c10", SYM_SAT_L, SYM_SAT_R);
1267 TEST_F(OsdTest, TestHdPositioning)
1269 // given
1270 // Try to round-trip the OSD_POS macro with an "HD location"
1271 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(53, 0) | OSD_PROFILE_1_FLAG;
1272 osdConfigMutable()->rssi_alarm = 0;
1273 // Also try to round-trip a raw value matching one generated by the Configurator.
1274 osdElementConfigMutable()->item_pos[OSD_CURRENT_DRAW] = 3125 | OSD_PROFILE_1_FLAG;
1276 osdAnalyzeActiveElements();
1278 // when
1279 simulationBatteryAmperage = 0;
1280 rssi = 1024;
1282 displayClearScreen(&testDisplayPort, DISPLAY_CLEAR_WAIT);
1283 osdRefresh();
1285 // then
1286 displayPortTestBufferSubstring(53, 0, "%c99", SYM_RSSI);
1287 displayPortTestBufferSubstring(53, 1, " 0.00%c", SYM_AMP);
1290 // STUBS
1291 extern "C" {
1292 bool featureIsEnabled(uint32_t f) { return simulationFeatureFlags & f; }
1294 void beeperConfirmationBeeps(uint8_t) {}
1296 bool isModeActivationConditionPresent(boxId_e) {
1297 return false;
1300 bool IS_RC_MODE_ACTIVE(boxId_e) {
1301 return false;
1304 uint32_t micros() {
1305 return simulationTime;
1308 uint32_t millis() {
1309 return micros() / 1000;
1312 bool isBeeperOn() {
1313 return false;
1316 bool airmodeIsEnabled() {
1317 return false;
1320 uint8_t getCurrentPidProfileIndex() {
1321 return 0;
1324 uint8_t getCurrentControlRateProfileIndex() {
1325 return 0;
1328 batteryState_e getBatteryState() {
1329 return simulationBatteryState;
1332 uint8_t getBatteryCellCount() {
1333 return simulationBatteryCellCount;
1336 uint16_t getBatteryVoltage() {
1337 return simulationBatteryVoltage;
1340 uint16_t getBatteryAverageCellVoltage() {
1341 return simulationBatteryVoltage / simulationBatteryCellCount;
1344 int32_t getAmperage() {
1345 return simulationBatteryAmperage;
1348 int32_t getMAhDrawn() {
1349 return simulationMahDrawn;
1352 float getWhDrawn() {
1353 return simulationWhDrawn;
1356 int32_t getEstimatedAltitudeCm() {
1357 return simulationAltitude;
1360 int32_t getEstimatedVario() {
1361 return simulationVerticalSpeed;
1364 int32_t blackboxGetLogNumber() {
1365 return 0;
1368 bool isBlackboxDeviceWorking() {
1369 return true;
1372 bool isBlackboxDeviceFull() {
1373 return false;
1376 bool isSerialTransmitBufferEmpty(const serialPort_t *) {
1377 return false;
1380 void serialWrite(serialPort_t *, uint8_t) {}
1382 bool cmsDisplayPortRegister(displayPort_t *) {
1383 return false;
1386 uint16_t getRssi(void) { return rssi; }
1388 uint8_t getRssiPercent(void) { return scaleRange(rssi, 0, RSSI_MAX_VALUE, 0, 100); }
1390 uint16_t rxGetLinkQuality(void) { return LINK_QUALITY_MAX_VALUE; }
1392 uint16_t getCoreTemperatureCelsius(void) { return simulationCoreTemperature; }
1394 bool isFlipOverAfterCrashActive(void) { return false; }
1396 float pidItermAccelerator(void) { return 1.0; }
1397 uint8_t getMotorCount(void){ return 4; }
1398 bool areMotorsRunning(void){ return true; }
1399 bool pidOsdAntiGravityActive(void) { return false; }
1400 bool failsafeIsActive(void) { return false; }
1401 bool gpsRescueIsConfigured(void) { return false; }
1402 bool gpsIsHealthy(void) { return simulationGpsHealthy; }
1403 int8_t calculateThrottlePercent(void) { return 0; }
1404 uint32_t persistentObjectRead(persistentObjectId_e) { return 0; }
1405 void persistentObjectWrite(persistentObjectId_e, uint32_t) {}
1406 bool isUpright(void) { return true; }
1407 float getMotorOutputLow(void) { return 1000.0; }
1408 float getMotorOutputHigh(void) { return 2047.0; }
1409 void schedulerIgnoreTaskStateTime(void) { }
1410 void schedulerIgnoreTaskExecRate(void) { }
1411 void schedulerIgnoreTaskExecTime(void) { }
1412 bool schedulerGetIgnoreTaskExecTime() { return false; }
1413 void schedulerSetNextStateTime(timeDelta_t) {}