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/>.
28 #include "common/axis.h"
29 #include "common/maths.h"
31 #include "drivers/sensor.h"
32 #include "drivers/accgyro.h"
33 #include "drivers/pwm_mapping.h"
35 #include "sensors/sensors.h"
36 #include "sensors/acceleration.h"
39 #include "flight/pid.h"
40 #include "flight/imu.h"
41 #include "flight/mixer.h"
42 #include "flight/lowpass.h"
44 #include "io/escservo.h"
45 #include "io/gimbal.h"
46 #include "io/rc_controls.h"
48 extern uint8_t servoCount
;
49 void forwardAuxChannelsToServos(uint8_t firstServoIndex
);
51 void mixerInit(mixerMode_e mixerMode
, motorMixer_t
*initialCustomMixers
, servoMixer_t
*initialCustomServoMixers
);
52 void mixerUsePWMOutputConfiguration(pwmOutputConfiguration_t
*pwmOutputConfiguration
);
55 #include "unittest_macros.h"
56 #include "gtest/gtest.h"
59 #define TEST_RC_MID 1500
62 #define TEST_MIN_COMMAND 1000
63 #define TEST_SERVO_MID 1500
65 typedef struct motor_s
{
69 typedef struct servo_s
{
73 motor_t motors
[MAX_SUPPORTED_MOTORS
];
74 servo_t servos
[MAX_SUPPORTED_SERVOS
];
76 uint8_t lastOneShotUpdateMotorCount
;
78 uint32_t testFeatureMask
= 0;
80 int updatedServoCount
;
81 int updatedMotorCount
;
83 class ChannelForwardingTest
: public ::testing::Test
{
85 virtual void SetUp() {
86 memset(&servos
, 0, sizeof(servos
));
91 TEST_F(ChannelForwardingTest
, TestForwardAuxChannelsToServosWithNoServos
)
96 rcData
[AUX1
] = TEST_RC_MID
;
97 rcData
[AUX2
] = TEST_RC_MID
;
98 rcData
[AUX3
] = TEST_RC_MID
;
99 rcData
[AUX4
] = TEST_RC_MID
;
102 forwardAuxChannelsToServos(MAX_SUPPORTED_SERVOS
);
105 for (uint8_t i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
106 EXPECT_EQ(0, servos
[i
].value
);
110 TEST_F(ChannelForwardingTest
, TestForwardAuxChannelsToServosWithMaxServos
)
113 servoCount
= MAX_SUPPORTED_SERVOS
;
121 forwardAuxChannelsToServos(MAX_SUPPORTED_SERVOS
);
125 for (i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
126 EXPECT_EQ(0, servos
[i
].value
);
130 TEST_F(ChannelForwardingTest
, TestForwardAuxChannelsToServosWithLessRemainingServosThanAuxChannelsToForward
)
133 servoCount
= MAX_SUPPORTED_SERVOS
- 2;
141 forwardAuxChannelsToServos(MAX_SUPPORTED_SERVOS
- 2);
145 for (i
= 0; i
< MAX_SUPPORTED_SERVOS
- 2; i
++) {
146 EXPECT_EQ(0, servos
[i
].value
);
149 // -1 for zero based offset
150 EXPECT_EQ(1000, servos
[MAX_SUPPORTED_SERVOS
- 1 - 1].value
);
151 EXPECT_EQ(1250, servos
[MAX_SUPPORTED_SERVOS
- 0 - 1].value
);
154 class BasicMixerIntegrationTest
: public ::testing::Test
{
156 mixerConfig_t mixerConfig
;
158 escAndServoConfig_t escAndServoConfig
;
159 servoParam_t servoConf
[MAX_SUPPORTED_SERVOS
];
160 gimbalConfig_t gimbalConfig
= {
161 .mode
= GIMBAL_MODE_NORMAL
164 motorMixer_t customMotorMixer
[MAX_SUPPORTED_MOTORS
];
165 servoMixer_t customServoMixer
[MAX_SUPPORTED_SERVOS
];
167 virtual void SetUp() {
168 updatedServoCount
= 0;
169 updatedMotorCount
= 0;
171 memset(&mixerConfig
, 0, sizeof(mixerConfig
));
172 memset(&rxConfig
, 0, sizeof(rxConfig
));
173 memset(&escAndServoConfig
, 0, sizeof(escAndServoConfig
));
174 memset(&servoConf
, 0, sizeof(servoConf
));
176 memset(rcData
, 0, sizeof(rcData
));
177 memset(rcCommand
, 0, sizeof(rcCommand
));
178 memset(axisPID
, 0, sizeof(axisPID
));
180 memset(&customMotorMixer
, 0, sizeof(customMotorMixer
));
183 virtual void withDefaultEscAndServoConfiguration(void) {
184 escAndServoConfig
.mincommand
= TEST_MIN_COMMAND
;
187 virtual void withDefaultRxConfig(void) {
188 rxConfig
.midrc
= 1500;
191 virtual void configureMixer(void) {
204 TEST_F(BasicMixerIntegrationTest
, TestTricopterServo
)
207 rxConfig
.midrc
= 1500;
209 mixerConfig
.tri_unarmed_servo
= 1;
211 withDefaultEscAndServoConfiguration();
212 withDefaultRxConfig();
214 servoConf
[5].min
= DEFAULT_SERVO_MIN
;
215 servoConf
[5].max
= DEFAULT_SERVO_MAX
;
216 servoConf
[5].middle
= DEFAULT_SERVO_MIDDLE
;
217 servoConf
[5].rate
= 100;
218 servoConf
[5].forwardFromChannel
= CHANNEL_FORWARDING_DISABLED
;
222 mixerInit(MIXER_TRI
, customMotorMixer
, customServoMixer
);
225 pwmOutputConfiguration_t pwmOutputConfiguration
= {
230 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration
);
240 EXPECT_EQ(1, updatedServoCount
);
241 EXPECT_EQ(TEST_SERVO_MID
, servos
[0].value
);
244 TEST_F(BasicMixerIntegrationTest
, TestQuadMotors
)
247 withDefaultEscAndServoConfiguration();
251 mixerInit(MIXER_QUADX
, customMotorMixer
, customServoMixer
);
254 pwmOutputConfiguration_t pwmOutputConfiguration
= {
259 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration
);
262 memset(rcCommand
, 0, sizeof(rcCommand
));
265 memset(axisPID
, 0, sizeof(axisPID
));
274 EXPECT_EQ(4, updatedMotorCount
);
276 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[0].value
);
277 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[1].value
);
278 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[2].value
);
279 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[3].value
);
283 class CustomMixerIntegrationTest
: public BasicMixerIntegrationTest
{
286 virtual void SetUp() {
288 BasicMixerIntegrationTest::SetUp();
290 memset(&servoConf
, 0, sizeof(servoConf
));
291 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
292 servoConf
[i
].min
= DEFAULT_SERVO_MIN
;
293 servoConf
[i
].max
= DEFAULT_SERVO_MAX
;
294 servoConf
[i
].middle
= DEFAULT_SERVO_MIDDLE
;
295 servoConf
[i
].rate
= 100;
296 servoConf
[i
].forwardFromChannel
= CHANNEL_FORWARDING_DISABLED
;
299 withDefaultEscAndServoConfiguration();
300 withDefaultRxConfig();
304 memset(&customMotorMixer
, 0, sizeof(customMotorMixer
));
305 memset(&customServoMixer
, 0, sizeof(customServoMixer
));
310 TEST_F(CustomMixerIntegrationTest
, TestCustomMixer
)
314 EXPECTED_SERVOS_TO_MIX_COUNT
= 6,
315 EXPECTED_MOTORS_TO_MIX_COUNT
= 2
318 servoMixer_t testServoMixer
[EXPECTED_SERVOS_TO_MIX_COUNT
] = {
319 { SERVO_ELEVATOR
, INPUT_STABILIZED_PITCH
, 100, 0, 0, 100, 0 },
320 { SERVO_FLAPPERON_1
, INPUT_STABILIZED_ROLL
, 100, 0, 0, 100, 0 },
321 { SERVO_FLAPPERON_2
, INPUT_STABILIZED_ROLL
, 100, 0, 0, 100, 0 },
322 { SERVO_RUDDER
, INPUT_STABILIZED_YAW
, 100, 0, 0, 100, 0 },
323 { SERVO_THROTTLE
, INPUT_STABILIZED_THROTTLE
, 100, 0, 0, 100, 0 },
324 { SERVO_FLAPS
, INPUT_RC_AUX1
, 100, 0, 0, 100, 0 },
326 memcpy(customServoMixer
, testServoMixer
, sizeof(testServoMixer
));
328 static const motorMixer_t testMotorMixer
[EXPECTED_MOTORS_TO_MIX_COUNT
] = {
329 { 1.0f
, 0.0f
, 0.0f
, -1.0f
}, // LEFT
330 { 1.0f
, 0.0f
, 0.0f
, 1.0f
}, // RIGHT
332 memcpy(customMotorMixer
, testMotorMixer
, sizeof(testMotorMixer
));
334 mixerInit(MIXER_CUSTOM_AIRPLANE
, customMotorMixer
, customServoMixer
);
336 pwmOutputConfiguration_t pwmOutputConfiguration
= {
341 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration
);
344 rcCommand
[THROTTLE
] = 1000;
350 memset(axisPID
, 0, sizeof(axisPID
));
360 EXPECT_EQ(EXPECTED_MOTORS_TO_MIX_COUNT
, updatedMotorCount
);
362 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[0].value
);
363 EXPECT_EQ(TEST_MIN_COMMAND
, motors
[1].value
);
365 EXPECT_EQ(EXPECTED_SERVOS_TO_MIX_COUNT
, updatedServoCount
);
367 EXPECT_EQ(TEST_SERVO_MID
, servos
[0].value
);
368 EXPECT_EQ(TEST_SERVO_MID
, servos
[1].value
);
369 EXPECT_EQ(TEST_SERVO_MID
, servos
[2].value
);
370 EXPECT_EQ(TEST_SERVO_MID
, servos
[3].value
);
371 EXPECT_EQ(1000, servos
[4].value
); // Throttle
372 EXPECT_EQ(2000, servos
[5].value
); // Flaps
379 rollAndPitchInclination_t inclination
;
380 rxRuntimeConfig_t rxRuntimeConfig
;
382 int16_t axisPID
[XYZ_AXIS_COUNT
];
383 int16_t rcCommand
[4];
384 int16_t rcData
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
386 uint32_t rcModeActivationMask
;
387 int16_t debug
[DEBUG16_VALUE_COUNT
];
390 uint16_t flightModeFlags
;
393 void delay(uint32_t) {}
395 bool feature(uint32_t mask
) {
396 return (mask
& testFeatureMask
);
399 int32_t lowpassFixed(lowpass_t
*, int32_t, int16_t) {
403 void pwmWriteMotor(uint8_t index
, uint16_t value
) {
404 motors
[index
].value
= value
;
408 void pwmShutdownPulsesForAllMotors(uint8_t motorCount
)
412 for(index
= 0; index
< motorCount
; index
++){
413 motors
[index
].value
= 0;
417 void pwmCompleteOneshotMotorUpdate(uint8_t motorCount
) {
418 lastOneShotUpdateMotorCount
= motorCount
;
421 void pwmWriteServo(uint8_t index
, uint16_t value
) {
422 // FIXME logic in test, mimic's production code.
423 // Perhaps the solution is to remove the logic from the production code version and assume that
424 // anything calling calling pwmWriteServo always uses a valid index?
425 // See MAX_SERVOS in pwm_output (driver) and MAX_SUPPORTED_SERVOS (flight)
426 if (index
< MAX_SERVOS
) {
427 servos
[index
].value
= value
;
432 bool failsafeIsActive(void) {