Move telemetry displayport init and cms device registering
[betaflight.git] / src / test / unit / osd_unittest.cc
blob3272f830273feb9fb462d1bf3945a9b2086a6762
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;
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;
123 osdConfigMutable()->framerate_hz = 12;
125 rssi = 1024;
127 simulationBatteryState = BATTERY_OK;
128 simulationBatteryCellCount = 4;
129 simulationBatteryVoltage = 1680;
130 simulationBatteryAmperage = 0;
131 simulationMahDrawn = 0;
132 simulationAltitude = 0;
133 simulationVerticalSpeed = 0;
134 simulationCoreTemperature = 0;
135 simulationGpsHealthy = false;
137 rcData[PITCH] = 1500;
139 osdFlyTime = 0;
141 DISABLE_ARMING_FLAG(ARMED);
144 void osdRefresh()
146 while (osdUpdateCheck(simulationTime, 0)) {
147 osdUpdate(simulationTime);
148 simulationTime += 10;
150 simulationTime += 0.1e6;
153 * Performs a test of the OSD actions on arming.
154 * (reused throughout the test suite)
156 void doTestArm(bool testEmpty = true)
158 // given
159 // craft has been armed
160 ENABLE_ARMING_FLAG(ARMED);
162 simulationTime += 5e6;
163 // when
164 // sufficient OSD updates have been called
165 osdRefresh();
167 // then
168 // arming alert displayed
169 displayPortTestBufferSubstring(12, 7, "ARMED");
171 // given
172 // armed alert times out (0.5 seconds)
173 simulationTime += 0.5e6;
175 // when
176 // sufficient OSD updates have been called
177 osdRefresh();
179 // then
180 // arming alert disappears
181 #ifdef DEBUG_OSD
182 displayPortTestPrint();
183 #endif
184 if (testEmpty) {
185 displayPortTestBufferIsEmpty();
190 * Auxiliary function. Test is there're stats that must be shown
192 bool isSomeStatEnabled(void) {
193 return (osdConfigMutable()->enabled_stats != 0);
197 * Performs a test of the OSD actions on disarming.
198 * (reused throughout the test suite)
200 void doTestDisarm()
202 // given
203 // craft is disarmed after having been armed
204 DISABLE_ARMING_FLAG(ARMED);
206 // when
207 // sufficient OSD updates have been called
208 osdRefresh();
210 // then
211 // post flight statistics displayed
212 if (isSomeStatEnabled()) {
213 unsigned enabledStats = osdConfigMutable()->enabled_stats;
214 unsigned count = 0;
215 while (enabledStats) {
216 count += enabledStats & 1;
217 enabledStats >>= 1;
220 displayPortTestBufferSubstring(2, 7 - count / 2, " --- STATS ---");
224 void setupStats(void)
226 // this set of enabled post flight statistics
227 osdStatSetState(OSD_STAT_MAX_SPEED, true);
228 osdStatSetState(OSD_STAT_MIN_BATTERY, true);
229 osdStatSetState(OSD_STAT_MIN_RSSI, true);
230 osdStatSetState(OSD_STAT_MAX_CURRENT, false);
231 osdStatSetState(OSD_STAT_USED_MAH, false);
232 osdStatSetState(OSD_STAT_MAX_ALTITUDE, true);
233 osdStatSetState(OSD_STAT_BLACKBOX, false);
234 osdStatSetState(OSD_STAT_END_BATTERY, true);
235 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
236 osdStatSetState(OSD_STAT_MAX_DISTANCE, true);
237 osdStatSetState(OSD_STAT_FLIGHT_DISTANCE, true);
238 osdStatSetState(OSD_STAT_BLACKBOX_NUMBER, false);
239 osdStatSetState(OSD_STAT_MAX_G_FORCE, false);
240 osdStatSetState(OSD_STAT_MAX_ESC_TEMP, false);
241 osdStatSetState(OSD_STAT_MAX_ESC_RPM, false);
244 void simulateFlight(void)
246 // these conditions occur during flight
247 rssi = 1024;
248 gpsSol.groundSpeed = 500;
249 GPS_distanceToHome = 20;
250 GPS_distanceFlownInCm = 2000;
251 simulationBatteryVoltage = 1580;
252 simulationAltitude = 100;
253 simulationTime += 1e6;
254 while (osdUpdateCheck(simulationTime, 0)) {
255 osdUpdate(simulationTime);
256 simulationTime += 10;
259 rssi = 512;
260 gpsSol.groundSpeed = 800;
261 GPS_distanceToHome = 50;
262 GPS_distanceFlownInCm = 10000;
263 simulationBatteryVoltage = 1470;
264 simulationAltitude = 150;
265 simulationTime += 1e6;
266 while (osdUpdateCheck(simulationTime, 0)) {
267 osdUpdate(simulationTime);
268 simulationTime += 10;
271 rssi = 256;
272 gpsSol.groundSpeed = 200;
273 GPS_distanceToHome = 100;
274 GPS_distanceFlownInCm = 20000;
275 simulationBatteryVoltage = 1520;
276 simulationAltitude = 200;
277 simulationTime += 1e6;
278 while (osdUpdateCheck(simulationTime, 0)) {
279 osdUpdate(simulationTime);
280 simulationTime += 10;
283 rssi = 256;
284 gpsSol.groundSpeed = 800;
285 GPS_distanceToHome = 100;
286 GPS_distanceFlownInCm = 10000;
287 simulationBatteryVoltage = 1470;
288 simulationAltitude = 200; // converts to 6.56168 feet which rounds to 6.6 in imperial units stats test
289 simulationTime += 1e6;
290 while (osdUpdateCheck(simulationTime, 0)) {
291 osdUpdate(simulationTime);
292 simulationTime += 10;
295 simulationBatteryVoltage = 1520;
296 simulationTime += 1e6;
297 while (osdUpdateCheck(simulationTime, 0)) {
298 osdUpdate(simulationTime);
299 simulationTime += 10;
302 rssi = 256;
303 gpsSol.groundSpeed = 800;
304 GPS_distanceToHome = 1150;
305 GPS_distanceFlownInCm = 1050000;
306 simulationBatteryVoltage = 1470;
307 simulationAltitude = 200;
308 simulationTime += 1e6;
309 while (osdUpdateCheck(simulationTime, 0)) {
310 osdUpdate(simulationTime);
311 simulationTime += 10;
314 simulationBatteryVoltage = 1520;
315 simulationTime += 1e6;
318 class OsdTest : public ::testing::Test
320 protected:
321 static void SetUpTestCase() {
322 displayPortTestInit();
325 virtual void SetUp() {
326 setDefaultSimulationState();
329 virtual void TearDown() {
330 // Clean up the armed state without showing stats at the end of a test
331 osdConfigMutable()->enabled_stats = 0;
333 doTestDisarm();
338 * Tests initialisation of the OSD and the power on splash screen.
340 TEST_F(OsdTest, TestInit)
342 // given
343 // display port is initialised
344 displayPortTestInit();
346 // and
347 // default state values are set
348 setDefaultSimulationState();
350 // and
351 // this battery configuration (used for battery voltage elements)
352 batteryConfigMutable()->vbatmincellvoltage = 330;
353 batteryConfigMutable()->vbatmaxcellvoltage = 430;
355 // when
356 // OSD is initialised
357 osdInit(&testDisplayPort, OSD_DISPLAYPORT_DEVICE_AUTO);
359 osdRefresh();
361 // then
362 // display buffer should contain splash screen
363 displayPortTestBufferSubstring(7, 8, "MENU:THR MID");
364 displayPortTestBufferSubstring(11, 9, "+ YAW LEFT");
365 displayPortTestBufferSubstring(11, 10, "+ PITCH UP");
367 // when
368 // splash screen timeout has elapsed
369 simulationTime += 4e6;
370 osdRefresh();
372 // then
373 // display buffer should be empty
374 #ifdef DEBUG_OSD
375 displayPortTestPrint();
376 #endif
377 displayPortTestBufferIsEmpty();
381 * Tests visibility of the ARMED notification after arming.
383 TEST_F(OsdTest, TestArm)
385 doTestArm();
389 * Tests display and timeout of the post flight statistics screen after disarming.
391 TEST_F(OsdTest, TestDisarm)
393 doTestArm();
395 doTestDisarm();
397 // given
398 // post flight stats times out (60 seconds)
399 simulationTime += 60e6;
401 // when
402 // sufficient OSD updates have been called
403 osdRefresh();
405 // then
406 // post flight stats screen disappears
407 #ifdef DEBUG_OSD
408 displayPortTestPrint();
409 #endif
410 displayPortTestBufferIsEmpty();
414 * Tests disarming and immediately rearming clears post flight stats and shows ARMED notification.
416 TEST_F(OsdTest, TestDisarmWithImmediateRearm)
418 doTestArm();
420 doTestDisarm();
422 doTestArm();
426 * Tests dismissing the statistics screen with pitch stick after disarming.
428 TEST_F(OsdTest, TestDisarmWithDismissStats)
430 doTestArm();
432 doTestDisarm();
434 // given
435 // sticks have been moved
436 rcData[PITCH] = 1800;
438 // when
439 // sufficient OSD updates have been called
440 osdRefresh();
442 // then
443 // post flight stats screen disappears
444 #ifdef DEBUG_OSD
445 displayPortTestPrint();
446 #endif
447 displayPortTestBufferIsEmpty();
451 * Tests the calculation of timing in statistics
453 TEST_F(OsdTest, TestStatsTiming)
455 // given
456 osdStatSetState(OSD_STAT_RTC_DATE_TIME, true);
457 osdStatSetState(OSD_STAT_TIMER_1, true);
458 osdStatSetState(OSD_STAT_TIMER_2, true);
460 // and
461 // this timer 1 configuration
462 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0);
464 // and
465 // this timer 2 configuration
466 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0);
468 // and
469 // this RTC time
470 dateTime_t dateTime;
471 dateTime.year = 2017;
472 dateTime.month = 11;
473 dateTime.day = 19;
474 dateTime.hours = 10;
475 dateTime.minutes = 12;
476 dateTime.seconds = 0;
477 dateTime.millis = 0;
478 rtcSetDateTime(&dateTime);
480 // when
481 // the craft is armed
482 doTestArm();
484 // and
485 // these conditions occur during flight
486 simulationTime += 1e6;
487 osdRefresh();
489 // and
490 // the craft is disarmed
491 doTestDisarm();
493 // and
494 // the craft is armed again
495 doTestArm();
497 // and
498 // these conditions occur during flight
499 simulationTime += 1e6;
500 osdRefresh();
502 // and
503 // the craft is disarmed
504 doTestDisarm();
506 // then
507 // statistics screen should display the following
508 int row = 7;
509 displayPortTestBufferSubstring(2, row++, "2017-11-19 10:12:");
510 displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:13.61");
511 displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:01");
515 * Tests the calculation of statistics with imperial unit output.
517 TEST_F(OsdTest, TestStatsImperial)
519 // given
520 setupStats();
522 // and
523 // using imperial unit system
524 osdConfigMutable()->units = UNIT_IMPERIAL;
526 // and
527 // a GPS fix is present
528 stateFlags |= GPS_FIX | GPS_FIX_HOME;
530 // when
531 // the craft is armed
532 doTestArm();
534 // and
535 simulateFlight();
537 // and
538 // the craft is disarmed
539 doTestDisarm();
541 // then
542 // statistics screen should display the following
543 int row = 5;
544 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.6%c", SYM_FT);
545 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 17");
546 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 3772%c", SYM_FT);
547 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 6.52%c", SYM_MILES);
548 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
549 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
550 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
554 * Tests the calculation of statistics with metric unit output.
555 * (essentially an abridged version of the previous test
557 TEST_F(OsdTest, TestStatsMetric)
559 // given
560 setupStats();
562 // and
563 // using metric unit system
564 osdConfigMutable()->units = UNIT_METRIC;
566 // when
567 // the craft is armed
568 doTestArm();
570 // and
571 simulateFlight();
573 // and
574 // the craft is disarmed
575 doTestDisarm();
577 // then
578 // statistics screen should display the following
579 int row = 5;
580 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
581 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
582 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
583 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
584 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
585 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
586 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
590 * Tests the calculation of statistics with metric unit output.
591 * (essentially an abridged version of the previous test
593 TEST_F(OsdTest, TestStatsMetricDistanceUnits)
595 // given
596 setupStats();
598 // and
599 // using metric unit system
600 osdConfigMutable()->units = UNIT_METRIC;
602 // when
603 // the craft is armed
604 doTestArm();
606 // and
607 simulateFlight();
609 // and
610 // the craft is disarmed
611 doTestDisarm();
613 // then
614 // statistics screen should display the following
615 int row = 5;
616 displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
617 displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
618 displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 1.15%c", SYM_KM);
619 displayPortTestBufferSubstring(2, row++, "FLIGHT DISTANCE : 10.5%c", SYM_KM);
620 displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.70%c", SYM_VOLT);
621 displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.20%c", SYM_VOLT);
622 displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
626 * Tests activation of alarms and element flashing.
628 TEST_F(OsdTest, TestAlarms)
630 // given
631 sensorsSet(SENSOR_GPS);
633 // and
634 // the following OSD elements are visible
635 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
636 osdElementConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | OSD_PROFILE_1_FLAG;
637 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | OSD_PROFILE_1_FLAG;
638 osdElementConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | OSD_PROFILE_1_FLAG;
639 osdElementConfigMutable()->item_pos[OSD_REMAINING_TIME_ESTIMATE] = OSD_POS(1, 2) | OSD_PROFILE_1_FLAG;
640 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
642 // and
643 // this set of alarm values
644 osdConfigMutable()->rssi_alarm = 20;
645 osdConfigMutable()->cap_alarm = 2200;
646 osdConfigMutable()->alt_alarm = 100; // meters
648 osdAnalyzeActiveElements();
650 // and
651 // this timer 1 configuration
652 osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 5);
653 EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]));
654 EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1]));
655 EXPECT_EQ(5, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1]));
657 // and
658 // this timer 2 configuration
659 osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 2);
660 EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]));
661 EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2]));
662 EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2]));
664 // and
665 // using the metric unit system
666 osdConfigMutable()->units = UNIT_METRIC;
668 // when
669 // time is passing by
670 simulationTime += 60e6;
671 osdRefresh();
673 // and
674 // the craft is armed
675 doTestArm(false);
677 simulationTime += 70e6;
678 osdRefresh();
680 // then
681 // no elements should flash as all values are out of alarm range
682 for (int i = 0; i < 30; i++) {
683 // Check for visibility every 100ms, elements should always be visible
684 simulationTime += 0.1e6;
685 osdRefresh();
687 #ifdef DEBUG_OSD
688 printf("%d\n", i);
689 #endif
690 displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer
691 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
692 displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT);
693 displayPortTestBufferSubstring(20, 1, "%c04:", SYM_ON_M); // only test the minute part of the timer
694 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
697 // when
698 // all values are out of range
699 rssi = 128;
700 simulationBatteryState = BATTERY_CRITICAL;
701 simulationBatteryVoltage = 1350;
702 simulationAltitude = 12000;
703 simulationMahDrawn = 999999;
705 simulationTime += 60e6;
706 osdRefresh();
708 // then
709 // elements showing values in alarm range should flash
710 simulationTime += 1000000;
711 simulationTime -= simulationTime % 1000000;
712 timeUs_t startTime = simulationTime;
713 for (int i = 0; i < 15; i++) {
714 // Blinking should happen at 2Hz
715 simulationTime = startTime + i*0.25e6;
716 osdRefresh();
718 #ifdef DEBUG_OSD
719 printf("%d\n", i);
720 displayPortTestPrint();
721 #endif
722 if (i % 2 == 1) {
723 displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI);
724 displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT);
725 displayPortTestBufferSubstring(1, 1, "%c02:", SYM_FLY_M); // only test the minute part of the timer
726 displayPortTestBufferSubstring(20, 1, "%c05:", SYM_ON_M); // only test the minute part of the timer
727 displayPortTestBufferSubstring(23, 7, "%c120.0%c", SYM_ALTITUDE, SYM_M);
728 } else {
729 displayPortTestBufferIsEmpty();
735 * Tests the RSSI OSD element.
737 TEST_F(OsdTest, TestElementRssi)
739 // given
740 osdElementConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
741 osdConfigMutable()->rssi_alarm = 0;
743 osdAnalyzeActiveElements();
745 // when
746 rssi = 1024;
747 displayClearScreen(&testDisplayPort);
748 osdRefresh();
750 // then
751 displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
753 // when
754 rssi = 0;
755 displayClearScreen(&testDisplayPort);
756 osdRefresh();
758 // then
759 displayPortTestBufferSubstring(8, 1, "%c 0", SYM_RSSI);
761 // when
762 rssi = 512;
763 displayClearScreen(&testDisplayPort);
764 osdRefresh();
766 // then
767 displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI);
771 * Tests the instantaneous battery current OSD element.
773 TEST_F(OsdTest, TestElementAmperage)
775 // given
776 osdElementConfigMutable()->item_pos[OSD_CURRENT_DRAW] = OSD_POS(1, 12) | OSD_PROFILE_1_FLAG;
778 osdAnalyzeActiveElements();
780 // when
781 simulationBatteryAmperage = 0;
782 displayClearScreen(&testDisplayPort);
783 osdRefresh();
785 // then
786 displayPortTestBufferSubstring(1, 12, " 0.00%c", SYM_AMP);
788 // when
789 simulationBatteryAmperage = 2156;
790 displayClearScreen(&testDisplayPort);
791 osdRefresh();
793 // then
794 displayPortTestBufferSubstring(1, 12, " 21.56%c", SYM_AMP);
796 // when
797 simulationBatteryAmperage = 12345;
798 displayClearScreen(&testDisplayPort);
799 osdRefresh();
801 // then
802 displayPortTestBufferSubstring(1, 12, "123.45%c", SYM_AMP);
806 * Tests the battery capacity drawn OSD element.
808 TEST_F(OsdTest, TestElementMahDrawn)
810 // given
811 osdElementConfigMutable()->item_pos[OSD_MAH_DRAWN] = OSD_POS(1, 11) | OSD_PROFILE_1_FLAG;
813 osdAnalyzeActiveElements();
815 // when
816 simulationMahDrawn = 0;
817 displayClearScreen(&testDisplayPort);
818 osdRefresh();
820 // then
821 displayPortTestBufferSubstring(1, 11, " 0%c", SYM_MAH);
823 // when
824 simulationMahDrawn = 4;
825 displayClearScreen(&testDisplayPort);
826 osdRefresh();
828 // then
829 displayPortTestBufferSubstring(1, 11, " 4%c", SYM_MAH);
831 // when
832 simulationMahDrawn = 15;
833 displayClearScreen(&testDisplayPort);
834 osdRefresh();
836 // then
837 displayPortTestBufferSubstring(1, 11, " 15%c", SYM_MAH);
839 // when
840 simulationMahDrawn = 246;
841 displayClearScreen(&testDisplayPort);
842 osdRefresh();
844 // then
845 displayPortTestBufferSubstring(1, 11, " 246%c", SYM_MAH);
847 // when
848 simulationMahDrawn = 1042;
849 displayClearScreen(&testDisplayPort);
850 osdRefresh();
852 // then
853 displayPortTestBufferSubstring(1, 11, "1042%c", SYM_MAH);
857 * Tests the instantaneous electrical power OSD element.
859 TEST_F(OsdTest, TestElementPower)
861 // given
862 osdElementConfigMutable()->item_pos[OSD_POWER] = OSD_POS(1, 10) | OSD_PROFILE_1_FLAG;
864 osdAnalyzeActiveElements();
866 // and
867 simulationBatteryVoltage = 1000; // 10V
869 // and
870 simulationBatteryAmperage = 0; // 0A
872 // when
873 displayClearScreen(&testDisplayPort);
874 osdRefresh();
876 // then
877 displayPortTestBufferSubstring(1, 10, " 0W");
879 // given
880 simulationBatteryAmperage = 10; // 0.1A
882 // when
883 displayClearScreen(&testDisplayPort);
884 osdRefresh();
886 // then
887 displayPortTestBufferSubstring(1, 10, " 1W");
889 // given
890 simulationBatteryAmperage = 120; // 1.2A
892 // when
893 displayClearScreen(&testDisplayPort);
894 osdRefresh();
896 // then
897 displayPortTestBufferSubstring(1, 10, " 12W");
899 // given
900 simulationBatteryAmperage = 1230; // 12.3A
902 // when
903 displayClearScreen(&testDisplayPort);
904 osdRefresh();
906 // then
907 displayPortTestBufferSubstring(1, 10, " 123W");
909 // given
910 simulationBatteryAmperage = 12340; // 123.4A
912 // when
913 displayClearScreen(&testDisplayPort);
914 osdRefresh();
916 // then
917 displayPortTestBufferSubstring(1, 10, "1234W");
921 * Tests the altitude OSD element.
923 TEST_F(OsdTest, TestElementAltitude)
925 // given
926 osdElementConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | OSD_PROFILE_1_FLAG;
928 osdAnalyzeActiveElements();
930 // and
931 osdConfigMutable()->units = UNIT_METRIC;
932 sensorsClear(SENSOR_GPS);
934 // when
935 simulationAltitude = 0;
936 displayClearScreen(&testDisplayPort);
937 osdRefresh();
939 // then
940 displayPortTestBufferSubstring(23, 7, "%c-", SYM_ALTITUDE);
942 // when
943 sensorsSet(SENSOR_GPS);
944 displayClearScreen(&testDisplayPort);
945 osdRefresh();
947 // then
948 displayPortTestBufferSubstring(23, 7, "%c0.0%c", SYM_ALTITUDE, SYM_M);
950 // when
951 simulationAltitude = 247; // rounds to 2.5m
952 displayClearScreen(&testDisplayPort);
953 osdRefresh();
955 // then
956 displayPortTestBufferSubstring(23, 7, "%c2.5%c", SYM_ALTITUDE, SYM_M);
958 // when
959 simulationAltitude = 4247; // rounds to 42.5m
960 displayClearScreen(&testDisplayPort);
961 osdRefresh();
963 // then
964 displayPortTestBufferSubstring(23, 7, "%c42.5%c", SYM_ALTITUDE, SYM_M);
966 // when
967 simulationAltitude = -247; // rounds to -2.5m
968 displayClearScreen(&testDisplayPort);
969 osdRefresh();
971 // then
972 displayPortTestBufferSubstring(23, 7, "%c-2.5%c", SYM_ALTITUDE, SYM_M);
974 // when
975 simulationAltitude = -70;
976 displayClearScreen(&testDisplayPort);
977 osdRefresh();
979 // then
980 displayPortTestBufferSubstring(23, 7, "%c-0.7%c", SYM_ALTITUDE, SYM_M);
985 * Tests the core temperature OSD element.
987 TEST_F(OsdTest, TestElementCoreTemperature)
989 // given
990 osdElementConfigMutable()->item_pos[OSD_CORE_TEMPERATURE] = OSD_POS(1, 8) | OSD_PROFILE_1_FLAG;
992 osdAnalyzeActiveElements();
994 // and
995 osdConfigMutable()->units = UNIT_METRIC;
997 // and
998 simulationCoreTemperature = 0;
1000 // when
1001 displayClearScreen(&testDisplayPort);
1002 osdRefresh();
1004 // then
1005 displayPortTestBufferSubstring(1, 8, "C%c 0%c", SYM_TEMPERATURE, SYM_C);
1007 // given
1008 simulationCoreTemperature = 33;
1010 // when
1011 displayClearScreen(&testDisplayPort);
1012 osdRefresh();
1014 // then
1015 displayPortTestBufferSubstring(1, 8, "C%c 33%c", SYM_TEMPERATURE, SYM_C);
1017 // given
1018 osdConfigMutable()->units = UNIT_IMPERIAL;
1020 // when
1021 displayClearScreen(&testDisplayPort);
1022 osdRefresh();
1024 // then
1025 displayPortTestBufferSubstring(1, 8, "C%c 91%c", SYM_TEMPERATURE, SYM_F);
1029 * Tests the battery notifications shown on the warnings OSD element.
1031 TEST_F(OsdTest, TestElementWarningsBattery)
1033 // given
1034 osdElementConfigMutable()->item_pos[OSD_WARNINGS] = OSD_POS(9, 10) | OSD_PROFILE_1_FLAG;
1035 osdConfigMutable()->enabledWarnings = 0; // disable all warnings
1036 osdWarnSetState(OSD_WARNING_BATTERY_WARNING, true);
1037 osdWarnSetState(OSD_WARNING_BATTERY_CRITICAL, true);
1038 osdWarnSetState(OSD_WARNING_BATTERY_NOT_FULL, true);
1040 osdAnalyzeActiveElements();
1042 // and
1043 batteryConfigMutable()->vbatfullcellvoltage = 410;
1045 // and
1046 // 4S battery
1047 simulationBatteryCellCount = 4;
1049 // and
1050 // used battery
1051 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount) - 1;
1052 simulationBatteryState = BATTERY_OK;
1054 // when
1055 displayClearScreen(&testDisplayPort);
1056 // Delay as the warnings are flashing
1057 simulationTime += 1000000;
1058 simulationTime -= simulationTime % 1000000;
1059 osdRefresh();
1061 // then
1062 displayPortTestBufferSubstring(9, 10, "BATT < FULL");
1064 // given
1065 // full battery
1066 simulationBatteryVoltage = 1680;
1067 simulationBatteryState = BATTERY_OK;
1069 // when
1070 displayClearScreen(&testDisplayPort);
1071 osdRefresh();
1073 // then
1074 displayPortTestBufferSubstring(9, 10, " ");
1076 // given
1077 // low battery
1078 simulationBatteryVoltage = 1400;
1079 simulationBatteryState = BATTERY_WARNING;
1081 // when
1082 displayClearScreen(&testDisplayPort);
1083 // Delay as the warnings are flashing
1084 simulationTime += 1000000;
1085 simulationTime -= simulationTime % 1000000;
1086 simulationTime += 0.25e6;
1087 osdRefresh();
1089 // then
1090 displayPortTestBufferSubstring(9, 10, "LOW BATTERY ");
1092 // given
1093 // critical battery
1094 simulationBatteryVoltage = 1320;
1095 simulationBatteryState = BATTERY_CRITICAL;
1097 // when
1098 displayClearScreen(&testDisplayPort);
1099 // Delay as the warnings are flashing
1100 simulationTime += 1000000;
1101 simulationTime -= simulationTime % 1000000;
1102 simulationTime += 0.25e6;
1103 osdRefresh();
1105 // then
1106 displayPortTestBufferSubstring(9, 10, " LAND NOW ");
1108 // given
1109 // full battery
1110 simulationBatteryVoltage = ((batteryConfig()->vbatmaxcellvoltage - 20) * simulationBatteryCellCount);
1111 simulationBatteryState = BATTERY_OK;
1113 // when
1114 displayClearScreen(&testDisplayPort);
1115 osdRefresh();
1117 // then
1118 displayPortTestBufferSubstring(9, 10, " ");
1120 // TODO
1124 * Tests the time string formatting function with a series of precision settings and time values.
1126 TEST_F(OsdTest, TestFormatTimeString)
1128 char buff[OSD_ELEMENT_BUFFER_LENGTH];
1130 /* Seconds precision, 0 us */
1131 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0);
1132 EXPECT_EQ(0, strcmp("00:00", buff));
1134 /* Seconds precision, 0.9 seconds */
1135 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6);
1136 EXPECT_EQ(0, strcmp("00:00", buff));
1138 /* Seconds precision, 10 seconds */
1139 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6);
1140 EXPECT_EQ(0, strcmp("00:10", buff));
1142 /* Seconds precision, 1 minute */
1143 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6);
1144 EXPECT_EQ(0, strcmp("01:00", buff));
1146 /* Seconds precision, 1 minute 59 seconds */
1147 osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6);
1148 EXPECT_EQ(0, strcmp("01:59", buff));
1150 /* Hundredths precision, 0 us */
1151 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0);
1152 EXPECT_EQ(0, strcmp("00:00.00", buff));
1154 /* Hundredths precision, 10 milliseconds (one 100th of a second) */
1155 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3);
1156 EXPECT_EQ(0, strcmp("00:00.01", buff));
1158 /* Hundredths precision, 0.9 seconds */
1159 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6);
1160 EXPECT_EQ(0, strcmp("00:00.90", buff));
1162 /* Hundredths precision, 10 seconds */
1163 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6);
1164 EXPECT_EQ(0, strcmp("00:10.00", buff));
1166 /* Hundredths precision, 1 minute */
1167 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6);
1168 EXPECT_EQ(0, strcmp("01:00.00", buff));
1170 /* Hundredths precision, 1 minute 59 seconds */
1171 osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6);
1172 EXPECT_EQ(0, strcmp("01:59.00", buff));
1175 TEST_F(OsdTest, TestConvertTemperatureUnits)
1177 /* In Celsius */
1178 osdConfigMutable()->units = UNIT_METRIC;
1179 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 40);
1181 /* In Fahrenheit */
1182 osdConfigMutable()->units = UNIT_IMPERIAL;
1183 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(40), 104);
1185 /* In Fahrenheit with rounding */
1186 osdConfigMutable()->units = UNIT_IMPERIAL;
1187 EXPECT_EQ(osdConvertTemperatureToSelectedUnit(41), 106);
1190 TEST_F(OsdTest, TestGpsElements)
1192 // given
1193 osdElementConfigMutable()->item_pos[OSD_GPS_SATS] = OSD_POS(2, 4) | OSD_PROFILE_1_FLAG;
1195 sensorsSet(SENSOR_GPS);
1196 osdAnalyzeActiveElements();
1198 // when
1199 simulationGpsHealthy = false;
1200 gpsSol.numSat = 0;
1202 displayClearScreen(&testDisplayPort);
1203 osdRefresh();
1205 // then
1206 // Sat indicator should blink and show "NC"
1207 simulationTime += 1000000;
1208 simulationTime -= simulationTime % 1000000;
1209 timeUs_t startTime = simulationTime;
1210 for (int i = 0; i < 15; i++) {
1211 // Blinking should happen at 2Hz
1212 simulationTime = startTime + i*0.25e6;
1213 osdRefresh();
1215 if (i % 2 == 1) {
1216 displayPortTestBufferSubstring(2, 4, "%c%cNC", SYM_SAT_L, SYM_SAT_R);
1217 } else {
1218 displayPortTestBufferIsEmpty();
1222 // when
1223 simulationGpsHealthy = true;
1224 gpsSol.numSat = 0;
1226 displayClearScreen(&testDisplayPort);
1227 osdRefresh();
1229 // then
1230 // Sat indicator should blink and show "0"
1231 simulationTime += 1000000;
1232 simulationTime -= simulationTime % 1000000;
1233 startTime = simulationTime;
1234 for (int i = 0; i < 15; i++) {
1235 // Blinking should happen at 2Hz
1236 simulationTime = startTime + i*0.25e6;
1237 osdRefresh();
1239 if (i % 2 == 1) {
1240 displayPortTestBufferSubstring(2, 4, "%c%c 0", SYM_SAT_L, SYM_SAT_R);
1241 } else {
1242 displayPortTestBufferIsEmpty();
1246 // when
1247 simulationGpsHealthy = true;
1248 gpsSol.numSat = 10;
1250 displayClearScreen(&testDisplayPort);
1251 osdRefresh();
1253 // then
1254 // Sat indicator should show "10" without flashing
1255 for (int i = 0; i < 15; i++) {
1256 // Blinking should happen at 2Hz
1257 simulationTime += 0.2e6;
1258 osdRefresh();
1260 displayPortTestBufferSubstring(2, 4, "%c%c10", SYM_SAT_L, SYM_SAT_R);
1264 // STUBS
1265 extern "C" {
1266 bool featureIsEnabled(uint32_t f) { return simulationFeatureFlags & f; }
1268 void beeperConfirmationBeeps(uint8_t) {}
1270 bool isModeActivationConditionPresent(boxId_e) {
1271 return false;
1274 bool IS_RC_MODE_ACTIVE(boxId_e) {
1275 return false;
1278 uint32_t micros() {
1279 return simulationTime;
1282 uint32_t millis() {
1283 return micros() / 1000;
1286 bool isBeeperOn() {
1287 return false;
1290 bool airmodeIsEnabled() {
1291 return false;
1294 uint8_t getCurrentPidProfileIndex() {
1295 return 0;
1298 uint8_t getCurrentControlRateProfileIndex() {
1299 return 0;
1302 batteryState_e getBatteryState() {
1303 return simulationBatteryState;
1306 uint8_t getBatteryCellCount() {
1307 return simulationBatteryCellCount;
1310 uint16_t getBatteryVoltage() {
1311 return simulationBatteryVoltage;
1314 uint16_t getBatteryAverageCellVoltage() {
1315 return simulationBatteryVoltage / simulationBatteryCellCount;
1318 int32_t getAmperage() {
1319 return simulationBatteryAmperage;
1322 int32_t getMAhDrawn() {
1323 return simulationMahDrawn;
1326 int32_t getEstimatedAltitudeCm() {
1327 return simulationAltitude;
1330 int32_t getEstimatedVario() {
1331 return simulationVerticalSpeed;
1334 int32_t blackboxGetLogNumber() {
1335 return 0;
1338 bool isBlackboxDeviceWorking() {
1339 return true;
1342 bool isBlackboxDeviceFull() {
1343 return false;
1346 bool isSerialTransmitBufferEmpty(const serialPort_t *) {
1347 return false;
1350 void serialWrite(serialPort_t *, uint8_t) {}
1352 bool cmsDisplayPortRegister(displayPort_t *) {
1353 return false;
1356 uint16_t getRssi(void) { return rssi; }
1358 uint8_t getRssiPercent(void) { return scaleRange(rssi, 0, RSSI_MAX_VALUE, 0, 100); }
1360 uint16_t rxGetLinkQuality(void) { return LINK_QUALITY_MAX_VALUE; }
1362 uint16_t getCoreTemperatureCelsius(void) { return simulationCoreTemperature; }
1364 bool isFlipOverAfterCrashActive(void) { return false; }
1366 float pidItermAccelerator(void) { return 1.0; }
1367 uint8_t getMotorCount(void){ return 4; }
1368 bool areMotorsRunning(void){ return true; }
1369 bool pidOsdAntiGravityActive(void) { return false; }
1370 bool failsafeIsActive(void) { return false; }
1371 bool gpsRescueIsConfigured(void) { return false; }
1372 bool gpsIsHealthy(void) { return simulationGpsHealthy; }
1373 int8_t calculateThrottlePercent(void) { return 0; }
1374 uint32_t persistentObjectRead(persistentObjectId_e) { return 0; }
1375 void persistentObjectWrite(persistentObjectId_e, uint32_t) {}
1376 bool isUpright(void) { return true; }
1377 float getMotorOutputLow(void) { return 1000.0; }
1378 float getMotorOutputHigh(void) { return 2047.0; }
1379 void schedulerIgnoreTaskStateTime(void) { }
1380 void schedulerIgnoreTaskExecRate(void) { }
1381 void schedulerIgnoreTaskExecTime(void) { }
1382 void schedulerSetNextStateTime(timeDelta_t) {}