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/>.
25 #include "build/debug.h"
27 #include "blackbox/blackbox.h"
28 #include "blackbox/blackbox_io.h"
30 #include "common/crc.h"
31 #include "common/printf.h"
32 #include "common/streambuf.h"
33 #include "common/time.h"
34 #include "common/utils.h"
36 #include "config/config.h"
38 #include "drivers/osd_symbols.h"
39 #include "drivers/persistent.h"
40 #include "drivers/serial.h"
41 #include "drivers/system.h"
44 #include "fc/rc_controls.h"
45 #include "fc/rc_modes.h"
46 #include "fc/runtime_config.h"
48 #include "flight/failsafe.h"
49 #include "flight/imu.h"
50 #include "flight/mixer.h"
51 #include "flight/pid.h"
53 #include "io/beeper.h"
55 #include "io/serial.h"
58 #include "osd/osd_elements.h"
59 #include "osd/osd_warnings.h"
62 #include "pg/pg_ids.h"
67 #include "sensors/battery.h"
69 attitudeEulerAngles_t attitude
;
72 pidProfile_t
*currentPidProfile
;
73 float rcData
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
75 uint16_t GPS_distanceToHome
;
76 int16_t GPS_directionToHome
;
77 uint32_t GPS_distanceFlownInCm
;
79 gpsSolutionData_t gpsSol
;
83 PG_REGISTER(batteryConfig_t
, batteryConfig
, PG_BATTERY_CONFIG
, 0);
84 PG_REGISTER(blackboxConfig_t
, blackboxConfig
, PG_BLACKBOX_CONFIG
, 0);
85 PG_REGISTER(systemConfig_t
, systemConfig
, PG_SYSTEM_CONFIG
, 0);
86 PG_REGISTER(pilotConfig_t
, pilotConfig
, PG_PILOT_CONFIG
, 0);
87 PG_REGISTER(imuConfig_t
, imuConfig
, PG_IMU_CONFIG
, 0);
88 PG_REGISTER(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 0);
89 PG_REGISTER(failsafeConfig_t
, failsafeConfig
, PG_FAILSAFE_CONFIG
, 0);
91 timeUs_t simulationTime
= 0;
93 void osdUpdate(timeUs_t currentTimeUs
);
94 uint16_t updateLinkQualitySamples(uint16_t value
);
95 #define LINK_QUALITY_SAMPLE_COUNT 16
98 /* #define DEBUG_OSD */
100 #include "unittest_macros.h"
101 #include "unittest_displayport.h"
102 #include "gtest/gtest.h"
105 PG_REGISTER(flight3DConfig_t
, flight3DConfig
, PG_MOTOR_3D_CONFIG
, 0);
107 boxBitmask_t rcModeActivationMask
;
108 int16_t debug
[DEBUG16_VALUE_COUNT
];
109 uint8_t debugMode
= 0;
111 uint16_t updateLinkQualitySamples(uint16_t value
);
113 extern uint16_t applyRxChannelRangeConfiguraton(int sample
, const rxChannelRangeConfig_t
*range
);
115 void setDefaultSimulationState()
117 setLinkQualityDirect(LINK_QUALITY_MAX_VALUE
);
118 osdConfigMutable()->framerate_hz
= 12;
121 * Performs a test of the OSD actions on arming.
122 * (reused throughout the test suite)
124 void doTestArm(bool testEmpty
= true)
127 // craft has been armed
128 ENABLE_ARMING_FLAG(ARMED
);
130 simulationTime
+= 0.1e6
;
132 // sufficient OSD updates have been called
133 while (osdUpdateCheck(simulationTime
, 0)) {
134 osdUpdate(simulationTime
);
135 simulationTime
+= 10;
139 // arming alert displayed
140 displayPortTestBufferSubstring(13, 8, "ARMED");
143 // armed alert times out (0.5 seconds)
144 simulationTime
+= 0.5e6
;
147 // sufficient OSD updates have been called
148 while (osdUpdateCheck(simulationTime
, 0)) {
149 osdUpdate(simulationTime
);
150 simulationTime
+= 10;
154 // arming alert disappears
156 displayPortTestPrint();
159 displayPortTestBufferIsEmpty();
164 * Auxiliary function. Test is there're stats that must be shown
166 bool isSomeStatEnabled(void)
168 return (osdConfigMutable()->enabled_stats
!= 0);
172 * Performs a test of the OSD actions on disarming.
173 * (reused throughout the test suite)
178 // craft is disarmed after having been armed
179 DISABLE_ARMING_FLAG(ARMED
);
182 // sufficient OSD updates have been called
183 while (osdUpdateCheck(simulationTime
, 0)) {
184 osdUpdate(simulationTime
);
185 simulationTime
+= 10;
189 // post flight statistics displayed
190 if (isSomeStatEnabled()) {
191 displayPortTestBufferSubstring(2, 2, " --- STATS ---");
196 * Tests initialisation of the OSD and the power on splash screen.
198 TEST(LQTest
, TestInit
)
201 // display port is initialised
202 displayPortTestInit();
205 // default state values are set
206 setDefaultSimulationState();
209 // this battery configuration (used for battery voltage elements)
210 batteryConfigMutable()->vbatmincellvoltage
= 330;
211 batteryConfigMutable()->vbatmaxcellvoltage
= 430;
214 // OSD is initialised
215 osdInit(&testDisplayPort
, OSD_DISPLAYPORT_DEVICE_AUTO
);
217 while (osdUpdateCheck(simulationTime
, 0)) {
218 osdUpdate(simulationTime
);
219 simulationTime
+= 10;
223 // display buffer should contain splash screen
224 displayPortTestBufferSubstring(7, 10, "MENU:THR MID");
225 displayPortTestBufferSubstring(11, 11, "+ YAW LEFT");
226 displayPortTestBufferSubstring(11, 12, "+ PITCH UP");
229 // splash screen timeout has elapsed
230 simulationTime
+= 4e6
;
231 while (osdUpdateCheck(simulationTime
, 0)) {
232 osdUpdate(simulationTime
);
233 simulationTime
+= 10;
237 // display buffer should be empty
239 displayPortTestPrint();
241 displayPortTestBufferIsEmpty();
244 * Tests the Tests the OSD_LINK_QUALITY element updateLinkQualitySamples default LQ_SOURCE_NONE
246 TEST(LQTest
, TestElement_LQ_SOURCE_NONE_SAMPLES
)
249 linkQualitySource
= LQ_SOURCE_NONE
;
251 osdElementConfigMutable()->item_pos
[OSD_LINK_QUALITY
] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG
;
252 osdConfigMutable()->link_quality_alarm
= 0;
254 osdAnalyzeActiveElements();
256 // when samples populated 100%
257 for (int x
= 0; x
< LINK_QUALITY_SAMPLE_COUNT
; x
++) {
258 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE
));
261 simulationTime
+= 1000000;
263 while (osdUpdateCheck(simulationTime
, 0)) {
264 osdUpdate(simulationTime
);
265 simulationTime
+= 10;
269 displayPortTestBufferSubstring(8, 1, "%c9", SYM_LINK_QUALITY
);
271 // when updateLinkQualitySamples used 50% rounds to 4
272 for (int x
= 0; x
< LINK_QUALITY_SAMPLE_COUNT
; x
++) {
273 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE
));
274 setLinkQualityDirect(updateLinkQualitySamples(0));
277 simulationTime
+= 1000000;
279 while (osdUpdateCheck(simulationTime
, 0)) {
280 osdUpdate(simulationTime
);
281 simulationTime
+= 10;
285 displayPortTestBufferSubstring(8, 1, "%c4", SYM_LINK_QUALITY
);
288 * Tests the Tests the OSD_LINK_QUALITY element values default LQ_SOURCE_NONE
290 TEST(LQTest
, TestElement_LQ_SOURCE_NONE_VALUES
)
294 linkQualitySource
= LQ_SOURCE_NONE
;
296 osdElementConfigMutable()->item_pos
[OSD_LINK_QUALITY
] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG
;
297 osdConfigMutable()->link_quality_alarm
= 0;
299 osdAnalyzeActiveElements();
300 // when LINK_QUALITY_MAX_VALUE to 1 by 10%
301 uint16_t testscale
= 0;
302 for (int testdigit
= 10; testdigit
> 0; testdigit
--) {
303 testscale
= testdigit
* 102.3;
304 setLinkQualityDirect(testscale
);
305 simulationTime
+= 100000;
306 while (osdUpdateCheck(simulationTime
, 0)) {
307 osdUpdate(simulationTime
);
308 simulationTime
+= 10;
311 printf("%d %d\n",testscale
, testdigit
);
312 displayPortTestPrint();
315 if (testdigit
>= 10) {
316 displayPortTestBufferSubstring(8, 1,"%c9", SYM_LINK_QUALITY
);
318 displayPortTestBufferSubstring(8, 1,"%c%1d", SYM_LINK_QUALITY
, testdigit
- 1);
324 * Tests the OSD_LINK_QUALITY element LQ RX_PROTOCOL_CRSF.
326 TEST(LQTest
, TestElementLQ_PROTOCOL_CRSF_VALUES
)
329 linkQualitySource
= LQ_SOURCE_RX_PROTOCOL_CRSF
;
331 osdElementConfigMutable()->item_pos
[OSD_LINK_QUALITY
] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG
;
332 osdConfigMutable()->link_quality_alarm
= 0;
334 osdAnalyzeActiveElements();
336 simulationTime
+= 1000000;
337 while (osdUpdateCheck(simulationTime
, 0)) {
338 osdUpdate(simulationTime
);
339 simulationTime
+= 10;
342 // crsf setLinkQualityDirect 0-300;
344 for (uint8_t x
= 0; x
<= 99; x
++) {
345 for (uint8_t m
= 0; m
<= 4; m
++) {
347 setLinkQualityDirect(x
);
349 // then rxGetLinkQuality Osd should be x
350 // and RfMode should be m
351 simulationTime
+= 100000;
352 while (osdUpdateCheck(simulationTime
, 0)) {
353 osdUpdate(simulationTime
);
354 simulationTime
+= 10;
356 displayPortTestBufferSubstring(8, 1, "%c%1d:%2d", SYM_LINK_QUALITY
, m
, x
);
361 * Tests the LQ Alarms
364 TEST(LQTest
, TestLQAlarm
)
366 timeUs_t startTime
= simulationTime
;
368 // default state is set
369 setDefaultSimulationState();
371 linkQualitySource
= LQ_SOURCE_NONE
;
374 // the following OSD elements are visible
376 osdElementConfigMutable()->item_pos
[OSD_LINK_QUALITY
] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG
;
379 // this set of alarm values
381 osdConfigMutable()->link_quality_alarm
= 80;
382 stateFlags
|= GPS_FIX
| GPS_FIX_HOME
;
384 osdAnalyzeActiveElements();
387 // using the metric unit system
388 osdConfigMutable()->units
= UNIT_METRIC
;
391 // the craft is armed
394 for (int x
= 0; x
< LINK_QUALITY_SAMPLE_COUNT
; x
++) {
395 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE
));
399 // no elements should flash as all values are out of alarm range
400 // Ensure a consistent start time for testing
401 simulationTime
+= 5000000;
402 simulationTime
-= simulationTime
% 1000000;
403 startTime
= simulationTime
;
404 for (int i
= 0; i
< 30; i
++) {
405 // Check for visibility every 100ms, elements should always be visible
406 simulationTime
= startTime
+ i
*0.1e6
;
407 while (osdUpdateCheck(simulationTime
, 0)) {
408 osdUpdate(simulationTime
);
409 simulationTime
+= 10;
415 displayPortTestBufferSubstring(8, 1, "%c9", SYM_LINK_QUALITY
);
419 setLinkQualityDirect(512);
420 while (osdUpdateCheck(simulationTime
, 0)) {
421 osdUpdate(simulationTime
);
422 simulationTime
+= 10;
426 // elements showing values in alarm range should flash
427 simulationTime
+= 1000000;
428 simulationTime
-= simulationTime
% 1000000;
429 startTime
= simulationTime
;
430 for (int i
= 0; i
< 15; i
++) {
431 // Blinking should happen at 2Hz
432 simulationTime
= startTime
+ i
*0.25e6
;
433 while (osdUpdateCheck(simulationTime
, 0)) {
434 osdUpdate(simulationTime
);
435 simulationTime
+= 10;
439 displayPortTestPrint();
442 displayPortTestBufferSubstring(8, 1, "%c5", SYM_LINK_QUALITY
);
444 displayPortTestBufferIsEmpty();
449 simulationTime
+= 1000000;
450 simulationTime
-= simulationTime
% 1000000;
451 while (osdUpdateCheck(simulationTime
, 0)) {
452 osdUpdate(simulationTime
);
453 simulationTime
+= 10;
461 return simulationTime
;
464 uint32_t microsISR() {
469 return micros() / 1000;
472 bool featureIsEnabled(uint32_t) { return true; }
473 void beeperConfirmationBeeps(uint8_t) {}
474 bool isBeeperOn() { return false; }
475 uint8_t getCurrentPidProfileIndex() { return 0; }
476 uint8_t getCurrentControlRateProfileIndex() { return 0; }
477 batteryState_e
getBatteryState() { return BATTERY_OK
; }
478 uint8_t getBatteryCellCount() { return 4; }
479 uint16_t getBatteryVoltage() { return 1680; }
480 uint16_t getBatteryAverageCellVoltage() { return 420; }
481 int32_t getAmperage() { return 0; }
482 int32_t getMAhDrawn() { return 0; }
483 float getWhDrawn() { return 0.0; }
484 int32_t getEstimatedAltitudeCm() { return 0; }
485 int32_t getEstimatedVario() { return 0; }
486 int32_t blackboxGetLogNumber() { return 0; }
487 bool isBlackboxDeviceWorking() { return true; }
488 bool isBlackboxDeviceFull() { return false; }
489 serialPort_t
*openSerialPort(serialPortIdentifier_e
, serialPortFunction_e
, serialReceiveCallbackPtr
, void *, uint32_t, portMode_e
, portOptions_e
) {return NULL
;}
490 const serialPortConfig_t
*findSerialPortConfig(serialPortFunction_e
) {return NULL
;}
491 bool telemetryCheckRxPortShared(const serialPortConfig_t
*) {return false;}
492 bool cmsDisplayPortRegister(displayPort_t
*) { return false; }
493 uint16_t getCoreTemperatureCelsius(void) { return 0; }
494 bool isFlipOverAfterCrashActive(void) { return false; }
495 float pidItermAccelerator(void) { return 1.0; }
496 uint8_t getMotorCount(void){ return 4; }
497 bool areMotorsRunning(void){ return true; }
498 bool pidOsdAntiGravityActive(void) { return false; }
499 bool failsafeIsActive(void) { return false; }
500 bool failsafeIsReceivingRxData(void) { return true; }
501 bool gpsIsHealthy(void) { return true; }
502 bool gpsRescueIsConfigured(void) { return false; }
503 int8_t calculateThrottlePercent(void) { return 0; }
504 uint32_t persistentObjectRead(persistentObjectId_e
) { return 0; }
505 void persistentObjectWrite(persistentObjectId_e
, uint32_t) {}
506 void failsafeOnRxSuspend(uint32_t ) {}
507 void failsafeOnRxResume(void) {}
508 uint32_t failsafeFailurePeriodMs(void) { return 400; }
509 void featureDisableImmediate(uint32_t) { }
510 bool rxMspFrameComplete(void) { return false; }
511 bool isPPMDataBeingReceived(void) { return false; }
512 bool isPWMDataBeingReceived(void) { return false; }
513 void resetPPMDataReceivedState(void){ }
514 void failsafeOnValidDataReceived(void) { }
515 void failsafeOnValidDataFailed(void) { }
516 void pinioBoxTaskControl(void) { }
517 bool taskUpdateRxMainInProgress(void) { return true; }
518 void schedulerIgnoreTaskStateTime(void) { }
519 void schedulerIgnoreTaskExecRate(void) { }
520 bool schedulerGetIgnoreTaskExecTime() { return false; }
521 void schedulerIgnoreTaskExecTime(void) { }
522 void schedulerSetNextStateTime(timeDelta_t
) {}
524 void rxPwmInit(rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
526 UNUSED(rxRuntimeState
);
530 bool sbusInit(rxConfig_t
*initialRxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
532 UNUSED(initialRxConfig
);
533 UNUSED(rxRuntimeState
);
538 bool spektrumInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
541 UNUSED(rxRuntimeState
);
546 bool sumdInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
549 UNUSED(rxRuntimeState
);
554 bool sumhInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
557 UNUSED(rxRuntimeState
);
562 bool crsfRxInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
);
564 bool jetiExBusInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
567 UNUSED(rxRuntimeState
);
572 bool ibusInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
575 UNUSED(rxRuntimeState
);
580 bool xBusInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
583 UNUSED(rxRuntimeState
);
588 bool rxMspInit(rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
, rcReadRawDataFnPtr
*callback
)
591 UNUSED(rxRuntimeState
);
596 float pt1FilterGain(float f_cut
, float dT
)
603 void pt1FilterInit(pt1Filter_t
*filter
, float k
)
609 void pt1FilterUpdateCutoff(pt1Filter_t
*filter
, float k
)
615 float pt1FilterApply(pt1Filter_t
*filter
, float input
)
622 bool isUpright(void) { return true; }
624 float getMotorOutputLow(void) { return 1000.0; }
626 float getMotorOutputHigh(void) { return 2047.0; }