LCOV - code coverage report
Current view: top level - AP_Motors - AP_MotorsMatrix.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 411 547 75.1 %
Date: 2026-09-09 11:02:08 Functions: 27 29 93.1 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include <AP_HAL/AP_HAL.h>
      17             : #include "AP_MotorsMatrix.h"
      18             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      19             : 
      20             : extern const AP_HAL::HAL& hal;
      21             : 
      22             : // init
      23        1194 : void AP_MotorsMatrix::init(motor_frame_class frame_class, motor_frame_type frame_type)
      24             : {
      25             :     // record requested frame class and type
      26        1194 :     _active_frame_class = frame_class;
      27        1194 :     _active_frame_type = frame_type;
      28             : 
      29        1194 :     if (frame_class == MOTOR_FRAME_SCRIPTING_MATRIX) {
      30             :         // if Scripting frame class, do nothing scripting must call its own dedicated init function
      31           9 :         return;
      32             :     }
      33             : 
      34             :     // setup the motors
      35        1185 :     setup_motors(frame_class, frame_type);
      36             : 
      37             :     // enable fast channels or instant pwm
      38        1185 :     set_update_rate(_speed_hz);
      39             : }
      40             : 
      41             : #if AP_SCRIPTING_ENABLED
      42             : // dedicated init for lua scripting
      43           3 : bool AP_MotorsMatrix::init(uint8_t expected_num_motors)
      44             : {
      45           3 :     if (_active_frame_class != MOTOR_FRAME_SCRIPTING_MATRIX) {
      46             :         // not the correct class
      47           0 :         return false;
      48             :     }
      49             : 
      50             :     // Make sure the correct number of motors have been added
      51           3 :     uint8_t num_motors = 0;
      52          99 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
      53          96 :         if (motor_enabled[i]) {
      54          64 :             num_motors++;
      55             :         }
      56             :     }
      57             : 
      58           3 :     set_initialised_ok(expected_num_motors == num_motors);
      59             : 
      60           3 :     if (!initialised_ok()) {
      61           0 :         _mav_type = MAV_TYPE_GENERIC;
      62           0 :         return false;
      63             :     }
      64             : 
      65           3 :     switch (num_motors) {
      66           0 :         case 3:
      67           0 :             _mav_type = MAV_TYPE_TRICOPTER;
      68           0 :             break;
      69           0 :         case 4:
      70           0 :             _mav_type = MAV_TYPE_QUADROTOR;
      71           0 :             break;
      72           0 :         case 6:
      73           0 :             _mav_type = MAV_TYPE_HEXAROTOR;
      74           0 :             break;
      75           0 :         case 8:
      76           0 :             _mav_type = MAV_TYPE_OCTOROTOR;
      77           0 :             break;
      78           0 :         case 10:
      79           0 :             _mav_type = MAV_TYPE_DECAROTOR;
      80           0 :             break;
      81           0 :         case 12:
      82           0 :             _mav_type = MAV_TYPE_DODECAROTOR;
      83           0 :             break;
      84           3 :         default:
      85           3 :             _mav_type = MAV_TYPE_GENERIC;
      86             :     }
      87             : 
      88           3 :     normalise_rpy_factors();
      89             : 
      90           3 :     set_update_rate(_speed_hz);
      91             : 
      92           3 :     return true;
      93             : }
      94             : 
      95             : // Set throttle factor from scripting
      96           0 : bool AP_MotorsMatrix::set_throttle_factor(int8_t motor_num, float throttle_factor)
      97             : {
      98           0 :     if ((_active_frame_class != MOTOR_FRAME_SCRIPTING_MATRIX) ) {
      99             :         // not the correct class
     100           0 :         return false;
     101             :     }
     102             : 
     103           0 :     if (initialised_ok() || !motor_enabled[motor_num]) {
     104             :         // Already setup or given motor is not enabled
     105           0 :         return false;
     106             :     }
     107             : 
     108           0 :     _throttle_factor[motor_num] = throttle_factor;
     109           0 :     return true;
     110             : }
     111             : 
     112             : #endif // AP_SCRIPTING_ENABLED
     113             : 
     114             : // set update rate to motors - a value in hertz
     115        2377 : void AP_MotorsMatrix::set_update_rate(uint16_t speed_hz)
     116             : {
     117             :     // record requested speed
     118        2377 :     _speed_hz = speed_hz;
     119             : 
     120        2377 :     uint32_t mask = 0;
     121       78441 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     122       76064 :         if (motor_enabled[i]) {
     123        9684 :             mask |= 1U << i;
     124             :         }
     125             :     }
     126        2377 :     rc_set_freq(mask, _speed_hz);
     127        2377 : }
     128             : 
     129             : // set frame class (i.e. quad, hexa, heli) and type (i.e. x, plus)
     130       23528 : void AP_MotorsMatrix::set_frame_class_and_type(motor_frame_class frame_class, motor_frame_type frame_type)
     131             : {
     132             :     // exit immediately if armed or no change
     133       23528 :     if (armed() || (frame_class == _active_frame_class && _active_frame_type == frame_type)) {
     134       23523 :         return;
     135             :     }
     136           5 :     _active_frame_class = frame_class;
     137           5 :     _active_frame_type = frame_type;
     138             : 
     139           5 :     init(frame_class, frame_type);
     140             : 
     141             : }
     142             : 
     143    33291922 : void AP_MotorsMatrix::output_to_motors()
     144             : {
     145             :     int8_t i;
     146             : 
     147    33291922 :     switch (_spool_state) {
     148    13702802 :         case SpoolState::SHUT_DOWN: {
     149             :             // no output
     150   452192466 :             for (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     151   438489664 :                 if (motor_enabled_mask(i)) {
     152    57035538 :                     _actuator[i] = 0.0f;
     153             :                 }
     154             :             }
     155    13702802 :             break;
     156             :         }
     157      251034 :         case SpoolState::GROUND_IDLE:
     158             :             // sends output to motors when armed but not flying
     159     8284122 :             for (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     160     8033088 :                 if (motor_enabled[i]) {
     161     1033138 :                     set_actuator_with_slew(_actuator[i], actuator_spin_up_to_ground_idle());
     162             :                 }
     163             :             }
     164      251034 :             break;
     165    19338086 :         case SpoolState::SPOOLING_UP:
     166             :         case SpoolState::THROTTLE_UNLIMITED:
     167             :         case SpoolState::SPOOLING_DOWN:
     168             :             // set motor output based on thrust requests
     169   638156838 :             for (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     170   618818752 :                 if (motor_enabled[i]) {
     171    81723534 :                     set_actuator_with_slew(_actuator[i], thr_lin.thrust_to_actuator(_thrust_rpyt_out[i]));
     172             :                 }
     173             :             }
     174    19338086 :             break;
     175             :     }
     176             : 
     177             :     // convert output to PWM and send to each motor
     178  1098633426 :     for (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     179  1065341504 :         if (motor_enabled[i]) {
     180   140179240 :             rc_write(i, output_to_pwm(_actuator[i]));
     181             :         }
     182             :     }
     183    33291922 : }
     184             : 
     185             : // get_motor_mask - returns a bitmask of which outputs are being used for motors (1 means being used)
     186             : //  this can be used to ensure other pwm outputs (i.e. for servos) do not conflict
     187       32788 : uint32_t AP_MotorsMatrix::get_motor_mask()
     188             : {
     189       32788 :     uint32_t motor_mask = 0;
     190     1082004 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     191     1049216 :         if (motor_enabled[i]) {
     192      142274 :             motor_mask |= 1U << i;
     193             :         }
     194             :     }
     195       32788 :     uint32_t mask = motor_mask_to_srv_channel_mask(motor_mask);
     196             : 
     197             :     // add parent's mask
     198       32788 :     mask |= AP_MotorsMulticopter::get_motor_mask();
     199             : 
     200       32788 :     return mask;
     201             : }
     202             : 
     203             : // helper to return value scaled between boost and normal based on the value of _thrust_boost_ratio
     204             : // _thrust_boost_ratio of 1 -> return = boost_value
     205             : // _thrust_boost_ratio of 0 -> return = normal_value
     206    66596701 : float AP_MotorsMatrix::boost_ratio(float boost_value, float normal_value) const
     207             : {
     208    66596701 :     return _thrust_boost_ratio * boost_value + (1.0 - _thrust_boost_ratio) * normal_value;
     209             : }
     210             : 
     211             : // output_armed - sends commands to the motors
     212             : // includes new scaling stability patch
     213    33291922 : void AP_MotorsMatrix::output_armed_stabilizing()
     214             : {
     215             :     // apply voltage and air pressure compensation
     216    33291922 :     const float compensation_gain = thr_lin.get_compensation_gain(); // compensation for battery voltage and altitude
     217             : 
     218             :     // pitch thrust input value, +/- 1.0
     219    33291922 :     const float roll_thrust = (_roll_in + _roll_in_ff) * compensation_gain;
     220             : 
     221             :     // pitch thrust input value, +/- 1.0
     222    33291922 :     const float pitch_thrust = (_pitch_in + _pitch_in_ff) * compensation_gain;
     223             : 
     224             :     // yaw thrust input value, +/- 1.0
     225    33291922 :     float yaw_thrust = (_yaw_in + _yaw_in_ff) * compensation_gain;
     226             : 
     227             :     // throttle thrust input value, 0.0 - 1.0
     228    33291922 :     float throttle_thrust = get_throttle() * compensation_gain;
     229             : 
     230             :     // throttle thrust average maximum value, 0.0 - 1.0
     231    33291922 :     float throttle_avg_max = _throttle_avg_max * compensation_gain;
     232             : 
     233             :     // throttle thrust maximum value, 0.0 - 1.0, If thrust boost is active then do not limit maximum thrust
     234    33291922 :     const float throttle_thrust_max = boost_ratio(1.0, _throttle_thrust_max * compensation_gain);
     235             : 
     236             :     // sanity check throttle is above zero and below current limited throttle
     237    33291922 :     if (throttle_thrust <= 0.0f) {
     238    14512910 :         throttle_thrust = 0.0f;
     239    14512910 :         limit.throttle_lower = true;
     240             :     }
     241    33291922 :     if (throttle_thrust >= throttle_thrust_max) {
     242    14007035 :         throttle_thrust = throttle_thrust_max;
     243    14007035 :         limit.throttle_upper = true;
     244             :     }
     245             : 
     246             :     // ensure that throttle_avg_max is between the input throttle and the maximum throttle
     247    33291922 :     throttle_avg_max = constrain_float(throttle_avg_max, throttle_thrust, throttle_thrust_max);
     248             : 
     249             :     // throttle providing maximum roll, pitch and yaw range
     250             :     // calculate the highest allowed average thrust that will provide maximum control range
     251    33291922 :     float throttle_thrust_best_rpy = MIN(0.5f, throttle_avg_max);
     252             : 
     253             :     // calculate throttle that gives most possible room for yaw which is the lower of:
     254             :     //      1. 0.5f - (rpy_low+rpy_high)/2.0 - this would give the maximum possible margin above the highest motor and below the lowest
     255             :     //      2. the higher of:
     256             :     //            a) the pilot's throttle input
     257             :     //            b) the point _throttle_rpy_mix between the pilot's input throttle and hover-throttle
     258             :     //      Situation #2 ensure we never increase the throttle above hover throttle unless the pilot has commanded this.
     259             :     //      Situation #2b allows us to raise the throttle above what the pilot commanded but not so far that it would actually cause the copter to rise.
     260             :     //      We will choose #1 (the best throttle for yaw control) if that means reducing throttle to the motors (i.e. we favor reducing throttle *because* it provides better yaw control)
     261             :     //      We will choose #2 (a mix of pilot and hover throttle) only when the throttle is quite low.  We favor reducing throttle instead of better yaw control because the pilot has commanded it
     262             : 
     263             :     // Under the motor lost condition we remove the highest motor output from our calculations and let that motor go greater than 1.0
     264             :     // To ensure control and maximum righting performance Hex and Octo have some optimal settings that should be used
     265             :     // Y6               : MOT_YAW_HEADROOM = 350, ATC_RAT_RLL_IMAX = 1.0,   ATC_RAT_PIT_IMAX = 1.0,   ATC_RAT_YAW_IMAX = 0.5
     266             :     // Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.375, ATC_RAT_PIT_IMAX = 0.375, ATC_RAT_YAW_IMAX = 0.375
     267             :     // Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.75,  ATC_RAT_PIT_IMAX = 0.75,  ATC_RAT_YAW_IMAX = 0.375
     268             :     // Usable minimums below may result in attitude offsets when motors are lost. Hex aircraft are only marginal and must be handles with care
     269             :     // Hex              : MOT_YAW_HEADROOM = 0,   ATC_RAT_RLL_IMAX = 1.0,   ATC_RAT_PIT_IMAX = 1.0,   ATC_RAT_YAW_IMAX = 0.5
     270             :     // Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.25,  ATC_RAT_PIT_IMAX = 0.25,  ATC_RAT_YAW_IMAX = 0.25
     271             :     // Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.5,   ATC_RAT_PIT_IMAX = 0.5,   ATC_RAT_YAW_IMAX = 0.25
     272             :     // Quads cannot make use of motor loss handling because it doesn't have enough degrees of freedom.
     273             : 
     274             :     // calculate amount of yaw we can fit into the throttle range
     275             :     // this is always equal to or less than the requested yaw from the pilot or rate controller
     276    33291922 :     float yaw_allowed = 1.0f; // amount of yaw we can fit in
     277  1098633426 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     278  1065341504 :         if (motor_enabled[i]) {
     279             :             // calculate the thrust outputs for roll and pitch
     280   140179240 :             _thrust_rpyt_out[i] = roll_thrust * _roll_factor[i] + pitch_thrust * _pitch_factor[i];
     281             : 
     282             :             // Check the maximum yaw control that can be used on this channel
     283             :             // Exclude any lost motors if thrust boost is enabled
     284   140179240 :             if (!is_zero(_yaw_factor[i]) && (!_thrust_boost || i != _motor_lost_index)) {
     285   139252793 :                 const float thrust_rp_best_throttle = throttle_thrust_best_rpy + _thrust_rpyt_out[i];
     286             :                 float motor_room;
     287   139252793 :                 if (is_positive(yaw_thrust * _yaw_factor[i])) {
     288             :                     // room to upper limit
     289    62372677 :                     motor_room = 1.0 - thrust_rp_best_throttle;
     290             :                 } else {
     291             :                     // room to lower limit
     292    76880116 :                     motor_room = thrust_rp_best_throttle;
     293             :                 }
     294   139252793 :                 const float motor_yaw_allowed = MAX(motor_room, 0.0)/fabsf(_yaw_factor[i]);
     295   139252793 :                 yaw_allowed = MIN(yaw_allowed, motor_yaw_allowed);
     296             :             }
     297             :         }
     298             :     }
     299             : 
     300             :     // calculate the maximum yaw control that can be used
     301             :     // todo: make _yaw_headroom 0 to 1
     302    33291922 :     float yaw_allowed_min = (float)_yaw_headroom * 0.001f;
     303             : 
     304             :     // increase yaw headroom to 50% if thrust boost enabled
     305    33291922 :     yaw_allowed_min = boost_ratio(0.5, yaw_allowed_min);
     306             : 
     307             :     // Let yaw access minimum amount of head room
     308    33291922 :     yaw_allowed = MAX(yaw_allowed, yaw_allowed_min);
     309             : 
     310             :     // Include the lost motor scaled by _thrust_boost_ratio to smoothly transition this motor in and out of the calculation
     311    33291922 :     if (_thrust_boost && motor_enabled[_motor_lost_index]) {
     312             :         // Check the maximum yaw control that can be used on this channel
     313             :         // Exclude any lost motors if thrust boost is enabled
     314       10549 :         if (!is_zero(_yaw_factor[_motor_lost_index])){
     315        7843 :             const float thrust_rp_best_throttle = throttle_thrust_best_rpy + _thrust_rpyt_out[_motor_lost_index];
     316             :             float motor_room;
     317        7843 :             if (is_positive(yaw_thrust * _yaw_factor[_motor_lost_index])) {
     318        5929 :                 motor_room = 1.0 - thrust_rp_best_throttle;
     319             :             } else {
     320        1914 :                 motor_room = thrust_rp_best_throttle;
     321             :             }
     322        7843 :             const float motor_yaw_allowed = MAX(motor_room, 0.0)/fabsf(_yaw_factor[_motor_lost_index]);
     323        7843 :             yaw_allowed = boost_ratio(yaw_allowed, MIN(yaw_allowed, motor_yaw_allowed));
     324             :         }
     325             :     }
     326             : 
     327    33291922 :     if (fabsf(yaw_thrust) > yaw_allowed) {
     328             :         // not all commanded yaw can be used
     329       38523 :         yaw_thrust = constrain_float(yaw_thrust, -yaw_allowed, yaw_allowed);
     330       38523 :         limit.yaw = true;
     331             :     }
     332             : 
     333             :     // add yaw control to thrust outputs
     334    33291922 :     float rpy_low = 1.0f;   // lowest thrust value
     335    33291922 :     float rpy_high = -1.0f; // highest thrust value
     336  1098633426 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     337  1065341504 :         if (motor_enabled[i]) {
     338   140179240 :             _thrust_rpyt_out[i] = _thrust_rpyt_out[i] + yaw_thrust * _yaw_factor[i];
     339             : 
     340             :             // record lowest roll + pitch + yaw command
     341   140179240 :             if (_thrust_rpyt_out[i] < rpy_low) {
     342    65748390 :                 rpy_low = _thrust_rpyt_out[i];
     343             :             }
     344             :             // record highest roll + pitch + yaw command
     345             :             // Exclude any lost motors if thrust boost is enabled
     346   140179240 :             if (_thrust_rpyt_out[i] > rpy_high && (!_thrust_boost || i != _motor_lost_index)) {
     347    71606578 :                 rpy_high = _thrust_rpyt_out[i];
     348             :             }
     349             :         }
     350             :     }
     351             :     // Include the lost motor scaled by _thrust_boost_ratio to smoothly transition this motor in and out of the calculation
     352    33291922 :     if (_thrust_boost) {
     353             :         // record highest roll + pitch + yaw command
     354       10549 :         if (_thrust_rpyt_out[_motor_lost_index] > rpy_high && motor_enabled[_motor_lost_index]) {
     355        5014 :             rpy_high = boost_ratio(rpy_high, _thrust_rpyt_out[_motor_lost_index]);
     356             :         }
     357             :     }
     358             : 
     359             :     // calculate any scaling needed to make the combined thrust outputs fit within the output range
     360    33291922 :     float rpy_scale = 1.0f;
     361    33291922 :     if (rpy_high - rpy_low > 1.0f) {
     362      342051 :         rpy_scale = 1.0f / (rpy_high - rpy_low);
     363             :     }
     364    33291922 :     if (throttle_avg_max + rpy_low < 0) {
     365    10431473 :         rpy_scale = MIN(rpy_scale, -throttle_avg_max / rpy_low);
     366             :     }
     367             : 
     368             :     // calculate how close the motors can come to the desired throttle
     369    33291922 :     rpy_high *= rpy_scale;
     370    33291922 :     rpy_low *= rpy_scale;
     371    33291922 :     throttle_thrust_best_rpy = -rpy_low;
     372    33291922 :     float thr_adj = throttle_thrust - throttle_thrust_best_rpy;
     373    33291922 :     if (rpy_scale < 1.0f) {
     374             :         // Full range is being used by roll, pitch, and yaw.
     375    10436918 :         limit.set_rpy(true);
     376    10436918 :         if (thr_adj > 0.0f) {
     377       11905 :             limit.throttle_upper = true;
     378             :         }
     379    10436918 :         thr_adj = 0.0f;
     380    22855004 :     } else if (thr_adj < 0.0f) {
     381             :         // Throttle can't be reduced to desired value
     382             :         // todo: add lower limit flag and ensure it is handled correctly in altitude controller
     383      601854 :         thr_adj = 0.0f;
     384    22253150 :     } else if (thr_adj > 1.0f - (throttle_thrust_best_rpy + rpy_high)) {
     385             :         // Throttle can't be increased to desired value
     386       93747 :         thr_adj = 1.0f - (throttle_thrust_best_rpy + rpy_high);
     387       93747 :         limit.throttle_upper = true;
     388             :     }
     389             : 
     390             :     // add scaled roll, pitch, constrained yaw and throttle for each motor
     391    33291922 :     const float throttle_thrust_best_plus_adj = throttle_thrust_best_rpy + thr_adj;
     392  1098633426 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     393  1065341504 :         if (motor_enabled[i]) {
     394   140179240 :             _thrust_rpyt_out[i] = (throttle_thrust_best_plus_adj * _throttle_factor[i]) + (rpy_scale * _thrust_rpyt_out[i]);
     395             :         }
     396             :     }
     397             : 
     398             :     // determine throttle thrust for harmonic notch
     399             :     // compensation_gain can never be zero
     400    33291922 :     _throttle_out = throttle_thrust_best_plus_adj / compensation_gain;
     401             : 
     402             :     // check for failed motor
     403    33291922 :     check_for_failed_motor(throttle_thrust_best_plus_adj);
     404    33291922 : }
     405             : 
     406             : // check for failed motor
     407             : //   should be run immediately after output_armed_stabilizing
     408             : //   first argument is the sum of:
     409             : //      a) throttle_thrust_best_rpy : throttle level (from 0 to 1) providing maximum roll, pitch and yaw range without climbing
     410             : //      b) thr_adj: the difference between the pilot's desired throttle and throttle_thrust_best_rpy
     411             : //   records filtered motor output values in _thrust_rpyt_out_filt array
     412             : //   sets thrust_balanced to true if motors are balanced, false if a motor failure is detected
     413             : //   sets _motor_lost_index to index of failed motor
     414    33291922 : void AP_MotorsMatrix::check_for_failed_motor(float throttle_thrust_best_plus_adj)
     415             : {
     416             :     // record filtered and scaled thrust output for motor loss monitoring purposes
     417    33291922 :     float alpha = _dt_s / (_dt_s + 0.5f);
     418  1098633426 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     419  1065341504 :         if (motor_enabled[i]) {
     420   140179240 :             _thrust_rpyt_out_filt[i] += alpha * (_thrust_rpyt_out[i] - _thrust_rpyt_out_filt[i]);
     421             :         }
     422             :     }
     423             : 
     424    33291922 :     float rpyt_high = 0.0f;
     425    33291922 :     float rpyt_sum = 0.0f;
     426    33291922 :     uint8_t number_motors = 0.0f;
     427  1098633426 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     428  1065341504 :         if (motor_enabled[i]) {
     429   140179240 :             number_motors += 1;
     430   140179240 :             rpyt_sum += _thrust_rpyt_out_filt[i];
     431             :             // record highest filtered thrust command
     432   140179240 :             if (_thrust_rpyt_out_filt[i] > rpyt_high) {
     433    48406060 :                 rpyt_high = _thrust_rpyt_out_filt[i];
     434             :                 // hold motor lost index constant while thrust boost is active
     435    48406060 :                 if (!_thrust_boost) {
     436    48388029 :                     _motor_lost_index = i;
     437             :                 }
     438             :             }
     439             :         }
     440             :     }
     441             : 
     442    33291922 :     float thrust_balance = 1.0f;
     443    33291922 :     if (rpyt_sum > 0.1f) {
     444    18997431 :         thrust_balance = rpyt_high * number_motors / rpyt_sum;
     445             :     }
     446             :     // ensure thrust balance does not activate for multirotors with less than 6 motors
     447             :     // also disable for co-rotating frames which have intentionally different throttle factors
     448    66535114 :     const bool is_corotating = _active_frame_type == MOTOR_FRAME_TYPE_X_COR ||
     449    33243192 :                                _active_frame_type == MOTOR_FRAME_TYPE_CW_X_COR;
     450    33291922 :     if (number_motors >= 6 && thrust_balance >= 1.5f && _thrust_balanced && !is_corotating) {
     451          52 :         _thrust_balanced = false;
     452             :     }
     453    33291922 :     if (thrust_balance <= 1.25f && !_thrust_balanced) {
     454          52 :         _thrust_balanced = true;
     455             :     }
     456             : 
     457             :     // check to see if thrust boost is using more throttle than _throttle_thrust_max
     458    33291922 :     if ((_throttle_thrust_max * thr_lin.get_compensation_gain() > throttle_thrust_best_plus_adj) && (rpyt_high < 0.9f) && _thrust_balanced) {
     459    19178344 :         _thrust_boost = false;
     460             :     }
     461    33291922 : }
     462             : 
     463             : // output_test_seq - spin a motor at the pwm value specified
     464             : //  motor_seq is the motor's sequence number from 1 to the number of motors on the frame
     465             : //  pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000
     466       11185 : void AP_MotorsMatrix::_output_test_seq(uint8_t motor_seq, int16_t pwm)
     467             : {
     468             :     // loop through all the possible orders spinning any motors that match that description
     469      369105 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
     470      357920 :         if (motor_enabled[i] && _test_order[i] == motor_seq) {
     471             :             // turn on this motor
     472       11185 :             rc_write(i, pwm);
     473             :         }
     474             :     }
     475       11185 : }
     476             : 
     477             : // output_test_num - spin a motor connected to the specified output channel
     478             : //  (should only be performed during testing)
     479             : //  If a motor output channel is remapped, the mapped channel is used.
     480             : //  Returns true if motor output is set, false otherwise
     481             : //  pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000
     482           0 : bool AP_MotorsMatrix::output_test_num(uint8_t output_channel, int16_t pwm)
     483             : {
     484           0 :     if (!armed()) {
     485           0 :         return false;
     486             :     }
     487             : 
     488             :     // Is channel in supported range?
     489           0 :     if (output_channel > AP_MOTORS_MAX_NUM_MOTORS - 1) {
     490           0 :         return false;
     491             :     }
     492             : 
     493             :     // Is motor enabled?
     494           0 :     if (!motor_enabled[output_channel]) {
     495           0 :         return false;
     496             :     }
     497             : 
     498           0 :     rc_write(output_channel, pwm); // output
     499           0 :     return true;
     500             : }
     501             : 
     502             : // add_motor
     503        5126 : void AP_MotorsMatrix::add_motor_raw(int8_t motor_num, float roll_fac, float pitch_fac, float yaw_fac, uint8_t testing_order, float throttle_factor)
     504             : {
     505        5126 :     if (initialised_ok()) {
     506             :         // do not allow motors to be set if the current frame type has init correctly
     507           0 :         return;
     508             :     }
     509             : 
     510             :     // ensure valid motor number is provided
     511        5126 :     if (motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS) {
     512             : 
     513             :         // enable motor
     514        5126 :         motor_enabled[motor_num] = true;
     515             : 
     516             :         // set roll, pitch, yaw and throttle factors
     517        5126 :         _roll_factor[motor_num] = roll_fac;
     518        5126 :         _pitch_factor[motor_num] = pitch_fac;
     519        5126 :         _yaw_factor[motor_num] = yaw_fac;
     520        5126 :         _throttle_factor[motor_num] = throttle_factor;
     521             : 
     522             :         // set order that motor appears in test
     523        5126 :         _test_order[motor_num] = testing_order;
     524             : 
     525             :         // call parent class method
     526        5126 :         add_motor_num(motor_num);
     527             :     }
     528             : }
     529             : 
     530             : // add_motor using just position and prop direction - assumes that for each motor, roll and pitch factors are equal
     531        4544 : void AP_MotorsMatrix::add_motor(int8_t motor_num, float angle_degrees, float yaw_factor, uint8_t testing_order)
     532             : {
     533        4544 :     add_motor(motor_num, angle_degrees, angle_degrees, yaw_factor, testing_order);
     534        4544 : }
     535             : 
     536             : // add_motor using position and prop direction. Roll and Pitch factors can differ (for asymmetrical frames)
     537        4544 : void AP_MotorsMatrix::add_motor(int8_t motor_num, float roll_factor_in_degrees, float pitch_factor_in_degrees, float yaw_factor, uint8_t testing_order)
     538             : {
     539        4544 :     add_motor_raw(
     540             :         motor_num,
     541             :         cosf(radians(roll_factor_in_degrees + 90)),
     542             :         cosf(radians(pitch_factor_in_degrees)),
     543             :         yaw_factor,
     544             :         testing_order);
     545        4544 : }
     546             : 
     547             : // remove_motor - disabled motor and clears all roll, pitch, throttle factors for this motor
     548       37856 : void AP_MotorsMatrix::remove_motor(int8_t motor_num)
     549             : {
     550             :     // ensure valid motor number is provided
     551       37856 :     if (motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS) {
     552             :         // disable the motor, set all factors to zero
     553       37856 :         motor_enabled[motor_num] = false;
     554       37856 :         _roll_factor[motor_num] = 0.0f;
     555       37856 :         _pitch_factor[motor_num] = 0.0f;
     556       37856 :         _yaw_factor[motor_num] = 0.0f;
     557       37856 :         _throttle_factor[motor_num] = 0.0f;
     558             :     }
     559       37856 : }
     560             : 
     561        1093 : void AP_MotorsMatrix::add_motors(const struct MotorDef *motors, uint8_t num_motors)
     562             : {
     563        5637 :     for (uint8_t i=0; i<num_motors; i++) {
     564        4544 :         const auto &motor = motors[i];
     565        4544 :         add_motor(i, motor.angle_degrees, motor.yaw_factor, motor.testing_order);
     566             :     }
     567        1093 : }
     568           3 : void AP_MotorsMatrix::add_motors_raw(const struct MotorDefRaw *motors, uint8_t num_motors)
     569             : {
     570          21 :     for (uint8_t i=0; i<num_motors; i++) {
     571          18 :         const auto &m = motors[i];
     572          18 :         add_motor_raw(i, m.roll_fac, m.pitch_fac, m.yaw_fac, m.testing_order);
     573             :     }
     574           3 : }
     575             : #if AP_MOTORS_FRAME_QUAD_ENABLED
     576        1050 : bool AP_MotorsMatrix::setup_quad_matrix(motor_frame_type frame_type)
     577             : {
     578        1050 :     _frame_class_string = "QUAD";
     579        1050 :     _mav_type = MAV_TYPE_QUADROTOR;
     580        1050 :     switch (frame_type) {
     581         852 :     case MOTOR_FRAME_TYPE_PLUS: {
     582         852 :         _frame_type_string = "PLUS";
     583             :         static const AP_MotorsMatrix::MotorDef motors[] {
     584             :             {  90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     585             :             { -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     586             :             {   0, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     587             :             { 180, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     588             :         };
     589         852 :         add_motors(motors, ARRAY_SIZE(motors));
     590         852 :         break;
     591             :     }
     592         192 :     case MOTOR_FRAME_TYPE_X: {
     593         192 :         _frame_type_string = "X";
     594             :         static const AP_MotorsMatrix::MotorDef motors[] {
     595             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     596             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     597             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     598             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     599             :         };
     600         192 :         add_motors(motors, ARRAY_SIZE(motors));
     601         192 :         break;
     602             :     }
     603             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
     604           0 :     case MOTOR_FRAME_TYPE_NYT_PLUS: {
     605           0 :         _frame_type_string = "NYT_PLUS";
     606             :         static const AP_MotorsMatrix::MotorDef motors[] {
     607             :             {  90, 0,  2 },
     608             :             { -90, 0,  4 },
     609             :             {   0, 0,  1 },
     610             :             { 180, 0,  3 },
     611             :         };
     612           0 :         add_motors(motors, ARRAY_SIZE(motors));
     613           0 :         break;
     614             :     }
     615           0 :     case MOTOR_FRAME_TYPE_NYT_X: {
     616           0 :         _frame_type_string = "NYT_X";
     617             :         static const AP_MotorsMatrix::MotorDef motors[] {
     618             :             {   45, 0,  1 },
     619             :             { -135, 0,  3 },
     620             :             {  -45, 0,  4 },
     621             :             {  135, 0,  2 },
     622             :         };
     623           0 :         add_motors(motors, ARRAY_SIZE(motors));
     624           0 :         break;
     625             :     }
     626             : #endif //APM_BUILD_TYPE(APM_BUILD_ArduPlane) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
     627           1 :     case MOTOR_FRAME_TYPE_BF_X: {
     628             :         // betaflight quad X order
     629             :         // see: https://fpvfrenzy.com/betaflight-motor-order/
     630           1 :         _frame_type_string = "BF_X";
     631             :         static const AP_MotorsMatrix::MotorDef motors[] {
     632             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  2 },
     633             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
     634             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
     635             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
     636             :         };
     637           1 :         add_motors(motors, ARRAY_SIZE(motors));
     638           1 :         break;
     639             :     }
     640           0 :     case MOTOR_FRAME_TYPE_BF_X_REV: {
     641             :         // betaflight quad X order, reversed motors
     642           0 :         _frame_type_string = "X_REV";
     643             :         static const AP_MotorsMatrix::MotorDef motors[] {
     644             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     645             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     646             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     647             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     648             :         };
     649           0 :         add_motors(motors, ARRAY_SIZE(motors));
     650           0 :         break;
     651             :     }
     652           1 :     case MOTOR_FRAME_TYPE_DJI_X: {
     653             :         // DJI quad X order
     654             :         // see https://forum44.djicdn.com/data/attachment/forum/201711/26/172348bppvtt1ot1nrtp5j.jpg
     655           1 :         _frame_type_string = "DJI_X";
     656             :         static const AP_MotorsMatrix::MotorDef motors[] {
     657             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     658             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     659             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     660             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     661             :         };
     662           1 :         add_motors(motors, ARRAY_SIZE(motors));
     663           1 :         break;
     664             :     }
     665           1 :     case MOTOR_FRAME_TYPE_CW_X: {
     666             :         // "clockwise X" motor order. Motors are ordered clockwise from front right
     667             :         // matching test order
     668           1 :         _frame_type_string = "CW_X";
     669             :         static const AP_MotorsMatrix::MotorDef motors[] {
     670             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     671             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     672             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     673             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     674             :         };
     675           1 :         add_motors(motors, ARRAY_SIZE(motors));
     676           1 :         break;
     677             :     }
     678           0 :     case MOTOR_FRAME_TYPE_V: {
     679           0 :         _frame_type_string = "V";
     680             :         static const AP_MotorsMatrix::MotorDef motors[] {
     681             :             {   45,   0.7981f,   1 },
     682             :             { -135,   1.0000f,   3 },
     683             :             {  -45,  -0.7981f,   4 },
     684             :             {  135,  -1.0000f,   2 },
     685             :         };
     686           0 :         add_motors(motors, ARRAY_SIZE(motors));
     687           0 :         break;
     688             :     }
     689           3 :     case MOTOR_FRAME_TYPE_H: {
     690             :         // H frame set-up - same as X but motors spin in opposite directions
     691           3 :         _frame_type_string = "H";
     692             :         static const AP_MotorsMatrix::MotorDef motors[] {
     693             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     694             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     695             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     696             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     697             :         };
     698           3 :         add_motors(motors, ARRAY_SIZE(motors));
     699           3 :         break;
     700             :     }
     701           0 :     case MOTOR_FRAME_TYPE_VTAIL: {
     702             :         /*
     703             :             Tested with: Lynxmotion Hunter Vtail 400
     704             :             - inverted rear outward blowing motors (at a 40 degree angle)
     705             :             - should also work with non-inverted rear outward blowing motors
     706             :             - no roll in rear motors
     707             :             - no yaw in front motors
     708             :             - should fly like some mix between a tricopter and X Quadcopter
     709             : 
     710             :             Roll control comes only from the front motors, Yaw control only from the rear motors.
     711             :             Roll & Pitch factor is measured by the angle away from the top of the forward axis to each arm.
     712             : 
     713             :             Note: if we want the front motors to help with yaw,
     714             :                 motors 1's yaw factor should be changed to sin(radians(40)).  Where "40" is the vtail angle
     715             :                 motors 3's yaw factor should be changed to -sin(radians(40))
     716             :         */
     717           0 :         _frame_type_string = "VTAIL";
     718           0 :         add_motor(AP_MOTORS_MOT_1, 60, 60, 0, 1);
     719           0 :         add_motor(AP_MOTORS_MOT_2, 0, -160, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 3);
     720           0 :         add_motor(AP_MOTORS_MOT_3, -60, -60, 0, 4);
     721           0 :         add_motor(AP_MOTORS_MOT_4, 0, 160, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2);
     722           0 :         break;
     723             :     }
     724           0 :     case MOTOR_FRAME_TYPE_ATAIL:
     725             :         /*
     726             :             The A-Shaped VTail is the exact same as a V-Shaped VTail, with one difference:
     727             :             - The Yaw factors are reversed, because the rear motors are facing different directions
     728             : 
     729             :             With V-Shaped VTails, the props make a V-Shape when spinning, but with
     730             :             A-Shaped VTails, the props make an A-Shape when spinning.
     731             :             - Rear thrust on a V-Shaped V-Tail Quad is outward
     732             :             - Rear thrust on an A-Shaped V-Tail Quad is inward
     733             : 
     734             :             Still functions the same as the V-Shaped VTail mixing below:
     735             :             - Yaw control is entirely in the rear motors
     736             :             - Roll is is entirely in the front motors
     737             :         */
     738           0 :         _frame_type_string = "ATAIL";
     739           0 :         add_motor(AP_MOTORS_MOT_1, 60, 60, 0, 1);
     740           0 :         add_motor(AP_MOTORS_MOT_2, 0, -160, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3);
     741           0 :         add_motor(AP_MOTORS_MOT_3, -60, -60, 0, 4);
     742           0 :         add_motor(AP_MOTORS_MOT_4, 0, 160, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 2);
     743           0 :         break;
     744           0 :     case MOTOR_FRAME_TYPE_PLUSREV: {
     745             :         // plus with reversed motor directions
     746           0 :         _frame_type_string = "PLUSREV";
     747             :         static const AP_MotorsMatrix::MotorDef motors[] {
     748             :             { 90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  2 },
     749             :             { -90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
     750             :             { 0, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     751             :             { 180, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     752             :         };
     753           0 :         add_motors(motors, ARRAY_SIZE(motors));
     754           0 :         break;
     755             :     }
     756           0 :     case MOTOR_FRAME_TYPE_Y4:
     757           0 :         _frame_type_string = "Y4";
     758             :         // Y4 motor definition with right front CCW, left front CW
     759             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
     760             :             { -1.0f,  1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
     761             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  2 },
     762             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
     763             :             {  1.0f,  1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
     764             :         };
     765           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
     766           0 :         break;
     767           0 :     default:
     768             :         // quad frame class does not support this frame type
     769           0 :         return false;
     770             :     }
     771        1050 :     return true;
     772             : }
     773             : #endif //AP_MOTORS_FRAME_QUAD_ENABLED
     774             : #if AP_MOTORS_FRAME_HEXA_ENABLED
     775           4 : bool AP_MotorsMatrix::setup_hexa_matrix(motor_frame_type frame_type)
     776             : {
     777           4 :     _frame_class_string = "HEXA";
     778           4 :     _mav_type = MAV_TYPE_HEXAROTOR;
     779           4 :     switch (frame_type) {
     780           1 :     case MOTOR_FRAME_TYPE_PLUS: {
     781           1 :         _frame_type_string = "PLUS";
     782             :         static const AP_MotorsMatrix::MotorDef motors[] {
     783             :             {    0, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     784             :             {  180, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     785             :             { -120, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   5 },
     786             :             {   60, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     787             :             {  -60, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  6 },
     788             :             {  120, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     789             :         };
     790           1 :         add_motors(motors, ARRAY_SIZE(motors));
     791           1 :         break;
     792             :     }
     793           1 :     case MOTOR_FRAME_TYPE_X: {
     794           1 :         _frame_type_string = "X";
     795             :         static const AP_MotorsMatrix::MotorDef motors[] {
     796             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     797             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     798             :             {  -30, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     799             :             {  150, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     800             :             {   30, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     801             :             { -150, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     802             :         };
     803           1 :         add_motors(motors, ARRAY_SIZE(motors));
     804           1 :         break;
     805             :     }
     806           0 :     case MOTOR_FRAME_TYPE_H: {
     807             :         // H is same as X except middle motors are closer to center
     808           0 :         _frame_type_string = "H";
     809             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
     810             :             { -1.0f, 0.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 2 },
     811             :             { 1.0f, 0.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 5 },
     812             :             { 1.0f, 1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 6 },
     813             :             { -1.0f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
     814             :             { -1.0f, 1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
     815             :             { 1.0f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 4 },
     816             :         };
     817           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
     818           0 :         break;
     819             :     }
     820           1 :     case MOTOR_FRAME_TYPE_DJI_X: {
     821           1 :         _frame_type_string = "DJI_X";
     822             :         static const AP_MotorsMatrix::MotorDef motors[] {
     823             :             {   30, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     824             :             {  -30, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     825             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     826             :             { -150, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     827             :             {  150, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     828             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     829             :         };
     830           1 :         add_motors(motors, ARRAY_SIZE(motors));
     831           1 :         break;
     832             :     }
     833           1 :     case MOTOR_FRAME_TYPE_CW_X: {
     834           1 :         _frame_type_string = "CW_X";
     835             :         static const AP_MotorsMatrix::MotorDef motors[] {
     836             :             {   30, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     837             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     838             :             {  150, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     839             :             { -150, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     840             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     841             :             {  -30, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     842             :         };
     843           1 :         add_motors(motors, ARRAY_SIZE(motors));
     844           1 :         break;
     845             :     }
     846           0 :     default:
     847             :         // hexa frame class does not support this frame type
     848           0 :         return false;
     849             :     } //hexa
     850           4 :     return true;
     851             : }
     852             : #endif ////AP_MOTORS_FRAME_HEXA_ENABLED
     853             : #if AP_MOTORS_FRAME_OCTA_ENABLED
     854           7 : bool AP_MotorsMatrix::setup_octa_matrix(motor_frame_type frame_type)
     855             : {
     856           7 :     _frame_class_string = "OCTA";
     857           7 :     _mav_type = MAV_TYPE_OCTOROTOR;
     858           7 :     switch (frame_type) {
     859           5 :     case MOTOR_FRAME_TYPE_PLUS: {
     860           5 :         _frame_type_string = "PLUS";
     861             :         static const AP_MotorsMatrix::MotorDef motors[] {
     862             :             {    0, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     863             :             {  180, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   5 },
     864             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     865             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     866             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 },
     867             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  6 },
     868             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 },
     869             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     870             :         };
     871             : 
     872           5 :         add_motors(motors, ARRAY_SIZE(motors));
     873           5 :         break;
     874             :     }
     875           0 :     case MOTOR_FRAME_TYPE_X: {
     876           0 :         _frame_type_string = "X";
     877             :         static const AP_MotorsMatrix::MotorDef motors[] {
     878             :             {   22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
     879             :             { -157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   5 },
     880             :             {   67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
     881             :             {  157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     882             :             {  -22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 },
     883             :             { -112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  6 },
     884             :             {  -67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 },
     885             :             {  112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     886             :         };
     887           0 :         add_motors(motors, ARRAY_SIZE(motors));
     888           0 :         break;
     889             :     }
     890           0 :     case MOTOR_FRAME_TYPE_V: {
     891           0 :         _frame_type_string = "V";
     892             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
     893             :             {  0.83f,  0.34f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
     894             :             { -0.67f, -0.32f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
     895             :             {  0.67f, -0.32f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
     896             :             { -0.50f, -1.00f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 4 },
     897             :             {  1.00f,  1.00f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 8 },
     898             :             { -0.83f,  0.34f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
     899             :             { -1.00f,  1.00f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
     900             :             {  0.50f, -1.00f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  5 },
     901             :         };
     902           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
     903           0 :         break;
     904             :     }
     905           0 :     case MOTOR_FRAME_TYPE_H: {
     906           0 :         _frame_type_string = "H";
     907             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
     908             :             { -1.0f,    1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
     909             :             {  1.0f,   -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  5 },
     910             :             { -1.0f,  0.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
     911             :             { -1.0f,   -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 4 },
     912             :             {  1.0f,    1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 8 },
     913             :             {  1.0f, -0.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
     914             :             {  1.0f,  0.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
     915             :             { -1.0f, -0.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
     916             :         };
     917           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
     918           0 :         break;
     919             :     }
     920           0 :     case MOTOR_FRAME_TYPE_I: {
     921           0 :         _frame_type_string = "I";
     922             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
     923             :             { 0.333f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   5 },
     924             :             { -0.333f,  1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
     925             :             {    1.0f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
     926             :             {  0.333f,  1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 8 },
     927             :             { -0.333f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 4 },
     928             :             {   -1.0f,  1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
     929             :             {   -1.0f, -1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
     930             :             {    1.0f,  1.0f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
     931             :         };
     932           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
     933           0 :         break;
     934             :     }
     935           1 :     case MOTOR_FRAME_TYPE_DJI_X: {
     936           1 :         _frame_type_string = "DJI_X";
     937             :         static const AP_MotorsMatrix::MotorDef motors[] {
     938             :             {   22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     939             :             {  -22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   8 },
     940             :             {  -67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  7 },
     941             :             { -112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     942             :             { -157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     943             :             {  157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     944             :             {  112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     945             :             {   67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     946             :         };
     947           1 :         add_motors(motors, ARRAY_SIZE(motors));
     948           1 :         break;
     949             :     }
     950           1 :     case MOTOR_FRAME_TYPE_CW_X: {
     951           1 :         _frame_type_string = "CW_X";
     952             :         static const AP_MotorsMatrix::MotorDef motors[] {
     953             :             {   22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     954             :             {   67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     955             :             {  112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
     956             :             {  157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
     957             :             { -157.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     958             :             { -112.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     959             :             {  -67.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  7 },
     960             :             {  -22.5f,  AP_MOTORS_MATRIX_YAW_FACTOR_CW,   8 },
     961             :         };
     962           1 :         add_motors(motors, ARRAY_SIZE(motors));
     963           1 :         break;
     964             :     }
     965           0 :     default:
     966             :         // octa frame class does not support this frame type
     967           0 :         return false;
     968             :     } // octa frame type
     969           7 :     return true;
     970             : }
     971             : #endif //AP_MOTORS_FRAME_OCTA_ENABLED
     972             : #if AP_MOTORS_FRAME_OCTAQUAD_ENABLED
     973          29 : bool AP_MotorsMatrix::setup_octaquad_matrix(motor_frame_type frame_type)
     974             : {
     975          29 :     _mav_type = MAV_TYPE_OCTOROTOR;
     976          29 :     _frame_class_string = "OCTAQUAD";
     977          29 :     switch (frame_type) {
     978           1 :     case MOTOR_FRAME_TYPE_PLUS: {
     979           1 :         _frame_type_string = "PLUS";
     980             :         static const AP_MotorsMatrix::MotorDef motors[] {
     981             :             {   0, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     982             :             { -90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 },
     983             :             { 180, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     984             :             {  90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
     985             :             { -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 },
     986             :             {   0, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
     987             :             {  90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
     988             :             { 180, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
     989             :         };
     990           1 :         add_motors(motors, ARRAY_SIZE(motors));
     991           1 :         break;
     992             :     }
     993           0 :     case MOTOR_FRAME_TYPE_X: {
     994           0 :         _frame_type_string = "X";
     995             :         static const AP_MotorsMatrix::MotorDef motors[] {
     996             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
     997             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 },
     998             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
     999             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
    1000             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 },
    1001             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
    1002             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
    1003             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
    1004             :         };
    1005           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1006           0 :         break;
    1007             :     }
    1008           0 :     case MOTOR_FRAME_TYPE_V: {
    1009           0 :         _frame_type_string = "V";
    1010             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1011             :             {   45,   0.7981f,  1 },
    1012             :             {  -45,  -0.7981f,  7 },
    1013             :             { -135,   1.0000f,  5 },
    1014             :             {  135,  -1.0000f,  3 },
    1015             :             {  -45,   0.7981f,  8 },
    1016             :             {   45,  -0.7981f,  2 },
    1017             :             {  135,   1.0000f,  4 },
    1018             :             { -135,  -1.0000f,  6 },
    1019             :         };
    1020           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1021           0 :         break;
    1022             :     }
    1023           0 :     case MOTOR_FRAME_TYPE_H: {
    1024             :         // H frame set-up - same as X but motors spin in opposite directions
    1025           0 :         _frame_type_string = "H";
    1026             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1027             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   1 },
    1028             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  7 },
    1029             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   5 },
    1030             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  3 },
    1031             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   8 },
    1032             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  2 },
    1033             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   4 },
    1034             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  6 },
    1035             :         };
    1036           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1037           0 :         break;
    1038             :     }
    1039          26 :     case MOTOR_FRAME_TYPE_CW_X: {
    1040          26 :         _frame_type_string = "CW_X";
    1041             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1042             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 },
    1043             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 },
    1044             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 },
    1045             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 },
    1046             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 },
    1047             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 },
    1048             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 },
    1049             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 },
    1050             :         };
    1051          26 :         add_motors(motors, ARRAY_SIZE(motors));
    1052          26 :         break;
    1053             :     }
    1054             :     // BF/X cinelifters using two 4-in-1 ESCs are quite common
    1055             :     // see: https://fpvfrenzy.com/betaflight-motor-order/
    1056           0 :     case MOTOR_FRAME_TYPE_BF_X: {
    1057           0 :         _frame_type_string = "BF_X";
    1058             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1059             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
    1060             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
    1061             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 5 },
    1062             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
    1063             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 4 },
    1064             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  2 },
    1065             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  6 },
    1066             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 8 },
    1067             :         };
    1068           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1069           0 :         break;
    1070             :     }
    1071           0 :     case MOTOR_FRAME_TYPE_BF_X_REV: {
    1072             :     // betaflight octa quad X order, reversed motors
    1073           0 :         _frame_type_string = "X_REV";
    1074             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1075             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
    1076             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
    1077             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  5 },
    1078             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 7 },
    1079             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
    1080             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
    1081             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
    1082             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  8 },
    1083             :         };
    1084           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1085           0 :         break;
    1086             :     }
    1087           1 :     case MOTOR_FRAME_TYPE_X_COR: {
    1088             :         static_assert(AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR < 1.0, "AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR must be less than 1.0");
    1089           1 :         _frame_type_string = "X_COR";
    1090             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1091             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
    1092             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
    1093             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 5 },
    1094             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
    1095             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  8 },
    1096             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
    1097             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
    1098             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
    1099             :         };
    1100           1 :         add_motors(motors, ARRAY_SIZE(motors));
    1101             :         // scale top layer to prevent a beat frequency between layers in co-rotating setups
    1102           5 :         for (uint8_t i=0; i<4; i++) {
    1103           4 :             _roll_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1104           4 :             _pitch_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1105           4 :             _yaw_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1106           4 :             _throttle_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1107             :         }
    1108           1 :         break;
    1109             :     }
    1110           1 :     case MOTOR_FRAME_TYPE_CW_X_COR: {
    1111           1 :         _frame_type_string = "CW_X_COR";
    1112             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1113             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
    1114             :             {   45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
    1115             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
    1116             :             {  135, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
    1117             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 5 },
    1118             :             { -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
    1119             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  7 },
    1120             :             {  -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  8 },
    1121             :         };
    1122           1 :         add_motors(motors, ARRAY_SIZE(motors));
    1123             :         // scale top layer to prevent a beat frequency between layers in co-rotating setups
    1124           5 :         for (uint8_t i=0; i<8; i += 2) {
    1125           4 :             _roll_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1126           4 :             _pitch_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1127           4 :             _yaw_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1128           4 :             _throttle_factor[i] *= AP_MOTORS_FRAME_OCTAQUAD_COROTATING_SCALE_FACTOR;
    1129             :         }
    1130           1 :         break;
    1131             :     }
    1132           0 :     default:
    1133             :         // octaquad frame class does not support this frame type
    1134           0 :         return false;
    1135             :     } //octaquad
    1136          29 :     return true;
    1137             : }
    1138             : #endif // AP_MOTORS_FRAME_OCTAQUAD_ENABLED
    1139             : #if AP_MOTORS_FRAME_DODECAHEXA_ENABLED
    1140           1 : bool AP_MotorsMatrix::setup_dodecahexa_matrix(motor_frame_type frame_type)
    1141             : {
    1142           1 :     _mav_type = MAV_TYPE_DODECAROTOR;
    1143           1 :     _frame_class_string = "DODECAHEXA";
    1144           1 :     switch (frame_type) {
    1145           1 :     case MOTOR_FRAME_TYPE_PLUS: {
    1146           1 :         _frame_type_string = "PLUS";
    1147             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1148             :             {    0, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  1 }, // forward-top
    1149             :             {    0, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   2 }, // forward-bottom
    1150             :             {   60, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   3 }, // forward-right-top
    1151             :             {   60, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  4 }, // forward-right-bottom
    1152             :             {  120, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  5 }, // back-right-top
    1153             :             {  120, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   6 }, // back-right-bottom
    1154             :             {  180, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   7 }, // back-top
    1155             :             {  180, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  8 }, // back-bottom
    1156             :             { -120, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  9 }, // back-left-top
    1157             :             { -120, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   10 }, // back-left-bottom
    1158             :             {  -60, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   11 }, // forward-left-top
    1159             :             {  -60, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  12 }, // forward-left-bottom
    1160             :         };
    1161           1 :         add_motors(motors, ARRAY_SIZE(motors));
    1162           1 :         break;
    1163             :     }
    1164           0 :     case MOTOR_FRAME_TYPE_X: {
    1165           0 :         _frame_type_string = "X";
    1166             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1167             :             {   30, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   1 }, // forward-right-top
    1168             :             {   30, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    2 }, // forward-right-bottom
    1169             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    3 }, // right-top
    1170             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   4 }, // right-bottom
    1171             :             {  150, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   5 }, // back-right-top
    1172             :             {  150, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    6 }, // back-right-bottom
    1173             :             { -150, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    7 }, // back-left-top
    1174             :             { -150, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   8 }, // back-left-bottom
    1175             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   9 }, // left-top
    1176             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   10 }, // left-bottom
    1177             :             {  -30, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   11 }, // forward-left-top
    1178             :             {  -30, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,  12 }, // forward-left-bottom
    1179             :         };
    1180           0 :         add_motors(motors, ARRAY_SIZE(motors));
    1181           0 :         break;
    1182             :     }
    1183           0 :     default:
    1184             :         // dodeca-hexa frame class does not support this frame type
    1185           0 :         return false;
    1186             :     } //dodecahexa
    1187           1 :     return true;
    1188             : }
    1189             : #endif //AP_MOTORS_FRAME_DODECAHEXA_ENABLED
    1190             : #if AP_MOTORS_FRAME_Y6_ENABLED
    1191           3 : bool AP_MotorsMatrix::setup_y6_matrix(motor_frame_type frame_type)
    1192             : {
    1193           3 :     _mav_type = MAV_TYPE_HEXAROTOR;
    1194           3 :     _frame_class_string = "Y6";
    1195           3 :     switch (frame_type) {
    1196           0 :     case MOTOR_FRAME_TYPE_Y6B: {
    1197             :         // Y6 motor definition with all top motors spinning clockwise, all bottom motors counter clockwise
    1198           0 :         _frame_type_string = "Y6B";
    1199             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
    1200             :             { -1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
    1201             :             { -1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
    1202             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  3 },
    1203             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 4 },
    1204             :             {  1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  5 },
    1205             :             {  1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
    1206             :         };
    1207           0 :         add_motors_raw(motors, ARRAY_SIZE(motors));
    1208           0 :         break;
    1209             :     }
    1210           2 :     case MOTOR_FRAME_TYPE_Y6F: {
    1211             :         // Y6 motor layout for FireFlyY6
    1212           2 :         _frame_type_string = "Y6F";
    1213             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
    1214             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
    1215             :             { -1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1 },
    1216             :             {  1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 5 },
    1217             :             {  0.0f, -1.000f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
    1218             :             { -1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  2 },
    1219             :             {  1.0f,  0.500f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  6 },
    1220             :         };
    1221           2 :         add_motors_raw(motors, ARRAY_SIZE(motors));
    1222           2 :         break;
    1223             :     }
    1224           1 :     default: {
    1225           1 :         _frame_type_string = "default";
    1226             :         static const AP_MotorsMatrix::MotorDefRaw motors[] {
    1227             :             { -1.0f,  0.666f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 2 },
    1228             :             {  1.0f,  0.666f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  5 },
    1229             :             {  1.0f,  0.666f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 6 },
    1230             :             {  0.0f, -1.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  4 },
    1231             :             { -1.0f,  0.666f, AP_MOTORS_MATRIX_YAW_FACTOR_CW,  1 },
    1232             :             {  0.0f, -1.333f, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3 },
    1233             :         };
    1234           1 :         add_motors_raw(motors, ARRAY_SIZE(motors));
    1235           1 :         break;
    1236             :     }
    1237             :     } //y6
    1238           3 :     return true;
    1239             : }
    1240             : #endif // AP_MOTORS_FRAME_Y6_ENABLED
    1241             : #if AP_MOTORS_FRAME_DECA_ENABLED
    1242           2 : bool AP_MotorsMatrix::setup_deca_matrix(motor_frame_type frame_type)
    1243             : {
    1244           2 :     _mav_type = MAV_TYPE_DECAROTOR;
    1245           2 :     _frame_class_string = "DECA";
    1246           2 :     switch (frame_type) {
    1247           1 :     case MOTOR_FRAME_TYPE_PLUS: {
    1248           1 :         _frame_type_string = "PLUS";
    1249             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1250             :             {    0, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   1 },
    1251             :             {   36, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    2 },
    1252             :             {   72, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   3 },
    1253             :             {  108, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    4 },
    1254             :             {  144, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   5 },
    1255             :             {  180, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    6 },
    1256             :             { -144, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   7 },
    1257             :             { -108, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    8 },
    1258             :             {  -72, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   9 },
    1259             :             {  -36, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   10 },
    1260             :         };
    1261           1 :         add_motors(motors, ARRAY_SIZE(motors));
    1262           1 :         break;
    1263             :     }
    1264           1 :     case MOTOR_FRAME_TYPE_X:
    1265             :     case MOTOR_FRAME_TYPE_CW_X: {
    1266           1 :         _frame_type_string = "X/CW_X";
    1267             :         static const AP_MotorsMatrix::MotorDef motors[] {
    1268             :             {   18, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   1 },
    1269             :             {   54, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    2 },
    1270             :             {   90, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   3 },
    1271             :             {  126, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    4 },
    1272             :             {  162, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   5 },
    1273             :             { -162, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    6 },
    1274             :             { -126, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   7 },
    1275             :             {  -90, AP_MOTORS_MATRIX_YAW_FACTOR_CW,    8 },
    1276             :             {  -54, AP_MOTORS_MATRIX_YAW_FACTOR_CCW,   9 },
    1277             :             {  -18, AP_MOTORS_MATRIX_YAW_FACTOR_CW,   10 },
    1278             :         };
    1279           1 :         add_motors(motors, ARRAY_SIZE(motors));
    1280           1 :         break;
    1281             :     }
    1282           0 :     default:
    1283             :         // deca frame class does not support this frame type
    1284           0 :         return false;
    1285             :     } //deca
    1286           2 :     return true;
    1287             : }
    1288             : #endif // AP_MOTORS_FRAME_DECA_ENABLED
    1289             : 
    1290        1100 : void AP_MotorsMatrix::setup_motors(motor_frame_class frame_class, motor_frame_type frame_type)
    1291             : {
    1292             :     // remove existing motors
    1293       36300 :     for (int8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
    1294       35200 :         remove_motor(i);
    1295             :     }
    1296        1100 :     set_initialised_ok(false);
    1297        1100 :     bool success = true;
    1298             : 
    1299        1100 :     switch (frame_class) {
    1300             : #if AP_MOTORS_FRAME_QUAD_ENABLED
    1301        1050 :     case MOTOR_FRAME_QUAD:
    1302        1050 :         success = setup_quad_matrix(frame_type);
    1303        1050 :         break;  // quad
    1304             : #endif //AP_MOTORS_FRAME_QUAD_ENABLED
    1305             : #if AP_MOTORS_FRAME_HEXA_ENABLED
    1306           4 :     case MOTOR_FRAME_HEXA:
    1307           4 :         success = setup_hexa_matrix(frame_type);
    1308           4 :         break;
    1309             : #endif //AP_MOTORS_FRAME_HEXA_ENABLED
    1310             : #if AP_MOTORS_FRAME_OCTA_ENABLED
    1311           7 :     case MOTOR_FRAME_OCTA:
    1312           7 :         success = setup_octa_matrix(frame_type);
    1313           7 :         break;
    1314             : #endif //AP_MOTORS_FRAME_OCTA_ENABLED
    1315             : #if AP_MOTORS_FRAME_OCTAQUAD_ENABLED
    1316          29 :     case MOTOR_FRAME_OCTAQUAD:
    1317          29 :         success = setup_octaquad_matrix(frame_type);
    1318          29 :         break;
    1319             : #endif //AP_MOTORS_FRAME_OCTAQUAD_ENABLED
    1320             : #if AP_MOTORS_FRAME_DODECAHEXA_ENABLED
    1321           1 :     case MOTOR_FRAME_DODECAHEXA:
    1322           1 :         success = setup_dodecahexa_matrix(frame_type);
    1323           1 :         break;
    1324             : #endif //AP_MOTORS_FRAME_DODECAHEXA_ENABLED
    1325             : #if AP_MOTORS_FRAME_Y6_ENABLED
    1326           3 :     case MOTOR_FRAME_Y6:
    1327           3 :         success = setup_y6_matrix(frame_type);
    1328           3 :         break;
    1329             : #endif //AP_MOTORS_FRAME_Y6_ENABLED
    1330             : #if AP_MOTORS_FRAME_DECA_ENABLED
    1331           2 :     case MOTOR_FRAME_DECA:
    1332           2 :         success = setup_deca_matrix(frame_type);
    1333           2 :         break;
    1334             : #endif //AP_MOTORS_FRAME_DECA_ENABLED
    1335           4 :     default:
    1336             :         // matrix doesn't support the configured class
    1337           4 :         success = false;
    1338           4 :         _mav_type = MAV_TYPE_GENERIC;
    1339           4 :         break;
    1340             :     } // switch frame_class
    1341             : 
    1342             :     // normalise factors to magnitude 0.5
    1343        1100 :     normalise_rpy_factors();
    1344             : 
    1345        1100 :     if (!success) {
    1346           4 :         _frame_class_string = "UNSUPPORTED";
    1347             :     }
    1348        1100 :     set_initialised_ok(success);
    1349        1100 : }
    1350             : 
    1351             : // normalizes the roll, pitch and yaw factors so maximum magnitude is 0.5
    1352             : // normalizes throttle factors so max value is 1 and no value is less than 0
    1353        1103 : void AP_MotorsMatrix::normalise_rpy_factors()
    1354             : {
    1355        1103 :     float roll_fac = 0.0f;
    1356        1103 :     float pitch_fac = 0.0f;
    1357        1103 :     float yaw_fac = 0.0f;
    1358        1103 :     float throttle_fac = 0.0f;
    1359             : 
    1360             :     // find maximum roll, pitch and yaw factors
    1361       36399 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
    1362       35296 :         if (motor_enabled[i]) {
    1363        4626 :             roll_fac = MAX(roll_fac,fabsf(_roll_factor[i]));
    1364        4626 :             pitch_fac = MAX(pitch_fac,fabsf(_pitch_factor[i]));
    1365        4626 :             yaw_fac = MAX(yaw_fac,fabsf(_yaw_factor[i]));
    1366        4626 :             throttle_fac = MAX(throttle_fac,MAX(0.0f,_throttle_factor[i]));
    1367             :         }
    1368             :     }
    1369             : 
    1370             :     // scale factors back to -0.5 to +0.5 for each axis
    1371       36399 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
    1372       35296 :         if (motor_enabled[i]) {
    1373        4626 :             if (!is_zero(roll_fac)) {
    1374        4626 :                 _roll_factor[i] = 0.5f * _roll_factor[i] / roll_fac;
    1375             :             }
    1376        4626 :             if (!is_zero(pitch_fac)) {
    1377        4626 :                 _pitch_factor[i] = 0.5f * _pitch_factor[i] / pitch_fac;
    1378             :             }
    1379        4626 :             if (!is_zero(yaw_fac)) {
    1380        4626 :                 _yaw_factor[i] = 0.5f * _yaw_factor[i] / yaw_fac;
    1381             :             }
    1382        4626 :             if (!is_zero(throttle_fac)) {
    1383        4626 :                 _throttle_factor[i] = MAX(0.0f,_throttle_factor[i] / throttle_fac);
    1384             :             }
    1385             :         }
    1386             :     }
    1387        1103 : }
    1388             : 
    1389             : 
    1390             : /*
    1391             :   call vehicle supplied thrust compensation if set. This allows
    1392             :   vehicle code to compensate for vehicle specific motor arrangements
    1393             :   such as tiltrotors or tiltwings
    1394             : */
    1395    35522565 : void AP_MotorsMatrix::thrust_compensation(void)
    1396             : {
    1397    35522565 :     if (_thrust_compensation_callback) {
    1398      320854 :         _thrust_compensation_callback(_thrust_rpyt_out, AP_MOTORS_MAX_NUM_MOTORS);
    1399             :     }
    1400    35522565 : }
    1401             : 
    1402             : /*
    1403             :   disable the use of motor torque to control yaw. Used when an
    1404             :   external mechanism such as vectoring is used for yaw control
    1405             : */
    1406           5 : void AP_MotorsMatrix::disable_yaw_torque(void)
    1407             : {
    1408         165 :     for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {
    1409         160 :         _yaw_factor[i] = 0;
    1410             :     }
    1411           5 : }
    1412             : 
    1413             : #if APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
    1414             : // examples can pull values direct
    1415             : float AP_MotorsMatrix::get_thrust_rpyt_out(uint8_t i) const
    1416             : {
    1417             :     if (i < AP_MOTORS_MAX_NUM_MOTORS) {
    1418             :         return _thrust_rpyt_out[i];
    1419             :     }
    1420             :     return 0.0;
    1421             : }
    1422             : 
    1423             : bool AP_MotorsMatrix::get_factors(uint8_t i, float &roll, float &pitch, float &yaw, float &throttle, uint8_t &testing_order) const
    1424             : {
    1425             :     if ((i < AP_MOTORS_MAX_NUM_MOTORS) && motor_enabled[i]) {
    1426             :         roll = _roll_factor[i];
    1427             :         pitch = _pitch_factor[i];
    1428             :         yaw = _yaw_factor[i];
    1429             :         throttle = _throttle_factor[i];
    1430             :         testing_order = _test_order[i];
    1431             :         return true;
    1432             :     }
    1433             :     return false;
    1434             : }
    1435             : #endif
    1436             : 
    1437             : // singleton instance
    1438             : AP_MotorsMatrix *AP_MotorsMatrix::_singleton;

Generated by: LCOV version 1.14