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_FLAPS, INPUT_RC_AUX1, 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_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0, 0, 100, 0 },
324 { SERVO_THROTTLE, INPUT_STABILIZED_THROTTLE, 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(2000, servos[0].value); // Flaps
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(TEST_SERVO_MID, servos[4].value);
372 EXPECT_EQ(1000, servos[5].value); // Throttle
379 rollAndPitchInclination_t inclination;
380 rxRuntimeState_t rxRuntimeState;
382 int16_t axisPID[XYZ_AXIS_COUNT];
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 featureIsEnabled(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) {