Bump clang version to 18 (#14116)
[betaflight.git] / src / test / unit / link_quality_unittest.cc
blobc01de0045598df62b9b89b9098b8d5653dc76465
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"
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"
35 #include "common/vector.h"
37 #include "config/config.h"
39 #include "drivers/osd_symbols.h"
40 #include "drivers/persistent.h"
41 #include "drivers/serial.h"
42 #include "drivers/system.h"
44 #include "fc/core.h"
45 #include "fc/rc_controls.h"
46 #include "fc/rc_modes.h"
47 #include "fc/runtime_config.h"
49 #include "flight/failsafe.h"
50 #include "flight/imu.h"
51 #include "flight/mixer.h"
52 #include "flight/pid.h"
54 #include "io/beeper.h"
55 #include "io/gps.h"
56 #include "io/serial.h"
58 #include "osd/osd.h"
59 #include "osd/osd_elements.h"
60 #include "osd/osd_warnings.h"
62 #include "pg/pg.h"
63 #include "pg/pg_ids.h"
64 #include "pg/pilot.h"
65 #include "pg/rx.h"
67 #include "rx/rx.h"
69 #include "sensors/battery.h"
71 attitudeEulerAngles_t attitude;
72 matrix33_t rMat;
74 pidProfile_t *currentPidProfile;
75 extern float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
76 uint8_t GPS_numSat;
77 uint16_t GPS_distanceToHome;
78 int16_t GPS_directionToHome;
79 uint32_t GPS_distanceFlownInCm;
80 int32_t GPS_coord[2];
81 gpsSolutionData_t gpsSol;
82 float motor[8];
83 acc_t acc;
85 PG_REGISTER(batteryConfig_t, batteryConfig, PG_BATTERY_CONFIG, 0);
86 PG_REGISTER(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 0);
87 PG_REGISTER(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 0);
88 PG_REGISTER(pilotConfig_t, pilotConfig, PG_PILOT_CONFIG, 0);
89 PG_REGISTER(imuConfig_t, imuConfig, PG_IMU_CONFIG, 0);
90 PG_REGISTER(gpsConfig_t, gpsConfig, PG_GPS_CONFIG, 0);
91 PG_REGISTER(failsafeConfig_t, failsafeConfig, PG_FAILSAFE_CONFIG, 0);
93 timeUs_t simulationTime = 0;
95 void osdUpdate(timeUs_t currentTimeUs);
96 uint16_t updateLinkQualitySamples(uint16_t value);
97 #define LINK_QUALITY_SAMPLE_COUNT 16
100 /* #define DEBUG_OSD */
102 #include "unittest_macros.h"
103 #include "unittest_displayport.h"
104 #include "gtest/gtest.h"
106 extern "C" {
107 PG_REGISTER(flight3DConfig_t, flight3DConfig, PG_MOTOR_3D_CONFIG, 0);
109 extern boxBitmask_t rcModeActivationMask;
110 int16_t debug[DEBUG16_VALUE_COUNT];
111 uint8_t debugMode = 0;
113 uint16_t updateLinkQualitySamples(uint16_t value);
115 extern uint16_t applyRxChannelRangeConfiguraton(int sample, const rxChannelRangeConfig_t *range);
117 void setDefaultSimulationState()
119 setLinkQualityDirect(LINK_QUALITY_MAX_VALUE);
120 osdConfigMutable()->framerate_hz = 12;
123 * Performs a test of the OSD actions on arming.
124 * (reused throughout the test suite)
126 void doTestArm(bool testEmpty = true)
128 // given
129 // craft has been armed
130 ENABLE_ARMING_FLAG(ARMED);
132 simulationTime += 0.1e6;
133 // when
134 // sufficient OSD updates have been called
135 while (osdUpdateCheck(simulationTime, 0)) {
136 osdUpdate(simulationTime);
137 simulationTime += 10;
140 // then
141 // arming alert displayed
142 displayPortTestBufferSubstring(13, 8, "ARMED");
144 // given
145 // armed alert times out (0.5 seconds)
146 simulationTime += 0.5e6;
148 // when
149 // sufficient OSD updates have been called
150 while (osdUpdateCheck(simulationTime, 0)) {
151 osdUpdate(simulationTime);
152 simulationTime += 10;
155 // then
156 // arming alert disappears
157 #ifdef DEBUG_OSD
158 displayPortTestPrint();
159 #endif
160 if (testEmpty) {
161 displayPortTestBufferIsEmpty();
166 * Auxiliary function. Test is there're stats that must be shown
168 bool isSomeStatEnabled(void)
170 return (osdConfigMutable()->enabled_stats != 0);
174 * Performs a test of the OSD actions on disarming.
175 * (reused throughout the test suite)
177 void doTestDisarm()
179 // given
180 // craft is disarmed after having been armed
181 DISABLE_ARMING_FLAG(ARMED);
183 // when
184 // sufficient OSD updates have been called
185 while (osdUpdateCheck(simulationTime, 0)) {
186 osdUpdate(simulationTime);
187 simulationTime += 10;
190 // then
191 // post flight statistics displayed
192 if (isSomeStatEnabled()) {
193 displayPortTestBufferSubstring(2, 2, " --- STATS ---");
198 * Tests initialisation of the OSD and the power on splash screen.
200 TEST(LQTest, TestInit)
202 // given
203 // display port is initialised
204 displayPortTestInit();
206 // and
207 // default state values are set
208 setDefaultSimulationState();
210 // and
211 // this battery configuration (used for battery voltage elements)
212 batteryConfigMutable()->vbatmincellvoltage = 330;
213 batteryConfigMutable()->vbatmaxcellvoltage = 430;
215 // when
216 // OSD is initialised
217 osdInit(&testDisplayPort, OSD_DISPLAYPORT_DEVICE_AUTO);
219 while (osdUpdateCheck(simulationTime, 0)) {
220 osdUpdate(simulationTime);
221 simulationTime += 10;
224 // then
225 // display buffer should contain splash screen
226 displayPortTestBufferSubstring(7, 10, "MENU:THR MID");
227 displayPortTestBufferSubstring(11, 11, "+ YAW LEFT");
228 displayPortTestBufferSubstring(11, 12, "+ PITCH UP");
230 // when
231 // splash screen timeout has elapsed
232 simulationTime += 4e6;
233 while (osdUpdateCheck(simulationTime, 0)) {
234 osdUpdate(simulationTime);
235 simulationTime += 10;
238 // then
239 // display buffer should be empty
240 #ifdef DEBUG_OSD
241 displayPortTestPrint();
242 #endif
243 displayPortTestBufferIsEmpty();
246 * Tests the Tests the OSD_LINK_QUALITY element updateLinkQualitySamples default LQ_SOURCE_NONE
248 TEST(LQTest, TestElement_LQ_SOURCE_NONE_SAMPLES)
250 // given
251 linkQualitySource = LQ_SOURCE_NONE;
253 osdElementConfigMutable()->item_pos[OSD_LINK_QUALITY] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
254 osdConfigMutable()->link_quality_alarm = 0;
256 osdAnalyzeActiveElements();
258 // when samples populated 100%
259 for (int x = 0; x < LINK_QUALITY_SAMPLE_COUNT; x++) {
260 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE));
263 simulationTime += 1000000;
265 while (osdUpdateCheck(simulationTime, 0)) {
266 osdUpdate(simulationTime);
267 simulationTime += 10;
270 // then
271 displayPortTestBufferSubstring(8, 1, "%c9", SYM_LINK_QUALITY);
273 // when updateLinkQualitySamples used 50% rounds to 4
274 for (int x = 0; x < LINK_QUALITY_SAMPLE_COUNT; x++) {
275 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE));
276 setLinkQualityDirect(updateLinkQualitySamples(0));
279 simulationTime += 1000000;
281 while (osdUpdateCheck(simulationTime, 0)) {
282 osdUpdate(simulationTime);
283 simulationTime += 10;
286 // then
287 displayPortTestBufferSubstring(8, 1, "%c4", SYM_LINK_QUALITY);
290 * Tests the Tests the OSD_LINK_QUALITY element values default LQ_SOURCE_NONE
292 TEST(LQTest, TestElement_LQ_SOURCE_NONE_VALUES)
294 // given
296 linkQualitySource = LQ_SOURCE_NONE;
298 osdElementConfigMutable()->item_pos[OSD_LINK_QUALITY] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
299 osdConfigMutable()->link_quality_alarm = 0;
301 osdAnalyzeActiveElements();
302 // when LINK_QUALITY_MAX_VALUE to 1 by 10%
303 uint16_t testscale = 0;
304 for (int testdigit = 10; testdigit > 0; testdigit--) {
305 testscale = testdigit * 102.3;
306 setLinkQualityDirect(testscale);
307 simulationTime += 100000;
308 while (osdUpdateCheck(simulationTime, 0)) {
309 osdUpdate(simulationTime);
310 simulationTime += 10;
312 #ifdef DEBUG_OSD
313 printf("%d %d\n",testscale, testdigit);
314 displayPortTestPrint();
315 #endif
316 // then
317 if (testdigit >= 10) {
318 displayPortTestBufferSubstring(8, 1,"%c9", SYM_LINK_QUALITY);
319 }else{
320 displayPortTestBufferSubstring(8, 1,"%c%1d", SYM_LINK_QUALITY, testdigit - 1);
326 * Tests the OSD_LINK_QUALITY element LQ RX_PROTOCOL_CRSF.
328 TEST(LQTest, TestElementLQ_PROTOCOL_CRSF_VALUES)
330 // given
331 linkQualitySource = LQ_SOURCE_RX_PROTOCOL_CRSF;
333 osdElementConfigMutable()->item_pos[OSD_LINK_QUALITY] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
334 osdConfigMutable()->link_quality_alarm = 0;
336 osdAnalyzeActiveElements();
338 simulationTime += 1000000;
339 while (osdUpdateCheck(simulationTime, 0)) {
340 osdUpdate(simulationTime);
341 simulationTime += 10;
344 // crsf setLinkQualityDirect 0-300;
346 for (uint8_t x = 0; x <= 99; x++) {
347 for (uint8_t m = 0; m <= 4; m++) {
348 // when x scaled
349 setLinkQualityDirect(x);
350 rxSetRfMode(m);
351 // then rxGetLinkQuality Osd should be x
352 // and RfMode should be m
353 simulationTime += 100000;
354 while (osdUpdateCheck(simulationTime, 0)) {
355 osdUpdate(simulationTime);
356 simulationTime += 10;
358 displayPortTestBufferSubstring(8, 1, "%c%1d:%2d", SYM_LINK_QUALITY, m, x);
363 * Tests the LQ Alarms
366 TEST(LQTest, TestLQAlarm)
368 timeUs_t startTime = simulationTime;
369 // given
370 // default state is set
371 setDefaultSimulationState();
373 linkQualitySource = LQ_SOURCE_NONE;
375 // and
376 // the following OSD elements are visible
378 osdElementConfigMutable()->item_pos[OSD_LINK_QUALITY] = OSD_POS(8, 1) | OSD_PROFILE_1_FLAG;
380 // and
381 // this set of alarm values
383 osdConfigMutable()->link_quality_alarm = 80;
384 stateFlags |= GPS_FIX | GPS_FIX_HOME;
386 osdAnalyzeActiveElements();
388 // and
389 // using the metric unit system
390 osdConfigMutable()->units = UNIT_METRIC;
392 // when
393 // the craft is armed
394 doTestArm(false);
396 for (int x = 0; x < LINK_QUALITY_SAMPLE_COUNT; x++) {
397 setLinkQualityDirect(updateLinkQualitySamples(LINK_QUALITY_MAX_VALUE));
400 // then
401 // no elements should flash as all values are out of alarm range
402 // Ensure a consistent start time for testing
403 simulationTime += 5000000;
404 simulationTime -= simulationTime % 1000000;
405 startTime = simulationTime;
406 for (int i = 0; i < 30; i++) {
407 // Check for visibility every 100ms, elements should always be visible
408 simulationTime = startTime + i*0.1e6;
409 while (osdUpdateCheck(simulationTime, 0)) {
410 osdUpdate(simulationTime);
411 simulationTime += 10;
414 #ifdef DEBUG_OSD
415 printf("%d\n", i);
416 #endif
417 displayPortTestBufferSubstring(8, 1, "%c9", SYM_LINK_QUALITY);
421 setLinkQualityDirect(512);
422 while (osdUpdateCheck(simulationTime, 0)) {
423 osdUpdate(simulationTime);
424 simulationTime += 10;
427 // then
428 // elements showing values in alarm range should flash
429 simulationTime += 1000000;
430 simulationTime -= simulationTime % 1000000;
431 startTime = simulationTime + 0.25e6;
432 for (int i = 0; i < 15; i++) {
433 // Blinking should happen at 2Hz
434 simulationTime = startTime + i*0.25e6;
435 while (osdUpdateCheck(simulationTime, 0)) {
436 osdUpdate(simulationTime);
437 simulationTime += 10;
440 #ifdef DEBUG_OSD
441 displayPortTestPrint();
442 #endif
444 if (i % 2 == 0) {
445 displayPortTestBufferSubstring(8, 1, "%c5", SYM_LINK_QUALITY);
446 } else {
447 displayPortTestBufferIsEmpty();
451 doTestDisarm();
452 simulationTime += 1000000;
453 simulationTime -= simulationTime % 1000000;
454 while (osdUpdateCheck(simulationTime, 0)) {
455 osdUpdate(simulationTime);
456 simulationTime += 10;
460 // STUBS
461 extern "C" {
463 uint32_t micros() {
464 return simulationTime;
467 uint32_t microsISR() {
468 return micros();
471 uint32_t millis() {
472 return micros() / 1000;
475 bool featureIsEnabled(uint32_t) { return true; }
476 void beeperConfirmationBeeps(uint8_t) {}
477 bool isBeeperOn() { return false; }
478 uint8_t getCurrentPidProfileIndex() { return 0; }
479 uint8_t getCurrentControlRateProfileIndex() { return 0; }
480 batteryState_e getBatteryState() { return BATTERY_OK; }
481 uint8_t getBatteryCellCount() { return 4; }
482 uint16_t getBatteryVoltage() { return 1680; }
483 uint16_t getBatteryAverageCellVoltage() { return 420; }
484 int32_t getAmperage() { return 0; }
485 int32_t getMAhDrawn() { return 0; }
486 float getWhDrawn() { return 0.0; }
487 int32_t getEstimatedAltitudeCm() { return 0; }
488 int32_t getAltitudeAsl() { return 0; }
489 int32_t getEstimatedVario() { return 0; }
490 int32_t blackboxGetLogNumber() { return 0; }
491 bool isBlackboxDeviceWorking() { return true; }
492 bool isBlackboxDeviceFull() { return false; }
493 serialPort_t *openSerialPort(serialPortIdentifier_e, serialPortFunction_e, serialReceiveCallbackPtr, void *, uint32_t, portMode_e, portOptions_e) {return NULL;}
494 const serialPortConfig_t *findSerialPortConfig(serialPortFunction_e ) {return NULL;}
495 bool telemetryCheckRxPortShared(const serialPortConfig_t *) {return false;}
496 bool cmsDisplayPortRegister(displayPort_t *) { return false; }
497 uint16_t getCoreTemperatureCelsius(void) { return 0; }
498 bool isCrashFlipModeActive(void) { return false; }
499 float pidItermAccelerator(void) { return 1.0; }
500 uint8_t getMotorCount(void){ return 4; }
501 bool areMotorsRunning(void){ return true; }
502 bool pidOsdAntiGravityActive(void) { return false; }
503 bool failsafeIsActive(void) { return false; }
504 bool failsafeIsReceivingRxData(void) { return true; }
505 bool gpsIsHealthy(void) { return true; }
506 bool gpsRescueIsConfigured(void) { return false; }
507 int8_t calculateThrottlePercent(void) { return 0; }
508 uint32_t persistentObjectRead(persistentObjectId_e) { return 0; }
509 void persistentObjectWrite(persistentObjectId_e, uint32_t) {}
510 void failsafeOnRxSuspend(uint32_t ) {}
511 void failsafeOnRxResume(void) {}
512 uint32_t failsafeFailurePeriodMs(void) { return 400; }
513 void featureDisableImmediate(uint32_t) { }
514 bool rxMspFrameComplete(void) { return false; }
515 bool isPPMDataBeingReceived(void) { return false; }
516 bool isPWMDataBeingReceived(void) { return false; }
517 void resetPPMDataReceivedState(void){ }
518 void failsafeOnValidDataReceived(void) { }
519 void failsafeOnValidDataFailed(void) { }
520 void pinioBoxTaskControl(void) { }
521 bool taskUpdateRxMainInProgress(void) { return true; }
522 void schedulerIgnoreTaskStateTime(void) { }
523 void schedulerIgnoreTaskExecRate(void) { }
524 bool schedulerGetIgnoreTaskExecTime() { return false; }
525 void schedulerIgnoreTaskExecTime(void) { }
526 void schedulerSetNextStateTime(timeDelta_t) {}
528 void rxPwmInit(rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
530 UNUSED(rxRuntimeState);
531 UNUSED(callback);
534 bool sbusInit(rxConfig_t *initialRxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
536 UNUSED(initialRxConfig);
537 UNUSED(rxRuntimeState);
538 UNUSED(callback);
539 return true;
542 bool spektrumInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
544 UNUSED(rxConfig);
545 UNUSED(rxRuntimeState);
546 UNUSED(callback);
547 return true;
550 bool sumdInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
552 UNUSED(rxConfig);
553 UNUSED(rxRuntimeState);
554 UNUSED(callback);
555 return true;
558 bool sumhInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
560 UNUSED(rxConfig);
561 UNUSED(rxRuntimeState);
562 UNUSED(callback);
563 return true;
566 bool crsfRxInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback);
568 bool jetiExBusInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
570 UNUSED(rxConfig);
571 UNUSED(rxRuntimeState);
572 UNUSED(callback);
573 return true;
576 bool ibusInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
578 UNUSED(rxConfig);
579 UNUSED(rxRuntimeState);
580 UNUSED(callback);
581 return true;
584 bool xBusInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
586 UNUSED(rxConfig);
587 UNUSED(rxRuntimeState);
588 UNUSED(callback);
589 return true;
592 bool rxMspInit(rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState, rcReadRawDataFnPtr *callback)
594 UNUSED(rxConfig);
595 UNUSED(rxRuntimeState);
596 UNUSED(callback);
597 return true;
600 float pt1FilterGain(float f_cut, float dT)
602 UNUSED(f_cut);
603 UNUSED(dT);
604 return 0.0;
607 void pt1FilterInit(pt1Filter_t *filter, float k)
609 UNUSED(filter);
610 UNUSED(k);
613 void pt1FilterUpdateCutoff(pt1Filter_t *filter, float k)
615 UNUSED(filter);
616 UNUSED(k);
619 float pt1FilterApply(pt1Filter_t *filter, float input)
621 UNUSED(filter);
622 UNUSED(input);
623 return 0.0;
626 bool isUpright(void) { return true; }
628 float getMotorOutputLow(void) { return 1000.0; }
630 float getMotorOutputHigh(void) { return 2047.0; }