New SPI API supporting DMA
[betaflight.git] / src / test / unit / osd_unittest.cc
blobd397afdcbc8336dbab5ef295e21d2470c610afb4
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 osdRefresh(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;
88 float accAverage[XYZ_AXIS_COUNT];
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 int32_t simulationAltitude;
105 int32_t simulationVerticalSpeed;
106 uint16_t simulationCoreTemperature;
107 bool simulationGpsHealthy;
110 uint32_t simulationFeatureFlags = FEATURE_GPS;
112 /* #define DEBUG_OSD */
114 #include "unittest_macros.h"
115 #include "unittest_displayport.h"
116 #include "gtest/gtest.h"
118 void setDefaultSimulationState()
120 memset(osdElementConfigMutable(), 0, sizeof(osdElementConfig_t));
122 osdConfigMutable()->enabled_stats = 0;
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 simulationTime = 0;
139 osdFlyTime = 0;
143 * Performs a test of the OSD actions on arming.
144 * (reused throughout the test suite)
146 void doTestArm(bool testEmpty = true)
148 // given
149 // craft has been armed
150 ENABLE_ARMING_FLAG(ARMED);
152 // when
153 // sufficient OSD updates have been called
154 osdRefresh(simulationTime);
156 // then
157 // arming alert displayed
158 displayPortTestBufferSubstring(12, 7, "ARMED");
160 // given
161 // armed alert times out (0.5 seconds)
162 simulationTime += 0.5e6;
164 // when
165 // sufficient OSD updates have been called
166 osdRefresh(simulationTime);
168 // then
169 // arming alert disappears
170 #ifdef DEBUG_OSD
171 displayPortTestPrint();
172 #endif
173 if (testEmpty) {
174 displayPortTestBufferIsEmpty();
179 * Auxiliary function. Test is there're stats that must be shown
181 bool isSomeStatEnabled(void) {
182 return (osdConfigMutable()->enabled_stats != 0);
186 * Performs a test of the OSD actions on disarming.
187 * (reused throughout the test suite)
189 void doTestDisarm()
191 // given
192 // craft is disarmed after having been armed
193 DISABLE_ARMING_FLAG(ARMED);
195 // when
196 // sufficient OSD updates have been called
197 osdRefresh(simulationTime);
199 // then
200 // post flight statistics displayed
201 if (isSomeStatEnabled()) {
202 unsigned enabledStats = osdConfigMutable()->enabled_stats;
203 unsigned count = 0;
204 while (enabledStats) {
205 count += enabledStats & 1;
206 enabledStats >>= 1;
209 displayPortTestBufferSubstring(2, 7 - count / 2, " --- STATS ---");
213 void setupStats(void)
215 // this set of enabled post flight statistics
216 osdStatSetState(OSD_STAT_MAX_SPEED, true);
217 osdStatSetState(OSD_STAT_MIN_BATTERY, true);
218 osdStatSetState(OSD_STAT_MIN_RSSI, true);
219 osdStatSetState(OSD_STAT_MAX_CURRENT, false);
220 osdStatSetState(OSD_STAT_USED_MAH, false);
221 osdStatSetState(OSD_STAT_MAX_ALTITUDE, true);
222 osdStatSetState(OSD_STAT_BLACKBOX, false);
223 osdStatSetState(OSD_STAT_END_BATTERY, true);
224 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
225 osdStatSetState(OSD_STAT_MAX_DISTANCE, true);
226 osdStatSetState(OSD_STAT_FLIGHT_DISTANCE, true);
227 osdStatSetState(OSD_STAT_BLACKBOX_NUMBER, false);
228 osdStatSetState(OSD_STAT_MAX_G_FORCE, false);
229 osdStatSetState(OSD_STAT_MAX_ESC_TEMP, false);
230 osdStatSetState(OSD_STAT_MAX_ESC_RPM, false);
233 void simulateFlight(void)
235 // these conditions occur during flight
236 rssi = 1024;
237 gpsSol.groundSpeed = 500;
238 GPS_distanceToHome = 20;
239 GPS_distanceFlownInCm = 2000;
240 simulationBatteryVoltage = 1580;
241 simulationAltitude = 100;
242 simulationTime += 1e6;
243 osdRefresh(simulationTime);
245 rssi = 512;
246 gpsSol.groundSpeed = 800;
247 GPS_distanceToHome = 50;
248 GPS_distanceFlownInCm = 10000;
249 simulationBatteryVoltage = 1470;
250 simulationAltitude = 150;
251 simulationTime += 1e6;
252 osdRefresh(simulationTime);
254 rssi = 256;
255 gpsSol.groundSpeed = 200;
256 GPS_distanceToHome = 100;
257 GPS_distanceFlownInCm = 20000;
258 simulationBatteryVoltage = 1520;
259 simulationAltitude = 200;
260 simulationTime += 1e6;
261 osdRefresh(simulationTime);
263 rssi = 256;
264 gpsSol.groundSpeed = 800;
265 GPS_distanceToHome = 100;
266 GPS_distanceFlownInCm = 10000;
267 simulationBatteryVoltage = 1470;
268 simulationAltitude = 200; // converts to 6.56168 feet which rounds to 6.6 in imperial units stats test
269 simulationTime += 1e6;
270 osdRefresh(simulationTime);
272 simulationBatteryVoltage = 1520;
273 simulationTime += 1e6;
274 osdRefresh(simulationTime);
276 rssi = 256;
277 gpsSol.groundSpeed = 800;
278 GPS_distanceToHome = 1150;
279 GPS_distanceFlownInCm = 1050000;
280 simulationBatteryVoltage = 1470;
281 simulationAltitude = 200;
282 simulationTime += 1e6;
283 osdRefresh(simulationTime);
285 simulationBatteryVoltage = 1520;
286 simulationTime += 1e6;
287 osdRefresh(simulationTime);
290 class OsdTest : public ::testing::Test
292 protected:
293 static void SetUpTestCase() {
294 displayPortTestInit();
297 virtual void SetUp() {
298 setDefaultSimulationState();
301 virtual void TearDown() {
302 // Clean up the armed state without showing stats at the end of a test
303 osdConfigMutable()->enabled_stats = 0;
305 doTestDisarm();
310 * Tests initialisation of the OSD and the power on splash screen.
312 TEST_F(OsdTest, TestInit)
314 // given
315 // this battery configuration (used for battery voltage elements)
316 batteryConfigMutable()->vbatmincellvoltage = 330;
317 batteryConfigMutable()->vbatmaxcellvoltage = 430;
319 // when
320 // OSD is initialised
321 osdInit(&testDisplayPort, OSD_DISPLAYPORT_DEVICE_AUTO);
323 // then
324 // display buffer should contain splash screen
325 displayPortTestBufferSubstring(7, 8, "MENU:THR MID");
326 displayPortTestBufferSubstring(11, 9, "+ YAW LEFT");
327 displayPortTestBufferSubstring(11, 10, "+ PITCH UP");
329 // when
330 // splash screen timeout has elapsed
331 simulationTime += 4e6;
332 osdUpdate(simulationTime);
334 // then
335 // display buffer should be empty
336 #ifdef DEBUG_OSD
337 displayPortTestPrint();
338 #endif
339 displayPortTestBufferIsEmpty();
343 * Tests visibility of the ARMED notification after arming.
345 TEST_F(OsdTest, TestArm)
347 doTestArm();
351 * Tests display and timeout of the post flight statistics screen after disarming.
353 TEST_F(OsdTest, TestDisarm)
355 doTestArm();
357 doTestDisarm();
359 // given
360 // post flight stats times out (60 seconds)
361 simulationTime += 60e6;
363 // when
364 // sufficient OSD updates have been called
365 osdRefresh(simulationTime);
367 // then
368 // post flight stats screen disappears
369 #ifdef DEBUG_OSD
370 displayPortTestPrint();
371 #endif
372 displayPortTestBufferIsEmpty();
376 * Tests disarming and immediately rearming clears post flight stats and shows ARMED notification.
378 TEST_F(OsdTest, TestDisarmWithImmediateRearm)
380 doTestArm();
382 doTestDisarm();
384 doTestArm();
388 * Tests dismissing the statistics screen with pitch stick after disarming.
390 TEST_F(OsdTest, TestDisarmWithDismissStats)
392 doTestArm();
394 doTestDisarm();
396 // given
397 // sticks have been moved
398 rcData[PITCH] = 1800;
400 // when
401 // sufficient OSD updates have been called
402 osdRefresh(simulationTime);
404 // then
405 // post flight stats screen disappears
406 #ifdef DEBUG_OSD
407 displayPortTestPrint();
408 #endif
409 displayPortTestBufferIsEmpty();
413 * Tests the calculation of timing in statistics
415 TEST_F(OsdTest, TestStatsTiming)
417 // given
418 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
419 osdStatSetState(OSD_STAT_TIMER_1, true);
420 osdStatSetState(OSD_STAT_TIMER_2, true);
422 // and
423 // this timer 1 configuration
424 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0);
426 // and
427 // this timer 2 configuration
428 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0);
430 // and
431 // this RTC time
432 dateTime_t dateTime;
433 dateTime.year = 2017;
434 dateTime.month = 11;
435 dateTime.day = 19;
436 dateTime.hours = 10;
437 dateTime.minutes = 12;
438 dateTime.seconds = 0;
439 dateTime.millis = 0;
440 rtcSetDateTime(&dateTime);
442 // when
443 // the craft is armed
444 doTestArm();
446 // and
447 // these conditions occur during flight
448 simulationTime += 1e6;
449 osdRefresh(simulationTime);
451 // and
452 // the craft is disarmed
453 doTestDisarm();
455 // and
456 // the craft is armed again
457 doTestArm();
459 // and
460 // these conditions occur during flight
461 simulationTime += 1e6;
462 osdRefresh(simulationTime);
464 // and
465 // the craft is disarmed
466 doTestDisarm();
468 // then
469 // statistics screen should display the following
470 int row = 7;
471 displayPortTestBufferSubstring(2, row++, "2017-11-19 10:12:");
472 displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:02.50");
473 displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:01");
477 * Tests the calculation of statistics with imperial unit output.
479 TEST_F(OsdTest, TestStatsImperial)
481 // given
482 setupStats();
484 // and
485 // using imperial unit system
486 osdConfigMutable()->units = UNIT_IMPERIAL;
488 // and
489 // a GPS fix is present
490 stateFlags |= GPS_FIX | GPS_FIX_HOME;
492 // when
493 // the craft is armed
494 doTestArm();
496 // and
497 simulateFlight();
499 // and
500 // the craft is disarmed
501 doTestDisarm();
503 // then
504 // statistics screen should display the following
505 int row = 5;
506 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.6%c", SYM_FT);
507 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 17");
508 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 3772%c", SYM_FT);
509 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 6.52%c", SYM_MILES);
510 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
511 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
512 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
516 * Tests the calculation of statistics with metric unit output.
517 * (essentially an abridged version of the previous test
519 TEST_F(OsdTest, TestStatsMetric)
521 // given
522 setupStats();
524 // and
525 // using metric unit system
526 osdConfigMutable()->units = UNIT_METRIC;
528 // when
529 // the craft is armed
530 doTestArm();
532 // and
533 simulateFlight();
535 // and
536 // the craft is disarmed
537 doTestDisarm();
539 // then
540 // statistics screen should display the following
541 int row = 5;
542 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
543 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
544 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
545 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
546 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
547 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
548 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
552 * Tests the calculation of statistics with metric unit output.
553 * (essentially an abridged version of the previous test
555 TEST_F(OsdTest, TestStatsMetricDistanceUnits)
557 // given
558 setupStats();
560 // and
561 // using metric unit system
562 osdConfigMutable()->units = UNIT_METRIC;
564 // when
565 // the craft is armed
566 doTestArm();
568 // and
569 simulateFlight();
571 // and
572 // the craft is disarmed
573 doTestDisarm();
575 // then
576 // statistics screen should display the following
577 int row = 5;
578 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
579 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
580 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
581 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
582 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
583 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
584 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
588 * Tests activation of alarms and element flashing.
590 TEST_F(OsdTest, TestAlarms)
592 // given
593 sensorsSet(SENSOR_GPS);
595 // and
596 // the following OSD elements are visible
597 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
598 osdElementConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | OSD_PROFILE_1_FLAG;
599 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | OSD_PROFILE_1_FLAG;
600 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | OSD_PROFILE_1_FLAG;
601 osdElementConfigMutable()->item_pos[OSD_REMAINING_TIME_ESTIMATE] = OSD_POS(1, 2) | OSD_PROFILE_1_FLAG;
602 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
604 // and
605 // this set of alarm values
606 osdConfigMutable()->rssi_alarm = 20;
607 osdConfigMutable()->cap_alarm = 2200;
608 osdConfigMutable()->alt_alarm = 100; // meters
610 osdAnalyzeActiveElements();
612 // and
613 // this timer 1 configuration
614 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 3);
615 EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]));
616 EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1]));
617 EXPECT_EQ(3, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1]));
619 // and
620 // this timer 2 configuration
621 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 2);
622 EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]));
623 EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2]));
624 EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2]));
626 // and
627 // using the metric unit system
628 osdConfigMutable()->units = UNIT_METRIC;
630 // when
631 // time is passing by
632 simulationTime += 60e6;
633 osdRefresh(simulationTime);
635 // and
636 // the craft is armed
637 doTestArm(false);
639 simulationTime += 70e6;
640 osdRefresh(simulationTime);
642 // then
643 // no elements should flash as all values are out of alarm range
644 for (int i = 0; i < 30; i++) {
645 // Check for visibility every 100ms, elements should always be visible
646 simulationTime += 0.1e6;
647 osdRefresh(simulationTime);
649 #ifdef DEBUG_OSD
650 printf("%d\n", i);
651 #endif
652 displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer
653 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
654 displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT);
655 displayPortTestBufferSubstring(20, 1, "%c02:", SYM_ON_M); // only test the minute part of the timer
656 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
659 // when
660 // all values are out of range
661 rssi = 128;
662 simulationBatteryState = BATTERY_CRITICAL;
663 simulationBatteryVoltage = 1350;
664 simulationAltitude = 12000;
665 simulationMahDrawn = 999999;
667 simulationTime += 60e6;
668 osdRefresh(simulationTime);
670 // then
671 // elements showing values in alarm range should flash
672 for (int i = 0; i < 15; i++) {
673 // Blinking should happen at 5Hz
674 simulationTime += 0.2e6;
675 osdRefresh(simulationTime);
677 #ifdef DEBUG_OSD
678 printf("%d\n", i);
679 displayPortTestPrint();
680 #endif
681 if (i % 2 == 1) {
682 displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI);
683 displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT);
684 displayPortTestBufferSubstring(1, 1, "%c02:", SYM_FLY_M); // only test the minute part of the timer
685 displayPortTestBufferSubstring(20, 1, "%c03:", SYM_ON_M); // only test the minute part of the timer
686 displayPortTestBufferSubstring(23, 7, "%c120.0%c", SYM_ALTITUDE, SYM_M);
687 } else {
688 displayPortTestBufferIsEmpty();
694 * Tests the RSSI OSD element.
696 TEST_F(OsdTest, TestElementRssi)
698 // given
699 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
700 osdConfigMutable()->rssi_alarm = 0;
702 osdAnalyzeActiveElements();
704 // when
705 rssi = 1024;
706 displayClearScreen(&testDisplayPort);
707 osdRefresh(simulationTime);
709 // then
710 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
712 // when
713 rssi = 0;
714 displayClearScreen(&testDisplayPort);
715 osdRefresh(simulationTime);
717 // then
718 displayPortTestBufferSubstring(8, 1, "%c 0", SYM_RSSI);
720 // when
721 rssi = 512;
722 displayClearScreen(&testDisplayPort);
723 osdRefresh(simulationTime);
725 // then
726 displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI);
730 * Tests the instantaneous battery current OSD element.
732 TEST_F(OsdTest, TestElementAmperage)
734 // given
735 osdElementConfigMutable()->item_pos[OSD_CURRENT_DRAW] = OSD_POS(1, 12) | OSD_PROFILE_1_FLAG;
737 osdAnalyzeActiveElements();
739 // when
740 simulationBatteryAmperage = 0;
741 displayClearScreen(&testDisplayPort);
742 osdRefresh(simulationTime);
744 // then
745 displayPortTestBufferSubstring(1, 12, " 0.00%c", SYM_AMP);
747 // when
748 simulationBatteryAmperage = 2156;
749 displayClearScreen(&testDisplayPort);
750 osdRefresh(simulationTime);
752 // then
753 displayPortTestBufferSubstring(1, 12, " 21.56%c", SYM_AMP);
755 // when
756 simulationBatteryAmperage = 12345;
757 displayClearScreen(&testDisplayPort);
758 osdRefresh(simulationTime);
760 // then
761 displayPortTestBufferSubstring(1, 12, "123.45%c", SYM_AMP);
765 * Tests the battery capacity drawn OSD element.
767 TEST_F(OsdTest, TestElementMahDrawn)
769 // given
770 osdElementConfigMutable()->item_pos[OSD_MAH_DRAWN] = OSD_POS(1, 11) | OSD_PROFILE_1_FLAG;
772 osdAnalyzeActiveElements();
774 // when
775 simulationMahDrawn = 0;
776 displayClearScreen(&testDisplayPort);
777 osdRefresh(simulationTime);
779 // then
780 displayPortTestBufferSubstring(1, 11, " 0%c", SYM_MAH);
782 // when
783 simulationMahDrawn = 4;
784 displayClearScreen(&testDisplayPort);
785 osdRefresh(simulationTime);
787 // then
788 displayPortTestBufferSubstring(1, 11, " 4%c", SYM_MAH);
790 // when
791 simulationMahDrawn = 15;
792 displayClearScreen(&testDisplayPort);
793 osdRefresh(simulationTime);
795 // then
796 displayPortTestBufferSubstring(1, 11, " 15%c", SYM_MAH);
798 // when
799 simulationMahDrawn = 246;
800 displayClearScreen(&testDisplayPort);
801 osdRefresh(simulationTime);
803 // then
804 displayPortTestBufferSubstring(1, 11, " 246%c", SYM_MAH);
806 // when
807 simulationMahDrawn = 1042;
808 displayClearScreen(&testDisplayPort);
809 osdRefresh(simulationTime);
811 // then
812 displayPortTestBufferSubstring(1, 11, "1042%c", SYM_MAH);
816 * Tests the instantaneous electrical power OSD element.
818 TEST_F(OsdTest, TestElementPower)
820 // given
821 osdElementConfigMutable()->item_pos[OSD_POWER] = OSD_POS(1, 10) | OSD_PROFILE_1_FLAG;
823 osdAnalyzeActiveElements();
825 // and
826 simulationBatteryVoltage = 1000; // 10V
828 // and
829 simulationBatteryAmperage = 0; // 0A
831 // when
832 displayClearScreen(&testDisplayPort);
833 osdRefresh(simulationTime);
835 // then
836 displayPortTestBufferSubstring(1, 10, " 0W");
838 // given
839 simulationBatteryAmperage = 10; // 0.1A
841 // when
842 displayClearScreen(&testDisplayPort);
843 osdRefresh(simulationTime);
845 // then
846 displayPortTestBufferSubstring(1, 10, " 1W");
848 // given
849 simulationBatteryAmperage = 120; // 1.2A
851 // when
852 displayClearScreen(&testDisplayPort);
853 osdRefresh(simulationTime);
855 // then
856 displayPortTestBufferSubstring(1, 10, " 12W");
858 // given
859 simulationBatteryAmperage = 1230; // 12.3A
861 // when
862 displayClearScreen(&testDisplayPort);
863 osdRefresh(simulationTime);
865 // then
866 displayPortTestBufferSubstring(1, 10, " 123W");
868 // given
869 simulationBatteryAmperage = 12340; // 123.4A
871 // when
872 displayClearScreen(&testDisplayPort);
873 osdRefresh(simulationTime);
875 // then
876 displayPortTestBufferSubstring(1, 10, "1234W");
880 * Tests the altitude OSD element.
882 TEST_F(OsdTest, TestElementAltitude)
884 // given
885 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
887 osdAnalyzeActiveElements();
889 // and
890 osdConfigMutable()->units = UNIT_METRIC;
891 sensorsClear(SENSOR_GPS);
893 // when
894 simulationAltitude = 0;
895 displayClearScreen(&testDisplayPort);
896 osdRefresh(simulationTime);
898 // then
899 displayPortTestBufferSubstring(23, 7, "%c-", SYM_ALTITUDE);
901 // when
902 sensorsSet(SENSOR_GPS);
903 displayClearScreen(&testDisplayPort);
904 osdRefresh(simulationTime);
906 // then
907 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
909 // when
910 simulationAltitude = 247; // rounds to 2.5m
911 displayClearScreen(&testDisplayPort);
912 osdRefresh(simulationTime);
914 // then
915 displayPortTestBufferSubstring(23, 7, "%c2.5%c", SYM_ALTITUDE, SYM_M);
917 // when
918 simulationAltitude = 4247; // rounds to 42.5m
919 displayClearScreen(&testDisplayPort);
920 osdRefresh(simulationTime);
922 // then
923 displayPortTestBufferSubstring(23, 7, "%c42.5%c", SYM_ALTITUDE, SYM_M);
925 // when
926 simulationAltitude = -247; // rounds to -2.5m
927 displayClearScreen(&testDisplayPort);
928 osdRefresh(simulationTime);
930 // then
931 displayPortTestBufferSubstring(23, 7, "%c-2.5%c", SYM_ALTITUDE, SYM_M);
933 // when
934 simulationAltitude = -70;
935 displayClearScreen(&testDisplayPort);
936 osdRefresh(simulationTime);
938 // then
939 displayPortTestBufferSubstring(23, 7, "%c-0.7%c", SYM_ALTITUDE, SYM_M);
944 * Tests the core temperature OSD element.
946 TEST_F(OsdTest, TestElementCoreTemperature)
948 // given
949 osdElementConfigMutable()->item_pos[OSD_CORE_TEMPERATURE] = OSD_POS(1, 8) | OSD_PROFILE_1_FLAG;
951 osdAnalyzeActiveElements();
953 // and
954 osdConfigMutable()->units = UNIT_METRIC;
956 // and
957 simulationCoreTemperature = 0;
959 // when
960 displayClearScreen(&testDisplayPort);
961 osdRefresh(simulationTime);
963 // then
964 displayPortTestBufferSubstring(1, 8, "C%c 0%c", SYM_TEMPERATURE, SYM_C);
966 // given
967 simulationCoreTemperature = 33;
969 // when
970 displayClearScreen(&testDisplayPort);
971 osdRefresh(simulationTime);
973 // then
974 displayPortTestBufferSubstring(1, 8, "C%c 33%c", SYM_TEMPERATURE, SYM_C);
976 // given
977 osdConfigMutable()->units = UNIT_IMPERIAL;
979 // when
980 displayClearScreen(&testDisplayPort);
981 osdRefresh(simulationTime);
983 // then
984 displayPortTestBufferSubstring(1, 8, "C%c 91%c", SYM_TEMPERATURE, SYM_F);
988 * Tests the battery notifications shown on the warnings OSD element.
990 TEST_F(OsdTest, TestElementWarningsBattery)
992 // given
993 osdElementConfigMutable()->item_pos[OSD_WARNINGS] = OSD_POS(9, 10) | OSD_PROFILE_1_FLAG;
994 osdConfigMutable()->enabledWarnings = 0; // disable all warnings
995 osdWarnSetState(OSD_WARNING_BATTERY_WARNING, true);
996 osdWarnSetState(OSD_WARNING_BATTERY_CRITICAL, true);
997 osdWarnSetState(OSD_WARNING_BATTERY_NOT_FULL, true);
999 osdAnalyzeActiveElements();
1001 // and
1002 batteryConfigMutable()->vbatfullcellvoltage = 410;
1004 // and
1005 // 4S battery
1006 simulationBatteryCellCount = 4;
1008 // and
1009 // used battery
1010 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount) - 1;
1011 simulationBatteryState = BATTERY_OK;
1013 // when
1014 displayClearScreen(&testDisplayPort);
1015 osdRefresh(simulationTime);
1017 // then
1018 displayPortTestBufferSubstring(9, 10, "BATT < FULL");
1020 // given
1021 // full battery
1022 simulationBatteryVoltage = 1680;
1023 simulationBatteryState = BATTERY_OK;
1025 // when
1026 displayClearScreen(&testDisplayPort);
1027 osdRefresh(simulationTime);
1029 // then
1030 displayPortTestBufferSubstring(9, 10, " ");
1032 // given
1033 // low battery
1034 simulationBatteryVoltage = 1400;
1035 simulationBatteryState = BATTERY_WARNING;
1037 // when
1038 displayClearScreen(&testDisplayPort);
1039 osdRefresh(simulationTime);
1041 // then
1042 displayPortTestBufferSubstring(9, 10, "LOW BATTERY ");
1044 // given
1045 // critical battery
1046 simulationBatteryVoltage = 1320;
1047 simulationBatteryState = BATTERY_CRITICAL;
1049 // when
1050 displayClearScreen(&testDisplayPort);
1051 osdRefresh(simulationTime);
1052 osdRefresh(simulationTime);
1054 // then
1055 displayPortTestBufferSubstring(9, 10, " LAND NOW ");
1057 // given
1058 // full battery
1059 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount);
1060 simulationBatteryState = BATTERY_OK;
1062 // when
1063 displayClearScreen(&testDisplayPort);
1064 osdRefresh(simulationTime);
1066 // then
1067 displayPortTestBufferSubstring(9, 10, " ");
1069 // TODO
1073 * Tests the time string formatting function with a series of precision settings and time values.
1075 TEST_F(OsdTest, TestFormatTimeString)
1077 char buff[OSD_ELEMENT_BUFFER_LENGTH];
1079 /* Seconds precision, 0 us */
1080 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0);
1081 EXPECT_EQ(0, strcmp("00:00", buff));
1083 /* Seconds precision, 0.9 seconds */
1084 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6);
1085 EXPECT_EQ(0, strcmp("00:00", buff));
1087 /* Seconds precision, 10 seconds */
1088 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6);
1089 EXPECT_EQ(0, strcmp("00:10", buff));
1091 /* Seconds precision, 1 minute */
1092 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6);
1093 EXPECT_EQ(0, strcmp("01:00", buff));
1095 /* Seconds precision, 1 minute 59 seconds */
1096 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6);
1097 EXPECT_EQ(0, strcmp("01:59", buff));
1099 /* Hundredths precision, 0 us */
1100 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0);
1101 EXPECT_EQ(0, strcmp("00:00.00", buff));
1103 /* Hundredths precision, 10 milliseconds (one 100th of a second) */
1104 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3);
1105 EXPECT_EQ(0, strcmp("00:00.01", buff));
1107 /* Hundredths precision, 0.9 seconds */
1108 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6);
1109 EXPECT_EQ(0, strcmp("00:00.90", buff));
1111 /* Hundredths precision, 10 seconds */
1112 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6);
1113 EXPECT_EQ(0, strcmp("00:10.00", buff));
1115 /* Hundredths precision, 1 minute */
1116 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6);
1117 EXPECT_EQ(0, strcmp("01:00.00", buff));
1119 /* Hundredths precision, 1 minute 59 seconds */
1120 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6);
1121 EXPECT_EQ(0, strcmp("01:59.00", buff));
1124 TEST_F(OsdTest, TestConvertTemperatureUnits)
1126 /* In Celsius */
1127 osdConfigMutable()->units = UNIT_METRIC;
1128 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 40);
1130 /* In Fahrenheit */
1131 osdConfigMutable()->units = UNIT_IMPERIAL;
1132 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 104);
1134 /* In Fahrenheit with rounding */
1135 osdConfigMutable()->units = UNIT_IMPERIAL;
1136 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(41), 106);
1139 TEST_F(OsdTest, TestGpsElements)
1141 // given
1142 osdElementConfigMutable()->item_pos[OSD_GPS_SATS] = OSD_POS(2, 4) | OSD_PROFILE_1_FLAG;
1144 sensorsSet(SENSOR_GPS);
1145 osdAnalyzeActiveElements();
1147 // when
1148 simulationGpsHealthy = false;
1149 gpsSol.numSat = 0;
1151 displayClearScreen(&testDisplayPort);
1152 osdRefresh(simulationTime);
1154 // then
1155 // Sat indicator should blink and show "NC"
1156 for (int i = 0; i < 15; i++) {
1157 // Blinking should happen at 5Hz
1158 simulationTime += 0.2e6;
1159 osdRefresh(simulationTime);
1161 if (i % 2 == 0) {
1162 displayPortTestBufferSubstring(2, 4, "%c%cNC", SYM_SAT_L, SYM_SAT_R);
1163 } else {
1164 displayPortTestBufferIsEmpty();
1168 // when
1169 simulationGpsHealthy = true;
1170 gpsSol.numSat = 0;
1172 displayClearScreen(&testDisplayPort);
1173 osdRefresh(simulationTime);
1175 // then
1176 // Sat indicator should blink and show "0"
1177 for (int i = 0; i < 15; i++) {
1178 // Blinking should happen at 5Hz
1179 simulationTime += 0.2e6;
1180 osdRefresh(simulationTime);
1182 if (i % 2 == 0) {
1183 displayPortTestBufferSubstring(2, 4, "%c%c 0", SYM_SAT_L, SYM_SAT_R);
1184 } else {
1185 displayPortTestBufferIsEmpty();
1189 // when
1190 simulationGpsHealthy = true;
1191 gpsSol.numSat = 10;
1193 displayClearScreen(&testDisplayPort);
1194 osdRefresh(simulationTime);
1196 // then
1197 // Sat indicator should show "10" without flashing
1198 for (int i = 0; i < 15; i++) {
1199 // Blinking should happen at 5Hz
1200 simulationTime += 0.2e6;
1201 osdRefresh(simulationTime);
1203 displayPortTestBufferSubstring(2, 4, "%c%c10", SYM_SAT_L, SYM_SAT_R);
1207 // STUBS
1208 extern "C" {
1209 bool featureIsEnabled(uint32_t f) { return simulationFeatureFlags & f; }
1211 void beeperConfirmationBeeps(uint8_t) {}
1213 bool isModeActivationConditionPresent(boxId_e) {
1214 return false;
1217 bool IS_RC_MODE_ACTIVE(boxId_e) {
1218 return false;
1221 uint32_t micros() {
1222 return simulationTime;
1225 uint32_t millis() {
1226 return micros() / 1000;
1229 bool isBeeperOn() {
1230 return false;
1233 bool airmodeIsEnabled() {
1234 return false;
1237 uint8_t getCurrentPidProfileIndex() {
1238 return 0;
1241 uint8_t getCurrentControlRateProfileIndex() {
1242 return 0;
1245 batteryState_e getBatteryState() {
1246 return simulationBatteryState;
1249 uint8_t getBatteryCellCount() {
1250 return simulationBatteryCellCount;
1253 uint16_t getBatteryVoltage() {
1254 return simulationBatteryVoltage;
1257 uint16_t getBatteryAverageCellVoltage() {
1258 return simulationBatteryVoltage / simulationBatteryCellCount;
1261 int32_t getAmperage() {
1262 return simulationBatteryAmperage;
1265 int32_t getMAhDrawn() {
1266 return simulationMahDrawn;
1269 int32_t getEstimatedAltitudeCm() {
1270 return simulationAltitude;
1273 int32_t getEstimatedVario() {
1274 return simulationVerticalSpeed;
1277 int32_t blackboxGetLogNumber() {
1278 return 0;
1281 bool isBlackboxDeviceWorking() {
1282 return true;
1285 bool isBlackboxDeviceFull() {
1286 return false;
1289 bool isSerialTransmitBufferEmpty(const serialPort_t *) {
1290 return false;
1293 void serialWrite(serialPort_t *, uint8_t) {}
1295 bool cmsDisplayPortRegister(displayPort_t *) {
1296 return false;
1299 uint16_t getRssi(void) { return rssi; }
1301 uint8_t getRssiPercent(void) { return scaleRange(rssi, 0, RSSI_MAX_VALUE, 0, 100); }
1303 uint16_t rxGetLinkQuality(void) { return LINK_QUALITY_MAX_VALUE; }
1305 uint16_t getCoreTemperatureCelsius(void) { return simulationCoreTemperature; }
1307 bool isFlipOverAfterCrashActive(void) { return false; }
1309 float pidItermAccelerator(void) { return 1.0; }
1310 uint8_t getMotorCount(void){ return 4; }
1311 bool areMotorsRunning(void){ return true; }
1312 bool pidOsdAntiGravityActive(void) { return false; }
1313 bool failsafeIsActive(void) { return false; }
1314 bool gpsRescueIsConfigured(void) { return false; }
1315 bool gpsIsHealthy(void) { return simulationGpsHealthy; }
1316 int8_t calculateThrottlePercent(void) { return 0; }
1317 uint32_t persistentObjectRead(persistentObjectId_e) { return 0; }
1318 void persistentObjectWrite(persistentObjectId_e, uint32_t) {}
1319 bool isUpright(void) { return true; }
1320 float getMotorOutputLow(void) { return 1000.0; }
1321 float getMotorOutputHigh(void) { return 2047.0; }