mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-20 14:55:21 +03:00
First cut of custom motor and servo mixer test.
Note: The test is rather heavyweight, more like an integration test than a unit test, but will allow for the underlying code to be refactored.
This commit is contained in:
parent
4fc7d517bf
commit
5be2276b6b
4 changed files with 187 additions and 40 deletions
|
@ -53,44 +53,9 @@
|
||||||
#include "config/runtime_config.h"
|
#include "config/runtime_config.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SERVO_GIMBAL_PITCH = 0,
|
|
||||||
SERVO_GIMBAL_ROLL = 1,
|
|
||||||
SERVO_FLAPS = 2,
|
|
||||||
SERVO_FLAPPERON_1 = 3,
|
|
||||||
SERVO_FLAPPERON_2 = 4,
|
|
||||||
SERVO_RUDDER = 5,
|
|
||||||
SERVO_ELEVATOR = 6,
|
|
||||||
SERVO_THROTTLE = 7, // for internal combustion (IC) planes
|
|
||||||
|
|
||||||
SERVO_BICOPTER_LEFT = 4,
|
|
||||||
SERVO_BICOPTER_RIGHT = 5,
|
|
||||||
|
|
||||||
SERVO_DUALCOPTER_LEFT = 4,
|
|
||||||
SERVO_DUALCOPTER_RIGHT = 5,
|
|
||||||
|
|
||||||
SERVO_SINGLECOPTER_1 = 3,
|
|
||||||
SERVO_SINGLECOPTER_2 = 4,
|
|
||||||
SERVO_SINGLECOPTER_3 = 5,
|
|
||||||
SERVO_SINGLECOPTER_4 = 6,
|
|
||||||
|
|
||||||
} servoIndex_e;
|
|
||||||
|
|
||||||
#define SERVO_PLANE_INDEX_MIN SERVO_FLAPS
|
|
||||||
#define SERVO_PLANE_INDEX_MAX SERVO_THROTTLE
|
|
||||||
|
|
||||||
#define SERVO_DUALCOPTER_INDEX_MIN SERVO_DUALCOPTER_LEFT
|
|
||||||
#define SERVO_DUALCOPTER_INDEX_MAX SERVO_DUALCOPTER_RIGHT
|
|
||||||
|
|
||||||
#define SERVO_SINGLECOPTER_INDEX_MIN SERVO_SINGLECOPTER_1
|
|
||||||
#define SERVO_SINGLECOPTER_INDEX_MAX SERVO_SINGLECOPTER_4
|
|
||||||
|
|
||||||
#define SERVO_FLAPPERONS_MIN SERVO_FLAPPERON_1
|
|
||||||
#define SERVO_FLAPPERONS_MAX SERVO_FLAPPERON_2
|
|
||||||
|
|
||||||
//#define MIXER_DEBUG
|
//#define MIXER_DEBUG
|
||||||
|
|
||||||
uint8_t motorCount = 0;
|
uint8_t motorCount;
|
||||||
|
|
||||||
int16_t motor[MAX_SUPPORTED_MOTORS];
|
int16_t motor[MAX_SUPPORTED_MOTORS];
|
||||||
int16_t motor_disarmed[MAX_SUPPORTED_MOTORS];
|
int16_t motor_disarmed[MAX_SUPPORTED_MOTORS];
|
||||||
|
@ -449,6 +414,7 @@ void mixerUsePWMOutputConfiguration(pwmOutputConfiguration_t *pwmOutputConfigura
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
motorCount = 0;
|
||||||
servoCount = pwmOutputConfiguration->servoCount;
|
servoCount = pwmOutputConfiguration->servoCount;
|
||||||
|
|
||||||
if (currentMixerMode == MIXER_CUSTOM || currentMixerMode == MIXER_CUSTOM_TRI || currentMixerMode == MIXER_CUSTOM_AIRPLANE) {
|
if (currentMixerMode == MIXER_CUSTOM || currentMixerMode == MIXER_CUSTOM_TRI || currentMixerMode == MIXER_CUSTOM_AIRPLANE) {
|
||||||
|
@ -695,7 +661,7 @@ void StopPwmAllMotors()
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef USE_QUAD_MIXER_ONLY
|
#ifndef USE_QUAD_MIXER_ONLY
|
||||||
static void servoMixer(void)
|
STATIC_UNIT_TESTED void servoMixer(void)
|
||||||
{
|
{
|
||||||
int16_t input[INPUT_SOURCE_COUNT]; // Range [-500:+500]
|
int16_t input[INPUT_SOURCE_COUNT]; // Range [-500:+500]
|
||||||
static int16_t currentOutput[MAX_SERVO_RULES];
|
static int16_t currentOutput[MAX_SERVO_RULES];
|
||||||
|
|
|
@ -114,6 +114,42 @@ enum {
|
||||||
INPUT_SOURCE_COUNT
|
INPUT_SOURCE_COUNT
|
||||||
} inputSource_e;
|
} inputSource_e;
|
||||||
|
|
||||||
|
// target servo channels
|
||||||
|
typedef enum {
|
||||||
|
SERVO_GIMBAL_PITCH = 0,
|
||||||
|
SERVO_GIMBAL_ROLL = 1,
|
||||||
|
SERVO_FLAPS = 2,
|
||||||
|
SERVO_FLAPPERON_1 = 3,
|
||||||
|
SERVO_FLAPPERON_2 = 4,
|
||||||
|
SERVO_RUDDER = 5,
|
||||||
|
SERVO_ELEVATOR = 6,
|
||||||
|
SERVO_THROTTLE = 7, // for internal combustion (IC) planes
|
||||||
|
|
||||||
|
SERVO_BICOPTER_LEFT = 4,
|
||||||
|
SERVO_BICOPTER_RIGHT = 5,
|
||||||
|
|
||||||
|
SERVO_DUALCOPTER_LEFT = 4,
|
||||||
|
SERVO_DUALCOPTER_RIGHT = 5,
|
||||||
|
|
||||||
|
SERVO_SINGLECOPTER_1 = 3,
|
||||||
|
SERVO_SINGLECOPTER_2 = 4,
|
||||||
|
SERVO_SINGLECOPTER_3 = 5,
|
||||||
|
SERVO_SINGLECOPTER_4 = 6,
|
||||||
|
|
||||||
|
} servoIndex_e; // FIXME rename to servoChannel_e
|
||||||
|
|
||||||
|
#define SERVO_PLANE_INDEX_MIN SERVO_FLAPS
|
||||||
|
#define SERVO_PLANE_INDEX_MAX SERVO_THROTTLE
|
||||||
|
|
||||||
|
#define SERVO_DUALCOPTER_INDEX_MIN SERVO_DUALCOPTER_LEFT
|
||||||
|
#define SERVO_DUALCOPTER_INDEX_MAX SERVO_DUALCOPTER_RIGHT
|
||||||
|
|
||||||
|
#define SERVO_SINGLECOPTER_INDEX_MIN SERVO_SINGLECOPTER_1
|
||||||
|
#define SERVO_SINGLECOPTER_INDEX_MAX SERVO_SINGLECOPTER_4
|
||||||
|
|
||||||
|
#define SERVO_FLAPPERONS_MIN SERVO_FLAPPERON_1
|
||||||
|
#define SERVO_FLAPPERONS_MAX SERVO_FLAPPERON_2
|
||||||
|
|
||||||
typedef struct servoMixer_t {
|
typedef struct servoMixer_t {
|
||||||
uint8_t targetChannel; // servo that receives the output of the rule
|
uint8_t targetChannel; // servo that receives the output of the rule
|
||||||
uint8_t inputSource; // input channel for this rule
|
uint8_t inputSource; // input channel for this rule
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "build_config.h"
|
||||||
|
|
||||||
#include "common/axis.h"
|
#include "common/axis.h"
|
||||||
#include "common/maths.h"
|
#include "common/maths.h"
|
||||||
|
|
||||||
|
@ -70,7 +72,10 @@ uint32_t rcModeActivationMask; // one bit per mode defined in boxId_e
|
||||||
|
|
||||||
|
|
||||||
void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction, int32_t newValue) {
|
void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction, int32_t newValue) {
|
||||||
#ifdef BLACKBOX
|
#ifndef BLACKBOX
|
||||||
|
UNUSED(adjustmentFunction);
|
||||||
|
UNUSED(newValue);
|
||||||
|
#else
|
||||||
if (feature(FEATURE_BLACKBOX)) {
|
if (feature(FEATURE_BLACKBOX)) {
|
||||||
flightLogEvent_inflightAdjustment_t eventData;
|
flightLogEvent_inflightAdjustment_t eventData;
|
||||||
eventData.adjustmentFunction = adjustmentFunction;
|
eventData.adjustmentFunction = adjustmentFunction;
|
||||||
|
@ -82,7 +87,10 @@ void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
void blackboxLogInflightAdjustmentEventFloat(adjustmentFunction_e adjustmentFunction, float newFloatValue) {
|
void blackboxLogInflightAdjustmentEventFloat(adjustmentFunction_e adjustmentFunction, float newFloatValue) {
|
||||||
#ifdef BLACKBOX
|
#ifndef BLACKBOX
|
||||||
|
UNUSED(adjustmentFunction);
|
||||||
|
UNUSED(newFloatValue);
|
||||||
|
#else
|
||||||
if (feature(FEATURE_BLACKBOX)) {
|
if (feature(FEATURE_BLACKBOX)) {
|
||||||
flightLogEvent_inflightAdjustment_t eventData;
|
flightLogEvent_inflightAdjustment_t eventData;
|
||||||
eventData.adjustmentFunction = adjustmentFunction;
|
eventData.adjustmentFunction = adjustmentFunction;
|
||||||
|
|
|
@ -77,6 +77,9 @@ uint8_t lastOneShotUpdateMotorCount;
|
||||||
|
|
||||||
uint32_t testFeatureMask = 0;
|
uint32_t testFeatureMask = 0;
|
||||||
|
|
||||||
|
int updatedServoCount;
|
||||||
|
int updatedMotorCount;
|
||||||
|
|
||||||
|
|
||||||
TEST(FlightMixerTest, TestForwardAuxChannelsToServosWithNoServos)
|
TEST(FlightMixerTest, TestForwardAuxChannelsToServosWithNoServos)
|
||||||
{
|
{
|
||||||
|
@ -148,11 +151,14 @@ TEST(FlightMixerTest, TestForwardAuxChannelsToServosWithLessRemainingServosThanA
|
||||||
TEST(FlightMixerTest, TestTricopterServo)
|
TEST(FlightMixerTest, TestTricopterServo)
|
||||||
{
|
{
|
||||||
// given
|
// given
|
||||||
|
updatedServoCount = 0;
|
||||||
|
|
||||||
mixerConfig_t mixerConfig;
|
mixerConfig_t mixerConfig;
|
||||||
memset(&mixerConfig, 0, sizeof(mixerConfig));
|
memset(&mixerConfig, 0, sizeof(mixerConfig));
|
||||||
|
|
||||||
rxConfig_t rxConfig;
|
rxConfig_t rxConfig;
|
||||||
memset(&rxConfig, 0, sizeof(rxConfig));
|
memset(&rxConfig, 0, sizeof(rxConfig));
|
||||||
|
rxConfig.midrc = 1500;
|
||||||
|
|
||||||
mixerConfig.tri_unarmed_servo = 1;
|
mixerConfig.tri_unarmed_servo = 1;
|
||||||
|
|
||||||
|
@ -205,18 +211,20 @@ TEST(FlightMixerTest, TestTricopterServo)
|
||||||
memset(axisPID, 0, sizeof(axisPID));
|
memset(axisPID, 0, sizeof(axisPID));
|
||||||
axisPID[YAW] = 0;
|
axisPID[YAW] = 0;
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
mixTable();
|
mixTable();
|
||||||
writeServos();
|
writeServos();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
EXPECT_EQ(1, updatedServoCount);
|
||||||
EXPECT_EQ(TEST_SERVO_MID, servos[0].value);
|
EXPECT_EQ(TEST_SERVO_MID, servos[0].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FlightMixerTest, TestQuadMotors)
|
TEST(FlightMixerTest, TestQuadMotors)
|
||||||
{
|
{
|
||||||
// given
|
// given
|
||||||
|
updatedMotorCount = 0;
|
||||||
|
|
||||||
mixerConfig_t mixerConfig;
|
mixerConfig_t mixerConfig;
|
||||||
memset(&mixerConfig, 0, sizeof(mixerConfig));
|
memset(&mixerConfig, 0, sizeof(mixerConfig));
|
||||||
|
|
||||||
|
@ -274,12 +282,139 @@ TEST(FlightMixerTest, TestQuadMotors)
|
||||||
writeMotors();
|
writeMotors();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
EXPECT_EQ(4, updatedMotorCount);
|
||||||
|
|
||||||
EXPECT_EQ(TEST_MIN_COMMAND, motors[0].value);
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[0].value);
|
||||||
EXPECT_EQ(TEST_MIN_COMMAND, motors[1].value);
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[1].value);
|
||||||
EXPECT_EQ(TEST_MIN_COMMAND, motors[2].value);
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[2].value);
|
||||||
EXPECT_EQ(TEST_MIN_COMMAND, motors[3].value);
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[3].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CustomMixerTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
// given
|
||||||
|
mixerConfig_t mixerConfig;
|
||||||
|
|
||||||
|
rxConfig_t rxConfig;
|
||||||
|
|
||||||
|
servoParam_t servoConf[MAX_SUPPORTED_SERVOS];
|
||||||
|
|
||||||
|
escAndServoConfig_t escAndServoConfig;
|
||||||
|
|
||||||
|
gimbalConfig_t gimbalConfig;
|
||||||
|
motorMixer_t customMotorMixer[MAX_SUPPORTED_MOTORS];
|
||||||
|
servoMixer_t customServoMixer[MAX_SUPPORTED_SERVOS];
|
||||||
|
|
||||||
|
virtual void SetUp() {
|
||||||
|
updatedServoCount = 0;
|
||||||
|
updatedMotorCount = 0;
|
||||||
|
|
||||||
|
memset(&rcData, 0, sizeof(rcData));
|
||||||
|
memset(&rcCommand, 0, sizeof(rcCommand));
|
||||||
|
memset(&mixerConfig, 0, sizeof(mixerConfig));
|
||||||
|
memset(&rxConfig, 0, sizeof(rxConfig));
|
||||||
|
rxConfig.midrc = 1500;
|
||||||
|
|
||||||
|
memset(&servoConf, 0, sizeof(servoConf));
|
||||||
|
for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
|
||||||
|
servoConf[i].min = DEFAULT_SERVO_MIN;
|
||||||
|
servoConf[i].max = DEFAULT_SERVO_MAX;
|
||||||
|
servoConf[i].middle = DEFAULT_SERVO_MIDDLE;
|
||||||
|
servoConf[i].rate = 100;
|
||||||
|
servoConf[i].forwardFromChannel = CHANNEL_FORWARDING_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&escAndServoConfig, 0, sizeof(escAndServoConfig));
|
||||||
|
|
||||||
|
escAndServoConfig.mincommand = TEST_MIN_COMMAND;
|
||||||
|
|
||||||
|
gimbalConfig.mode = GIMBAL_MODE_NORMAL;
|
||||||
|
|
||||||
|
mixerUseConfigs(
|
||||||
|
servoConf,
|
||||||
|
&gimbalConfig,
|
||||||
|
NULL,
|
||||||
|
&escAndServoConfig,
|
||||||
|
&mixerConfig,
|
||||||
|
NULL,
|
||||||
|
&rxConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
memset(&customMotorMixer, 0, sizeof(customMotorMixer));
|
||||||
|
|
||||||
|
memset(&customServoMixer, 0, sizeof(customServoMixer));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CustomMixerTest, TestCustomMixer)
|
||||||
|
{
|
||||||
|
// given
|
||||||
|
enum {
|
||||||
|
EXPECTED_SERVOS_TO_MIX_COUNT = 6,
|
||||||
|
EXPECTED_MOTORS_TO_MIX_COUNT = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
servoMixer_t testServoMixer[EXPECTED_SERVOS_TO_MIX_COUNT] = {
|
||||||
|
{ SERVO_FLAPS, INPUT_RC_AUX1, 100, 0, 0, 100, 0 },
|
||||||
|
{ SERVO_FLAPPERON_1, INPUT_STABILIZED_ROLL, 100, 0, 0, 100, 0 },
|
||||||
|
{ SERVO_FLAPPERON_2, INPUT_STABILIZED_ROLL, 100, 0, 0, 100, 0 },
|
||||||
|
{ SERVO_RUDDER, INPUT_STABILIZED_YAW, 100, 0, 0, 100, 0 },
|
||||||
|
{ SERVO_ELEVATOR, INPUT_STABILIZED_PITCH, 100, 0, 0, 100, 0 },
|
||||||
|
{ SERVO_THROTTLE, INPUT_STABILIZED_THROTTLE, 100, 0, 0, 100, 0 },
|
||||||
|
};
|
||||||
|
memcpy(customServoMixer, testServoMixer, sizeof(testServoMixer));
|
||||||
|
|
||||||
|
static const motorMixer_t testMotorMixer[EXPECTED_MOTORS_TO_MIX_COUNT] = {
|
||||||
|
{ 1.0f, 0.0f, 0.0f, -1.0f }, // LEFT
|
||||||
|
{ 1.0f, 0.0f, 0.0f, 1.0f }, // RIGHT
|
||||||
|
};
|
||||||
|
memcpy(customMotorMixer, testMotorMixer, sizeof(testMotorMixer));
|
||||||
|
|
||||||
|
|
||||||
|
mixerInit(MIXER_CUSTOM_AIRPLANE, customMotorMixer, customServoMixer);
|
||||||
|
|
||||||
|
pwmOutputConfiguration_t pwmOutputConfiguration = {
|
||||||
|
.servoCount = 6,
|
||||||
|
.motorCount = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
mixerUsePWMOutputConfiguration(&pwmOutputConfiguration);
|
||||||
|
|
||||||
|
// and
|
||||||
|
rcCommand[THROTTLE] = 1000;
|
||||||
|
|
||||||
|
// and
|
||||||
|
rcData[AUX1] = 2000;
|
||||||
|
|
||||||
|
// and
|
||||||
|
memset(axisPID, 0, sizeof(axisPID));
|
||||||
|
axisPID[YAW] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
mixTable();
|
||||||
|
writeMotors();
|
||||||
|
writeServos();
|
||||||
|
|
||||||
|
// then
|
||||||
|
EXPECT_EQ(EXPECTED_MOTORS_TO_MIX_COUNT, updatedMotorCount);
|
||||||
|
|
||||||
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[0].value);
|
||||||
|
EXPECT_EQ(TEST_MIN_COMMAND, motors[1].value);
|
||||||
|
|
||||||
|
EXPECT_EQ(EXPECTED_SERVOS_TO_MIX_COUNT, updatedServoCount);
|
||||||
|
|
||||||
|
EXPECT_EQ(2000, servos[0].value); // Flaps
|
||||||
|
EXPECT_EQ(TEST_SERVO_MID, servos[1].value);
|
||||||
|
EXPECT_EQ(TEST_SERVO_MID, servos[2].value);
|
||||||
|
EXPECT_EQ(TEST_SERVO_MID, servos[3].value);
|
||||||
|
EXPECT_EQ(TEST_SERVO_MID, servos[4].value);
|
||||||
|
EXPECT_EQ(1000, servos[5].value); // Throttle
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// STUBS
|
// STUBS
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -309,6 +444,7 @@ int32_t lowpassFixed(lowpass_t *, int32_t, int16_t) {
|
||||||
|
|
||||||
void pwmWriteMotor(uint8_t index, uint16_t value) {
|
void pwmWriteMotor(uint8_t index, uint16_t value) {
|
||||||
motors[index].value = value;
|
motors[index].value = value;
|
||||||
|
updatedMotorCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwmShutdownPulsesForAllMotors(uint8_t motorCount)
|
void pwmShutdownPulsesForAllMotors(uint8_t motorCount)
|
||||||
|
@ -332,6 +468,7 @@ void pwmWriteServo(uint8_t index, uint16_t value) {
|
||||||
if (index < MAX_SERVOS) {
|
if (index < MAX_SERVOS) {
|
||||||
servos[index].value = value;
|
servos[index].value = value;
|
||||||
}
|
}
|
||||||
|
updatedServoCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool failsafeIsActive(void) {
|
bool failsafeIsActive(void) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue