LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduPlane - altitude.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 315 372 84.7 %
Date: 2026-09-09 11:02:08 Functions: 35 36 97.2 %

          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 "Plane.h"
      17             : 
      18             : /*
      19             :   altitude handling routines. These cope with both barometric control
      20             :   and terrain following control
      21             :  */
      22             : 
      23             : /*
      24             :   adjust altitude target depending on mode
      25             :  */
      26      550966 : void Plane::adjust_altitude_target()
      27             : {
      28      550966 :     control_mode->update_target_altitude();
      29      550966 : }
      30             : 
      31      511367 : void Plane::check_home_alt_change(void)
      32             : {
      33      511367 :     int32_t home_alt_cm = ahrs.get_home().alt;
      34      511367 :     if (home_alt_cm != auto_state.last_home_alt_cm && hal.util->get_soft_armed()) {
      35             :         // cope with home altitude changing
      36         291 :         const int32_t alt_change_cm = home_alt_cm - auto_state.last_home_alt_cm;
      37         291 :         fix_terrain_WP(next_WP_loc, __AP_LINE__);
      38             : 
      39             :         // reset TECS to force the field elevation estimate to reset
      40         291 :         TECS_controller.offset_altitude(alt_change_cm * 0.01f);
      41             :     }
      42      511367 :     auto_state.last_home_alt_cm = home_alt_cm;
      43      511367 : }
      44             : 
      45             : /*
      46             :   setup for a gradual altitude slope to the next waypoint, if appropriate
      47             :  */
      48        4166 : void Plane::setup_alt_slope(void)
      49             : {
      50             :     // establish the distance we are travelling to the next waypoint,
      51             :     // for calculating out rate of change of altitude
      52        4166 :     auto_state.wp_distance = current_loc.get_distance(next_WP_loc);
      53        4166 :     auto_state.wp_proportion = current_loc.line_path_proportion(prev_WP_loc, next_WP_loc);
      54        4166 :     TECS_controller.set_path_proportion(auto_state.wp_proportion);
      55        4166 :     update_flight_stage();
      56             : 
      57             :     /*
      58             :       work out if we will gradually change altitude, or try to get to
      59             :       the new altitude as quickly as possible.
      60             :      */
      61        4166 :     switch (control_mode->mode_number()) {
      62             : #if MODE_AUTOLAND_ENABLED
      63         284 :     case Mode::Number::AUTOLAND:
      64             : #endif
      65             :     case Mode::Number::RTL:
      66             :     case Mode::Number::AVOID_ADSB:
      67             :     case Mode::Number::GUIDED:
      68             :         /* glide down slowly if above target altitude, but ascend more
      69             :            rapidly if below it. See
      70             :            https://github.com/ArduPilot/ardupilot/issues/39
      71             :         */
      72         284 :         if (above_location_current(next_WP_loc)) {
      73          86 :             set_offset_altitude_location(prev_WP_loc, next_WP_loc);
      74             :         } else {
      75         198 :             reset_offset_altitude();
      76             :         }
      77         284 :         break;
      78             : 
      79        3842 :     case Mode::Number::AUTO:
      80             :         // climb without doing slope if option is enabled
      81        3842 :         if (!above_location_current(next_WP_loc) && plane.flight_option_enabled(FlightOptions::IMMEDIATE_CLIMB_IN_AUTO)) {
      82           0 :             reset_offset_altitude();
      83           0 :             break;
      84             :         }
      85             : 
      86             :         // otherwise we set up an altitude slope for this leg
      87        3842 :         set_offset_altitude_location(prev_WP_loc, next_WP_loc);
      88             : 
      89        3842 :         break;
      90          40 :     default:
      91          40 :         reset_offset_altitude();
      92          40 :         break;
      93             :     }
      94        4166 : }
      95             : 
      96             : /*
      97             :   return RTL altitude as AMSL cm
      98             :  */
      99          69 : int32_t Plane::get_RTL_altitude_cm() const
     100             : {
     101          69 :     if (g.RTL_altitude < 0) {
     102           0 :         return current_loc.alt;
     103             :     }
     104          69 :     return g.RTL_altitude*100 + home.alt;
     105             : }
     106             : 
     107             : /*
     108             :   return relative altitude in meters (relative to terrain, if available,
     109             :   or home otherwise)
     110             :  */
     111     3116016 : float Plane::relative_ground_altitude(enum RangeFinderUse use_rangefinder, bool use_terrain_if_available)
     112             : {
     113             : #if AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     114             :    float height_AGL;
     115             :    // use external HAGL if available
     116     3116016 :    if (get_external_HAGL(height_AGL)) {
     117           0 :        return height_AGL;
     118             :    }
     119             : #endif // AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     120             : 
     121             : #if AP_RANGEFINDER_ENABLED
     122     3116016 :    if (rangefinder_use(use_rangefinder) && rangefinder_state.in_range) {
     123      113690 :         return rangefinder_state.height_estimate;
     124             :    }
     125             : #endif
     126             : 
     127             : #if HAL_QUADPLANE_ENABLED && AP_RANGEFINDER_ENABLED
     128     3002326 :    if (rangefinder_use(use_rangefinder) && quadplane.in_vtol_land_final() &&
     129           0 :        rangefinder.status_orient(rangefinder_orientation()) == RangeFinder::Status::OutOfRangeLow) {
     130             :        // a special case for quadplane landing when rangefinder goes
     131             :        // below minimum. Consider our height above ground to be zero
     132           0 :        return 0;
     133             :    }
     134             : #endif
     135             : 
     136             : #if AP_TERRAIN_AVAILABLE
     137             :     float altitude;
     138      506922 :     if (use_terrain_if_available &&
     139     3509248 :         terrain.status() == AP_Terrain::TerrainStatusOK &&
     140      506922 :         terrain.height_above_terrain(altitude, true)) {
     141      506922 :         return altitude;
     142             :     }
     143             : #endif
     144             : 
     145             : #if HAL_QUADPLANE_ENABLED
     146     3575242 :     if (quadplane.in_vtol_land_descent() &&
     147     1079838 :         !quadplane.landing_with_fixed_wing_spiral_approach()) {
     148             :         // when doing a VTOL landing we can use the waypoint height as
     149             :         // ground height. We can't do this if using the
     150             :         // LAND_FW_APPROACH as that uses the wp height as the approach
     151             :         // height
     152     1033134 :         return height_above_target();
     153             :     }
     154             : #endif
     155             : 
     156     1462270 :     return relative_altitude;
     157             : }
     158             : 
     159             : /*
     160             :   return true if we should use the rangefinder for a specific use case
     161             :  */
     162     6226560 : bool Plane::rangefinder_use(enum RangeFinderUse use_rangefinder) const
     163             : {
     164     6226560 :     const uint8_t use = uint8_t(g.rangefinder_landing.get());
     165     6226560 :     if (use == uint8_t(RangeFinderUse::NONE)) {
     166     5943845 :         return false;
     167             :     }
     168      282715 :     if (use & uint8_t(RangeFinderUse::ALL)) {
     169             :         // if ALL bit is set then ignore other bits
     170      282715 :         return true;
     171             :     }
     172           0 :     return (use & uint8_t(use_rangefinder)) != 0;
     173             : }
     174             : 
     175             : // Helper for above method using terrain if the vehicle is currently terrain following
     176     3092962 : float Plane::relative_ground_altitude(enum RangeFinderUse use_rangefinder)
     177             : {
     178             : #if AP_TERRAIN_AVAILABLE
     179     3092962 :     return relative_ground_altitude(use_rangefinder, target_altitude.terrain_following);
     180             : #else
     181             :     return relative_ground_altitude(use_rangefinder, false);
     182             : #endif
     183             : }
     184             : 
     185             : 
     186             : /*
     187             :   set the target altitude to the current altitude. This is used when 
     188             :   setting up for altitude hold, such as when releasing elevator in
     189             :   CRUISE mode.
     190             :  */
     191        8131 : void Plane::set_target_altitude_current(void)
     192             : {
     193             :     // record altitude above sea level at the current time as our
     194             :     // target altitude
     195        8131 :     target_altitude.amsl_cm = current_loc.alt;
     196             : 
     197             :     // reset any altitude slope offset
     198        8131 :     reset_offset_altitude();
     199             : 
     200             : #if AP_TERRAIN_AVAILABLE
     201             :     // also record the terrain altitude if possible
     202             :     float terrain_altitude;
     203        8131 :     if (terrain_enabled_in_current_mode() && terrain.height_above_terrain(terrain_altitude, true) && !terrain_disabled()) {
     204          16 :         target_altitude.terrain_following = true;
     205          16 :         target_altitude.terrain_alt_cm = terrain_altitude*100;
     206             :     } else {
     207             :         // if terrain following is disabled, or we don't know our
     208             :         // terrain altitude when we set the altitude then don't
     209             :         // terrain follow
     210        8115 :         target_altitude.terrain_following = false;        
     211             :     }
     212             : #endif
     213        8131 : }
     214             : 
     215             : /*
     216             :   set target altitude based on a location structure
     217             :  */
     218      536260 : void Plane::set_target_altitude_location(const Location &loc)
     219             : {
     220      536260 :     target_altitude.amsl_cm = loc.alt;
     221      536260 :     if (loc.relative_alt) {
     222       74656 :         target_altitude.amsl_cm += home.alt;
     223             :     }
     224             : #if AP_TERRAIN_AVAILABLE
     225      536260 :     if (target_altitude.terrain_following_pending) {
     226             :         /* we didn't get terrain data to init when we started on this
     227             :            target, retry
     228             :         */
     229           0 :         setup_terrain_target_alt(next_WP_loc);
     230             :     }
     231             :     /*
     232             :       if this location has the terrain_alt flag set and we know the
     233             :       terrain altitude of our current location then treat it as a
     234             :       terrain altitude
     235             :      */
     236             :     float height;
     237      536260 :     if (loc.terrain_alt && terrain.height_above_terrain(height, true)) {
     238       71596 :         target_altitude.terrain_following = true;
     239       71596 :         target_altitude.terrain_alt_cm = loc.alt;
     240             :     } else {
     241      464664 :         target_altitude.terrain_following = false;
     242             :     }
     243             : #endif
     244      536260 : }
     245             : 
     246             : /*
     247             :   return relative to home target altitude in centimeters. Used for
     248             :   altitude control libraries
     249             :  */
     250      313893 : int32_t Plane::relative_target_altitude_cm(void)
     251             : {
     252             : #if AP_TERRAIN_AVAILABLE
     253             :     float relative_home_height;
     254      373431 :     if (target_altitude.terrain_following && 
     255       59538 :         terrain.height_relative_home_equivalent(target_altitude.terrain_alt_cm*0.01f,
     256             :                                                 relative_home_height, true)) {
     257             :         // add lookahead adjustment the target altitude
     258       59538 :         target_altitude.lookahead = lookahead_adjustment();
     259       59538 :         relative_home_height += target_altitude.lookahead;
     260             : 
     261             : #if AP_RANGEFINDER_ENABLED
     262             :         // correct for rangefinder data
     263       59538 :         relative_home_height += rangefinder_correction();
     264             : #endif
     265             : 
     266             :         // we are following terrain, and have terrain data for the
     267             :         // current location. Use it.
     268       59538 :         return relative_home_height*100;
     269             :     }
     270             : #endif
     271      254355 :     int32_t relative_alt = target_altitude.amsl_cm - home.alt;
     272      254355 :     relative_alt += mission_alt_offset()*100;
     273             : #if AP_RANGEFINDER_ENABLED
     274      254355 :     relative_alt += rangefinder_correction() * 100;
     275             : #endif
     276      254355 :     return relative_alt;
     277             : }
     278             : 
     279             : /*
     280             :   change the current target altitude by an amount in centimeters. Used
     281             :   to cope with changes due to elevator in CRUISE or FBWB
     282             :  */
     283       92318 : void Plane::change_target_altitude(int32_t change_cm)
     284             : {
     285       92318 :     target_altitude.amsl_cm += change_cm;
     286             : #if AP_TERRAIN_AVAILABLE
     287       92318 :     if (target_altitude.terrain_following && !terrain_disabled()) {
     288         925 :         target_altitude.terrain_alt_cm += change_cm;
     289             :     }
     290             : #endif
     291       92318 : }
     292             : 
     293             : /*
     294             :   change target altitude by a proportion of the target altitude offset
     295             :   (difference in height to next WP from previous WP). proportion
     296             :   should be between 0 and 1. 
     297             : 
     298             :   When proportion is zero we have reached the destination. When
     299             :   proportion is 1 we are at the starting waypoint.
     300             : 
     301             :   Note that target_altitude is setup initially based on the
     302             :   destination waypoint
     303             :  */
     304       75179 : void Plane::set_target_altitude_proportion(const Location &loc, float proportion)
     305             : {
     306       75179 :     set_target_altitude_location(loc);
     307             : 
     308             :     // Only do altitude slope handling when above CLIMB_SLOPE_HGT or when
     309             :     // descending. This is meant to prevent situations where the aircraft tries
     310             :     // to slowly gain height at low altitudes, potentially hitting obstacles.
     311       86657 :     if (target_altitude.offset_cm > 0 &&
     312       11478 :         (adjusted_relative_altitude_cm() <
     313       11478 :          (g2.waypoint_climb_slope_height_min * 100))) {
     314             :         // Early return to ensure a full-rate climb past CLIMB_SLOPE_HGT
     315        2385 :         return;
     316             :     }
     317             : 
     318       72794 :     proportion = constrain_float(proportion, 0.0f, 1.0f);
     319       72794 :     change_target_altitude(-target_altitude.offset_cm*proportion);
     320             : 
     321             :     // rebuild the altitude slope if we are above it and supposed to be climbing
     322       72794 :     if (g.alt_slope_max_height > 0) {
     323       72055 :         if (target_altitude.offset_cm > 0 && calc_altitude_error_cm() < -100 * g.alt_slope_max_height) {
     324          88 :             set_target_altitude_location(loc);
     325          88 :             set_offset_altitude_location(current_loc, loc);
     326          88 :             change_target_altitude(-target_altitude.offset_cm*proportion);
     327             :             // adjust the new target offset altitude to reflect that we are partially already done
     328          88 :             if (proportion > 0.0f)
     329          88 :                 target_altitude.offset_cm = ((float)target_altitude.offset_cm)/proportion;
     330             :         }
     331             :     }
     332             : }
     333             : 
     334             : #if AP_TERRAIN_AVAILABLE
     335             : /*
     336             :   change target altitude along a path between two locations
     337             :   (prev_WP_loc and next_WP_loc) where the second location is a terrain
     338             :   altitude
     339             :  */
     340       58495 : bool Plane::set_target_altitude_proportion_terrain(void)
     341             : {
     342       58495 :     if (!next_WP_loc.terrain_alt ||
     343       58495 :         !next_WP_loc.relative_alt) {
     344           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     345           0 :         return false;
     346             :     }
     347             :     /*
     348             :       we first need to get the height of the terrain at prev_WP_loc
     349             :      */
     350             :     float prev_WP_height_terrain;
     351       58495 :     if (!plane.prev_WP_loc.get_alt_m(Location::AltFrame::ABOVE_TERRAIN,
     352             :                                      prev_WP_height_terrain)) {
     353           0 :         return false;
     354             :     }
     355             :     // and next_WP_loc alt as terrain
     356             :     float next_WP_height_terrain;
     357       58495 :     if (!plane.next_WP_loc.get_alt_m(Location::AltFrame::ABOVE_TERRAIN,
     358             :                                      next_WP_height_terrain)) {
     359           0 :         return false;
     360             :     }
     361       58495 :     Location loc = next_WP_loc;
     362       58495 :     const auto alt = linear_interpolate(prev_WP_height_terrain, next_WP_height_terrain,
     363             :                                         plane.auto_state.wp_proportion, 0, 1);
     364             : 
     365       58495 :     loc.set_alt_m(alt, Location::AltFrame::ABOVE_TERRAIN);
     366             : 
     367       58495 :     set_target_altitude_location(loc);
     368             : 
     369       58495 :     return true;
     370             : }
     371             : #endif // AP_TERRAIN_AVAILABLE
     372             : 
     373             : /*
     374             :   constrain target altitude to be between two locations. Used to
     375             :   ensure we stay within two waypoints in altitude
     376             :  */
     377       75179 : void Plane::constrain_target_altitude_location(const Location &loc1, const Location &loc2)
     378             : {
     379       75179 :     if (loc1.alt > loc2.alt) {
     380       18376 :         target_altitude.amsl_cm = constrain_int32(target_altitude.amsl_cm, loc2.alt, loc1.alt);
     381             :     } else {
     382       56803 :         target_altitude.amsl_cm = constrain_int32(target_altitude.amsl_cm, loc1.alt, loc2.alt);
     383             :     }
     384       75179 : }
     385             : 
     386             : /*
     387             :   return error between target altitude and current altitude
     388             :  */
     389     1945976 : int32_t Plane::calc_altitude_error_cm(void)
     390             : {
     391             : #if AP_TERRAIN_AVAILABLE
     392             :     float terrain_height;
     393     2232335 :     if (target_altitude.terrain_following && 
     394      286359 :         terrain.height_above_terrain(terrain_height, true)) {
     395      286359 :         return target_altitude.lookahead*100 + target_altitude.terrain_alt_cm - (terrain_height*100);
     396             :     }
     397             : #endif
     398     1659617 :     return target_altitude.amsl_cm - adjusted_altitude_cm();
     399             : }
     400             : 
     401             : /*
     402             :   check for cruise_alt_floor and fence min/max altitude
     403             :  */
     404      195631 : void Plane::check_fbwb_altitude(void)
     405             : {
     406      195631 :     float max_alt_cm = 0.0;
     407      195631 :     float min_alt_cm = 0.0;
     408      195631 :     bool should_check_max = false;
     409      195631 :     bool should_check_min = false;
     410             : 
     411             : #if AP_FENCE_ENABLED
     412             :     // taking fence max and min altitude (with margin)
     413      195631 :     const uint8_t enabled_fences = plane.fence.get_enabled_fences();
     414      195631 :     if ((enabled_fences & AC_FENCE_TYPE_ALT_MIN) != 0) {
     415         493 :         min_alt_cm = plane.fence.get_relative_safe_alt_min_m()*100.0;
     416         493 :         should_check_min = true;
     417             :     }
     418      195631 :     if ((enabled_fences & AC_FENCE_TYPE_ALT_MAX) != 0) {
     419           0 :         max_alt_cm = plane.fence.get_relative_safe_alt_max_m()*100.0;
     420           0 :         should_check_max = true;
     421             :     }
     422             : #endif
     423             : 
     424      195631 :     if (g.cruise_alt_floor > 0) {
     425             :         // FBWB min altitude exists
     426           0 :         min_alt_cm = MAX(min_alt_cm, plane.g.cruise_alt_floor*100.0);
     427           0 :         should_check_min = true;
     428             :     }
     429             : 
     430      195631 :     if (!should_check_min && !should_check_max) {
     431      195138 :         return;
     432             :     }
     433             : 
     434             : //check if terrain following (min and max)
     435             : #if AP_TERRAIN_AVAILABLE
     436         493 :     if (target_altitude.terrain_following) {
     437             :         // set our target terrain height to be at least the min set
     438           0 :         if (should_check_max) {
     439           0 :             target_altitude.terrain_alt_cm = MIN(target_altitude.terrain_alt_cm, max_alt_cm);
     440             :         }
     441           0 :         if (should_check_min) {
     442           0 :             target_altitude.terrain_alt_cm = MAX(target_altitude.terrain_alt_cm, min_alt_cm);
     443             :         }
     444           0 :         return;
     445             :     }
     446             : #endif
     447             : 
     448         493 :     if (should_check_max) {
     449           0 :         target_altitude.amsl_cm = MIN(target_altitude.amsl_cm, home.alt + max_alt_cm);
     450             :     }
     451         493 :     if (should_check_min) {
     452         493 :         target_altitude.amsl_cm = MAX(target_altitude.amsl_cm, home.alt + min_alt_cm);
     453             :     }
     454             : }
     455             : 
     456             : /*
     457             :   reset the altitude offset used for altitude slopes
     458             :  */
     459       16567 : void Plane::reset_offset_altitude(void)
     460             : {
     461       16567 :     target_altitude.offset_cm = 0;
     462       16567 : }
     463             : 
     464             : 
     465             : /*
     466             :   reset the altitude offset used for slopes, based on difference between
     467             :   altitude at a destination and a specified start altitude. If destination is
     468             :   above the starting altitude then the result is positive.
     469             :  */
     470        4016 : void Plane::set_offset_altitude_location(const Location &start_loc, const Location &destination_loc)
     471             : {
     472        4016 :     ftype alt_difference_m = 0;
     473        4016 :     if (destination_loc.get_height_above(start_loc, alt_difference_m)) {
     474        3997 :         target_altitude.offset_cm = alt_difference_m * 100;
     475             :     } else {
     476          19 :         target_altitude.offset_cm = 0;
     477             :     }
     478             : 
     479             : #if AP_TERRAIN_AVAILABLE
     480             :     /*
     481             :       if this location has the terrain_alt flag set and we know the
     482             :       terrain altitude of our current location then treat it as a
     483             :       terrain altitude
     484             :      */
     485             :     float height;
     486        8155 :     if (destination_loc.terrain_alt && 
     487        4097 :         target_altitude.terrain_following &&
     488          81 :         terrain.height_above_terrain(height, true)) {
     489          81 :         target_altitude.offset_cm = target_altitude.terrain_alt_cm - (height * 100);
     490             :     }
     491             : #endif
     492             : 
     493        4016 :     if (flight_stage != AP_FixedWing::FlightStage::LAND) {
     494             :         // if we are within ALT_SLOPE_MIN meters of the target altitude then
     495             :         // reset the offset to not use an altitude slope. This allows for more
     496             :         // accurate flight of missions where the aircraft may lose or gain a bit
     497             :         // of altitude near waypoint turn points due to local terrain changes
     498        7810 :         if (g.alt_slope_min <= 0 ||
     499        3905 :             labs(target_altitude.offset_cm)*0.01f < g.alt_slope_min) {
     500        1180 :             target_altitude.offset_cm = 0;
     501             :         }
     502             :     }
     503        4016 : }
     504             : 
     505             : /*
     506             :   return true if current_loc is above loc. Used for altitude slope
     507             :   calculations.
     508             : 
     509             :   "above" is simple if we are not terrain following, as it just means
     510             :   the pressure altitude of one is above the other.
     511             : 
     512             :   When in terrain following mode "above" means the over-the-terrain
     513             :   current altitude is above the over-the-terrain alt of loc. It is
     514             :   quite possible for current_loc to be "above" loc when it is at a
     515             :   lower pressure altitude, if current_loc is in a low part of the
     516             :   terrain
     517             :  */
     518        4126 : bool Plane::above_location_current(const Location &loc)
     519             : {
     520             : #if AP_TERRAIN_AVAILABLE
     521             :     float terrain_alt;
     522        4266 :     if (loc.terrain_alt && 
     523         140 :         terrain.height_above_terrain(terrain_alt, true)) {
     524         140 :         float loc_alt = loc.alt*0.01f;
     525         140 :         if (!loc.relative_alt) {
     526           0 :             loc_alt -= home.alt*0.01f;
     527             :         }
     528         140 :         return terrain_alt > loc_alt;
     529             :     }
     530             : #endif
     531             : 
     532        3986 :     float loc_alt_cm = loc.alt;
     533        3986 :     if (loc.relative_alt) {
     534          12 :         loc_alt_cm += home.alt;
     535             :     }
     536        3986 :     return current_loc.alt > loc_alt_cm;
     537             : }
     538             : 
     539             : /*
     540             :   modify a destination to be setup for terrain following if
     541             :   TERRAIN_FOLLOW is enabled
     542             :  */
     543         133 : void Plane::setup_terrain_target_alt(Location &loc)
     544             : {
     545             : #if AP_TERRAIN_AVAILABLE
     546         133 :     if (terrain_enabled_in_current_mode()) {
     547           4 :         if (!loc.change_alt_frame(Location::AltFrame::ABOVE_TERRAIN)) {
     548           0 :             target_altitude.terrain_following_pending = true;
     549           0 :             return;
     550             :         }
     551             :     }
     552         133 :     target_altitude.terrain_following_pending = false;
     553             : #endif
     554             : }
     555             : 
     556             : /*
     557             :   return current_loc.alt adjusted for ALT_OFFSET
     558             :   This is useful during long flights to account for barometer changes
     559             :   from the GCS, or to adjust the flying height of a long mission
     560             :  */
     561     2792490 : int32_t Plane::adjusted_altitude_cm(void)
     562             : {
     563     2792490 :     return current_loc.alt - (mission_alt_offset()*100);
     564             : }
     565             : 
     566             : /*
     567             :   return home-relative altitude adjusted for ALT_OFFSET. This is useful
     568             :   during long flights to account for barometer changes from the GCS,
     569             :   or to adjust the flying height of a long mission.
     570             :  */
     571       83762 : int32_t Plane::adjusted_relative_altitude_cm(void)
     572             : {
     573       83762 :     return (relative_altitude - mission_alt_offset())*100;
     574             : }
     575             : 
     576             : 
     577             : /*
     578             :   return the mission altitude offset. This raises or lowers all
     579             :   mission items. It is primarily set using the ALT_OFFSET parameter,
     580             :   but can also be adjusted by the rangefinder landing code for a
     581             :   NAV_LAND command if we have aborted a steep landing
     582             :  */
     583     3130607 : float Plane::mission_alt_offset(void)
     584             : {
     585     3130607 :     float ret = g.alt_offset;
     586     3130607 :     if (control_mode == &mode_auto &&
     587     1830719 :             (flight_stage == AP_FixedWing::FlightStage::LAND || auto_state.wp_is_land_approach)) {
     588             :         // when landing after an aborted landing due to too high glide
     589             :         // slope we use an offset from the last landing attempt
     590      268863 :         ret += landing.alt_offset;
     591             :     }
     592     3130607 :     return ret;
     593             : }
     594             : 
     595        3684 : void Plane::reset_alt_offset(bool force)
     596             : {
     597        3684 :     if (!force && !flight_option_enabled(FlightOptions::RESET_ALT_OFFSET)) {
     598        2987 :         return;
     599             :     }
     600             : 
     601         697 :     if (g.alt_offset == 0) {
     602         689 :         return;
     603             :     }
     604             : 
     605           8 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Reset ALT_OFFSET");
     606           8 :     g.alt_offset.set(0);
     607             : }
     608             : 
     609             : /*
     610             :   return the height in meters above the next_WP_loc altitude
     611             :  */
     612     1156434 : float Plane::height_above_target(void)
     613             : {
     614     1156434 :     float target_alt = next_WP_loc.alt*0.01;
     615     1156434 :     if (!next_WP_loc.relative_alt) {
     616     1132873 :         target_alt -= ahrs.get_home().alt*0.01f;
     617             :     }
     618             : 
     619             : #if AP_TERRAIN_AVAILABLE
     620             :     // also record the terrain altitude if possible
     621             :     float terrain_altitude;
     622     1179995 :     if (next_WP_loc.terrain_alt && 
     623       23561 :         terrain.height_above_terrain(terrain_altitude, true)) {
     624       23561 :         return terrain_altitude - target_alt;
     625             :     }
     626             : #endif
     627             : 
     628     1132873 :     return (adjusted_altitude_cm()*0.01f - ahrs.get_home().alt*0.01f) - target_alt;
     629             : }
     630             : 
     631             : /*
     632             :   work out target altitude adjustment from terrain lookahead
     633             :  */
     634       59538 : float Plane::lookahead_adjustment(void)
     635             : {
     636             : #if AP_TERRAIN_AVAILABLE
     637             :     int32_t bearing_cd;
     638             :     int16_t distance;
     639             :     // work out distance and bearing to target
     640       59538 :     if (control_mode == &mode_fbwb) {
     641             :         // there is no target waypoint in FBWB, so use yaw as an approximation
     642           0 :         bearing_cd = ahrs.yaw_sensor;
     643           0 :         distance = g.terrain_lookahead;
     644       59538 :     } else if (!reached_loiter_target()) {
     645       48615 :         bearing_cd = nav_controller->target_bearing_cd();
     646       48615 :         distance = constrain_float(auto_state.wp_distance, 0, g.terrain_lookahead);
     647             :     } else {
     648             :         // no lookahead when loitering
     649       10923 :         bearing_cd = 0;
     650       10923 :         distance = 0;
     651             :     }
     652       59538 :     if (distance <= 0) {
     653             :         // no lookahead
     654       11850 :         return 0;
     655             :     }
     656             : 
     657             :     
     658       47688 :     float groundspeed = ahrs.groundspeed();
     659       47688 :     if (groundspeed < 1) {
     660             :         // we're not moving
     661         170 :         return 0;
     662             :     }
     663             :     // we need to know the climb ratio. We use 50% of the maximum
     664             :     // climb rate so we are not constantly at 100% throttle and to
     665             :     // give a bit more margin on terrain
     666       47518 :     float climb_ratio = 0.5f * TECS_controller.get_max_climbrate() / groundspeed;
     667             : 
     668       47518 :     if (climb_ratio <= 0) {
     669             :         // lookahead makes no sense for negative climb rates
     670           0 :         return 0;
     671             :     }
     672             :     
     673             :     // ask the terrain code for the lookahead altitude change
     674       47518 :     float lookahead = terrain.lookahead(bearing_cd*0.01f, distance, climb_ratio);
     675             :     
     676       47518 :     if (target_altitude.offset_cm < 0) {
     677             :         // we are heading down to the waypoint, so we don't need to
     678             :         // climb as much
     679       11951 :         lookahead += target_altitude.offset_cm*0.01f;
     680             :     }
     681             : 
     682             :     // constrain lookahead to a reasonable limit
     683       47518 :     return constrain_float(lookahead, 0, 1000.0f);
     684             : #else
     685             :     return 0;
     686             : #endif
     687             : }
     688             : 
     689             : 
     690             : #if AP_RANGEFINDER_ENABLED
     691             : /*
     692             :   correct target altitude using rangefinder data. Returns offset in
     693             :   meters to correct target altitude. A positive number means we need
     694             :   to ask the speed/height controller to fly higher
     695             :  */
     696      388885 : float Plane::rangefinder_correction(void)
     697             : {
     698      388885 :     if (millis() - rangefinder_state.last_correction_time_ms > 5000) {
     699             :         // we haven't had any rangefinder data for 5s - don't use it
     700      382198 :         return 0;
     701             :     }
     702             : 
     703             :     // for now we only support the rangefinder for landing
     704        6687 :     bool using_rangefinder = (rangefinder_use(RangeFinderUse::TAKEOFF_LANDING) &&
     705        6687 :                               flight_stage == AP_FixedWing::FlightStage::LAND &&
     706           0 :                               rangefinder_state.in_use);
     707        6687 :     if (!using_rangefinder) {
     708        6687 :         return 0;
     709             :     }
     710             : 
     711           0 :     return rangefinder_state.correction;
     712             : }
     713             : 
     714             : /*
     715             :   correct rangefinder data for terrain height difference between
     716             :   NAV_LAND point and current location
     717             :  */
     718       53630 : void Plane::rangefinder_terrain_correction(float &height)
     719             : {
     720             : #if AP_TERRAIN_AVAILABLE
     721       53630 :     if (!rangefinder_use(RangeFinderUse::TAKEOFF_LANDING) ||
     722       53630 :         flight_stage != AP_FixedWing::FlightStage::LAND ||
     723           0 :         !terrain_enabled_in_current_mode()) {
     724       53630 :         return;
     725             :     }
     726             :     float terrain_amsl1, terrain_amsl2;
     727           0 :     if (!terrain.height_amsl(current_loc, terrain_amsl1) ||
     728           0 :         !terrain.height_amsl(next_WP_loc, terrain_amsl2)) {
     729           0 :         return;
     730             :     }
     731           0 :     float correction = (terrain_amsl1 - terrain_amsl2);
     732           0 :     height += correction;
     733           0 :     auto_state.terrain_correction = correction;
     734             : #endif
     735             : }
     736             : 
     737             : /*
     738             :   update the offset between rangefinder height and terrain height
     739             :  */
     740     2736341 : void Plane::rangefinder_height_update(void)
     741             : {
     742     2736341 :     const auto orientation = rangefinder_orientation();
     743     2736341 :     bool range_ok = rangefinder.status_orient(orientation) == RangeFinder::Status::Good;
     744     2736341 :     float distance = rangefinder.distance_orient(orientation);
     745     2736341 :     float corrected_distance = distance;
     746             : 
     747             :     /*
     748             :       correct distance for attitude
     749             :      */
     750     2736341 :     if (range_ok) {
     751             :         // correct the range for attitude
     752       56395 :         const auto &dcm = ahrs.get_rotation_body_to_ned();
     753             : 
     754       56395 :         Vector3f v{corrected_distance, 0, 0};
     755       56395 :         v.rotate(orientation);
     756       56395 :         v = dcm * v;
     757             : 
     758       56395 :         if (!is_positive(v.z)) {
     759             :             // not pointing at the ground
     760           5 :             range_ok = false;
     761             :         } else {
     762       56390 :             corrected_distance = v.z;
     763             :         }
     764             :     }
     765             : 
     766     2736341 :     if (range_ok && ahrs.home_is_set()) {
     767       53630 :         if (!rangefinder_state.have_initial_reading) {
     768           9 :             rangefinder_state.have_initial_reading = true;
     769           9 :             rangefinder_state.initial_range = distance;
     770             :         }
     771       53630 :         rangefinder_state.height_estimate = corrected_distance;
     772             : 
     773       53630 :         rangefinder_terrain_correction(rangefinder_state.height_estimate);
     774             : 
     775             :         // we consider ourselves to be fully in range when we have 10
     776             :         // good samples (0.2s) that are different by 5% of the maximum
     777             :         // range from the initial range we see. The 5% change is to
     778             :         // catch Lidars that are giving a constant range, either due
     779             :         // to misconfiguration or a faulty sensor
     780       53630 :         if (rangefinder_state.in_range_count < 10) {
     781        6472 :             if (!is_equal(distance, rangefinder_state.last_distance) &&
     782        1150 :                 fabsf(rangefinder_state.initial_range - distance) > 0.05f * rangefinder.max_distance_orient(rangefinder_orientation())) {
     783         320 :                 rangefinder_state.in_range_count++;
     784             :             }
     785        5322 :             if (fabsf(rangefinder_state.last_distance - distance) > rangefinder.max_distance_orient(rangefinder_orientation())*0.2) {
     786             :                 // changes by more than 20% of full range will reset counter
     787           2 :                 rangefinder_state.in_range_count = 0;
     788             :             }
     789             :         } else {
     790       48308 :             rangefinder_state.in_range = true;
     791       48308 :             bool flightstage_good_for_rangefinder_landing = false;
     792       48308 :             if (flight_stage == AP_FixedWing::FlightStage::LAND) {
     793         592 :                 flightstage_good_for_rangefinder_landing = true;
     794             :             }
     795             : #if HAL_QUADPLANE_ENABLED
     796      142413 :             if (control_mode == &mode_qland ||
     797       91294 :                 control_mode == &mode_qrtl ||
     798       42986 :                 (control_mode == &mode_auto && quadplane.is_vtol_land(plane.mission.get_current_nav_cmd().id))) {
     799       14341 :                 flightstage_good_for_rangefinder_landing = true;
     800             :             }
     801             : #endif
     802             : 
     803             :             // Check if the aircraft is within RNGFND_LND_DIST meters from the
     804             :             // landing point to engage it
     805       48308 :             const int16_t land_engage_dist_m = g2.rangefinder_land_engage_dist_m;
     806       48308 :             const bool is_within_engagement_distance =
     807       48308 :                 (land_engage_dist_m <= 0) ||
     808           0 :                 (auto_state.wp_distance <= land_engage_dist_m);
     809             : 
     810       40664 :             if (!rangefinder_state.in_use &&
     811        7289 :                 flightstage_good_for_rangefinder_landing &&
     812       96261 :                 rangefinder_use(RangeFinderUse::TAKEOFF_LANDING) &&
     813             :                 is_within_engagement_distance) {
     814           3 :                 rangefinder_state.in_use = true;
     815           3 :                 gcs().send_text(MAV_SEVERITY_INFO, "Rangefinder engaged at %.2fm", (double)rangefinder_state.height_estimate);
     816             :             }
     817             :         }
     818       53630 :         rangefinder_state.last_distance = distance;
     819             :     } else {
     820     2682711 :         rangefinder_state.in_range_count = 0;
     821     2682711 :         rangefinder_state.in_range = false;
     822             :     }
     823             : 
     824     2736341 :     if (rangefinder_state.in_range) {
     825             :         // If not using terrain data, we expect zero correction when our height above target is equal to our rangefinder measurement
     826       48308 :         float correction = height_above_target() - rangefinder_state.height_estimate;
     827             : 
     828             : #if AP_TERRAIN_AVAILABLE
     829             :         // if we are terrain following then correction is based on terrain data
     830             :         float terrain_altitude;
     831       84047 :         if ((target_altitude.terrain_following || terrain_enabled_in_current_mode()) && 
     832       35739 :             terrain.height_above_terrain(terrain_altitude, true)) {
     833       35739 :             correction = terrain_altitude - rangefinder_state.height_estimate;
     834             :         }
     835             : #endif    
     836             : 
     837             :         // remember the last correction. Use a low pass filter unless
     838             :         // the old data is more than 5 seconds old
     839       48308 :         uint32_t now = millis();
     840       48308 :         if (now - rangefinder_state.last_correction_time_ms > 5000) {
     841          24 :             rangefinder_state.correction = correction;
     842          24 :             rangefinder_state.initial_correction = correction;
     843          24 :             if (rangefinder_use(RangeFinderUse::TAKEOFF_LANDING)) {
     844           7 :                 landing.set_initial_slope();
     845             :             }
     846          24 :             rangefinder_state.last_correction_time_ms = now;
     847             :         } else {
     848       48284 :             rangefinder_state.correction = 0.8f*rangefinder_state.correction + 0.2f*correction;
     849       48284 :             rangefinder_state.last_correction_time_ms = now;
     850       48284 :             if (fabsf(rangefinder_state.correction - rangefinder_state.initial_correction) > 30) {
     851             :                 // the correction has changed by more than 30m, reset use of Lidar. We may have a bad lidar
     852           1 :                 if (rangefinder_state.in_use) {
     853           0 :                     gcs().send_text(MAV_SEVERITY_INFO, "Rangefinder disengaged at %.2fm", (double)rangefinder_state.height_estimate);
     854             :                 }
     855           1 :                 memset(&rangefinder_state, 0, sizeof(rangefinder_state));
     856             :             }
     857             :         }
     858             :         
     859             :     }
     860     2736341 : }
     861             : #endif  // AP_RANGEFINDER_ENABLED
     862             : 
     863             : /*
     864             :   determine if Non Auto Terrain Disable is active and allowed in present control mode
     865             :  */
     866         941 : bool Plane::terrain_disabled()
     867             : {
     868         941 :     return control_mode->allows_terrain_disable() && non_auto_terrain_disable;
     869             : }
     870             : 
     871             : 
     872             : /*
     873             :   Check if terrain following is enabled for the current mode
     874             :  */
     875             : #if AP_TERRAIN_AVAILABLE
     876             : const Plane::TerrainLookupTable Plane::Terrain_lookup[] = {
     877             :     {Mode::Number::FLY_BY_WIRE_B, terrain_bitmask::FLY_BY_WIRE_B},
     878             :     {Mode::Number::CRUISE, terrain_bitmask::CRUISE},
     879             :     {Mode::Number::AUTO, terrain_bitmask::AUTO},
     880             :     {Mode::Number::RTL, terrain_bitmask::RTL},
     881             :     {Mode::Number::AVOID_ADSB, terrain_bitmask::AVOID_ADSB},
     882             :     {Mode::Number::GUIDED, terrain_bitmask::GUIDED},
     883             :     {Mode::Number::LOITER, terrain_bitmask::LOITER},
     884             :     {Mode::Number::CIRCLE, terrain_bitmask::CIRCLE},
     885             : #if HAL_QUADPLANE_ENABLED
     886             :     {Mode::Number::QRTL, terrain_bitmask::QRTL},
     887             :     {Mode::Number::QLAND, terrain_bitmask::QLAND},
     888             :     {Mode::Number::QLOITER, terrain_bitmask::QLOITER},
     889             : #endif
     890             : #if MODE_AUTOLAND_ENABLED
     891             :     {Mode::Number::AUTOLAND, terrain_bitmask::AUTOLAND},
     892             : #endif
     893             : };
     894             : 
     895       32998 : bool Plane::terrain_enabled_in_current_mode() const
     896             : {
     897       32998 :     return terrain_enabled_in_mode(control_mode->mode_number());
     898             : }
     899             : 
     900       33010 : bool Plane::terrain_enabled_in_mode(Mode::Number num) const
     901             : {
     902             :     // Global enable
     903       33010 :     if ((g.terrain_follow.get() & int32_t(terrain_bitmask::ALL)) != 0) {
     904       12182 :         return true;
     905             :     }
     906             : 
     907             :     // Specific enable
     908       94396 :     for (const struct TerrainLookupTable entry : Terrain_lookup) {
     909       94394 :         if (entry.mode_num == num) {
     910       20826 :             if ((g.terrain_follow.get() & int32_t(entry.bitmask)) != 0) {
     911           1 :                 return true;
     912             :             }
     913       20825 :             break;
     914             :         }
     915             :     }
     916             : 
     917       20827 :     return false;
     918             : }
     919             : #endif
     920             : 
     921             : #if AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     922             : /*
     923             :   handle a MAV_CMD_SET_HAGL request. The accuracy is ignored
     924             :  */
     925           0 : void Plane::handle_external_hagl(const mavlink_command_int_t &packet)
     926             : {
     927           0 :     auto &hagl = plane.external_hagl;
     928           0 :     hagl.hagl = packet.param1;
     929           0 :     hagl.last_update_ms = AP_HAL::millis();
     930           0 :     hagl.timeout_ms = uint32_t(packet.param3 * 1000);
     931           0 : }
     932             : 
     933             : /*
     934             :   get HAGL from external source if current
     935             :  */
     936     3191008 : bool Plane::get_external_HAGL(float &height_agl)
     937             : {
     938     3191008 :     auto &hagl = plane.external_hagl;
     939     3191008 :     if (hagl.last_update_ms != 0) {
     940           0 :         const uint32_t now_ms = AP_HAL::millis();
     941           0 :         if (now_ms - hagl.last_update_ms <= hagl.timeout_ms) {
     942           0 :             height_agl = hagl.hagl;
     943           0 :             return true;
     944             :         }
     945           0 :         hagl.last_update_ms = 0;
     946             :     }
     947     3191008 :     return false;
     948             : }
     949             : #endif // AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     950             : 
     951             : /*
     952             :   get height for landing. Set using_rangefinder to true if a rangefinder
     953             :   or external HAGL source is active
     954             :  */
     955       38886 : float Plane::get_landing_height(bool &rangefinder_active)
     956             : {
     957             :     float height;
     958             : 
     959             : #if AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     960             :     // if external HAGL is active use that
     961       38886 :     if (get_external_HAGL(height)) {
     962             :         // ensure no terrain correction is applied - that is the job
     963             :         // of the external system if it is wanted
     964           0 :         auto_state.terrain_correction = 0;
     965             : 
     966             :         // an external HAGL is considered to be a type of rangefinder
     967           0 :         rangefinder_active = true;
     968           0 :         return height;
     969             :     }
     970             : #endif
     971             : 
     972             :     // get basic height above target
     973       38886 :     height = height_above_target();
     974       38886 :     rangefinder_active = false;
     975             : 
     976             : #if AP_RANGEFINDER_ENABLED
     977             :     // possibly correct with rangefinder
     978       38886 :     height -= rangefinder_correction();
     979       38886 :     rangefinder_active = rangefinder_use(RangeFinderUse::TAKEOFF_LANDING) &&
     980       38886 :                          rangefinder_state.in_use && rangefinder_state.in_range;
     981             : #endif
     982             : 
     983       38886 :     return height;
     984             : }
     985             : 
     986             : /*
     987             :   if a terrain location doesn't have the relative_alt flag set
     988             :   then fix the alt and trigger a flow of control error
     989             :  */
     990        8074 : void Plane::fix_terrain_WP(Location &loc, uint32_t linenum)
     991             : {
     992        8074 :     if (loc.terrain_alt && !loc.relative_alt) {
     993           0 :         AP::internalerror().error(AP_InternalError::error_t::flow_of_control, linenum);
     994             :         /*
     995             :           we definitely have a bug, now we need to guess what was
     996             :           really meant. The lack of the relative_alt flag notionally
     997             :           means that home.alt has been added to loc.alt, so remove it,
     998             :           but only if it doesn't lead to a negative terrain altitude
     999             :          */
    1000           0 :         if (loc.alt - home.alt > -500) {
    1001           0 :             loc.alt -= home.alt;
    1002             :         }
    1003           0 :         loc.relative_alt = true;
    1004             :     }
    1005        8074 : }

Generated by: LCOV version 1.14