PX4 Firmware
PX4 Autopilot Software http://px4.io
test_mixer.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (C) 2013-2019 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file test_mixer.cpp
36  * Mixer load test
37  */
38 
39 #include <dirent.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <math.h>
44 
45 #include <px4_platform_common/px4_config.h>
46 #include <lib/mixer/MixerGroup.hpp>
47 #include <lib/mixer/mixer_load.h>
49 #include <drivers/drv_hrt.h>
50 #include <drivers/drv_pwm_output.h>
51 #include <px4iofirmware/mixer.h>
52 #include <px4iofirmware/protocol.h>
53 
55 
56 #include "tests_main.h"
57 
58 #include <unit_test.h>
59 
60 static int mixer_callback(uintptr_t handle,
61  uint8_t control_group,
62  uint8_t control_index,
63  float &control);
64 
65 static const unsigned output_max = 8;
67 static bool should_prearm = false;
68 
69 #ifdef __PX4_DARWIN
70 #define MIXER_DIFFERENCE_THRESHOLD 30
71 #else
72 #define MIXER_DIFFERENCE_THRESHOLD 2
73 #endif
74 
75 #ifndef PATH_MAX
76 #ifdef __PX4_NUTTX
77 #define PATH_MAX 512
78 #else
79 #define PATH_MAX 4096
80 #endif
81 #endif
82 
83 #if defined(CONFIG_ARCH_BOARD_PX4_SITL)
84 #define MIXER_PATH(_file) "etc/mixers/"#_file
85 #define MIXER_ONBOARD_PATH "etc/mixers"
86 #else
87 #define MIXER_ONBOARD_PATH "/etc/mixers"
88 #define MIXER_PATH(_file) MIXER_ONBOARD_PATH"/"#_file
89 #endif
90 
91 
92 #define MIXER_VERBOSE
93 
94 class MixerTest : public UnitTest
95 {
96 public:
97  virtual bool run_tests();
98  MixerTest() = default;
99 
100 private:
101  bool mixerTest();
102  bool loadIOPass();
103  bool loadVTOL1Test();
104  bool loadVTOL2Test();
105  bool loadQuadTest();
106  bool loadComplexTest();
107  bool loadAllTest();
108  bool load_mixer(const char *filename, unsigned expected_count, bool verbose = false);
109  bool load_mixer(const char *filename, const char *buf, unsigned loaded, unsigned expected_count,
110  const unsigned chunk_size, bool verbose);
111 
113 };
114 
116 {
124 
125  return (_tests_failed == 0);
126 }
127 
129 
131 {
132  return load_mixer(MIXER_PATH(IO_pass.mix), 8);
133 }
134 
136 {
137  return load_mixer(MIXER_PATH(quad_test.mix), 5);
138 }
139 
141 {
142  return load_mixer(MIXER_PATH(vtol1_test.mix), 4);
143 }
144 
146 {
147  return load_mixer(MIXER_PATH(vtol2_test.mix), 6);
148 }
149 
151 {
152  return load_mixer(MIXER_PATH(complex_test.mix), 8);
153 }
154 
156 {
157  PX4_INFO("Testing all mixers in %s", MIXER_ONBOARD_PATH);
158 
159  DIR *dp = opendir(MIXER_ONBOARD_PATH);
160 
161  if (dp == nullptr) {
162  PX4_ERR("File open failed");
163  return false;
164  }
165 
166  struct dirent *result = nullptr;
167 
168  for (;;) {
169  errno = 0;
170  result = readdir(dp);
171 
172  // read the directory entry
173  if (result == nullptr) {
174  if (errno) {
175  PX4_ERR("readdir failed");
176  closedir(dp);
177 
178  return false;
179  }
180 
181  // We are just at the last directory entry
182  break;
183  }
184 
185  // Determine the directory entry type
186  switch (result->d_type) {
187 #ifdef __PX4_NUTTX
188 
189  case DTYPE_FILE:
190 #else
191  case DT_REG:
192 #endif
193  if (strncmp(result->d_name, ".", 1) != 0) {
194 
195  char buf[PATH_MAX];
196 
197  if (snprintf(buf, PATH_MAX, "%s/%s", MIXER_ONBOARD_PATH, result->d_name) >= PATH_MAX) {
198  PX4_ERR("mixer path too long %s", result->d_name);
199  closedir(dp);
200  return false;
201  }
202 
203  bool ret = load_mixer(buf, 0);
204 
205  if (!ret) {
206  PX4_ERR("Error testing mixer %s", buf);
207  closedir(dp);
208  return false;
209  }
210  }
211 
212  break;
213 
214  default:
215  break;
216  }
217  }
218 
219  closedir(dp);
220 
221  return true;
222 }
223 
224 bool MixerTest::load_mixer(const char *filename, unsigned expected_count, bool verbose)
225 {
226  char buf[2048];
227 
228  load_mixer_file(filename, &buf[0], sizeof(buf));
229  unsigned loaded = strlen(buf);
230 
231  if (verbose) {
232  PX4_INFO("loaded: \n\"%s\"\n (file: %s, %d chars)", &buf[0], filename, loaded);
233  }
234 
235  // Test a number of chunk sizes
236  for (unsigned chunk_size = 6; chunk_size < PX4IO_MAX_TRANSFER_LEN + 1; chunk_size++) {
237  bool ret = load_mixer(filename, buf, loaded, expected_count, chunk_size, verbose);
238 
239  if (!ret) {
240  PX4_ERR("Mixer load failed with chunk size %u", chunk_size);
241  return ret;
242  }
243  }
244 
245  return true;
246 }
247 
248 bool MixerTest::load_mixer(const char *filename, const char *buf, unsigned loaded, unsigned expected_count,
249  const unsigned chunk_size, bool verbose)
250 {
251  /* load the mixer in chunks, like
252  * in the case of a remote load,
253  * e.g. on PX4IO.
254  */
255 
256  /* load at once test */
257  unsigned xx = loaded;
258  mixer_group.reset();
259  mixer_group.load_from_buf(mixer_callback, 0, &buf[0], xx);
260 
261  if (expected_count > 0) {
262  ut_compare("check number of mixers loaded", mixer_group.count(), expected_count);
263  }
264 
265  unsigned empty_load = 2;
266  char empty_buf[2];
267  empty_buf[0] = ' ';
268  empty_buf[1] = '\0';
269  mixer_group.reset();
270  mixer_group.load_from_buf(mixer_callback, 0, &empty_buf[0], empty_load);
271 
272  if (verbose) {
273  PX4_INFO("empty buffer load: loaded %u mixers, used: %u", mixer_group.count(), empty_load);
274  }
275 
276  ut_compare("empty buffer load", empty_load, 0);
277 
278  /* reset, load in chunks */
279  mixer_group.reset();
280  char mixer_text[PX4IO_MAX_MIXER_LENGTH]; /* large enough for one mixer */
281 
282  unsigned mixer_text_length = 0;
283  unsigned transmitted = 0;
284  unsigned resid = 0;
285 
286  while (transmitted < loaded) {
287 
288  unsigned text_length = (loaded - transmitted > chunk_size) ? chunk_size : loaded - transmitted;
289 
290  /* check for overflow - this would be really fatal */
291  if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) {
292  PX4_ERR("Mixer text length overflow for file: %s. Is PX4IO_MAX_MIXER_LENGTH too small? (curr len: %d)", filename,
294  return false;
295  }
296 
297  /* append mixer text and nul-terminate */
298  memcpy(&mixer_text[mixer_text_length], &buf[transmitted], text_length);
299  mixer_text_length += text_length;
300  mixer_text[mixer_text_length] = '\0';
301  //fprintf(stderr, "buflen %u, text:\n\"%s\"\n", mixer_text_length, &mixer_text[0]);
302 
303  /* process the text buffer, adding new mixers as their descriptions can be parsed */
304  resid = mixer_text_length;
305  mixer_group.load_from_buf(mixer_callback, 0, &mixer_text[0], resid);
306 
307  /* if anything was parsed */
308  if (resid != mixer_text_length) {
309  //PX4_INFO("loaded %d mixers, used %u\n", mixer_group.count(), mixer_text_length - resid);
310 
311  /* copy any leftover text to the base of the buffer for re-use */
312  if (resid > 0) {
313  memmove(&mixer_text[0], &mixer_text[mixer_text_length - resid], resid);
314  /* enforce null termination */
315  mixer_text[resid] = '\0';
316  }
317 
318  mixer_text_length = resid;
319  }
320 
321  transmitted += text_length;
322 
323  if (verbose) {
324  PX4_INFO("transmitted: %d, loaded: %d", transmitted, loaded);
325  }
326  }
327 
328  if (verbose) {
329  PX4_INFO("chunked load: loaded %u mixers", mixer_group.count());
330  }
331 
332  if (expected_count > 0 && mixer_group.count() != expected_count) {
333  PX4_ERR("Load of mixer failed, last chunk: %s, transmitted: %u, text length: %u, resid: %u", mixer_text, transmitted,
334  mixer_text_length, resid);
335  ut_compare("check number of mixers loaded (chunk)", mixer_group.count(), expected_count);
336  }
337 
338  return true;
339 }
340 
342 {
343  /*
344  * Output limit structure
345  */
346  output_limit_t output_limit;
347  bool should_arm = false;
351  uint16_t r_page_servos[output_max];
352  uint16_t servo_predicted[output_max];
353  int16_t reverse_pwm_mask = 0;
354 
355  bool load_ok = load_mixer(MIXER_PATH(IO_pass.mix), 8);
356 
357  if (!load_ok) {
358  return load_ok;
359  }
360 
361  /* execute the mixer */
362 
363  float outputs[output_max];
364  unsigned mixed;
365  const int jmax = 5;
366 
367  output_limit_init(&output_limit);
368 
369  /* run through arming phase */
370  for (unsigned i = 0; i < output_max; i++) {
371  actuator_controls[i] = 0.1f;
372  r_page_servo_disarmed[i] = PWM_MOTOR_OFF;
373  r_page_servo_control_min[i] = PWM_DEFAULT_MIN;
374  r_page_servo_control_max[i] = PWM_DEFAULT_MAX;
375  }
376 
377  //PX4_INFO("PRE-ARM TEST: DISABLING SAFETY");
378 
379  /* mix */
380  should_prearm = true;
381  mixed = mixer_group.mix(&outputs[0], output_max);
382 
383  output_limit_calc(should_arm, should_prearm, mixed, reverse_pwm_mask, r_page_servo_disarmed, r_page_servo_control_min,
384  r_page_servo_control_max, outputs, r_page_servos, &output_limit);
385 
386  //PX4_INFO("mixed %d outputs (max %d), values:", mixed, output_max);
387 
388  for (unsigned i = 0; i < mixed; i++) {
389  //PX4_ERR("pre-arm:\t %d: out: %8.4f, servo: %d", i, (double)outputs[i], (int)r_page_servos[i]);
390 
391  if (i != actuator_controls_s::INDEX_THROTTLE) {
392  if (r_page_servos[i] < r_page_servo_control_min[i]) {
393  PX4_ERR("active servo < min");
394  return false;
395  }
396 
397  } else {
398  if (r_page_servos[i] != r_page_servo_disarmed[i]) {
399  PX4_ERR("throttle output != 0 (this check assumed the IO pass mixer!)");
400  return false;
401  }
402  }
403  }
404 
405  should_arm = true;
406  should_prearm = false;
407 
408  /* simulate another orb_copy() from actuator controls */
409  for (unsigned i = 0; i < output_max; i++) {
410  actuator_controls[i] = 0.1f;
411  }
412 
413  //PX4_INFO("ARMING TEST: STARTING RAMP");
414  unsigned sleep_quantum_us = 10000;
415 
416  hrt_abstime starttime = hrt_absolute_time();
417  unsigned sleepcount = 0;
418 
419  while (hrt_elapsed_time(&starttime) < INIT_TIME_US + RAMP_TIME_US + 2 * sleep_quantum_us) {
420 
421  /* mix */
422  mixed = mixer_group.mix(&outputs[0], output_max);
423 
424  output_limit_calc(should_arm, should_prearm, mixed, reverse_pwm_mask, r_page_servo_disarmed, r_page_servo_control_min,
425  r_page_servo_control_max, outputs, r_page_servos, &output_limit);
426 
427  //warnx("mixed %d outputs (max %d), values:", mixed, output_max);
428  for (unsigned i = 0; i < mixed; i++) {
429 
430  //fprintf(stderr, "ramp:\t %d: out: %8.4f, servo: %d \n", i, (double)outputs[i], (int)r_page_servos[i]);
431 
432  /* check mixed outputs to be zero during init phase */
433  if (hrt_elapsed_time(&starttime) < INIT_TIME_US &&
434  r_page_servos[i] != r_page_servo_disarmed[i]) {
435  PX4_ERR("disarmed servo value mismatch: %d vs %d", r_page_servos[i], r_page_servo_disarmed[i]);
436  return false;
437  }
438 
439  if (hrt_elapsed_time(&starttime) >= INIT_TIME_US &&
440  r_page_servos[i] + 1 <= r_page_servo_disarmed[i]) {
441  PX4_ERR("ramp servo value mismatch");
442  return false;
443  }
444  }
445 
446  px4_usleep(sleep_quantum_us);
447  sleepcount++;
448 
449  if (sleepcount % 10 == 0) {
450  fflush(stdout);
451  }
452  }
453 
454  //PX4_INFO("ARMING TEST: NORMAL OPERATION");
455 
456  for (int j = -jmax; j <= jmax; j++) {
457 
458  for (unsigned i = 0; i < output_max; i++) {
459  actuator_controls[i] = j / 10.0f + 0.1f * i;
460  r_page_servo_disarmed[i] = PWM_LOWEST_MIN;
461  r_page_servo_control_min[i] = PWM_DEFAULT_MIN;
462  r_page_servo_control_max[i] = PWM_DEFAULT_MAX;
463  }
464 
465  /* mix */
466  mixed = mixer_group.mix(&outputs[0], output_max);
467 
468  output_limit_calc(should_arm, should_prearm, mixed, reverse_pwm_mask, r_page_servo_disarmed, r_page_servo_control_min,
469  r_page_servo_control_max, outputs, r_page_servos, &output_limit);
470 
471  //fprintf(stderr, "mixed %d outputs (max %d)", mixed, output_max);
472 
473  for (unsigned i = 0; i < mixed; i++) {
474  servo_predicted[i] = 1500 + outputs[i] * (r_page_servo_control_max[i] - r_page_servo_control_min[i]) / 2.0f;
475 
476  if (abs(servo_predicted[i] - r_page_servos[i]) > MIXER_DIFFERENCE_THRESHOLD) {
477  fprintf(stderr, "\t %d: %8.4f predicted: %d, servo: %d\n", i, (double)outputs[i], servo_predicted[i],
478  (int)r_page_servos[i]);
479  PX4_ERR("mixer violated predicted value");
480  return false;
481  }
482  }
483  }
484 
485  //PX4_INFO("ARMING TEST: DISARMING");
486 
487  starttime = hrt_absolute_time();
488  sleepcount = 0;
489  should_arm = false;
490 
491  while (hrt_elapsed_time(&starttime) < 600000) {
492 
493  /* mix */
494  mixed = mixer_group.mix(&outputs[0], output_max);
495 
496  output_limit_calc(should_arm, should_prearm, mixed, reverse_pwm_mask, r_page_servo_disarmed, r_page_servo_control_min,
497  r_page_servo_control_max, outputs, r_page_servos, &output_limit);
498 
499  //warnx("mixed %d outputs (max %d), values:", mixed, output_max);
500  for (unsigned i = 0; i < mixed; i++) {
501 
502  //fprintf(stderr, "disarmed:\t %d: out: %8.4f, servo: %d \n", i, (double)outputs[i], (int)r_page_servos[i]);
503 
504  /* check mixed outputs to be zero during init phase */
505  if (r_page_servos[i] != r_page_servo_disarmed[i]) {
506  PX4_ERR("disarmed servo value mismatch");
507  return false;
508  }
509  }
510 
511  px4_usleep(sleep_quantum_us);
512  sleepcount++;
513 
514  if (sleepcount % 10 == 0) {
515  //printf(".");
516  //fflush(stdout);
517  }
518  }
519 
520  //printf("\n");
521 
522  //PX4_INFO("ARMING TEST: REARMING: STARTING RAMP");
523 
524  starttime = hrt_absolute_time();
525  sleepcount = 0;
526  should_arm = true;
527 
528  while (hrt_elapsed_time(&starttime) < 600000 + RAMP_TIME_US) {
529 
530  /* mix */
531  mixed = mixer_group.mix(&outputs[0], output_max);
532 
533  output_limit_calc(should_arm, should_prearm, mixed, reverse_pwm_mask, r_page_servo_disarmed, r_page_servo_control_min,
534  r_page_servo_control_max, outputs, r_page_servos, &output_limit);
535 
536  //warnx("mixed %d outputs (max %d), values:", mixed, output_max);
537  for (unsigned i = 0; i < mixed; i++) {
538  /* predict value */
539  servo_predicted[i] = 1500 + outputs[i] * (r_page_servo_control_max[i] - r_page_servo_control_min[i]) / 2.0f;
540 
541  /* check ramp */
542 
543  //fprintf(stderr, "ramp:\t %d: out: %8.4f, servo: %d \n", i, (double)outputs[i], (int)r_page_servos[i]);
544 
545  if (hrt_elapsed_time(&starttime) < RAMP_TIME_US &&
546  (r_page_servos[i] + 1 <= r_page_servo_disarmed[i] ||
547  r_page_servos[i] > servo_predicted[i])) {
548  PX4_ERR("ramp servo value mismatch");
549  return false;
550  }
551 
552  /* check post ramp phase */
553  if (hrt_elapsed_time(&starttime) > RAMP_TIME_US &&
554  abs(servo_predicted[i] - r_page_servos[i]) > 2) {
555  printf("\t %d: %8.4f predicted: %d, servo: %d\n", i, (double)outputs[i], servo_predicted[i], (int)r_page_servos[i]);
556  PX4_ERR("mixer violated predicted value");
557  return false;
558  }
559  }
560 
561  px4_usleep(sleep_quantum_us);
562  sleepcount++;
563 
564  if (sleepcount % 10 == 0) {
565  // printf(".");
566  // fflush(stdout);
567  }
568  }
569 
570  return true;
571 }
572 
573 static int
574 mixer_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &control)
575 {
576  control = 0.0f;
577 
578  if (control_group != 0) {
579  return -1;
580  }
581 
582  if (control_index >= (sizeof(actuator_controls) / sizeof(actuator_controls[0]))) {
583  return -1;
584  }
585 
586  control = actuator_controls[control_index];
587 
588  if (should_prearm && control_group == actuator_controls_s::GROUP_INDEX_ATTITUDE &&
589  control_index == actuator_controls_s::INDEX_THROTTLE) {
590  control = NAN;
591  }
592 
593  return 0;
594 }
#define PWM_DEFAULT_MAX
Default maximum PWM in us.
int load_from_buf(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen)
Adds mixers to the group based on a text description in a buffer.
Definition: MixerGroup.cpp:173
bool load_mixer(const char *filename, unsigned expected_count, bool verbose=false)
Definition: test_mixer.cpp:224
static unsigned mixer_text_length
Definition: mixer.cpp:519
void reset()
Remove all the mixers from the group.
Definition: MixerGroup.hpp:74
void output_limit_calc(const bool armed, const bool pre_armed, const unsigned num_channels, const uint16_t reverse_mask, const uint16_t *disarmed_output, const uint16_t *min_output, const uint16_t *max_output, const float *output, uint16_t *effective_output, output_limit_t *limit)
int test_mixer(int argc, char *argv[])
bool loadQuadTest()
Definition: test_mixer.cpp:135
static char mixer_text[PX4IO_MAX_MIXER_LENGTH]
Definition: mixer.cpp:518
static volatile bool should_arm
Definition: mixer.cpp:74
bool loadIOPass()
unsigned count() const
Count the mixers in the group.
Definition: MixerGroup.hpp:79
Base class to be used for unit tests.
Definition: unit_test.h:54
ut_declare_test_c(test_mixer, MixerTest) bool MixerTest
Definition: test_mixer.cpp:128
PX4IO interface protocol.
High-resolution timer with callouts and timekeeping.
static int mixer_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &control)
Definition: test_mixer.cpp:574
int _tests_failed
The number of unit tests which failed.
Definition: unit_test.h:206
#define RAMP_TIME_US
Definition: output_limit.h:57
uint16_t r_page_servo_control_max[]
PAGE 107.
Definition: registers.c:256
#define MIXER_DIFFERENCE_THRESHOLD
Definition: test_mixer.cpp:72
static bool should_prearm
Definition: test_mixer.cpp:67
bool loadVTOL1Test()
Definition: test_mixer.cpp:140
#define PATH_MAX
Definition: test_mixer.cpp:79
#define MIXER_PATH(_file)
Definition: test_mixer.cpp:88
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
virtual bool run_tests()
Override to run your unit tests.
Definition: test_mixer.cpp:115
Tests declaration file.
bool loadComplexTest()
Definition: test_mixer.cpp:150
bool loadAllTest()
Definition: test_mixer.cpp:155
uint16_t r_page_servo_disarmed[]
PAGE 109.
Definition: registers.c:272
int load_mixer_file(const char *fname, char *buf, unsigned maxlen)
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
#define PX4IO_MAX_TRANSFER_LEN
Definition: protocol.h:99
#define PX4IO_MAX_MIXER_LENGTH
Definition: mixer.h:42
bool mixerTest()
Definition: test_mixer.cpp:341
MixerTest()=default
uint16_t r_page_servo_control_min[]
PAGE 106.
Definition: registers.c:248
#define MIXER_ONBOARD_PATH
Definition: test_mixer.cpp:87
MixerGroup mixer_group
Definition: test_mixer.cpp:112
static const unsigned output_max
Definition: test_mixer.cpp:65
uint16_t r_page_servos[]
PAGE 3.
Definition: registers.c:108
void output_limit_init(output_limit_t *limit)
#define ut_compare(message, v1, v2)
Used to compare two integer values within a unit test.
Definition: unit_test.h:150
static float actuator_controls[output_max]
Definition: test_mixer.cpp:66
unsigned mix(float *outputs, unsigned space)
Definition: MixerGroup.cpp:53
PX4IO mixer definitions.
#define PWM_MOTOR_OFF
Default value for a shutdown motor.
#define ut_run_test(test)
Runs a single unit test.
Definition: unit_test.h:96
Group of mixers, built up from single mixers and processed in order when mixing.
Definition: MixerGroup.hpp:42
Dual< Scalar, N > abs(const Dual< Scalar, N > &a)
Definition: Dual.hpp:196
bool loadVTOL2Test()
Definition: test_mixer.cpp:145
#define PWM_DEFAULT_MIN
Default minimum PWM in us.
Library for output limiting (PWM for example)
#define INIT_TIME_US
Definition: output_limit.h:53
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
#define PWM_LOWEST_MIN
Lowest minimum PWM in us.