dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
unittest.cpp
Go to the documentation of this file.
1 
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>
24  */
25 
26 /*
27  * NOTE: This program uses the Google Test infrastructure to drive the unit test
28  *
29  * Main site for Google Test: http://code.google.com/p/googletest/
30  * Documentation and examples: http://code.google.com/p/googletest/wiki/Documentation
31  */
32 
33 #include "gtest/gtest.h"
34 
35 #include <stdio.h> /* printf */
36 #include <stdlib.h> /* abort */
37 #include <string.h> /* memset */
38 #include <stdint.h> /* uint*_t */
39 
40 extern "C" {
41 #define restrict /* neuter restrict keyword since it's not in C++ */
42 
43 /* Run unit tests with a much more conservative convergence limit than flight */
44 #define PSEUDOINV_CONVERGE_LIMIT 19
45 
46 #include "misc_math.h" /* API for misc_math functions */
47 
48 }
49 
50 #include <math.h> /* fabs() */
51 
52 // To use a test fixture, derive a class from testing::Test.
53 class MiscMath : public testing::Test {
54 protected:
55  virtual void SetUp() {
56  }
57 
58  virtual void TearDown() {
59  }
60 };
61 
62 // Test fixture for bound_min_max()
63 class BoundMinMax : public MiscMath {
64 protected:
65  virtual void SetUp() {
66  }
67 
68  virtual void TearDown() {
69  }
70 };
71 
72 TEST_F(BoundMinMax, ValBelowZeroRange) {
73  // Test lower bounding when min = max with (val < min)
74  EXPECT_EQ(-1.0f, bound_min_max(-10.0f, -1.0f, -1.0f));
75  EXPECT_EQ(0.0f, bound_min_max(-10.0f, 0.0f, 0.0f));
76  EXPECT_EQ(1.0f, bound_min_max(-10.0f, 1.0f, 1.0f));
77 };
78 
79 TEST_F(BoundMinMax, ValWithinZeroRange) {
80  // Test bounding when min = max = val
81  EXPECT_EQ(-1.0f, bound_min_max(-1.0f, -1.0f, -1.0f));
82  EXPECT_EQ(0.0f, bound_min_max(0.0f, 0.0f, 0.0f));
83  EXPECT_EQ(1.0f, bound_min_max(1.0f, 1.0f, 1.0f));
84 };
85 
86 TEST_F(BoundMinMax, ValAboveZeroRange) {
87  // Test upper bounding when min = max with (val > max)
88  EXPECT_EQ(-1.0f, bound_min_max(10.0f, -1.0f, -1.0f));
89  EXPECT_EQ(0.0f, bound_min_max(10.0f, 0.0f, 0.0f));
90  EXPECT_EQ(1.0f, bound_min_max(10.0f, 1.0f, 1.0f));
91 }
92 
93 TEST_F(BoundMinMax, PositiveMinMax) {
94  float min = 1.0f;
95  float max = 10.0f;
96 
97  // Below Lower Bound
98  EXPECT_EQ(min, bound_min_max(min - 1.0f, min, max));
99  // At Lower Bound
100  EXPECT_EQ(min, bound_min_max(min, min, max));
101  // In Bounds
102  EXPECT_EQ(2.0f, bound_min_max(2.0f, min, max));
103  // At Upper Bound
104  EXPECT_EQ(max, bound_min_max(max, min, max));
105  // Above Upper Bound
106  EXPECT_EQ(max, bound_min_max(max + 1.0f, min, max));
107 }
108 
109 TEST_F(BoundMinMax, NegativeMinMax) {
110  float min = -10.0f;
111  float max = -1.0f;
112 
113  // Below Lower Bound
114  EXPECT_EQ(min, bound_min_max(min - 1.0f, min, max));
115  // At Lower Bound
116  EXPECT_EQ(min, bound_min_max(min, min, max));
117  // In Bounds
118  EXPECT_EQ(-2.0f, bound_min_max(-2.0f, min, max));
119  // At Upper Bound
120  EXPECT_EQ(max, bound_min_max(max, min, max));
121  // Above Upper Bound
122  EXPECT_EQ(max, bound_min_max(max + 1.0f, min, max));
123 }
124 
125 TEST_F(BoundMinMax, StraddleZeroMinMax) {
126  float min = -10.0f;
127  float max = 10.0f;
128 
129  // Below Lower Bound
130  EXPECT_EQ(min, bound_min_max(min - 1.0f, min, max));
131  // At Lower Bound
132  EXPECT_EQ(min, bound_min_max(min, min, max));
133  // In Bounds
134  EXPECT_EQ(0.0f, bound_min_max(0.0f, min, max));
135  // At Upper Bound
136  EXPECT_EQ(max, bound_min_max(max, min, max));
137  // Above Upper Bound
138  EXPECT_EQ(max, bound_min_max(max + 1.0f, min, max));
139 }
140 
141 // Test fixture for bound_sym()
142 class BoundSym : public MiscMath {
143 protected:
144  virtual void SetUp() {
145  }
146 
147  virtual void TearDown() {
148  }
149 };
150 
151 TEST_F(BoundSym, ZeroRange) {
152  float range = 0.0f;
153 
154  // Below Lower Bound
155  EXPECT_EQ(-range, bound_sym(-range - 1.0f, range));
156  // At Lower Bound
157  EXPECT_EQ(-range, bound_sym(-range, range));
158  // In Bounds
159  EXPECT_EQ(0.0f, bound_sym(0.0f, range));
160  // At Upper Bound
161  EXPECT_EQ(range, bound_sym(range, range));
162  // Above Upper Bound
163  EXPECT_EQ(range, bound_sym(range + 1.0f, range));
164 };
165 
166 TEST_F(BoundSym, NonZeroRange) {
167  float range = 10.0f;
168 
169  // Below Lower Bound
170  EXPECT_EQ(-range, bound_sym(-range - 1.0f, range));
171  // At Lower Bound
172  EXPECT_EQ(-range, bound_sym(-range, range));
173  // In Bounds
174  EXPECT_EQ(0.0f, bound_sym(0.0f, range));
175  // At Upper Bound
176  EXPECT_EQ(range, bound_sym(range, range));
177  // Above Upper Bound
178  EXPECT_EQ(range, bound_sym(range + 1.0f, range));
179 };
180 
181 // Test fixture for circular_modulus_deg()
182 class CircularModulusDeg : public MiscMath {
183 protected:
184  virtual void SetUp() {
185  }
186 
187  virtual void TearDown() {
188  }
189 };
190 
192  float error = 0.0f;
193  EXPECT_EQ(-error, circular_modulus_deg(error - 3600000));
194  EXPECT_EQ(-error, circular_modulus_deg(error - 1080));
195  EXPECT_EQ(-error, circular_modulus_deg(error - 720));
196  EXPECT_EQ(-error, circular_modulus_deg(error - 360));
197  EXPECT_EQ(-error, circular_modulus_deg(error));
198  EXPECT_EQ(-error, circular_modulus_deg(error + 360));
199  EXPECT_EQ(-error, circular_modulus_deg(error + 720));
200  EXPECT_EQ(-error, circular_modulus_deg(error + 1080));
201  EXPECT_EQ(-error, circular_modulus_deg(error + 3600000));
202 };
203 
204 TEST_F(CircularModulusDeg, MaxPosError) {
205  // Use fabs() for +/-180.0 to accept either -180.0 or +180.0 as valid and correct
206  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f - 3600000)));
207  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f - 1080)));
208  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f - 720)));
209  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f - 360)));
210  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f)));
211  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f + 360)));
212  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f + 720)));
213  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f + 1080)));
214  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(180.0f + 3600000)));
215 };
216 
217 TEST_F(CircularModulusDeg, MaxNegError) {
218  // Use fabs() for +/-180.0 to accept either -180.0 or +180.0 as valid and correct
219  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f - 3600000)));
220  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f - 1080)));
221  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f - 720)));
222  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f - 360)));
223  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f)));
224  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f + 360)));
225  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f + 720)));
226  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f + 1080)));
227  EXPECT_EQ(180.0f, fabs(circular_modulus_deg(-180.0f + 3600000)));
228 };
229 
231  float eps = 0.0001f;
232 
233  for (float error = -179.9f; error < 179.9f; error += 0.001f) {
234  ASSERT_NEAR(error, circular_modulus_deg(error - 1080), eps);
235  ASSERT_NEAR(error, circular_modulus_deg(error - 720), eps);
236  ASSERT_NEAR(error, circular_modulus_deg(error - 360), eps);
237  ASSERT_NEAR(error, circular_modulus_deg(error), eps);
238  ASSERT_NEAR(error, circular_modulus_deg(error + 360), eps);
239  ASSERT_NEAR(error, circular_modulus_deg(error + 720), eps);
240  ASSERT_NEAR(error, circular_modulus_deg(error + 1080), eps);
241  }
242 };
243 
244 // Test fixture for vector2_clip()
245 class Vector2Clip : public MiscMath {
246 protected:
247  virtual void SetUp() {
248  }
249 
250  virtual void TearDown() {
251  }
252 };
253 
254 TEST_F(Vector2Clip, TestScale) {
255  float eps = 0.000001f;
256 
257  // Choose a non-integer limit
258  for (int i=0; i<3; i++) {
259  // Change the limit on each interation. This tests limit < 1, limit = 1, and limit > 1.
260  float limit = 0.5*i;
261 
262  float test_vec_null[2] = {0, 0};
263  float test_vec_within[2] = {limit/2, limit/2};
264  float test_vec_edge_numerically_stable[2] = {limit, 0};
265  float test_vec_edge_numerically_unstable[2] =
266  {(float) (sqrt(2)/2*limit), (float) (sqrt(2)/2*limit)};
267  float test_vec_outside[2] = {limit, limit};
268 
269  // Test for 0 vector
270  vector2_clip(test_vec_null, limit);
271  ASSERT_NEAR(test_vec_null[0], 0, eps);
272  ASSERT_NEAR(test_vec_null[1], 0, eps);
273 
274  // Test for vector within limits
275  vector2_clip(test_vec_within, limit);
276  EXPECT_EQ(test_vec_within[0], limit/2);
277  EXPECT_EQ(test_vec_within[1], limit/2);
278 
279  // Test for vector numerically identically at limits
280  vector2_clip(test_vec_edge_numerically_stable, limit);
281  EXPECT_EQ(test_vec_edge_numerically_stable[0], limit);
282  EXPECT_EQ(test_vec_edge_numerically_stable[1], 0.0f);
283 
284  // Test for vector identically at limits, but suffering from numerical imprecision
285  vector2_clip(test_vec_edge_numerically_unstable, limit);
286  ASSERT_NEAR(test_vec_edge_numerically_unstable[0], sqrt(2)/2*limit, eps);
287  ASSERT_NEAR(test_vec_edge_numerically_unstable[1], sqrt(2)/2*limit, eps);
288 
289  // Test for vector outside limits
290  vector2_clip(test_vec_outside, limit);
291  ASSERT_NEAR(test_vec_outside[0], sqrt(2)/2*limit, eps);
292  ASSERT_NEAR(test_vec_outside[1], sqrt(2)/2*limit, eps);
293  }
294 };
295 
296 // Test fixture for linear_interpolate()
297 class LinearInterpolate : public MiscMath {
298 protected:
299  virtual void SetUp() {
300  }
301 
302  virtual void TearDown() {
303  }
304 };
305 
306 TEST_F(LinearInterpolate, ThrottleCurve1to1) {
307  float const range_min = 0.0f;
308  float const range_max = 1.0f;
309  uint8_t const curve_numpts = 5;
310  float const curve[5] = { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f };
311  float eps = 0.000001f;
312 
313  // 21 points in range
314  for (size_t i = 0; i <= 20; ++i) {
315  float const input = i * 0.05f;
316  EXPECT_NEAR(input, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
317  }
318 
319  // 10 points below min range
320  for (size_t i = 1; i <= 10; ++i) {
321  float const input = range_min - i * 0.1f;
322  EXPECT_NEAR(range_min, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
323  }
324 
325  // 10 points above max range
326  for (size_t i = 1; i <= 10; ++i) {
327  float const input = range_max + i * 0.1f;
328  EXPECT_NEAR(range_max, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
329  }
330 };
331 
332 TEST_F(LinearInterpolate, CollectiveCurve1to1) {
333  float const range_min = -1.0f;
334  float const range_max = 1.0f;
335  uint8_t const curve_numpts = 5;
336  float const curve[5] = { -1.0f, -0.5f, 0.0f, 0.5f, 1.0f };
337  float eps = 0.000001f;
338 
339  // 21 points in range
340  for (size_t i = 0; i <= 20; ++i) {
341  float const input = i * 0.1f - 1.0f;
342  EXPECT_NEAR(input, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
343  }
344 
345  // 10 points below min range
346  for (size_t i = 1; i <= 10; ++i) {
347  float const input = range_min - i * 0.1f;
348  EXPECT_NEAR(range_min, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
349  }
350 
351  // 10 points above max range
352  for (size_t i = 1; i <= 10; ++i) {
353  float const input = range_max + i * 0.1f;
354  EXPECT_NEAR(range_max, linear_interpolate(input, curve, curve_numpts, range_min, range_max), eps);
355  }
356 };
357 
358 // Test fixture for matrix math
359 class MatrixMath : public MiscMath {
360 protected:
361  virtual void SetUp() {
362  }
363 
364  virtual void TearDown() {
365  }
366 };
367 
368 TEST_F(MatrixMath, MultipliesAndInverses) {
369  const float eps = 0.00001f;
370  const float bigeps = 0.001f;
371  const float hugeeps = 0.005f;
372  const float trivial[] = {
373  2.0f
374  };
375 
376  const float row_vector[] = {
377  1.0f, 2.0f, 3.0f
378  };
379 
380  const float col_vector[] = {
381  8.0f,
382  7.0f,
383  6.0f
384  };
385 
386  const float identity_3x3[] = {
387  1.0f, 0, 0,
388  0, 1.0f, 0,
389  0, 0, 1.0f
390  };
391 
392  const float simple_3x4[] = {
393  1.0f, 2.0f, 3.0f, 4.0f,
394  2.0f, 3.0f, 4.0f, 5.0f,
395  3.0f, 4.0f, 5.0f, 6.0f
396  };
397 
398  const float pseudo_4x3[] = {
399  -0.75f , -0.1f , 0.55f,
400  -0.333333f, -0.033333f, 0.266667f,
401  0.083333f, 0.033333f, -0.016667f,
402  0.5f , 0.1f , -0.3f
403  };
404 
405  const float simple_4x4[] = {
406  1.0f, 1.0f, 1.0f, 1.0f,
407  -1.0f, -1.0f, 1.0f, 1.0f,
408  1.0f, -1.0f, -1.0f, 1.0f,
409  -1.0f, 1.0f, -1.0f, 1.0f,
410  };
411 
412  const float degen_5x3[] = {
413  0, 1, 1, 1, 1,
414  -1, 0, 0, 0, 0,
415  -1, 0, 0, 0, 0
416  };
417 
418  float single[1];
419  float vect3[3];
420  float matr_3x3[3*3];
421  float matr_3x4[3*4];
422  float matr_4x3[4*3];
423  float matr_4x4[4*4];
424  float matrb_4x4[4*4];
425 
426  float matr_5x3[5*3];
427  float matr_3x5[3*5];
428 
429  matrix_mul_check(trivial, trivial, single, 1, 1, 1);
430 
431  EXPECT_NEAR(4.0f, single[0], eps);
432 
433  matrix_mul_check(row_vector, trivial, vect3, 3, 1, 1);
434 
435  for (int i = 0; i < 3; i++) {
436  EXPECT_NEAR(row_vector[i] * 2, vect3[i], eps);
437  }
438 
439  matrix_mul_check(trivial, col_vector, vect3, 1, 1, 3);
440 
441  for (int i = 0; i < 3; i++) {
442  EXPECT_NEAR(col_vector[i] * 2, vect3[i], eps);
443  }
444 
445  matrix_mul_check(row_vector, col_vector, single, 1, 3, 1);
446 
447  EXPECT_NEAR(40, single[0], eps);
448 
449  matrix_mul_check(identity_3x3, identity_3x3, matr_3x3, 3, 3, 3);
450 
451  for (int i = 0; i < 9; i++) {
452  EXPECT_NEAR(identity_3x3[i], matr_3x3[i], eps);
453  }
454 
455  matrix_mul_check(identity_3x3, simple_3x4, matr_3x4, 3, 3, 4);
456 
457  for (int i = 0; i < 9; i++) {
458  EXPECT_NEAR(simple_3x4[i], matr_3x4[i], eps);
459  }
460 
461  matrix_mul_check(simple_3x4, simple_4x4, matr_3x4, 3, 4, 4);
462 
463  EXPECT_NEAR(-2, matr_3x4[0], eps);
464  for (int i = 0; i < 12; i += 4) {
465  EXPECT_NEAR(-2, matr_3x4[i], eps);
466  EXPECT_NEAR(0, matr_3x4[i+1], eps);
467  EXPECT_NEAR(-4, matr_3x4[i+2], eps);
468  }
469 
470  EXPECT_NEAR(10, matr_3x4[3], eps);
471  EXPECT_NEAR(14, matr_3x4[7], eps);
472  EXPECT_NEAR(18, matr_3x4[11], eps);
473 
474  EXPECT_TRUE(matrix_pseudoinv(trivial, single, 1, 1));
475 
476  EXPECT_NEAR(1.0f / trivial[0], single[0], bigeps);
477 
478  EXPECT_TRUE(matrix_pseudoinv(identity_3x3, matr_3x3, 1, 1));
479 
480  for (int i = 0; i < 9; i++) {
481  EXPECT_NEAR(identity_3x3[i], matr_3x3[i], bigeps);
482  }
483 
484  EXPECT_TRUE(matrix_pseudoinv(simple_4x4, matr_4x4, 4, 4));
485 
486  /* Check for values near +/- 0.25f */
487  for (int i = 0; i < 16; i++) {
488  EXPECT_NEAR(0.25f, fabsf(matr_4x4[i]), bigeps);
489  }
490 
491  matrix_mul_check(simple_4x4, matr_4x4, matrb_4x4, 4, 4, 4);
492 
493  for (int i = 0; i < 4; i++) {
494  for (int j = 0; j < 4; j++) {
495  if (i == j) {
496  EXPECT_NEAR(1, matrb_4x4[i*4 + j], bigeps);
497  } else {
498  EXPECT_NEAR(0, matrb_4x4[i*4 + j], bigeps);
499  }
500  }
501  }
502 
503  EXPECT_TRUE(matrix_pseudoinv(matr_4x4, matrb_4x4, 4, 4));
504 
505  for (int i = 0; i < 16; i++) {
506  EXPECT_NEAR(matrb_4x4[i], simple_4x4[i], bigeps);
507  }
508 
509  EXPECT_TRUE(matrix_pseudoinv(simple_3x4, matr_4x3, 3, 4));
510 
511  for (int i = 0; i < 12; i++) {
512  EXPECT_NEAR(matr_4x3[i], pseudo_4x3[i], bigeps);
513  }
514 
515  EXPECT_TRUE(matrix_pseudoinv(matr_4x3, matr_3x4, 4, 3));
516 
517  for (int i = 0; i < 12; i++) {
518  EXPECT_NEAR(matr_3x4[i], simple_3x4[i], hugeeps);
519  }
520 
521  EXPECT_TRUE(matrix_pseudoinv(pseudo_4x3, matr_3x4, 4, 3));
522 
523  for (int i = 0; i < 12; i++) {
524  EXPECT_NEAR(matr_4x3[i], pseudo_4x3[i], bigeps);
525  }
526 
527  EXPECT_TRUE(matrix_pseudoinv(degen_5x3, matr_3x5, 5, 3));
528  EXPECT_TRUE(matrix_pseudoinv(matr_3x5, matr_5x3, 3, 5));
529 
530  for (int i = 0; i < 15; i++) {
531  EXPECT_NEAR(matr_5x3[i], degen_5x3[i], hugeeps);
532  }
533 }
534 
535 TEST_F(MatrixMath, MixerMatrixInverses) {
536  const float eps = 0.0005f;
537 
538  float quad_mixer[10 * 8] = {
539  0.5f, 0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
540  -0.5f, -0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
541  0.49f, -0.5f, -0.5f, 1.0f, 0, 0 ,0, 0,
542  /* Some degeneracy in the middle */
543  0, 0, 0, 0, 1, 0, 0, 0,
544  0, 0, 0, 0, 1, 0, 0, 0,
545  0, 0, 0, 0, 0, 1, 1, 0,
546  0, 0, 0, 0, 0, -2, -2, 0,
547  -0.5f, 0.5f, -0.5f, 1.0f, 0, 0, 0, 0,
548  -0.5f, 0.5f, -0.5f, 1.0f, 0, 0, 0, 0,
549  };
550 
551  float hexacoax_mixer[10 * 8] = {
552  1.0f, 0.25f, 0.742f, 1.0f, 0, 0, 0, 0,
553  1.0f, 0.25f, -0.75f, 1.0f, 0, 0, 0, 0,
554  -1.0f, 0.25f, 0.742f, 1.0f, 0, 0, 0, 0,
555  -1.0f, 0.25f, -0.75f, 1.0f, 0, 0, 0, 0,
556  0.0f, -0.492f, 0.742f, 1.0f, 0, 0, 0, 0,
557  0.0f, -0.492f, -0.75f, 1.0f, 0, 0, 0, 0,
558  /* Duplicate actuator for fun */
559  0.0f, -0.492f, -0.75f, 1.0f, 0, 0, 0, 0,
560  /* And crud */
561  0, 0, 0, 0, 0, 1, 1, 0,
562  0, 0, 0, 0, 0, -2, -2, 0,
563  };
564 
565  float lotsquad_mixer[10 * 8] = {
566  0.5f, 0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
567  -0.5f, -0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
568  0.5f, -0.5f, -0.5f, 1.0f, 0, 0 ,0, 0,
569  -0.5f, 0.5f, -0.5f, 1.0f, 0, 0, 0, 0,
570  0.5f, 0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
571  -0.5f, -0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
572  0.5f, -0.5f, -0.5f, 1.0f, 0, 0 ,0, 0,
573  -0.5f, 0.5f, -0.5f, 1.0f, 0, 0, 0, 0,
574  0.5f, 0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
575  -0.5f, -0.5f, 0.5f, 1.0f, 0, 0, 0, 0,
576  };
577 
578  float octo_mixer[10 * 8] = {
579  0.707f, 0.707f, 0.5f, 1.0f, 0, 0, 0, 0,
580  -0.707f, -0.707f, 0.5f, 1.0f, 0, 0, 0, 0,
581  0.707f, -0.707f, -0.5f, 1.0f, 0, 0 ,0, 0,
582  -0.707f, 0.707f, -0.5f, 1.0f, 0, 0, 0, 0,
583  1.0f, 0.0f, 0.5f, 1.0f, 0, 0, 0, 0,
584  -1.0f, 0.0f, -0.5f, 1.0f, 0, 0 ,0, 0,
585  0.0f, -1.0f, 0.5f, 1.0f, 0, 0, 0, 0,
586  0.0f, 1.0f, -0.5f, 1.0f, 0, 0, 0, 0,
587  0, 0, 0, 1, 0, 0, 0, 0,
588  0, 0, 0, 0, 0, -2, -2, 0,
589  };
590 
591  float elevon_mixer[10 * 8] = {
592  0.5f, 0.5f, 0.0f, 0.0f, 0, 0, 0, 0,
593  -0.492f, 0.5f, 0.0f, 0.0f, 0, 0, 0, 0,
594  0.0f, 0.0f, 0.0f, 1.0f, 0, 0, 0, 0,
595  0.0f, 0.0f, 1.0f, 0.0f, 0, 0, 0, 0,
596  0.0f, 0.0f, 0.0f, 0.0f, 1, 1, 0, 0,
597  0.0f, 0.0f, 0.0f, 0.0f, -1, 1, 0, 0,
598  0.0f, 0.0f, 0.0f, 0.0f, 1, 1, 0, 0,
599  0.0f, 0.0f, 0.0f, 0.0f, -1, 1, 0, 0,
600  };
601 
602  float inv_motor_mixer[8 * 10];
603  float temporary[10 * 10];
604  float should_be_motor_mixer[10 * 8];
605 
606  EXPECT_TRUE(matrix_pseudoinv(quad_mixer, inv_motor_mixer, 10, 8));
607 
608  matrix_mul_check(quad_mixer, inv_motor_mixer, temporary, 10, 8, 10);
609 
610  matrix_mul_check(temporary, quad_mixer, should_be_motor_mixer, 10, 10, 8);
611 
612  for (int i = 0; i < 10*8; i++) {
613  EXPECT_NEAR(quad_mixer[i], should_be_motor_mixer[i], eps);
614  }
615 
616  EXPECT_TRUE(matrix_pseudoinv(hexacoax_mixer, inv_motor_mixer, 10, 8));
617 
618  matrix_mul_check(hexacoax_mixer, inv_motor_mixer, temporary, 10, 8, 10);
619 
620  matrix_mul_check(temporary, hexacoax_mixer, should_be_motor_mixer, 10, 10, 8);
621 
622  for (int i = 0; i< 10*8; i++) {
623  EXPECT_NEAR(hexacoax_mixer[i], should_be_motor_mixer[i], eps);
624  }
625 
626  for (int j = 0; j < 500; j++) {
627  /* Round trip a few times */
628 
629  EXPECT_TRUE(matrix_pseudoinv(should_be_motor_mixer, inv_motor_mixer, 10, 8));
630  matrix_mul_check(should_be_motor_mixer, inv_motor_mixer, temporary, 10, 8, 10);
631 
632  matrix_mul_check(temporary, hexacoax_mixer, should_be_motor_mixer, 10, 10, 8);
633 
634  for (int i = 0; i < 10*8; i++) {
635  EXPECT_NEAR(hexacoax_mixer[i], should_be_motor_mixer[i], eps);
636  }
637  }
638 
639  EXPECT_TRUE(matrix_pseudoinv(lotsquad_mixer, inv_motor_mixer, 10, 8));
640 
641  matrix_mul_check(lotsquad_mixer, inv_motor_mixer, temporary, 10, 8, 10);
642 
643  matrix_mul_check(temporary, lotsquad_mixer, should_be_motor_mixer, 10, 10, 8);
644 
645  for (int i = 0; i < 10*8; i++) {
646  EXPECT_NEAR(lotsquad_mixer[i], should_be_motor_mixer[i], eps);
647  }
648 
649  EXPECT_TRUE(matrix_pseudoinv(octo_mixer, inv_motor_mixer, 10, 8));
650 
651  matrix_mul_check(octo_mixer, inv_motor_mixer, temporary, 10, 8, 10);
652 
653  matrix_mul_check(temporary, octo_mixer, should_be_motor_mixer, 10, 10, 8);
654 
655  for (int i = 0; i < 10*8; i++) {
656  EXPECT_NEAR(octo_mixer[i], should_be_motor_mixer[i], eps);
657  }
658 
659  EXPECT_TRUE(matrix_pseudoinv(elevon_mixer, inv_motor_mixer, 10, 8));
660 
661  matrix_mul_check(elevon_mixer, inv_motor_mixer, temporary, 10, 8, 10);
662 
663  matrix_mul_check(temporary, elevon_mixer, should_be_motor_mixer, 10, 10, 8);
664 
665  for (int i = 0; i < 10*8; i++) {
666  EXPECT_NEAR(elevon_mixer[i], should_be_motor_mixer[i], eps);
667  }
668 
669  /* Now try more and more cut-down versions of them */
670  for (int j = 9; j > 0; j--) {
671  EXPECT_TRUE(matrix_pseudoinv(lotsquad_mixer, inv_motor_mixer, j, 8));
672 
673  matrix_mul(lotsquad_mixer, inv_motor_mixer, temporary, j, 8, j);
674 
675  matrix_mul(temporary, lotsquad_mixer, should_be_motor_mixer, j, j, 8);
676 
677  for (int i = 0; i < j*8; i++) {
678  EXPECT_NEAR(lotsquad_mixer[i], should_be_motor_mixer[i], eps);
679  }
680  }
681 
682  for (int j = 9; j > 0; j--) {
683  EXPECT_TRUE(matrix_pseudoinv(hexacoax_mixer, inv_motor_mixer, j, 8));
684 
685  matrix_mul(hexacoax_mixer, inv_motor_mixer, temporary, j, 8, j);
686 
687  matrix_mul(temporary, hexacoax_mixer, should_be_motor_mixer, j, j, 8);
688 
689  for (int i=0; i < j*8; i++) {
690  EXPECT_NEAR(hexacoax_mixer[i], should_be_motor_mixer[i], eps);
691  }
692  }
693 
694  for (int j = 9; j > 0; j--) {
695  EXPECT_TRUE(matrix_pseudoinv(octo_mixer, inv_motor_mixer, j, 8));
696 
697  matrix_mul(octo_mixer, inv_motor_mixer, temporary, j, 8, j);
698 
699  matrix_mul(temporary, octo_mixer, should_be_motor_mixer, j, j, 8);
700 
701  for (int i = 0; i < j*8; i++) {
702  EXPECT_NEAR(octo_mixer[i], should_be_motor_mixer[i], eps);
703  }
704  }
705 
706  for (int j = 9; j > 0; j--) {
707  EXPECT_TRUE(matrix_pseudoinv(elevon_mixer, inv_motor_mixer, j, 8));
708 
709  matrix_mul(elevon_mixer, inv_motor_mixer, temporary, j, 8, j);
710 
711  matrix_mul(temporary, elevon_mixer, should_be_motor_mixer, j, j, 8);
712 
713  for (int i = 0; i < j*8; i++) {
714  EXPECT_NEAR(elevon_mixer[i], should_be_motor_mixer[i], eps);
715  }
716  }
717 
718  float tempb[8*10];
719 
720  /* Play with cutting out columns too */
721  for (int j = 7; j > 0; j--) {
722  for (int i = 0; i < 10; i++) {
723  memcpy(tempb + i * j, octo_mixer + i * 8, j * sizeof(float));
724  }
725 
726  EXPECT_TRUE(matrix_pseudoinv(tempb, inv_motor_mixer, 10, j));
727 
728  matrix_mul(tempb, inv_motor_mixer, temporary, 10, j, 10);
729 
730  matrix_mul(temporary, tempb, should_be_motor_mixer, 10, 10, j);
731 
732  for (int i = 0; i < j*10; i++) {
733  EXPECT_NEAR(tempb[i], should_be_motor_mixer[i], eps);
734  }
735  }
736 }
virtual void TearDown()
Definition: unittest.cpp:58
#define matrix_mul_check(a, b, out, arows, acolsbrows, bcols)
Definition: misc_math.h:447
TEST_F(RneFromLLATest, Equator)
Definition: unittest.cpp:68
volatile int j
Definition: loadabletest.c:12
virtual void SetUp()
Definition: unittest.cpp:299
static bool matrix_pseudoinv(const float *a, float *out, int arows, int acols)
Finds a pseudo-inverse of a matrix.
Definition: misc_math.h:423
virtual void TearDown()
Definition: unittest.cpp:364
virtual void TearDown()
Definition: unittest.cpp:147
virtual void SetUp()
Definition: unittest.cpp:65
float circular_modulus_deg(float err)
Circular modulus.
Definition: misc_math.c:62
virtual void TearDown()
Definition: unittest.cpp:68
void vector2_clip(float *vels, float limit)
Definition: misc_math.c:208
virtual void SetUp()
Definition: unittest.cpp:184
uint8_t i
Definition: msp_messages.h:97
float bound_min_max(float val, float min, float max)
Bound input value between min and max.
Definition: misc_math.c:38
float bound_sym(float val, float range)
Bound input value within range (plus or minus)
Definition: misc_math.c:50
static void matrix_mul(const float *a, const float *b, float *out, int arows, int acolsbrows, int bcols)
Multiplies out = a b.
Definition: misc_math.h:121
virtual void SetUp()
Definition: unittest.cpp:55
virtual void SetUp()
Definition: unittest.cpp:361
virtual void TearDown()
Definition: unittest.cpp:250
tuple f
Definition: px_mkfw.py:81
float linear_interpolate(float const input, float const *curve, uint8_t num_points, const float input_min, const float input_max)
Definition: misc_math.c:290
virtual void SetUp()
Definition: unittest.cpp:247
virtual void TearDown()
Definition: unittest.cpp:187
void error(int)
Definition: main.c:130
virtual void TearDown()
Definition: unittest.cpp:302
uint16_t max
Definition: msp_messages.h:97
virtual void SetUp()
Definition: unittest.cpp:144
uint16_t min
Definition: msp_messages.h:96