LCOV - code coverage report
Current view: top level - AP_Terrain - AP_Terrain.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 194 214 90.7 %
Date: 2026-09-09 11:02:08 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include "AP_Terrain.h"
      17             : 
      18             : #if AP_TERRAIN_AVAILABLE
      19             : 
      20             : #include <AP_HAL/AP_HAL.h>
      21             : #include <AP_Common/AP_Common.h>
      22             : #include <AP_Math/AP_Math.h>
      23             : #include <GCS_MAVLink/GCS_MAVLink.h>
      24             : #include <GCS_MAVLink/GCS.h>
      25             : #include <AP_Logger/AP_Logger.h>
      26             : #include <AP_AHRS/AP_AHRS.h>
      27             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      28             : #include <AP_Filesystem/AP_Filesystem.h>
      29             : #include <AP_Rally/AP_Rally.h>
      30             : 
      31             : extern const AP_HAL::HAL& hal;
      32             : 
      33             : AP_Terrain *AP_Terrain::singleton;
      34             : 
      35             : #if APM_BUILD_TYPE(APM_BUILD_ArduSub)
      36             : #define TERRAIN_ENABLE_DEFAULT 0
      37             : #else
      38             : #define TERRAIN_ENABLE_DEFAULT 1
      39             : #endif
      40             : 
      41             : // table of user settable parameters
      42             : const AP_Param::GroupInfo AP_Terrain::var_info[] = {
      43             :     // @Param: ENABLE
      44             :     // @DisplayName: Terrain data enable
      45             :     // @Description: enable terrain data. This enables the vehicle storing a database of terrain data on the SD card. The terrain data is requested from the ground station as needed, and stored for later use on the SD card. To be useful the ground station must support TERRAIN_REQUEST messages and have access to a terrain database, such as the SRTM database.
      46             :     // @Values: 0:Disable,1:Enable
      47             :     // @User: Advanced
      48             :     AP_GROUPINFO_FLAGS("ENABLE", 0, AP_Terrain, enable, TERRAIN_ENABLE_DEFAULT, AP_PARAM_FLAG_ENABLE),
      49             : 
      50             :     // @Param: SPACING
      51             :     // @DisplayName: Terrain grid spacing
      52             :     // @Description: Distance between terrain grid points in meters. This controls the horizontal resolution of the terrain data that is stored on te SD card and requested from the ground station. If your GCS is using the ArduPilot SRTM database like Mission Planner or MAVProxy, then a resolution of 100 meters is appropriate. Grid spacings lower than 100 meters waste SD card space if the GCS cannot provide that resolution. The grid spacing also controls how much data is kept in memory during flight. A larger grid spacing will allow for a larger amount of data in memory. A grid spacing of 100 meters results in the vehicle keeping 12 grid squares in memory with each grid square having a size of 2.7 kilometers by 3.2 kilometers. Any additional grid squares are stored on the SD once they are fetched from the GCS and will be loaded as needed.
      53             :     // @Units: m
      54             :     // @Increment: 1
      55             :     // @User: Advanced
      56             :     AP_GROUPINFO("SPACING",   1, AP_Terrain, grid_spacing, 100),
      57             : 
      58             :     // @Param: OPTIONS
      59             :     // @DisplayName: Terrain options
      60             :     // @Description: Options to change behavior of terrain system. The Accept Old Terrain Data option can be used to accept terrain data generated from before the terrain database bugs were fixed. The bugs in the data were all fixed from 24th February 2026. If you really want to risk using the old terrain data then you can set this option, otherwise remove the old terrain data by formatting your microSD card or renaming the /APM/TERRAIN folder. Then downloaded updated data from https://terrain.ardupilot.club, or let the automatic terrain download repopulate your terrain data.
      61             :     // @Bitmask: 0:Disable Download,1:Disable Disk,2:Accept Old Terrain Data
      62             :     // @User: Advanced
      63             :     AP_GROUPINFO("OPTIONS",   2, AP_Terrain, options, 0),
      64             : 
      65             :     // @Param: MARGIN
      66             :     // @DisplayName: Acceptance margin
      67             :     // @Description: Margin in centi-meters to accept terrain data from the GCS. This can be used to allow older terrain data generated with less accurate latitude/longitude scaling to be used
      68             :     // @Units: m
      69             :     // @Range: 0.05 50000
      70             :     // @User: Advanced
      71             :     AP_GROUPINFO("MARGIN",   3, AP_Terrain, margin, 0.05),
      72             : 
      73             :     // @Param: OFS_MAX
      74             :     // @DisplayName: Terrain reference offset maximum
      75             :     // @Description: The maximum adjustment of terrain altitude based on the assumption that the vehicle is on the ground when it is armed. When the vehicle is armed the location of the vehicle is recorded, and when terrain data is available for that location a height adjustment for terrain data is calculated that aligns the terrain height at that location with the altitude recorded at arming. This height adjustment is applied to all terrain data. This parameter clamps the amount of adjustment. A value of zero disables the use of terrain height adjustment.
      76             :     // @Units: m
      77             :     // @Range: 0 50
      78             :     // @User: Advanced
      79             :     AP_GROUPINFO("OFS_MAX",  4, AP_Terrain, offset_max, 30),
      80             : 
      81             :     // @Param: CACHE_SZ
      82             :     // @DisplayName: Terrain cache size
      83             :     // @Description: The number of 32x28 cache blocks to keep in memory. Each block uses about 1800 bytes of memory
      84             :     // @Range: 0 128
      85             :     // @User: Advanced
      86             :     AP_GROUPINFO("CACHE_SZ",  5, AP_Terrain, config_cache_size, TERRAIN_GRID_BLOCK_CACHE_SIZE),
      87             : 
      88             :     AP_GROUPEND
      89             : };
      90             : 
      91             : // constructor
      92        1778 : AP_Terrain::AP_Terrain() :
      93        1778 :     disk_io_state(DiskIoIdle),
      94        1778 :     fd(-1)
      95             : {
      96        1778 :     AP_Param::setup_object_defaults(this, var_info);
      97             : 
      98             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      99        1777 :     if (singleton != nullptr) {
     100           0 :         AP_HAL::panic("Terrain must be singleton");
     101             :     }
     102             : #endif
     103        1778 :     singleton = this;
     104        1778 : }
     105             : 
     106             : /*
     107             :   return terrain height in meters above average sea level (WGS84) for
     108             :   a given position
     109             : 
     110             :   This is the base function that other height calculations are derived
     111             :   from. The functions below are more convenient for most uses
     112             : 
     113             :   This function costs about 20 microseconds on Pixhawk
     114             :  */
     115  1359994592 : bool AP_Terrain::height_amsl(const Location &loc, float &height, bool corrected)
     116             : {
     117  1359994592 :     if (!allocate()) {
     118    10319891 :         return false;
     119             :     }
     120             : 
     121  1349674547 :     const AP_AHRS &ahrs = AP::ahrs();
     122             : 
     123             :     // quick access for home altitude
     124  1349674442 :     if (loc.lat == home_loc.lat &&
     125   383210685 :         loc.lng == home_loc.lng) {
     126   342349050 :         height = home_height;
     127   342349050 :         if (corrected && have_reference_offset) {
     128     2116194 :             height += reference_offset;
     129             :         }
     130   342349050 :         return true;
     131             :     }
     132             : 
     133             :     struct grid_info info;
     134             : 
     135  1007325392 :     calculate_grid_info(loc, info);
     136             : 
     137             :     // find the grid
     138  1007324413 :     const struct grid_block &grid = find_grid_cache(info).grid;
     139             : 
     140             :     /*
     141             :       note that we rely on the one square overlap to ensure these
     142             :       calculations don't go past the end of the arrays
     143             :      */
     144             :     ASSERT_RANGE(info.idx_x, 0, TERRAIN_GRID_BLOCK_SIZE_X-2);
     145             :     ASSERT_RANGE(info.idx_y, 0, TERRAIN_GRID_BLOCK_SIZE_Y-2);
     146             : 
     147             : 
     148             :     // check we have all 4 required heights
     149  1007324170 :     if (!check_bitmap(grid, info.idx_x,   info.idx_y) ||
     150   926149768 :         !check_bitmap(grid, info.idx_x,   info.idx_y+1) ||
     151  2859623652 :         !check_bitmap(grid, info.idx_x+1, info.idx_y) ||
     152   926149803 :         !check_bitmap(grid, info.idx_x+1, info.idx_y+1)) {
     153    81174291 :         return false;
     154             :     }
     155             : 
     156             :     // hXY are the heights of the 4 surrounding grid points
     157   926149697 :     const auto h00 = grid.height[info.idx_x+0][info.idx_y+0];
     158   926149697 :     const auto h01 = grid.height[info.idx_x+0][info.idx_y+1];
     159   926149697 :     const auto h10 = grid.height[info.idx_x+1][info.idx_y+0];
     160   926149697 :     const auto h11 = grid.height[info.idx_x+1][info.idx_y+1];
     161             : 
     162             :     // do a simple dual linear interpolation. We could do something
     163             :     // fancier, but it probably isn't worth it as long as the
     164             :     // grid_spacing is kept small enough
     165   926149697 :     const float avg1 = (1.0f-info.frac_x) * h00  + info.frac_x * h10;
     166   926149697 :     const float avg2 = (1.0f-info.frac_x) * h01  + info.frac_x * h11;
     167   926149697 :     const float avg  = (1.0f-info.frac_y) * avg1 + info.frac_y * avg2;
     168             : 
     169   926149697 :     height = avg;
     170             : 
     171   967014037 :     if (loc.lat == ahrs.get_home().lat &&
     172    40864334 :         loc.lng == ahrs.get_home().lng) {
     173             :         // remember home altitude as a special case
     174        1473 :         home_height = height;
     175        1473 :         home_loc = loc;
     176        1473 :         have_home_height = true;
     177             :     }
     178             : 
     179   926149799 :     if (corrected && have_reference_offset) {
     180    21549845 :         height += reference_offset;
     181             :     }
     182             :     
     183   926149799 :     return true;
     184             : }
     185             : 
     186             : 
     187             : /* 
     188             :    find difference between home terrain height and the terrain
     189             :    height at the current location in meters. A positive result
     190             :    means the terrain is higher than home.
     191             : 
     192             :    return false is terrain at the current location or at home
     193             :    location is not available
     194             : 
     195             :    If extrapolate is true then allow return of an extrapolated
     196             :    terrain altitude based on the last available data
     197             : */
     198       59538 : bool AP_Terrain::height_terrain_difference_home(float &terrain_difference, bool extrapolate)
     199             : {
     200       59538 :     const AP_AHRS &ahrs = AP::ahrs();
     201             : 
     202             :     float height_home, height_loc;
     203       59538 :     if (!height_amsl(ahrs.get_home(), height_home)) {
     204             :         // we don't know the height of home
     205           0 :         return false;
     206             :     }
     207             : 
     208       59538 :     Location loc;
     209       59538 :     if (!ahrs.get_location(loc)) {
     210             :         // we don't know where we are
     211           0 :         return false;
     212             :     }
     213             : 
     214       59538 :     if (!height_amsl(loc, height_loc)) {
     215           0 :         if (!extrapolate || !have_current_loc_height) {
     216             :             // we don't know the height of the given location
     217           0 :             return false;
     218             :         }
     219             :         // we don't have data at the current location, but the caller
     220             :         // has asked for extrapolation, so use the last available
     221             :         // terrain height. This can be used to fill in while new data
     222             :         // is fetched. It should be very rarely used
     223           0 :         height_loc = last_current_loc_height;
     224             :     }
     225             : 
     226       59538 :     terrain_difference = height_loc - height_home;
     227             : 
     228       59538 :     return true;
     229             : }
     230             : 
     231             : /* 
     232             :    return current height above terrain at current AHRS
     233             :    position. 
     234             :    
     235             :    If extrapolate is true then extrapolate from most recently
     236             :    available terrain data is terrain data is not available for the
     237             :    current location.
     238             :    
     239             :    Return true if height is available, otherwise false.
     240             : */
     241     6208143 : bool AP_Terrain::height_above_terrain(float &terrain_altitude, bool extrapolate)
     242             : {
     243     6208143 :     const AP_AHRS &ahrs = AP::ahrs();
     244             : 
     245     6208143 :     Location current_loc;
     246     6208143 :     if (!ahrs.get_location(current_loc)) {
     247             :         // we don't know where we are
     248      178654 :         return false;
     249             :     }
     250             : 
     251             :     float theight_loc;
     252     6029489 :     if (!height_amsl(current_loc, theight_loc)) {
     253      313099 :         if (!extrapolate || !have_current_loc_height) {
     254      286549 :             return false;
     255             :         }
     256             :         // we don't have data at the current location, but the caller
     257             :         // has asked for extrapolation, so use the last available
     258             :         // terrain height. This can be used to fill in while new data
     259             :         // is fetched. It should be very rarely used
     260       26550 :         theight_loc = last_current_loc_height;
     261             :     }
     262             : 
     263     5742940 :     int32_t height_amsl_cm = 0;
     264     5742940 :     UNUSED_RESULT(current_loc.get_alt_cm(Location::AltFrame::ABSOLUTE, height_amsl_cm));
     265             : 
     266     5742940 :     terrain_altitude = height_amsl_cm*0.01 - theight_loc;
     267     5742940 :     return true;
     268             : }
     269             : 
     270             : /* 
     271             :    return estimated equivalent relative-to-home altitude in meters
     272             :    of a given height above the terrain at the current location
     273             :        
     274             :    This function allows existing height controllers which work on
     275             :    barometric altitude (relative to home) to be used with terrain
     276             :    based target altitude, by translating the "above terrain" altitude
     277             :    into an equivalent barometric relative height.
     278             :    
     279             :    return false if terrain data is not available either at the given
     280             :    location or at the home location.  
     281             :    
     282             :    If extrapolate is true then allow return of an extrapolated
     283             :    terrain altitude based on the last available data
     284             : */
     285       59538 : bool AP_Terrain::height_relative_home_equivalent(float terrain_altitude, 
     286             :                                                  float &relative_home_altitude,
     287             :                                                  bool extrapolate)
     288             : {
     289             :     float terrain_difference;
     290       59538 :     if (!height_terrain_difference_home(terrain_difference, extrapolate)) {
     291           0 :         return false;
     292             :     }
     293       59538 :     relative_home_altitude = terrain_altitude + terrain_difference;
     294             : 
     295             :     /*
     296             :       adjust for height of home above terrain height at home
     297             :      */
     298       59538 :     const AP_AHRS &ahrs = AP::ahrs();
     299       59538 :     const auto &home = ahrs.get_home();
     300       59538 :     int32_t home_height_amsl_cm = 0;
     301       59538 :     UNUSED_RESULT(home.get_alt_cm(Location::AltFrame::ABSOLUTE, home_height_amsl_cm));
     302             : 
     303             :     float theight_home;
     304       59538 :     if (!height_amsl(home, theight_home)) {
     305           0 :         return false;
     306             :     }
     307             : 
     308       59538 :     relative_home_altitude += theight_home - home_height_amsl_cm*0.01;
     309             :     
     310       59538 :     return true;
     311             : }
     312             : 
     313             : 
     314             : /*
     315             :   calculate lookahead rise in terrain. This returns extra altitude
     316             :   needed to clear upcoming terrain in meters
     317             : */
     318       47518 : float AP_Terrain::lookahead(float bearing, float distance, float climb_ratio)
     319             : {
     320       47518 :     if (!allocate() || grid_spacing <= 0) {
     321           0 :         return 0;
     322             :     }
     323             : 
     324       47518 :     Location loc;
     325       47518 :     if (!AP::ahrs().get_location(loc)) {
     326             :         // we don't know where we are
     327           0 :         return 0;
     328             :     }
     329             :     float base_height;
     330       47518 :     if (!height_amsl(loc, base_height)) {
     331             :         // we don't know our current terrain height
     332           0 :         return 0;
     333             :     }
     334             : 
     335       47518 :     float climb = 0;
     336       47518 :     float lookahead_estimate = 0;
     337             : 
     338             :     // check for terrain at grid spacing intervals
     339      780087 :     while (distance > 0) {
     340      732569 :         loc.offset_bearing(bearing, grid_spacing);
     341      732569 :         climb += climb_ratio * grid_spacing;
     342      732569 :         distance -= grid_spacing;
     343             :         float height;
     344      732569 :         if (height_amsl(loc, height)) {
     345      732401 :             float rise = (height - base_height) - climb;
     346      732401 :             if (rise > lookahead_estimate) {
     347       38423 :                 lookahead_estimate = rise;
     348             :             }
     349             :         }
     350             :     }
     351             : 
     352       47518 :     return lookahead_estimate;
     353             : }
     354             : 
     355             : 
     356             : /*
     357             :   1hz update function. This is here to ensure progress is made on disk
     358             :   IO even if no MAVLink send_request() operations are called for a
     359             :   while.
     360             :  */
     361     1361075 : void AP_Terrain::update(void)
     362             : {
     363     1361075 :     if (!enable) { return; }
     364             :     // just schedule any needed disk IO
     365     1293011 :     schedule_disk_io();
     366             : 
     367     1293011 :     AP_AHRS &ahrs = AP::ahrs();
     368             : 
     369             :     // try to ensure the home location is populated
     370             :     float height;
     371     1293011 :     height_amsl(ahrs.get_home(), height);
     372             : 
     373             :     // update the cached current location height
     374     1293011 :     Location loc;
     375     1293011 :     bool pos_valid = ahrs.get_location(loc);
     376     1293011 :     bool terrain_valid = pos_valid && height_amsl(loc, height);
     377     1293011 :     if (pos_valid && terrain_valid) {
     378     1127469 :         last_current_loc_height = height;
     379     1127469 :         have_current_loc_height = true;
     380             : 
     381             :         // send terrain altitude to AHRS for optical flow when rangefinder is out of range
     382     1127469 :         ahrs.writeTerrainAMSL(height);
     383             :     }
     384             : 
     385             :     // check for pending mission data
     386     1293011 :     update_mission_data();
     387             : 
     388             : #if HAL_RALLY_ENABLED
     389             :     // check for pending rally data
     390     1293011 :     update_rally_data();
     391             : #endif
     392             : 
     393             :     // update tiles surrounding our current location:
     394     1293011 :     if (pos_valid) {
     395     1202374 :         have_surrounding_tiles = update_surrounding_tiles(loc);
     396             :     } else {
     397       90637 :         have_surrounding_tiles = false;
     398             :     }
     399             : 
     400             :     // update capabilities and status
     401     1293011 :     if (allocate()) {
     402     1293011 :         if (!pos_valid) {
     403             :             // we don't know where we are
     404       90637 :             system_status = TerrainStatusUnhealthy;
     405     1202374 :         } else if (!terrain_valid) {
     406             :             // we don't have terrain data at current location
     407       74905 :             system_status = TerrainStatusUnhealthy;
     408             :         } else {
     409     1127469 :             system_status = TerrainStatusOK;
     410             :         }
     411             :     } else {
     412           0 :         system_status = TerrainStatusDisabled;
     413             :     }
     414             : }
     415             : 
     416     1202374 : bool AP_Terrain::update_surrounding_tiles(const Location &loc)
     417             : {
     418             :     // also request a larger set of up to 9 grids
     419     1202374 :     bool ret = true;
     420     4809496 :     for (int8_t x=-1; x<=1; x++) {
     421    14428488 :         for (int8_t y=-1; y<=1; y++) {
     422    10821366 :             Location loc2 = loc;
     423    10821366 :             loc2.offset(x*TERRAIN_GRID_BLOCK_SIZE_X*0.7f*grid_spacing,
     424    10821366 :                         y*TERRAIN_GRID_BLOCK_SIZE_Y*0.7f*grid_spacing);
     425             :             float height;
     426    10821366 :             if (!height_amsl(loc2, height)) {
     427      733766 :                 ret = false;
     428             :             }
     429             :         }
     430             :     }
     431     1202374 :     return ret;
     432             : }
     433             : 
     434         906 : bool AP_Terrain::pre_arm_checks(char *failure_msg, uint8_t failure_msg_len) const
     435             : {
     436             :     // check no outstanding requests for data:
     437             :     uint16_t terr_pending, terr_loaded;
     438         906 :     get_statistics(terr_pending, terr_loaded);
     439         906 :     if (terr_pending != 0 ||
     440         736 :         !have_current_loc_height ||
     441         576 :         !have_home_height ||
     442         556 :         next_mission_index != 0 ||
     443         555 :         next_rally_index != 0) {
     444         351 :         hal.util->snprintf(failure_msg, failure_msg_len, "waiting for terrain data");
     445         351 :         return false;
     446             :     }
     447         555 :     if (grid_spacing <= 0) {
     448           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "TERRAIN_SPACING can't be <= 0");
     449           0 :         return false;
     450             :     }
     451             : 
     452         555 :     if (!option_set(Options::AcceptOldData) && found_old_data) {
     453             :         /*
     454             :           we have found old terrain data on the microSD card, warn the
     455             :           user that they have out of date terrain data that may
     456             :           contain errors.
     457             :          */
     458           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "terrain data expired, possible errors");
     459           0 :         return false;
     460             :     }
     461             : 
     462         555 :     return true;
     463             : }
     464             : 
     465             : #if HAL_LOGGING_ENABLED
     466      129567 : void AP_Terrain::log_terrain_data()
     467             : {
     468      129567 :     if (!allocate()) {
     469       13827 :         return;
     470             :     }
     471      123014 :     Location loc;
     472      123014 :     if (!AP::ahrs().get_location(loc)) {
     473             :         // we don't know where we are
     474        7274 :         return;
     475             :     }
     476      115740 :     float terrain_height = 0;
     477      115740 :     float current_height = 0;
     478             :     uint16_t pending, loaded;
     479             : 
     480      115740 :     height_amsl(loc, terrain_height);
     481      115740 :     height_above_terrain(current_height, true);
     482      115740 :     get_statistics(pending, loaded);
     483             : 
     484      115740 :     struct log_TERRAIN pkt = {
     485             :         LOG_PACKET_HEADER_INIT(LOG_TERRAIN_MSG),
     486      115740 :         time_us        : AP_HAL::micros64(),
     487      115740 :         status         : (uint8_t)status(),
     488      115740 :         lat            : loc.lat,
     489      115740 :         lng            : loc.lng,
     490      231480 :         spacing        : (uint16_t)grid_spacing,
     491             :         terrain_height : terrain_height,
     492             :         current_height : current_height,
     493             :         pending        : pending,
     494             :         loaded         : loaded,
     495      115740 :         reference_offset : have_reference_offset?reference_offset:0,
     496      115740 :     };
     497      115740 :     AP::logger().WriteBlock(&pkt, sizeof(pkt));
     498             : }
     499             : #endif
     500             : 
     501             : /*
     502             :   allocate terrain cache. Making this dynamically allocated allows
     503             :   memory to be saved when terrain functionality is disabled
     504             :  */
     505  1364679315 : bool AP_Terrain::allocate(void)
     506             : {
     507  1364679315 :     if (enable == 0 || memory_alloc_failed) {
     508    10387028 :         return false;
     509             :     }
     510  1354292205 :     if (cache != nullptr) {
     511  1354290506 :         return true;
     512             :     }
     513        1699 :     cache = (struct grid_cache *)calloc(config_cache_size, sizeof(cache[0]));
     514        1694 :     if (cache == nullptr) {
     515           0 :         GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Terrain: Allocation failed");
     516           0 :         memory_alloc_failed = true;
     517           0 :         return false;
     518             :     }
     519        1694 :     cache_size = config_cache_size;
     520        1694 :     return true;
     521             : }
     522             : 
     523             : /*
     524             :   setup a reference location for terrain adjustment. This should
     525             :   be called when the vehicle is definately on the ground
     526             : */
     527        1014 : void AP_Terrain::set_reference_location(void)
     528             : {
     529        1014 :     const auto &ahrs = AP::ahrs();
     530             : 
     531             :     // check we have absolute position
     532             :     nav_filter_status status;
     533        1014 :     if (!ahrs.get_filter_status(status) ||
     534        1014 :         !status.flags.vert_pos ||
     535        3024 :         !status.flags.horiz_pos_abs ||
     536         996 :         !status.flags.attitude) {
     537         102 :         return;
     538             :     }
     539             : 
     540             :     // check we have a small 3D velocity
     541         996 :     Vector3f vel;
     542        1992 :     if (!ahrs.get_velocity_NED(vel) ||
     543         996 :         vel.length() > 3) {
     544          84 :         return;
     545             :     }
     546             : 
     547         912 :     have_reference_offset = false;
     548         912 :     have_reference_loc = ahrs.get_location(reference_loc);
     549             : 
     550         912 :     update_reference_offset();
     551             : }
     552             : 
     553             : /*
     554             :   get the offset between terrain height and reference alt at the
     555             :   reference location
     556             :  */
     557   207677899 : void AP_Terrain::update_reference_offset(void)
     558             : {
     559             :     // TERR_OFS_MAX of zero means no adjustment
     560   207677899 :     if (!is_positive(offset_max)) {
     561     1921383 :         have_reference_offset = false;
     562   207677090 :         return;
     563             :     }
     564             : 
     565             :     // allow for change to TERRAIN_OFS_MAX while flying
     566   205756516 :     if (have_reference_offset) {
     567   138786350 :         reference_offset = constrain_float(reference_offset, -offset_max, offset_max);
     568   138786350 :         return;
     569             :     }
     570             : 
     571    66970166 :     if (!have_reference_loc) {
     572             :         // no reference available yet
     573    55516914 :         return;
     574             :     }
     575             : 
     576             :     // calculate adjustment
     577             :     float height;
     578    11453252 :     if (!height_amsl(reference_loc, height)) {
     579    11452443 :         return;
     580             :     }
     581             :     int32_t alt_cm;
     582         809 :     if (!reference_loc.get_alt_cm(Location::AltFrame::ABSOLUTE, alt_cm)) {
     583           0 :         return;
     584             :     }
     585         809 :     const float adjustment = alt_cm*0.01 - height;
     586         809 :     reference_offset = constrain_float(adjustment, -offset_max, offset_max);
     587         809 :     if (fabsf(adjustment) > offset_max.get()+0.5) {
     588           1 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Terrain: clamping offset %.0f to %.0f",
     589             :                       adjustment, reference_offset);
     590             :     }
     591         809 :     have_reference_offset = true;
     592             : }
     593             : 
     594             : 
     595             : namespace AP {
     596             : 
     597   623645235 : AP_Terrain *terrain()
     598             : {
     599   623645235 :     return AP_Terrain::get_singleton();
     600             : }
     601             : 
     602             : };
     603             : 
     604             : #endif // AP_TERRAIN_AVAILABLE

Generated by: LCOV version 1.14