LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduPlane - servos.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 350 523 66.9 %
Date: 2026-09-09 11:02:08 Functions: 22 24 91.7 %

          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             :   main logic for servo control
      17             :  */
      18             : 
      19             : #include "Plane.h"
      20             : #include <utility>
      21             : 
      22             : /*****************************************
      23             : * Throttle slew limit
      24             : *****************************************/
      25     8377244 : void Plane::throttle_slew_limit()
      26             : {
      27             : #if HAL_QUADPLANE_ENABLED
      28     8377244 :     const bool do_throttle_slew = (control_mode->does_auto_throttle() || quadplane.in_assisted_flight() || quadplane.in_vtol_mode());
      29             : #else
      30             :     const bool do_throttle_slew = control_mode->does_auto_throttle();
      31             : #endif
      32             : 
      33     8377244 :     if (!do_throttle_slew) {
      34             :         // only do throttle slew limiting in modes where throttle control is automatic
      35     1661897 :         SRV_Channels::set_slew_rate(SRV_Channel::k_throttle,      0.0, 100, G_Dt);
      36     1661897 :         SRV_Channels::set_slew_rate(SRV_Channel::k_throttleLeft,  0.0, 100, G_Dt);
      37     1661897 :         SRV_Channels::set_slew_rate(SRV_Channel::k_throttleRight, 0.0, 100, G_Dt);
      38     1661897 :         return;
      39             :     }
      40             : 
      41     6715347 :     uint16_t slewrate = aparm.throttle_slewrate;
      42     6715347 :     if (control_mode == &mode_auto) {
      43     3603874 :         if (auto_state.takeoff_complete == false && g.takeoff_throttle_slewrate != 0) {
      44           0 :             slewrate = g.takeoff_throttle_slewrate;
      45     3603874 :         } else if (landing.get_throttle_slewrate() != 0 && flight_stage == AP_FixedWing::FlightStage::LAND) {
      46           0 :             slewrate = landing.get_throttle_slewrate();
      47             :         }
      48             :     }
      49     6715347 :     if (g.takeoff_throttle_slewrate != 0 &&
      50           0 :         (flight_stage == AP_FixedWing::FlightStage::TAKEOFF ||
      51           0 :          flight_stage == AP_FixedWing::FlightStage::VTOL)) {
      52             :         // for VTOL we use takeoff slewrate, which helps with transition
      53           0 :         slewrate = g.takeoff_throttle_slewrate;
      54             :     }
      55             : #if HAL_QUADPLANE_ENABLED
      56     6715347 :     if (g.takeoff_throttle_slewrate != 0 && quadplane.in_frwd_transition()) {
      57           0 :         slewrate = g.takeoff_throttle_slewrate;
      58             :     }
      59             : #endif
      60     6715347 :     SRV_Channels::set_slew_rate(SRV_Channel::k_throttle,      slewrate, 100, G_Dt);
      61     6715347 :     SRV_Channels::set_slew_rate(SRV_Channel::k_throttleLeft,  slewrate, 100, G_Dt);
      62     6715347 :     SRV_Channels::set_slew_rate(SRV_Channel::k_throttleRight, slewrate, 100, G_Dt);
      63             : }
      64             : 
      65             : /* We want to suppress the throttle if we think we are on the ground and in an autopilot controlled throttle mode.
      66             : 
      67             :    Disable throttle if following conditions are met:
      68             :    *       1 - We are in Circle mode (which we use for short term failsafe), or in FBW-B or higher
      69             :    *       AND
      70             :    *       2 - Our reported altitude is within 10 meters of the home altitude.
      71             :    *       3 - Our reported speed is under 5 meters per second.
      72             :    *       4 - We are not performing a takeoff in Auto mode or takeoff speed/accel not yet reached
      73             :    *       OR
      74             :    *       5 - Home location is not set
      75             :    *       OR
      76             :    *       6- Landing does not want to allow throttle
      77             : */
      78     8377244 : bool Plane::suppress_throttle(void)
      79             : {
      80     8377244 :     if (control_mode == &mode_manual) {
      81             :         // Throttle is never suppressed in manual mode
      82      613998 :         return false;
      83             :     }
      84             : 
      85             : #if HAL_PARACHUTE_ENABLED
      86     7763246 :     if (control_mode->does_auto_throttle() && parachute.release_initiated()) {
      87             :         // throttle always suppressed in auto-throttle modes after parachute release initiated
      88         246 :         throttle_suppressed = true;
      89         246 :         return true;
      90             :     }
      91             : #endif
      92             : 
      93     7763000 :     if (landing.is_throttle_suppressed()) {
      94      123870 :         return true;
      95             :     }
      96             : 
      97     7639130 :     if (!throttle_suppressed) {
      98             :         // we've previously met a condition for unsupressing the throttle
      99     7412472 :         return false;
     100             :     }
     101      226658 :     if (!control_mode->does_auto_throttle()) {
     102             :         // the user controls the throttle
     103           0 :         throttle_suppressed = false;
     104           0 :         return false;
     105             :     }
     106             : 
     107      226658 :     bool gps_movement = (gps.status() >= AP_GPS_FixType::FIX_2D && gps.ground_speed() >= 5);
     108             :     
     109      226658 :     if ((control_mode == &mode_auto &&
     110      140578 :          auto_state.takeoff_complete == false) ||
     111      218617 :         control_mode == &mode_takeoff) {
     112             : 
     113       36959 :         uint32_t launch_duration_ms = ((int32_t)g.takeoff_throttle_delay)*100 + 2000;
     114       36959 :         if (is_flying() &&
     115         220 :             millis() - started_flying_ms > MAX(launch_duration_ms, 5000U) && // been flying >5s in any mode
     116          66 :             adjusted_relative_altitude_cm() > 500 && // are >5m above AGL/home
     117       37113 :             fabsf(ahrs.get_pitch_deg()) < 30 && // not high pitch, which happens when held before launch
     118             :             gps_movement) { // definite gps movement
     119             :             // we're already flying, do not suppress the throttle. We can get
     120             :             // stuck in this condition if we reset a mission and cmd 1 is takeoff
     121             :             // but we're currently flying around below the takeoff altitude
     122          65 :             throttle_suppressed = false;
     123          65 :             return false;
     124             :         }
     125       36894 :         if (auto_takeoff_check()) {
     126             :             // we're in auto takeoff 
     127         102 :             throttle_suppressed = false;
     128         102 :             auto_state.baro_takeoff_alt = barometer.get_altitude();
     129         102 :             return false;
     130             :         }
     131             :         // keep throttle suppressed
     132       36792 :         return true;
     133             :     }
     134             :     
     135      189699 :     if (fabsf(relative_altitude) >= 10.0f) {
     136             :         // we're more than 10m from the home altitude
     137         321 :         throttle_suppressed = false;
     138         321 :         return false;
     139             :     }
     140             : 
     141      189378 :     if (gps_movement) {
     142             :         // if we have an airspeed sensor, then check it too, and
     143             :         // require 5m/s. This prevents throttle up due to spiky GPS
     144             :         // groundspeed with bad GPS reception
     145             : #if AP_AIRSPEED_ENABLED
     146         414 :         if ((!ahrs.using_airspeed_sensor()) || airspeed.get_airspeed() >= 5) {
     147             :             // we're moving at more than 5 m/s
     148           2 :             throttle_suppressed = false;
     149           2 :             return false;        
     150             :         }
     151             : #else
     152             :         // no airspeed sensor, so we trust that the GPS's movement is truthful
     153             :         throttle_suppressed = false;
     154             :         return false;
     155             : #endif
     156             :     }
     157             : 
     158             : #if HAL_QUADPLANE_ENABLED
     159      189376 :     if (quadplane.is_flying()) {
     160          50 :         throttle_suppressed = false;
     161          50 :         return false;
     162             :     }
     163             : #endif
     164             : 
     165             :     // throttle remains suppressed
     166      189326 :     return true;
     167             : }
     168             : 
     169             : 
     170             : /*
     171             :   mixer for elevon and vtail channels setup using designated servo
     172             :   function values. This mixer operates purely on scaled values,
     173             :   allowing the user to trim and limit individual servos using the
     174             :   SERVOn_* parameters
     175             :  */
     176    16877342 : void Plane::channel_function_mixer(SRV_Channel::Function func1_in, SRV_Channel::Function func2_in,
     177             :                                    SRV_Channel::Function func1_out, SRV_Channel::Function func2_out) const
     178             : {
     179             :     // the order is setup so that non-reversed servos go "up", and
     180             :     // func1 is the "left" channel. Users can adjust with channel
     181             :     // reversal as needed
     182    16877342 :     float in1 = SRV_Channels::get_output_scaled(func1_in);
     183    16877342 :     float in2 = SRV_Channels::get_output_scaled(func2_in);
     184             : 
     185             :     // apply MIXING_OFFSET to input channels
     186    16877342 :     if (g.mixing_offset < 0) {
     187           0 :         in2 *= (100 - g.mixing_offset) * 0.01;
     188    16877342 :     } else if (g.mixing_offset > 0) {
     189           0 :         in1 *= (100 + g.mixing_offset) * 0.01;
     190             :     }
     191             :     
     192    16877342 :     float out1 = constrain_float((in2 - in1) * g.mixing_gain, -4500, 4500);
     193    16877342 :     float out2 = constrain_float((in2 + in1) * g.mixing_gain, -4500, 4500);
     194    16877342 :     SRV_Channels::set_output_scaled(func1_out, out1);
     195    16877342 :     SRV_Channels::set_output_scaled(func2_out, out2);
     196    16877342 : }
     197             : 
     198             : 
     199             : /*
     200             :   setup flaperon output channels
     201             :  */
     202     8437677 : void Plane::flaperon_update()
     203             : {
     204             :     /*
     205             :       flaperons are implemented as a mixer between aileron and a
     206             :       percentage of flaps. Flap input can come from a manual channel
     207             :       or from auto flaps.
     208             :      */
     209     8437677 :     float aileron = SRV_Channels::get_output_scaled(SRV_Channel::k_aileron);
     210     8437677 :     float flap_percent = SRV_Channels::get_slew_limited_output_scaled(SRV_Channel::k_flap_auto);
     211     8437677 :     float flaperon_left  = constrain_float(aileron + flap_percent * 45, -4500, 4500);
     212     8437677 :     float flaperon_right = constrain_float(aileron - flap_percent * 45, -4500, 4500);
     213     8437677 :     SRV_Channels::set_output_scaled(SRV_Channel::k_flaperon_left, flaperon_left);
     214     8437677 :     SRV_Channels::set_output_scaled(SRV_Channel::k_flaperon_right, flaperon_right);
     215     8437677 : }
     216             : 
     217             : 
     218             : /*
     219             :   setup differential spoiler output channels
     220             : 
     221             :   Differential spoilers are a type of elevon that is split on each
     222             :   wing to give yaw control, mixed from rudder
     223             :  */
     224     8438671 : void Plane::dspoiler_update(void)
     225             : {
     226     8438671 :     const int8_t bitmask = g2.crow_flap_options.get();
     227     8438671 :     const bool flying_wing       = (bitmask & CrowFlapOptions::FLYINGWING) != 0;
     228     8438671 :     const bool full_span_aileron = (bitmask & CrowFlapOptions::FULLSPAN) != 0;
     229             :     //progressive crow when option is set or RC switch is set to progressive 
     230     8438671 :     const bool progressive_crow   = (bitmask & CrowFlapOptions::PROGRESSIVE_CROW) != 0  || crow_mode == CrowMode::PROGRESSIVE; 
     231             : 
     232             :     // if flying wing use elevons else use ailerons
     233             :     float elevon_left;
     234             :     float elevon_right;
     235     8438671 :     if (flying_wing) {
     236     8438671 :         elevon_left = SRV_Channels::get_output_scaled(SRV_Channel::k_elevon_left);
     237     8438671 :         elevon_right = SRV_Channels::get_output_scaled(SRV_Channel::k_elevon_right);
     238             :     } else {
     239           0 :         const float aileron = SRV_Channels::get_output_scaled(SRV_Channel::k_aileron);
     240           0 :         elevon_left = -aileron;
     241           0 :         elevon_right = aileron;
     242             :     }
     243             : 
     244     8438671 :     const float rudder_rate = g.dspoiler_rud_rate * 0.01f;
     245     8438671 :     const float rudder = SRV_Channels::get_output_scaled(SRV_Channel::k_rudder) * rudder_rate;
     246     8438671 :     float dspoiler_outer_left = elevon_left;
     247     8438671 :     float dspoiler_outer_right = elevon_right;
     248             : 
     249     8438671 :     float dspoiler_inner_left = 0;
     250     8438671 :     float dspoiler_inner_right = 0;
     251             : 
     252             :     // full span ailerons / elevons
     253     8438671 :     if (full_span_aileron) {
     254     8438671 :         dspoiler_inner_left = elevon_left;
     255     8438671 :         dspoiler_inner_right = elevon_right;
     256             :     }
     257             : 
     258     8438671 :     if (rudder > 0) {
     259             :         // apply rudder to right wing
     260     2625312 :         dspoiler_outer_right = constrain_float(dspoiler_outer_right + rudder, -4500, 4500);
     261     2625312 :         dspoiler_inner_right = constrain_float(dspoiler_inner_right - rudder, -4500, 4500);
     262             :     } else {
     263             :         // apply rudder to left wing
     264     5813359 :         dspoiler_outer_left = constrain_float(dspoiler_outer_left - rudder, -4500, 4500);
     265     5813359 :         dspoiler_inner_left = constrain_float(dspoiler_inner_left + rudder, -4500, 4500);
     266             :     }
     267             : 
     268             :     // limit flap throw used for aileron
     269     8438671 :     const int8_t aileron_matching = g2.crow_flap_aileron_matching.get();
     270     8438671 :     if (aileron_matching < 100) {
     271             :         // only do matching if it will make a difference
     272           0 :         const float aileron_matching_scaled = aileron_matching * 0.01;
     273           0 :         if (is_negative(dspoiler_inner_left)) {
     274           0 :             dspoiler_inner_left *= aileron_matching_scaled;
     275             :         }
     276           0 :         if (is_negative(dspoiler_inner_right)) {
     277           0 :             dspoiler_inner_right *= aileron_matching_scaled;
     278             :         }
     279             :     }
     280             : 
     281     8438671 :     int16_t weight_outer = g2.crow_flap_weight_outer.get();
     282     8438671 :     if (crow_mode == Plane::CrowMode::CROW_DISABLED) {   //override totally aileron crow if crow RC switch set to disabled
     283           0 :         weight_outer = 0;
     284             :     }
     285     8438671 :     const int16_t weight_inner = g2.crow_flap_weight_inner.get();
     286     8438671 :     if (weight_outer > 0 || weight_inner > 0) {
     287             :         /*
     288             :           apply crow flaps by apply the same split of the differential
     289             :           spoilers to both wings. Get flap percentage from k_flap_auto, which is set
     290             :           in set_servos_flaps() as the maximum of manual and auto flap control
     291             :          */
     292           0 :         const float flap_percent = SRV_Channels::get_slew_limited_output_scaled(SRV_Channel::k_flap_auto);
     293             : 
     294           0 :         if (is_positive(flap_percent)) {
     295           0 :             float inner_flap_scaled = flap_percent;
     296           0 :             float outer_flap_scaled = flap_percent;
     297           0 :             if (progressive_crow) {
     298             :                 // apply 0 - full inner from 0 to 50% flap then add in outer above 50%
     299           0 :                 inner_flap_scaled = constrain_float(inner_flap_scaled * 2, 0,100);
     300           0 :                 outer_flap_scaled = constrain_float(outer_flap_scaled - 50, 0,50) * 2;
     301             :             }
     302             :             // scale flaps so when weights are 100 they give full up or down
     303           0 :             dspoiler_outer_left  = constrain_float(dspoiler_outer_left  + outer_flap_scaled * weight_outer * 0.45, -4500, 4500);
     304           0 :             dspoiler_inner_left  = constrain_float(dspoiler_inner_left  - inner_flap_scaled * weight_inner * 0.45, -4500, 4500);
     305           0 :             dspoiler_outer_right = constrain_float(dspoiler_outer_right + outer_flap_scaled * weight_outer * 0.45, -4500, 4500);
     306           0 :             dspoiler_inner_right = constrain_float(dspoiler_inner_right - inner_flap_scaled * weight_inner * 0.45, -4500, 4500);
     307             :         }
     308             :     }
     309             : 
     310     8438671 :     SRV_Channels::set_output_scaled(SRV_Channel::k_dspoilerLeft1, dspoiler_outer_left);
     311     8438671 :     SRV_Channels::set_output_scaled(SRV_Channel::k_dspoilerLeft2, dspoiler_inner_left);
     312     8438671 :     SRV_Channels::set_output_scaled(SRV_Channel::k_dspoilerRight1, dspoiler_outer_right);
     313     8438671 :     SRV_Channels::set_output_scaled(SRV_Channel::k_dspoilerRight2, dspoiler_inner_right);
     314     8438671 : }
     315             : 
     316             : /*
     317             :  set airbrakes based on reverse thrust and/or manual input RC channel
     318             :  */
     319     8377244 : void Plane::airbrake_update(void)
     320             : {
     321             :     // Calculate any manual airbrake input from RC channel option.
     322     8377244 :     float manual_airbrake_percent = 0;
     323             : 
     324     8377244 :     if (channel_airbrake != nullptr && !failsafe.rc_failsafe && failsafe.throttle_counter == 0) {
     325           0 :         manual_airbrake_percent = channel_airbrake->percent_input();
     326             :     }
     327             : 
     328             :     // Calculate auto airbrake from negative throttle.
     329     8377244 :     float throttle_min = aparm.throttle_min.get();
     330     8377244 :     float airbrake_pc = 0;
     331             : 
     332     8377244 :     float throttle_pc = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle);
     333             : 
     334     8377244 :     if (throttle_min < 0) {
     335           0 :         if (landing.is_flaring()) {
     336             :             // Full airbrakes during the flare.
     337           0 :             airbrake_pc = 100;
     338             :         }
     339             :         else {
     340             :             // Determine fraction between zero and full negative throttle.
     341           0 :             airbrake_pc = constrain_float(-throttle_pc, 0, 100);
     342             :         }
     343             :     }
     344             : 
     345             :     // Manual overrides auto airbrake setting.
     346     8377244 :     if (airbrake_pc < manual_airbrake_percent) {
     347           0 :         airbrake_pc = manual_airbrake_percent;
     348             :     }
     349             : 
     350             :     // Output to airbrake servo types.
     351     8377244 :     SRV_Channels::set_output_scaled(SRV_Channel::k_airbrake, airbrake_pc);
     352     8377244 : }
     353             : 
     354             : /*
     355             :   setup servos for idle wiggle mode
     356             :   Idle mode is used during balloon launch to keep servos still, apart
     357             :   from occasional wiggle to prevent freezing up
     358             :  */
     359      199220 : void ModeAuto::wiggle_servos()
     360             : {
     361             :     // This is only active while in AUTO running NAV_ALTITUDE_WAIT with wiggle_time > 0
     362      199220 :     if (wiggle.last_ms == 0) {
     363        5198 :         return;
     364             :     }
     365             : 
     366             :     int16_t servo_valueElevator;
     367             :     int16_t servo_valueAileronRudder;
     368             :     // Wiggle the control surfaces in stages: elevators first, then rudders + ailerons, through the full range over 4 seconds
     369      194022 :     if (wiggle.stage != 0) {
     370        6766 :         wiggle.stage += 1;
     371             :     }
     372      194022 :     if (wiggle.stage == 0) {
     373      187256 :         servo_valueElevator = 0;
     374      187256 :         servo_valueAileronRudder = 0;
     375        6766 :     } else if (wiggle.stage < 25) { 
     376         782 :         servo_valueElevator = wiggle.stage * (4500 / 25);      
     377         782 :         servo_valueAileronRudder = 0;
     378        5984 :     } else if (wiggle.stage < 75) {
     379        1700 :         servo_valueElevator = (50 - wiggle.stage) * (4500 / 25);        
     380        1700 :         servo_valueAileronRudder = 0;
     381        4284 :     } else if (wiggle.stage < 100) {
     382         850 :         servo_valueElevator = (wiggle.stage - 100) * (4500 / 25);        
     383         850 :         servo_valueAileronRudder = 0;
     384        3434 :     } else if (wiggle.stage < 125) {
     385         850 :         servo_valueElevator = 0;
     386         850 :         servo_valueAileronRudder = (wiggle.stage - 100) * (4500 / 25);
     387        2584 :     } else if (wiggle.stage < 175) {
     388        1700 :         servo_valueElevator = 0;
     389        1700 :         servo_valueAileronRudder = (150 - wiggle.stage) * (4500 / 25);  
     390         884 :     } else if (wiggle.stage < 200) {
     391         850 :         servo_valueElevator = 0;
     392         850 :         servo_valueAileronRudder = (wiggle.stage - 200) * (4500 / 25); 
     393             :     } else {
     394          34 :         wiggle.stage = 0;
     395          34 :         servo_valueElevator = 0;
     396          34 :         servo_valueAileronRudder = 0;
     397             :     }
     398      194022 :     SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, servo_valueAileronRudder);
     399      194022 :     SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, servo_valueElevator);
     400      194022 :     SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, servo_valueAileronRudder);
     401             : 
     402             : }
     403             : 
     404             : 
     405             : /*
     406             :   Calculate the throttle scale to compensate for battery voltage drop
     407             :  */
     408     8377244 : void ParametersG2::FWD_BATT_CMP::update()
     409             : {
     410             :     // Assume disabled
     411     8377244 :     enabled = false;
     412             : 
     413             :     // return if not enabled, or setup incorrectly
     414     8377244 :     if (!is_positive(batt_voltage_min) || batt_voltage_min >= batt_voltage_max) {
     415     8377244 :         return;
     416             :     }
     417             : 
     418           0 :     float batt_voltage_resting_estimate = AP::battery().voltage_resting_estimate(batt_idx);
     419             :     // Return for a very low battery
     420           0 :     if (batt_voltage_resting_estimate < 0.25f * batt_voltage_min) {
     421           0 :         return;
     422             :     }
     423             : 
     424             :     // constrain read voltage to min and max params
     425           0 :     batt_voltage_resting_estimate = constrain_float(batt_voltage_resting_estimate, batt_voltage_min, batt_voltage_max);
     426             : 
     427             :     // don't apply compensation if the voltage is excessively low
     428           0 :     if (batt_voltage_resting_estimate < 1) {
     429           0 :         return;
     430             :     }
     431             : 
     432             :     // Scale the throttle up to compensate for voltage drop
     433             :     // Ratio = 1 when voltage = voltage max, ratio increases as voltage drops
     434           0 :     ratio = batt_voltage_max / batt_voltage_resting_estimate;
     435             : 
     436             :     // Got this far then ratio is valid
     437           0 :     enabled = true;
     438             : }
     439             : 
     440             : // Apply throttle scale to min and max limits
     441     7338760 : void ParametersG2::FWD_BATT_CMP::apply_min_max(int8_t &min_throttle, int8_t &max_throttle) const
     442             : {
     443             :     // Cut off throttle if FWD_BAT_IDX battery resting voltage is below
     444             :     // FWD_THR_CUTOFF_V (if set), to preserve battery life for the electronics
     445             :     // and actuators. Only applies when the battery monitor is working and the
     446             :     // current mode does auto-throttle.
     447     7338760 :     if (is_positive(batt_voltage_throttle_cutoff) &&
     448     7338760 :         plane.control_mode->does_auto_throttle() && AP::battery().healthy(batt_idx) &&
     449           0 :         (AP::battery().voltage_resting_estimate(batt_idx) < batt_voltage_throttle_cutoff)) {
     450           0 :         min_throttle = 0;
     451           0 :         max_throttle = 0;
     452             : 
     453           0 :         return;
     454             :     }
     455             : 
     456             :     // return if not enabled
     457     7338760 :     if (!enabled) {
     458     7338760 :         return;
     459             :     }
     460             : 
     461             :     // Scale the throttle limits to prevent subsequent clipping
     462             :     // Ratio will always be >= 1, ensure still within max limits
     463           0 :     min_throttle = int8_t(MAX((ratio * (float)min_throttle), -100));
     464           0 :     max_throttle = int8_t(MIN((ratio * (float)max_throttle),  100));
     465             : 
     466             : }
     467             : 
     468             : // Apply throttle scale to throttle demand
     469     3895383 : float ParametersG2::FWD_BATT_CMP::apply_throttle(float throttle) const
     470             : {
     471             :     // return if not enabled
     472     3895383 :     if (!enabled) {
     473     3895383 :         return throttle;
     474             :     }
     475             : 
     476             :     // Ratio will always be >= 1, ensure still within max limits
     477           0 :     return constrain_float(throttle * ratio, -100, 100);
     478             : 
     479             : }
     480             : 
     481             : /*
     482             :   calculate any throttle limits based on the watt limiter
     483             :  */
     484             : #if AP_BATTERY_WATT_MAX_ENABLED
     485     7338760 : void Plane::throttle_watt_limiter(int8_t &min_throttle, int8_t &max_throttle)
     486             : {
     487     7338760 :     uint32_t now = millis();
     488     7338760 :     if (battery.overpower_detected()) {
     489             :         // overpower detected, cut back on the throttle if we're maxing it out by calculating a limiter value
     490             :         // throttle limit will attack by 10% per second
     491             :         
     492           0 :         if (is_positive(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle)) && // demanding too much positive thrust
     493           0 :             throttle_watt_limit_max < max_throttle - 25 &&
     494           0 :             now - throttle_watt_limit_timer_ms >= 1) {
     495             :             // always allow for 25% throttle available regardless of battery status
     496           0 :             throttle_watt_limit_timer_ms = now;
     497           0 :             throttle_watt_limit_max++;
     498             :             
     499           0 :         } else if (is_negative(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle)) &&
     500           0 :                    min_throttle < 0 && // reverse thrust is available
     501           0 :                    throttle_watt_limit_min < -(min_throttle) - 25 &&
     502           0 :                    now - throttle_watt_limit_timer_ms >= 1) {
     503             :             // always allow for 25% throttle available regardless of battery status
     504           0 :             throttle_watt_limit_timer_ms = now;
     505           0 :             throttle_watt_limit_min++;
     506             :         }
     507             :         
     508     7338760 :     } else if (now - throttle_watt_limit_timer_ms >= 1000) {
     509             :         // it has been 1 second since last over-current, check if we can resume higher throttle.
     510             :         // this throttle release is needed to allow raising the max_throttle as the battery voltage drains down
     511             :         // throttle limit will release by 1% per second
     512    11157204 :         if (SRV_Channels::get_output_scaled(SRV_Channel::k_throttle) > throttle_watt_limit_max && // demanding max forward thrust
     513     3818444 :             throttle_watt_limit_max > 0) { // and we're currently limiting it
     514           0 :             throttle_watt_limit_timer_ms = now;
     515           0 :             throttle_watt_limit_max--;
     516             :             
     517     7338760 :         } else if (SRV_Channels::get_output_scaled(SRV_Channel::k_throttle) < throttle_watt_limit_min && // demanding max negative thrust
     518           0 :                    throttle_watt_limit_min > 0) { // and we're limiting it
     519           0 :             throttle_watt_limit_timer_ms = now;
     520           0 :             throttle_watt_limit_min--;
     521             :         }
     522             :     }
     523             :     
     524     7338760 :     max_throttle = constrain_int16(max_throttle, 0, max_throttle - throttle_watt_limit_max);
     525     7338760 :     if (min_throttle < 0) {
     526           0 :         min_throttle = constrain_int16(min_throttle, min_throttle + throttle_watt_limit_min, 0);
     527             :     }
     528     7338760 : }
     529             : #endif // #if AP_BATTERY_WATT_MAX_ENABLED
     530             : 
     531             : /*
     532             :   Apply min/max safety limits to throttle.
     533             :  */
     534     7338760 : float Plane::apply_throttle_limits(float throttle_in)
     535             : {
     536             :     // Pull the base throttle limits.
     537             :     // These are usually set to map the ESC operating range.
     538     7338760 :     int8_t min_throttle = aparm.throttle_min.get();
     539     7338760 :     int8_t max_throttle = aparm.throttle_max.get();
     540             : 
     541             : #if AP_ICENGINE_ENABLED
     542             :     // Get the idle throttle (parameter or idle governor) from AP_ICEngine
     543     7338760 :     min_throttle = MAX(min_throttle, g2.ice_control.get_min_throttle_pct());
     544             : #endif
     545             : 
     546             :     // If reverse thrust is enabled not allowed right now, the minimum throttle must not fall below 0.
     547     7338760 :     if (min_throttle < 0 && !allow_reverse_thrust()) {
     548             :         // reverse thrust is available but inhibited.
     549           0 :         min_throttle = 0;
     550             :     }
     551             : 
     552             :     // Handle throttle limits for takeoff conditions.
     553             :     // Query the conditions where TKOFF_THR_MAX applies.
     554     7338760 :     const bool use_takeoff_throttle =
     555    14615651 :         (flight_stage == AP_FixedWing::FlightStage::TAKEOFF) ||
     556     7276891 :         (flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING);
     557             : 
     558             :     // Handle throttle limits for takeoff conditions.
     559     7338760 :     if (use_takeoff_throttle) {
     560             :         // Read from takeoff_state
     561       63104 :         max_throttle = takeoff_state.throttle_lim_max;
     562       63104 :         min_throttle = takeoff_state.throttle_lim_min;
     563     7275656 :     } else if (landing.is_flaring()) {
     564             :         // Allow throttle cutoff when flaring.
     565             :         // This is to allow the aircraft to bleed speed faster and land with a shut off thruster.
     566      123090 :         min_throttle = 0;
     567             :     }
     568             : 
     569             :     // Handle throttle limits for transition conditions.
     570             : #if HAL_QUADPLANE_ENABLED
     571     7338760 :     if (quadplane.in_frwd_transition()) {
     572      467918 :         if (aparm.takeoff_throttle_max != 0) {
     573           0 :             max_throttle = aparm.takeoff_throttle_max.get();
     574             :         }
     575             : 
     576             :         // Apply minimum throttle limits only for SLT thrust types.
     577             :         // The other types don't support it well.
     578      467918 :         if (quadplane.get_thrust_type() == QuadPlane::ThrustType::SLT
     579      467918 :             && control_mode->does_auto_throttle()
     580             :         ) {
     581      290609 :             if (aparm.takeoff_throttle_min.get() != 0) {
     582        3061 :                 min_throttle = MAX(min_throttle, aparm.takeoff_throttle_min.get());
     583             :             } else {
     584      287548 :                 min_throttle = MAX(min_throttle, aparm.throttle_cruise.get());
     585             :             }
     586             :         }
     587             :     }
     588             : #endif
     589             : 
     590             :     // Compensate the limits for battery voltage drop.
     591             :     // This relaxes the limits when the battery is getting depleted.
     592     7338760 :     g2.fwd_batt_cmp.apply_min_max(min_throttle, max_throttle);
     593             : 
     594             : #if AP_BATTERY_WATT_MAX_ENABLED
     595             :     // Ensure that the power draw limits are not exceeded.
     596     7338760 :     throttle_watt_limiter(min_throttle, max_throttle);
     597             : #endif
     598             : 
     599             :     // Do a sanity check on them. Constrain down if necessary.
     600     7338760 :     min_throttle = MIN(min_throttle, max_throttle);
     601             : 
     602             :     // Let TECS know about the updated throttle limits.
     603             :     // These will be taken into account on the next iteration.
     604     7338760 :     TECS_controller.set_throttle_min(0.01f*min_throttle);
     605     7338760 :     TECS_controller.set_throttle_max(0.01f*max_throttle);
     606     7338760 :     return constrain_float(throttle_in, min_throttle, max_throttle);
     607             : }
     608             : 
     609             : /*
     610             :   setup output channels all non-manual modes
     611             :  */
     612     8377244 : void Plane::set_throttle(void)
     613             : {
     614             : 
     615             :     // Update voltage scaling
     616     8377244 :     g2.fwd_batt_cmp.update();
     617             : 
     618     8377244 :     if (control_mode->use_battery_compensation()) {
     619             :         // Apply voltage compensation to throttle output from flight mode
     620     3895383 :         const float throttle = g2.fwd_batt_cmp.apply_throttle(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle));
     621     3895383 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, throttle);
     622             :     }
     623             : 
     624     8377244 :     if (control_mode->use_throttle_limits()) {
     625             :         // Apply min/max throttle limits
     626     7338760 :         const float limited_throttle = apply_throttle_limits(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle));
     627     7338760 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, limited_throttle);
     628             :     }
     629             : 
     630     8377244 :     if (suppress_throttle()) {
     631      350234 :         if (g.throttle_suppress_manual) {
     632             :             // manual pass through of throttle while throttle is suppressed
     633           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, get_throttle_input(true));
     634             : 
     635      350234 :         } else if (landing.is_flaring() && landing.use_thr_min_during_flare() ) {
     636             :             // throttle is suppressed (above) to zero in final flare in auto mode, but we allow instead thr_min if user prefers, eg turbines:
     637           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, aparm.throttle_min.get());
     638             : 
     639      700468 :         } else if ((flight_stage == AP_FixedWing::FlightStage::TAKEOFF)
     640      350234 :                     && (aparm.takeoff_throttle_idle.get() > 0)
     641             :                   ) {
     642             :             // we want to spin at idle throttle before the takeoff conditions are met
     643         661 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, aparm.takeoff_throttle_idle.get());
     644             :         } else {
     645             :             // default
     646      349573 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, 0.0);
     647             : 
     648             :         }
     649             :     }
     650             : 
     651     8377244 : }
     652             : 
     653             : /*
     654             :   Warn AHRS that we might take off soon
     655             :  */
     656     8377244 : void Plane::set_takeoff_expected(void)
     657             : {
     658             :     // let EKF know to start GSF yaw estimator before takeoff movement starts so that yaw angle is better estimated
     659     8377244 :     const float throttle = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle);
     660     8377244 :     if (!is_flying() && arming.is_armed()) {
     661             :         // Check if rate of change of velocity along X axis exceeds 1-g which normally indicates a throw.
     662             :         // Tests with hand carriage of micro UAS indicates that a 1-g threshold does not false trigger prior
     663             :         // to the throw, but there is margin to increase this threshold if false triggering becomes problematic.
     664      382722 :         const float accel_x_due_to_gravity = GRAVITY_MSS * ahrs.sin_pitch();
     665      382722 :         const float accel_x_due_to_throw = ahrs.get_accel().x - accel_x_due_to_gravity;
     666      382722 :         bool throw_detected = accel_x_due_to_throw > GRAVITY_MSS;
     667      382722 :         bool throttle_up_detected = throttle > aparm.throttle_cruise;
     668      382722 :         if (throw_detected || throttle_up_detected) {
     669       28348 :             plane.ahrs.set_takeoff_expected(true);
     670             :         }
     671             :     }
     672     8377244 : }
     673             : 
     674             : // Return the speed which should be used for auto flap deployment calculation
     675     5388522 : float Plane::get_auto_flap_speed() const
     676             : {
     677             :     float est_airspeed;
     678     5388522 :     const auto flap_actual_speed = flight_option_enabled(FlightOptions::FLAP_ACTUAL_SPEED) && ahrs.airspeed_EAS(est_airspeed) && TECS_controller.use_airspeed();
     679     5388522 :     const bool has_target_airspeed = control_mode->does_auto_throttle() && (target_airspeed_cm > 0);
     680             : 
     681             :     // Use airspeed if enabled and available
     682     5388522 :     if (flap_actual_speed) {
     683             :         // If there is a target airspeed return the smaller of the measured and the target
     684             :         // This means the flaps are deployed in anticipation of slowing down
     685          45 :         if (has_target_airspeed) {
     686           9 :             return MIN(est_airspeed, target_airspeed_cm * 0.01f);
     687             :         }
     688             : 
     689             :         // No target, use measurement only
     690          36 :         return est_airspeed;
     691             :     }
     692             : 
     693             :     // If there is a target airspeed use it
     694     5388477 :     if (has_target_airspeed) {
     695     5388442 :         return target_airspeed_cm * 0.01f;
     696             :     }
     697             : 
     698             :     // Default to cruise speed
     699          35 :     return aparm.airspeed_cruise.get();
     700             : }
     701             : 
     702             : /*
     703             :   setup flap outputs
     704             :  */
     705     8377244 : void Plane::set_servos_flaps(void)
     706             : {
     707             :     // Auto flap deployment
     708     8377244 :     int8_t auto_flap_percent = 0;
     709     8377244 :     int8_t manual_flap_percent = 0;
     710             : 
     711             :     // work out any manual flap input
     712     8377244 :     if (channel_flap != nullptr && rc().has_valid_input()) {
     713       13279 :         manual_flap_percent = channel_flap->percent_input();
     714             :     }
     715             : 
     716     8377244 :     const auto autoflap_in_manual = flight_option_enabled(FlightOptions::FLAP_ACTUAL_SPEED);
     717     8377244 :     const bool has_target_airspeed = control_mode->does_auto_throttle();
     718     8377244 :     if (has_target_airspeed || autoflap_in_manual) {
     719     5388522 :         const float flapSpeedSource = get_auto_flap_speed();
     720     5388522 :         if (g.flap_2_speed != 0 && flapSpeedSource <= g.flap_2_speed) {
     721          25 :             auto_flap_percent = g.flap_2_percent;
     722     5388497 :         } else if ( g.flap_1_speed != 0 && flapSpeedSource <= g.flap_1_speed) {
     723        3161 :             auto_flap_percent = g.flap_1_percent;
     724             :         } //else flaps stay at default zero deflection
     725             : 
     726             : #if HAL_SOARING_ENABLED
     727     5388522 :         if (control_mode == &mode_thermal) {
     728       13618 :             auto_flap_percent = g2.soaring_controller.get_thermalling_flap();
     729             :         }
     730             : #endif
     731             : 
     732             :         /*
     733             :           special flap levels for takeoff and landing. This works
     734             :           better than speed based flaps as it leads to less
     735             :           possibility of oscillation
     736             :          */
     737     5388522 :         switch (flight_stage) {
     738       63104 :             case AP_FixedWing::FlightStage::TAKEOFF:
     739             :             case AP_FixedWing::FlightStage::ABORT_LANDING:
     740       63104 :                 if (g.takeoff_flap_percent != 0) {
     741           0 :                     auto_flap_percent = g.takeoff_flap_percent;
     742             :                 }
     743       63104 :                 break;
     744     4002630 :             case AP_FixedWing::FlightStage::NORMAL:
     745     4002630 :                 if (g.takeoff_flap_percent != 0 && in_preLaunch_flight_stage()) {
     746             :                     // TODO: move this to a new FLIGHT_PRE_TAKEOFF stage
     747           0 :                     auto_flap_percent = g.takeoff_flap_percent;
     748             :                 }
     749     4002630 :                 break;
     750      197641 :             case AP_FixedWing::FlightStage::LAND:
     751      197641 :                 if (landing.get_flap_percent() != 0) {
     752        1420 :                   auto_flap_percent = landing.get_flap_percent();
     753             :                 }
     754      197641 :                 break;
     755     1125147 :             default:
     756     1125147 :                 break;
     757             :         }
     758             :     }
     759             : 
     760             :     // manual flap input overrides auto flap input
     761     8377244 :     if (abs(manual_flap_percent) > auto_flap_percent) {
     762          91 :         auto_flap_percent = manual_flap_percent;
     763             :     }
     764             : 
     765     8377244 :     SRV_Channels::set_output_scaled(SRV_Channel::k_flap_auto, auto_flap_percent);
     766     8377244 :     SRV_Channels::set_output_scaled(SRV_Channel::k_flap, manual_flap_percent);
     767             : 
     768     8377244 :     SRV_Channels::set_slew_rate(SRV_Channel::k_flap_auto, g.flap_slewrate, 100, G_Dt);
     769     8377244 :     SRV_Channels::set_slew_rate(SRV_Channel::k_flap, g.flap_slewrate, 100, G_Dt);
     770             : 
     771             :     // output to flaperons, if any
     772     8377244 :     flaperon_update();
     773     8377244 : }
     774             : 
     775             : /*
     776             :   support for twin-engine planes
     777             :  */
     778     8438671 : void Plane::servos_twin_engine_mix(void)
     779             : {
     780     8438671 :     float throttle = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle);
     781     8438671 :     float rud_gain = float(plane.g2.rudd_dt_gain) * 0.01f;
     782     8438671 :     rudder_dt = rud_gain * SRV_Channels::get_output_scaled(SRV_Channel::k_rudder) / SERVO_MAX;
     783             : 
     784             : #if AP_ADVANCEDFAILSAFE_ENABLED
     785     8438671 :     if (afs.should_crash_vehicle()) {
     786             :         // when in AFS failsafe force rudder input for differential thrust to zero
     787         303 :         rudder_dt = 0;
     788             :     }
     789             : #endif
     790             : 
     791             :     float throttle_left, throttle_right;
     792             : 
     793     8438671 :     if (throttle < 0 && have_reverse_thrust() && allow_reverse_thrust()) {
     794             :         // doing reverse thrust
     795           0 :         throttle_left  = constrain_float(throttle + 50 * rudder_dt, -100, 0);
     796           0 :         throttle_right = constrain_float(throttle - 50 * rudder_dt, -100, 0);
     797     8438671 :     } else if (throttle <= 0) {
     798     4736379 :         throttle_left  = throttle_right = 0;
     799             :     } else {
     800             :         // doing forward thrust
     801     3702292 :         throttle_left  = constrain_float(throttle + 50 * rudder_dt, 0, 100);
     802     3702292 :         throttle_right = constrain_float(throttle - 50 * rudder_dt, 0, 100);
     803             :     }
     804     8438671 :     if (!arming.is_armed_and_safety_off()) {
     805     1979600 :         if (arming.arming_required() == AP_Arming::Required::YES_ZERO_PWM) {
     806           0 :             SRV_Channels::set_output_limit(SRV_Channel::k_throttleLeft, SRV_Channel::Limit::ZERO_PWM);
     807           0 :             SRV_Channels::set_output_limit(SRV_Channel::k_throttleRight, SRV_Channel::Limit::ZERO_PWM);
     808             :         } else {
     809     1979600 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, 0);
     810     1979600 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, 0);
     811             :         }
     812             :     } else {
     813     6459071 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, throttle_left);
     814     6459071 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, throttle_right);
     815             :     }
     816     8438671 : }
     817             : 
     818             : /*
     819             :   Set throttle,attitude(in Attitude.cpp), and tilt servos for forced flare by RCx_OPTION switch for landing in FW mode
     820             :   For Fixed Wind modes with manual throttle control only. Forces tilts up and throttle to THR_MIN.
     821             :   Throttle stick must be in idle deadzone. This allows non-momentary switch to be used and quick bailouts
     822             :   for go-arounds. Also helps prevent propstrike after landing with switch release on ground.
     823             : */
     824     8438671 : void Plane::force_flare(void)
     825             : {
     826             : #if HAL_QUADPLANE_ENABLED
     827     8438671 :     if (quadplane.in_frwd_transition() && plane.arming.is_armed()) { //allows for ground checking of flare tilts
     828      416489 :         return;
     829             :     }
     830     8022182 :     if (control_mode->is_vtol_mode()) {
     831     1601324 :         return;
     832             :     }
     833             :     /* to be active must be:
     834             :        -manual throttle mode
     835             :        -in an enabled flare mode (RC switch active)
     836             :        -at zero thrust: in throttle trim dz except for sprung throttle option where trim is at hover stick
     837             :     */
     838     6420858 :     if (!control_mode->does_auto_throttle() && flare_mode != FlareMode::FLARE_DISABLED && throttle_at_zero()) {
     839           0 :         int32_t tilt = -SERVO_MAX;  //this is tilts up for a normal tiltrotor if at zero thrust throttle stick      
     840           0 :         if (quadplane.tiltrotor.enabled() && (quadplane.tiltrotor.type == Tiltrotor::TILT_TYPE_BICOPTER)) {
     841           0 :             tilt = 0; // this is tilts up for a Bicopter
     842             :         }
     843           0 :         if (quadplane.tailsitter.enabled()) {
     844           0 :             tilt = SERVO_MAX; //this is tilts up for a tailsitter
     845             :         }
     846           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_motor_tilt, tilt);
     847           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, tilt);
     848           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, tilt);
     849           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRear, tilt);
     850           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRearLeft, tilt);
     851           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRearRight, tilt);
     852           0 :         float throttle_min = MAX(aparm.throttle_min.get(),0); //allows ICE to run if used but accounts for reverse thrust setups
     853           0 :         if (arming.is_armed()) {  //prevent running motors if unarmed
     854           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, throttle_min);
     855           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, throttle_min);
     856           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, throttle_min);
     857             :         }
     858             :     }
     859             : #endif
     860             : }
     861             : 
     862             : /* Set the flight control servos based on the current calculated values
     863             : 
     864             :   This function operates by first building up output values for
     865             :   channels using set_servo() and set_radio_out(). Using
     866             :   set_radio_out() is for when a raw PWM value of output is given which
     867             :   does not depend on any output scaling. Using set_servo() is for when
     868             :   scaling and mixing will be needed.
     869             : 
     870             :   Finally servos_output() is called to push the final PWM values
     871             :   for output channels
     872             : */
     873     8377547 : void Plane::set_servos(void)
     874             : {
     875             :     // start with output corked. the cork is released when we run
     876             :     // servos_output(), which is run from all code paths in this
     877             :     // function
     878     8377547 :     AP::srv().cork();
     879             : 
     880             :     // this is to allow the failsafe module to deliberately crash 
     881             :     // the plane. Only used in extreme circumstances to meet the
     882             :     // OBC rules
     883             : #if AP_ADVANCEDFAILSAFE_ENABLED
     884     8377547 :     if (afs.should_crash_vehicle()) {
     885         303 :         afs.terminate_vehicle();
     886         303 :         if (!afs.terminating_vehicle_via_landing()) {
     887         303 :             return;
     888             :         }
     889             :     }
     890             : #endif
     891             : 
     892             :     // do any transition updates for quadplane
     893             : #if HAL_QUADPLANE_ENABLED
     894     8377244 :     quadplane.update();
     895             : #endif
     896             : 
     897     8377244 :     if (flight_stage == AP_FixedWing::FlightStage::LAND) {
     898             :         // allow landing to override servos if it would like to
     899      197641 :         landing.override_servos();
     900             :     }
     901             : 
     902     8377244 :     set_throttle();
     903             : 
     904     8377244 :     if ((control_mode != &mode_manual) && !arming.is_armed_and_safety_off()) {
     905             :         // Always set 0 scaled even if overriding to zero pwm.
     906             :         // This ensures slew limits and other functions using the scaled value pick up in the correct place
     907     1366804 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, 0.0);
     908     1366804 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, 0.0);
     909     1366804 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, 0.0);
     910             : 
     911     1366804 :         if (arming.arming_required() == AP_Arming::Required::YES_ZERO_PWM) {
     912           0 :             SRV_Channels::set_output_limit(SRV_Channel::k_throttle, SRV_Channel::Limit::ZERO_PWM);
     913           0 :             SRV_Channels::set_output_limit(SRV_Channel::k_throttleLeft, SRV_Channel::Limit::ZERO_PWM);
     914           0 :             SRV_Channels::set_output_limit(SRV_Channel::k_throttleRight, SRV_Channel::Limit::ZERO_PWM);
     915             :         }
     916             :     }
     917             : 
     918             :     // Warn AHRS if we might take off soon
     919     8377244 :     set_takeoff_expected();
     920             : 
     921             :     // setup flap outputs
     922     8377244 :     set_servos_flaps();
     923             : 
     924             :     // set airbrake outputs
     925     8377244 :     airbrake_update();
     926             : 
     927             :     // slew rate limit throttle
     928     8377244 :     throttle_slew_limit();
     929             : 
     930     8377244 :     int8_t min_throttle = 0;
     931             : #if AP_ICENGINE_ENABLED
     932     8377244 :     if (g2.ice_control.allow_throttle_while_disarmed()) {
     933       72114 :         min_throttle = MAX(aparm.throttle_min.get(), 0);
     934             :     }
     935     8377244 :     const float base_throttle = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle);
     936             : #endif
     937             : 
     938     8377244 :     if (!arming.is_armed()) {
     939             :         //Some ESCs get noisy (beep error msgs) if PWM == 0.
     940             :         //This little segment aims to avoid this.
     941     1918176 :         switch (arming.arming_required()) { 
     942           0 :         case AP_Arming::Required::NO:
     943             :             //keep existing behavior: do nothing to radio_out
     944             :             //(don't disarm throttle channel even if AP_Arming class is)
     945           0 :             break;
     946             : 
     947           0 :         case AP_Arming::Required::YES_ZERO_PWM:
     948           0 :             SRV_Channels::set_output_pwm(SRV_Channel::k_throttle, 0);
     949           0 :             SRV_Channels::set_output_pwm(SRV_Channel::k_throttleLeft, 0);
     950           0 :             SRV_Channels::set_output_pwm(SRV_Channel::k_throttleRight, 0);
     951           0 :             break;
     952             : 
     953     1918176 :         case AP_Arming::Required::YES_MIN_PWM:
     954             :         default:
     955     1918176 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, min_throttle);
     956     1918176 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, min_throttle);
     957     1918176 :             SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, min_throttle);
     958     1918176 :             break;
     959             :         }
     960             :     }
     961             : 
     962             : #if AP_ICENGINE_ENABLED
     963     8377244 :     float override_pct = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle);
     964     8377244 :     if (g2.ice_control.throttle_override(override_pct, base_throttle)) {
     965             :         // the ICE controller wants to override the throttle for starting, idle, or redline
     966      107170 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, override_pct);
     967             : #if HAL_QUADPLANE_ENABLED
     968      107170 :         quadplane.vel_forward.integrator = 0;
     969             : #endif
     970             :     }
     971             : #endif  // AP_ICENGINE_ENABLED
     972             : 
     973             :     // run output mixer and send values to the hal for output
     974     8377244 :     servos_output();
     975             : }
     976             : 
     977             : /*
     978             :     This sets servos to neutral if it is a control surface servo in auto mode
     979             : */
     980     8438671 : void Plane::landing_neutral_control_surface_servos(void)
     981             : {
     982     8438671 :     if (!(landing.get_then_servos_neutral() > 0 &&
     983           0 :             control_mode == &mode_auto &&
     984           0 :             landing.get_disarm_delay() > 0 &&
     985           0 :             landing.is_complete() &&
     986           0 :             !arming.is_armed())) {
     987     8438671 :                 return;
     988             :     }
     989             : 
     990             : 
     991             :     // after an auto land and auto disarm, set the servos to be neutral just
     992             :     // in case we're upside down or some crazy angle and straining the servos.
     993           0 :     for (uint8_t i = 0; i < NUM_SERVO_CHANNELS ; i++) {
     994           0 :             SRV_Channel *chan = SRV_Channels::srv_channel(i);
     995           0 :             if (chan == nullptr || !SRV_Channel::is_control_surface(chan->get_function())) {
     996           0 :                 continue;
     997             :             }
     998             : 
     999           0 :             if (landing.get_then_servos_neutral() == 1) {
    1000           0 :                 SRV_Channels::set_output_scaled(chan->get_function(), 0);
    1001           0 :             } else if (landing.get_then_servos_neutral() == 2) {
    1002           0 :                 SRV_Channels::set_output_limit(chan->get_function(), SRV_Channel::Limit::ZERO_PWM);
    1003             :             }
    1004             :     }
    1005             :  
    1006             : }
    1007             : 
    1008             : /*
    1009             :   sets rudder/vtail , and elevon to indicator positions that we are in a rudder arming waiting for neutral stick state
    1010             : */
    1011           0 : void Plane::indicate_waiting_for_rud_neutral_to_takeoff(void)
    1012             : {
    1013           0 :     if (takeoff_state.waiting_for_rudder_neutral)  {
    1014           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, 0);
    1015           0 :         channel_function_mixer(SRV_Channel::k_rudder,  SRV_Channel::k_elevator, SRV_Channel::k_vtail_right, SRV_Channel::k_vtail_left);
    1016           0 :         if (!SRV_Channels::function_assigned(SRV_Channel::k_rudder) && !SRV_Channels::function_assigned(SRV_Channel::k_vtail_left)) {
    1017             :             // if no rudder indication possible, neutral elevons during wait because on takeoff stance they are usually both full up
    1018           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_elevon_right, 0);
    1019           0 :             SRV_Channels::set_output_scaled(SRV_Channel::k_elevon_left, 0);        
    1020             :         }
    1021             :     }
    1022           0 : }
    1023             : 
    1024             : 
    1025             : /*
    1026             :   run configured output mixer. This takes calculated servo_out values
    1027             :   for each channel and calculates PWM values, then pushes them to
    1028             :   hal.rcout
    1029             :  */
    1030     8438671 : void Plane::servos_output(void)
    1031             : {
    1032     8438671 :     auto &srv = AP::srv();
    1033     8438671 :     srv.cork();
    1034             : 
    1035             :     // support twin-engine aircraft
    1036     8438671 :     servos_twin_engine_mix();
    1037             : 
    1038             :     // run vtail and elevon mixers
    1039     8438671 :     channel_function_mixer(SRV_Channel::k_aileron, SRV_Channel::k_elevator, SRV_Channel::k_elevon_left, SRV_Channel::k_elevon_right);
    1040     8438671 :     channel_function_mixer(SRV_Channel::k_rudder,  SRV_Channel::k_elevator, SRV_Channel::k_vtail_right, SRV_Channel::k_vtail_left);
    1041             : 
    1042             : #if HAL_QUADPLANE_ENABLED
    1043             :     // cope with tailsitters and bicopters
    1044     8438671 :     quadplane.tailsitter.output();
    1045     8438671 :     quadplane.tiltrotor.bicopter_output();
    1046             : #endif
    1047             : 
    1048             :     // support forced flare option
    1049     8438671 :     force_flare();
    1050             : 
    1051             :     // implement differential spoilers
    1052     8438671 :     dspoiler_update();
    1053             : 
    1054             :     //  set control surface servos to neutral
    1055     8438671 :     landing_neutral_control_surface_servos();
    1056             :     
    1057             :     // set rudder arm waiting for neutral control throws (rudder neutral, aileron/rt vtail/rt elevon to full right)
    1058     8438671 :     if (flight_option_enabled(FlightOptions::INDICATE_WAITING_FOR_RUDDER_NEUTRAL)) {
    1059           0 :         indicate_waiting_for_rud_neutral_to_takeoff();
    1060             :     }
    1061             : 
    1062             :     // support MANUAL_RCMASK
    1063     8438671 :     if (g2.manual_rc_mask.get() != 0 && control_mode == &mode_manual) {
    1064           0 :         SRV_Channels::copy_radio_in_out_mask(uint32_t(g2.manual_rc_mask.get()));
    1065             :     }
    1066             : 
    1067     8438671 :     SRV_Channels::calc_pwm();
    1068             : 
    1069     8438671 :     SRV_Channels::output_ch_all();
    1070             : 
    1071     8438671 :     srv.push();
    1072             : 
    1073     8438671 :     if (g2.servo_channels.auto_trim_enabled()) {
    1074           0 :         servos_auto_trim();
    1075             :     }
    1076     8438671 : }
    1077             : 
    1078     3894351 : void Plane::update_throttle_hover() {
    1079             :     // update hover throttle at 100Hz
    1080             : #if HAL_QUADPLANE_ENABLED
    1081     3894351 :     quadplane.update_throttle_hover();
    1082             : #endif
    1083     3894351 : }
    1084             : 
    1085             : /*
    1086             :   implement automatic persistent trim of control surfaces with
    1087             :   AUTO_TRIM=2, only available when SERVO_RNG_ENABLE=1 as otherwise it
    1088             :   would impact R/C transmitter calibration
    1089             :  */
    1090           0 : void Plane::servos_auto_trim(void)
    1091             : {
    1092             :     // only in auto modes and FBWA
    1093           0 :     if (!control_mode->does_auto_throttle() && control_mode != &mode_fbwa) {
    1094           0 :         return;
    1095             :     }
    1096           0 :     if (!arming.is_armed_and_safety_off()) {
    1097           0 :         return;
    1098             :     }
    1099           0 :     if (!is_flying()) {
    1100           0 :         return;
    1101             :     }
    1102             : #if HAL_QUADPLANE_ENABLED
    1103           0 :     if (!quadplane.allow_servo_auto_trim()) {
    1104             :         // can't auto-trim with quadplane motors running
    1105           0 :         return;
    1106             :     }
    1107             : #endif
    1108           0 :     if (abs(nav_roll_cd) > 700 || abs(nav_pitch_cd) > 700) {
    1109             :         // only when close to level
    1110           0 :         return;
    1111             :     }
    1112           0 :     uint32_t now = AP_HAL::millis();
    1113           0 :     if (now - auto_trim.last_trim_check < 500) {
    1114             :         // check twice a second. We want slow trim update
    1115           0 :         return;
    1116             :     }
    1117           0 :     if (ahrs.groundspeed() < 8 || smoothed_airspeed < 8) {
    1118             :         // only when definitely moving
    1119           0 :         return;
    1120             :     }
    1121             : 
    1122             :     // adjust trim on channels by a small amount according to I value
    1123           0 :     float roll_I = rollController.get_pid_info().I;
    1124           0 :     float pitch_I = pitchController.get_pid_info().I;
    1125             : 
    1126           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_aileron, roll_I);
    1127           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_elevator, pitch_I);
    1128             : 
    1129           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_elevon_left,  pitch_I - roll_I);
    1130           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_elevon_right, pitch_I + roll_I);
    1131             : 
    1132           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_vtail_left,  pitch_I);
    1133           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_vtail_right, pitch_I);
    1134             : 
    1135           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_flaperon_left,  roll_I);
    1136           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_flaperon_right, roll_I);
    1137             : 
    1138             :     // cope with various dspoiler options
    1139           0 :     const int8_t bitmask = g2.crow_flap_options.get();
    1140           0 :     const bool flying_wing       = (bitmask & CrowFlapOptions::FLYINGWING) != 0;
    1141           0 :     const bool full_span_aileron = (bitmask & CrowFlapOptions::FULLSPAN) != 0;
    1142             : 
    1143           0 :     float dspoiler_outer_left = - roll_I;
    1144           0 :     float dspoiler_inner_left = 0.0f;
    1145           0 :     float dspoiler_outer_right = roll_I;
    1146           0 :     float dspoiler_inner_right = 0.0f;
    1147             : 
    1148           0 :     if (flying_wing) {
    1149           0 :         dspoiler_outer_left += pitch_I;
    1150           0 :         dspoiler_outer_right += pitch_I;
    1151             :     }
    1152           0 :     if (full_span_aileron) {
    1153           0 :         dspoiler_inner_left = dspoiler_outer_left;
    1154           0 :         dspoiler_inner_right = dspoiler_outer_right;
    1155             :     }
    1156             : 
    1157           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_dspoilerLeft1,  dspoiler_outer_left);
    1158           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_dspoilerLeft2,  dspoiler_inner_left);
    1159           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_dspoilerRight1, dspoiler_outer_right);
    1160           0 :     g2.servo_channels.adjust_trim(SRV_Channel::k_dspoilerRight2, dspoiler_inner_right);
    1161             : 
    1162           0 :     auto_trim.last_trim_check = now;
    1163             : 
    1164           0 :     if (now - auto_trim.last_trim_save > 10000) {
    1165           0 :         auto_trim.last_trim_save = now;
    1166           0 :         g2.servo_channels.save_trim();
    1167             :     }
    1168             :     
    1169             : }

Generated by: LCOV version 1.14