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/>.
23 #include "common/axis.h"
24 #include "common/sensor_alignment.h"
25 #include "common/sensor_alignment_impl.h"
26 #include "common/utils.h"
27 #include "drivers/sensor.h"
28 #include "sensors/boardalignment.h"
29 #include "sensors/sensors.h"
32 #include "gtest/gtest.h"
35 * This test file contains an independent method of rotating a vector.
36 * The output of alignSensor() is compared to the output of the test
39 * For each alignment condition (ALIGN_CW0, CW90, etc) the source vector under
40 * test is set to a unit vector along each axis (x-axis, y-axis, z-axis)
41 * plus one additional random vector is tested.
44 #define DEG2RAD 0.01745329251
46 static void rotateVector(int32_t mat
[3][3], float vec
[3], float *out
)
50 for(int i
=0; i
<3; i
++) {
52 for(int j
=0; j
<3; j
++) {
53 tmp
[i
] += mat
[j
][i
] * vec
[j
];
63 //static void initXAxisRotation(int32_t mat[][3], int32_t angle)
69 // mat[1][1] = cos(angle*DEG2RAD);
70 // mat[1][2] = -sin(angle*DEG2RAD);
72 // mat[2][1] = sin(angle*DEG2RAD);
73 // mat[2][2] = cos(angle*DEG2RAD);
76 static void initYAxisRotation(int32_t mat
[][3], int32_t angle
)
78 mat
[0][0] = cos(angle
*DEG2RAD
);
80 mat
[0][2] = sin(angle
*DEG2RAD
);
84 mat
[2][0] = -sin(angle
*DEG2RAD
);
86 mat
[2][2] = cos(angle
*DEG2RAD
);
89 static void initZAxisRotation(int32_t mat
[][3], int32_t angle
)
91 mat
[0][0] = cos(angle
*DEG2RAD
);
92 mat
[0][1] = -sin(angle
*DEG2RAD
);
94 mat
[1][0] = sin(angle
*DEG2RAD
);
95 mat
[1][1] = cos(angle
*DEG2RAD
);
102 #define TOL 1e-5 // TOLERANCE
104 static void alignSensorViaMatrixFromRotation(float *dest
, sensor_align_e alignment
)
106 fp_rotationMatrix_t sensorRotationMatrix
;
108 sensorAlignment_t sensorAlignment
;
110 buildAlignmentFromStandardAlignment(&sensorAlignment
, alignment
);
112 buildRotationMatrixFromAlignment(&sensorAlignment
, &sensorRotationMatrix
);
114 alignSensorViaMatrix(dest
, &sensorRotationMatrix
);
117 static void testCW(sensor_align_e rotation
, int32_t angle
)
119 float src
[XYZ_AXIS_COUNT
];
120 float test
[XYZ_AXIS_COUNT
];
122 // unit vector along x-axis
127 int32_t matrix
[3][3];
128 initZAxisRotation(matrix
, angle
);
129 rotateVector(matrix
, src
, test
);
131 alignSensorViaMatrixFromRotation(src
, rotation
);
132 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "X-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
133 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "X-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
134 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "X-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
136 // unit vector along y-axis
141 rotateVector(matrix
, src
, test
);
142 alignSensorViaMatrixFromRotation(src
, rotation
);
143 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Y-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
144 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Y-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
145 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Y-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
147 // unit vector along z-axis
152 rotateVector(matrix
, src
, test
);
153 alignSensorViaMatrixFromRotation(src
, rotation
);
154 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Z-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
155 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Z-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
156 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Z-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
158 // random vector to test
163 rotateVector(matrix
, src
, test
);
164 alignSensorViaMatrixFromRotation(src
, rotation
);
165 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Random alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
166 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Random alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
167 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Random alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
171 * Since the order of flip and rotation matters, these tests make the
172 * assumption that the 'flip' occurs first, followed by clockwise rotation
174 static void testCWFlip(sensor_align_e rotation
, int32_t angle
)
176 float src
[XYZ_AXIS_COUNT
];
177 float test
[XYZ_AXIS_COUNT
];
179 // unit vector along x-axis
184 int32_t matrix
[3][3];
185 initYAxisRotation(matrix
, 180);
186 rotateVector(matrix
, src
, test
);
187 initZAxisRotation(matrix
, angle
);
188 rotateVector(matrix
, test
, test
);
190 alignSensorViaMatrixFromRotation(src
, rotation
);
192 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "X-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
193 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "X-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
194 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "X-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
196 // unit vector along y-axis
201 initYAxisRotation(matrix
, 180);
202 rotateVector(matrix
, src
, test
);
203 initZAxisRotation(matrix
, angle
);
204 rotateVector(matrix
, test
, test
);
206 alignSensorViaMatrixFromRotation(src
, rotation
);
208 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Y-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
209 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Y-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
210 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Y-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
212 // unit vector along z-axis
217 initYAxisRotation(matrix
, 180);
218 rotateVector(matrix
, src
, test
);
219 initZAxisRotation(matrix
, angle
);
220 rotateVector(matrix
, test
, test
);
222 alignSensorViaMatrixFromRotation(src
, rotation
);
224 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Z-Unit alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
225 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Z-Unit alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
226 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Z-Unit alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
228 // random vector to test
233 initYAxisRotation(matrix
, 180);
234 rotateVector(matrix
, src
, test
);
235 initZAxisRotation(matrix
, angle
);
236 rotateVector(matrix
, test
, test
);
238 alignSensorViaMatrixFromRotation(src
, rotation
);
240 EXPECT_NEAR(test
[X
], src
[X
], TOL
) << "Random alignment does not match in X-Axis. " << test
[X
] << " " << src
[X
];
241 EXPECT_NEAR(test
[Y
], src
[Y
], TOL
) << "Random alignment does not match in Y-Axis. " << test
[Y
] << " " << src
[Y
];
242 EXPECT_NEAR(test
[Z
], src
[Z
], TOL
) << "Random alignment does not match in Z-Axis. " << test
[Z
] << " " << src
[Z
];
246 TEST(AlignSensorTest
, ClockwiseZeroDegrees
)
252 TEST(AlignSensorTest
, ClockwiseNinetyDegrees
)
254 testCW(CW90_DEG
, 90);
257 TEST(AlignSensorTest
, ClockwiseOneEightyDegrees
)
259 testCW(CW180_DEG
, 180);
262 TEST(AlignSensorTest
, ClockwiseTwoSeventyDegrees
)
264 testCW(CW270_DEG
, 270);
267 TEST(AlignSensorTest
, ClockwiseZeroDegreesFlip
)
269 testCWFlip(CW0_DEG_FLIP
, 0);
272 TEST(AlignSensorTest
, ClockwiseNinetyDegreesFlip
)
274 testCWFlip(CW90_DEG_FLIP
, 90);
277 TEST(AlignSensorTest
, ClockwiseOneEightyDegreesFlip
)
279 testCWFlip(CW180_DEG_FLIP
, 180);
282 TEST(AlignSensorTest
, ClockwiseTwoSeventyDegreesFlip
)
284 testCWFlip(CW270_DEG_FLIP
, 270);
287 static void testBuildAlignmentWithStandardAlignment(sensor_align_e alignment
, sensorAlignment_t expectedSensorAlignment
)
289 sensorAlignment_t sensorAlignment
= SENSOR_ALIGNMENT(6, 6, 6);
291 buildAlignmentFromStandardAlignment(&sensorAlignment
, alignment
);
293 for (unsigned i
= 0; i
< ARRAYLEN(sensorAlignment
.raw
); i
++) {
294 EXPECT_EQ(expectedSensorAlignment
.raw
[i
], sensorAlignment
.raw
[i
]) << "Sensor alignment was not updated. alignment: " << alignment
;
298 TEST(AlignSensorTest
, AttemptBuildAlignmentWithStandardAlignment
)
300 testBuildAlignmentWithStandardAlignment(CW0_DEG
, CUSTOM_ALIGN_CW0_DEG
);
301 testBuildAlignmentWithStandardAlignment(CW90_DEG
, CUSTOM_ALIGN_CW90_DEG
);
302 testBuildAlignmentWithStandardAlignment(CW180_DEG
, CUSTOM_ALIGN_CW180_DEG
);
303 testBuildAlignmentWithStandardAlignment(CW270_DEG
, CUSTOM_ALIGN_CW270_DEG
);
304 testBuildAlignmentWithStandardAlignment(CW0_DEG_FLIP
, CUSTOM_ALIGN_CW0_DEG_FLIP
);
305 testBuildAlignmentWithStandardAlignment(CW90_DEG_FLIP
, CUSTOM_ALIGN_CW90_DEG_FLIP
);
306 testBuildAlignmentWithStandardAlignment(CW180_DEG_FLIP
, CUSTOM_ALIGN_CW180_DEG_FLIP
);
307 testBuildAlignmentWithStandardAlignment(CW270_DEG_FLIP
, CUSTOM_ALIGN_CW270_DEG_FLIP
);
310 TEST(AlignSensorTest
, AttemptBuildAlignmentFromCustomAlignment
)
312 sensorAlignment_t sensorAlignment
= SENSOR_ALIGNMENT(1, 2, 3);
314 buildAlignmentFromStandardAlignment(&sensorAlignment
, ALIGN_CUSTOM
);
316 sensorAlignment_t expectedSensorAlignment
= SENSOR_ALIGNMENT(1, 2, 3);
318 for (unsigned i
= 0; i
< ARRAYLEN(sensorAlignment
.raw
); i
++) {
319 EXPECT_EQ(expectedSensorAlignment
.raw
[i
], sensorAlignment
.raw
[i
]) << "Custom alignment should not be updated.";
323 TEST(AlignSensorTest
, AttemptBuildAlignmentFromDefaultAlignment
)
325 sensorAlignment_t sensorAlignment
= SENSOR_ALIGNMENT(1, 2, 3);
327 buildAlignmentFromStandardAlignment(&sensorAlignment
, ALIGN_DEFAULT
);
329 sensorAlignment_t expectedSensorAlignment
= SENSOR_ALIGNMENT(1, 2, 3);
331 for (unsigned i
= 0; i
< ARRAYLEN(sensorAlignment
.raw
); i
++) {
332 EXPECT_EQ(expectedSensorAlignment
.raw
[i
], sensorAlignment
.raw
[i
]) << "Default alignment should not be updated.";
336 TEST(AlignSensorTest
, AlignmentBitmasks
)
340 bits
= ALIGNMENT_TO_BITMASK(CW0_DEG
);
341 EXPECT_EQ(0x0, bits
); // 000000
342 EXPECT_EQ(0, ALIGNMENT_YAW_ROTATIONS(bits
));
343 EXPECT_EQ(0, ALIGNMENT_PITCH_ROTATIONS(bits
));
344 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
346 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
347 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
348 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
350 bits
= ALIGNMENT_TO_BITMASK(CW90_DEG
);
351 EXPECT_EQ(0x1, bits
); // 000001
352 EXPECT_EQ(1, ALIGNMENT_YAW_ROTATIONS(bits
));
353 EXPECT_EQ(0, ALIGNMENT_PITCH_ROTATIONS(bits
));
354 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
356 EXPECT_EQ(1, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
357 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
358 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
360 bits
= ALIGNMENT_TO_BITMASK(CW180_DEG
);
361 EXPECT_EQ(0x2, bits
); // 000010
362 EXPECT_EQ(2, ALIGNMENT_YAW_ROTATIONS(bits
));
363 EXPECT_EQ(0, ALIGNMENT_PITCH_ROTATIONS(bits
));
364 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
366 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
367 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
368 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
370 bits
= ALIGNMENT_TO_BITMASK(CW270_DEG
);
371 EXPECT_EQ(0x3, bits
); // 000011
372 EXPECT_EQ(3, ALIGNMENT_YAW_ROTATIONS(bits
));
373 EXPECT_EQ(0, ALIGNMENT_PITCH_ROTATIONS(bits
));
374 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
376 EXPECT_EQ(3, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
377 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
378 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
380 bits
= ALIGNMENT_TO_BITMASK(CW0_DEG_FLIP
);
381 EXPECT_EQ(0x8, bits
); // 001000
382 EXPECT_EQ(0, ALIGNMENT_YAW_ROTATIONS(bits
));
383 EXPECT_EQ(2, ALIGNMENT_PITCH_ROTATIONS(bits
));
384 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
386 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
387 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
388 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
390 bits
= ALIGNMENT_TO_BITMASK(CW90_DEG_FLIP
);
391 EXPECT_EQ(0x9, bits
); // 001001
392 EXPECT_EQ(1, ALIGNMENT_YAW_ROTATIONS(bits
));
393 EXPECT_EQ(2, ALIGNMENT_PITCH_ROTATIONS(bits
));
394 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
396 EXPECT_EQ(1, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
397 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
398 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
400 bits
= ALIGNMENT_TO_BITMASK(CW180_DEG_FLIP
);
401 EXPECT_EQ(0xA, bits
); // 001010
402 EXPECT_EQ(2, ALIGNMENT_YAW_ROTATIONS(bits
));
403 EXPECT_EQ(2, ALIGNMENT_PITCH_ROTATIONS(bits
));
404 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
406 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
407 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
408 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));
410 bits
= ALIGNMENT_TO_BITMASK(CW270_DEG_FLIP
);
411 EXPECT_EQ(0xB, bits
); // 001011
412 EXPECT_EQ(3, ALIGNMENT_YAW_ROTATIONS(bits
));
413 EXPECT_EQ(2, ALIGNMENT_PITCH_ROTATIONS(bits
));
414 EXPECT_EQ(0, ALIGNMENT_ROLL_ROTATIONS(bits
));
416 EXPECT_EQ(3, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_YAW
));
417 EXPECT_EQ(2, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_PITCH
));
418 EXPECT_EQ(0, ALIGNMENT_AXIS_ROTATIONS(bits
, FD_ROLL
));