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/rc_controls.h"
47 void mixerInit(mixerMode_e mixerMode, motorMixer_t *initialCustomMixers, servoMixer_t *initialCustomServoMixers);
48 void mixerUsePWMOutputConfiguration(pwmOutputConfiguration_t *pwmOutputConfiguration);
51 #include "unittest_macros.h"
52 #include "gtest/gtest.h"
55 #define TEST_RC_MID 1500
58 #define TEST_MIN_COMMAND 1000
59 #define TEST_SERVO_MID 1500
61 typedef struct motor_s {
65 typedef struct servo_s {
69 motor_t motors[MAX_SUPPORTED_MOTORS];
70 servo_t servos[MAX_SUPPORTED_SERVOS];
72 uint8_t lastOneShotUpdateMotorCount;
74 uint32_t testFeatureMask = 0;
76 int updatedServoCount;
77 int updatedMotorCount;
79 class ChannelForwardingTest : public ::testing::Test {
81 virtual void SetUp() {
82 memset(&servos, 0, sizeof(servos));
87 TEST_F(ChannelForwardingTest, TestForwardAuxChannelsToServosWithNoServos)
90 rcData[AUX1] = TEST_RC_MID;
91 rcData[AUX2] = TEST_RC_MID;
92 rcData[AUX3] = TEST_RC_MID;
93 rcData[AUX4] = TEST_RC_MID;
98 for (uint8_t i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
99 EXPECT_EQ(0, servos[i].value);
103 TEST_F(ChannelForwardingTest, TestForwardAuxChannelsToServosWithMaxServos)
113 forwardAuxChannelsToServos(MAX_SUPPORTED_SERVOS);
117 for (i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
118 EXPECT_EQ(0, servos[i].value);
122 TEST_F(ChannelForwardingTest, TestForwardAuxChannelsToServosWithLessRemainingServosThanAuxChannelsToForward)
132 forwardAuxChannelsToServos(MAX_SUPPORTED_SERVOS - 2);
136 for (i = 0; i < MAX_SUPPORTED_SERVOS - 2; i++) {
137 EXPECT_EQ(0, servos[i].value);
140 // -1 for zero based offset
141 EXPECT_EQ(1000, servos[MAX_SUPPORTED_SERVOS - 1 - 1].value);
142 EXPECT_EQ(1250, servos[MAX_SUPPORTED_SERVOS - 0 - 1].value);
145 class BasicMixerIntegrationTest : public ::testing::Test {
147 mixerConfig_t mixerConfig;
149 escAndServoConfig_t escAndServoConfig;
150 servoParam_t servoConf[MAX_SUPPORTED_SERVOS];
151 gimbalConfig_t gimbalConfig = {
152 .mode = GIMBAL_MODE_NORMAL
155 motorMixer_t customMotorMixer[MAX_SUPPORTED_MOTORS];
156 servoMixer_t customServoMixer[MAX_SUPPORTED_SERVOS];
158 virtual void SetUp() {
159 updatedServoCount = 0;
160 updatedMotorCount = 0;
162 memset(&mixerConfig, 0, sizeof(mixerConfig));
163 memset(&rxConfig, 0, sizeof(rxConfig));
164 memset(&escAndServoConfig, 0, sizeof(escAndServoConfig));
165 memset(&servoConf, 0, sizeof(servoConf));
167 memset(rcData, 0, sizeof(rcData));
168 memset(rcCommand, 0, sizeof(rcCommand));
169 memset(axisPID, 0, sizeof(axisPID));
171 memset(&customMotorMixer, 0, sizeof(customMotorMixer));
174 virtual void withDefaultEscAndServoConfiguration(void) {
175 escAndServoConfig.mincommand = TEST_MIN_COMMAND;
178 virtual void withDefaultRxConfig(void) {
179 rxConfig.midrc = 1500;
182 virtual void configureMixer(void) {
195 TEST_F(BasicMixerIntegrationTest, TestTricopterServo)
198 rxConfig.midrc = 1500;
200 mixerConfig.tri_unarmed_servo = 1;
202 withDefaultEscAndServoConfiguration();
203 withDefaultRxConfig();
205 servoConf[5].min = DEFAULT_SERVO_MIN;
206 servoConf[5].max = DEFAULT_SERVO_MAX;
207 servoConf[5].middle = DEFAULT_SERVO_MIDDLE;
208 servoConf[5].rate = 100;
212 mixerInit(MIXER_TRI, customMotorMixer, customServoMixer);
215 pwmOutputConfiguration_t pwmOutputConfiguration = {
220 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration);
230 EXPECT_EQ(1, updatedServoCount);
231 EXPECT_EQ(TEST_SERVO_MID, servos[0].value);
234 TEST_F(BasicMixerIntegrationTest, TestQuadMotors)
237 withDefaultEscAndServoConfiguration();
241 mixerInit(MIXER_QUADX, customMotorMixer, customServoMixer);
244 pwmOutputConfiguration_t pwmOutputConfiguration = {
249 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration);
252 memset(rcCommand, 0, sizeof(rcCommand));
255 memset(axisPID, 0, sizeof(axisPID));
264 EXPECT_EQ(4, updatedMotorCount);
266 EXPECT_EQ(TEST_MIN_COMMAND, motors[0].value);
267 EXPECT_EQ(TEST_MIN_COMMAND, motors[1].value);
268 EXPECT_EQ(TEST_MIN_COMMAND, motors[2].value);
269 EXPECT_EQ(TEST_MIN_COMMAND, motors[3].value);
273 class CustomMixerIntegrationTest : public BasicMixerIntegrationTest {
276 virtual void SetUp() {
278 BasicMixerIntegrationTest::SetUp();
280 memset(&servoConf, 0, sizeof(servoConf));
281 for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
282 servoConf[i].min = DEFAULT_SERVO_MIN;
283 servoConf[i].max = DEFAULT_SERVO_MAX;
284 servoConf[i].middle = DEFAULT_SERVO_MIDDLE;
285 servoConf[i].rate = 100;
288 withDefaultEscAndServoConfiguration();
289 withDefaultRxConfig();
293 memset(&customMotorMixer, 0, sizeof(customMotorMixer));
294 memset(&customServoMixer, 0, sizeof(customServoMixer));
299 TEST_F(CustomMixerIntegrationTest, TestCustomMixer)
303 EXPECTED_SERVOS_TO_MIX_COUNT = 6,
304 EXPECTED_MOTORS_TO_MIX_COUNT = 2
307 servoMixer_t testServoMixer[EXPECTED_SERVOS_TO_MIX_COUNT] = {
308 { SERVO_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0, 0, 100, 0 },
309 { SERVO_FLAPPERON_1, INPUT_STABILIZED_ROLL, 100, 0, 0, 100, 0 },
310 { SERVO_FLAPPERON_2, INPUT_STABILIZED_ROLL, 100, 0, 0, 100, 0 },
311 { SERVO_RUDDER, INPUT_STABILIZED_YAW, 100, 0, 0, 100, 0 },
312 { SERVO_THROTTLE, INPUT_STABILIZED_THROTTLE, 100, 0, 0, 100, 0 },
313 { SERVO_FLAPS, INPUT_RC_AUX1, 100, 0, 0, 100, 0 },
315 memcpy(customServoMixer, testServoMixer, sizeof(testServoMixer));
317 static const motorMixer_t testMotorMixer[EXPECTED_MOTORS_TO_MIX_COUNT] = {
318 { 1.0f, 0.0f, 0.0f, -1.0f }, // LEFT
319 { 1.0f, 0.0f, 0.0f, 1.0f }, // RIGHT
321 memcpy(customMotorMixer, testMotorMixer, sizeof(testMotorMixer));
323 mixerInit(MIXER_CUSTOM_AIRPLANE, customMotorMixer, customServoMixer);
325 pwmOutputConfiguration_t pwmOutputConfiguration = {
330 mixerUsePWMOutputConfiguration(&pwmOutputConfiguration);
333 rcCommand[THROTTLE] = 1000;
339 memset(axisPID, 0, sizeof(axisPID));
349 EXPECT_EQ(EXPECTED_MOTORS_TO_MIX_COUNT, updatedMotorCount);
351 EXPECT_EQ(TEST_MIN_COMMAND, motors[0].value);
352 EXPECT_EQ(TEST_MIN_COMMAND, motors[1].value);
354 EXPECT_EQ(EXPECTED_SERVOS_TO_MIX_COUNT, updatedServoCount);
356 EXPECT_EQ(TEST_SERVO_MID, servos[0].value);
357 EXPECT_EQ(TEST_SERVO_MID, servos[1].value);
358 EXPECT_EQ(TEST_SERVO_MID, servos[2].value);
359 EXPECT_EQ(TEST_SERVO_MID, servos[3].value);
360 EXPECT_EQ(1000, servos[4].value); // Throttle
361 EXPECT_EQ(2000, servos[5].value); // Flaps
368 attitudeEulerAngles_t attitude;
369 rxRuntimeConfig_t rxRuntimeConfig;
371 int16_t axisPID[XYZ_AXIS_COUNT];
372 int16_t rcCommand[4];
373 int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
375 uint32_t rcModeActivationMask;
376 int32_t debug[DEBUG32_VALUE_COUNT];
379 uint16_t flightModeFlags;
382 void delay(uint32_t) {}
384 bool feature(uint32_t mask) {
385 return (mask & testFeatureMask);
388 int32_t lowpassFixed(lowpass_t *, int32_t, int16_t) {
392 void pwmWriteMotor(uint8_t index, uint16_t value) {
393 motors[index].value = value;
397 void pwmShutdownPulsesForAllMotors(uint8_t motorCount)
401 for(index = 0; index < motorCount; index++){
402 motors[index].value = 0;
406 void pwmCompleteOneshotMotorUpdate(uint8_t motorCount) {
407 lastOneShotUpdateMotorCount = motorCount;
410 void pwmWriteServo(uint8_t index, uint16_t value) {
411 // FIXME logic in test, mimic's production code.
412 // Perhaps the solution is to remove the logic from the production code version and assume that
413 // anything calling calling pwmWriteServo always uses a valid index?
414 // See MAX_SERVOS in pwm_output (driver) and MAX_SUPPORTED_SERVOS (flight)
415 if (index < MAX_SERVOS) {
416 servos[index].value = value;
421 bool failsafeIsActive(void) {