LCOV - code coverage report
Current view: top level - AP_Avoidance - AP_Avoidance.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 199 262 76.0 %
Date: 2026-09-09 11:02:08 Functions: 21 23 91.3 %

          Line data    Source code
       1             : #include "AP_Avoidance_config.h"
       2             : 
       3             : #if AP_ADSB_AVOIDANCE_ENABLED
       4             : 
       5             : #include "AP_Avoidance.h"
       6             : 
       7             : extern const AP_HAL::HAL& hal;
       8             : 
       9             : #include <limits>
      10             : #include <AP_AHRS/AP_AHRS.h>
      11             : #include <GCS_MAVLink/GCS.h>
      12             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      13             : 
      14             : #define AVOIDANCE_DEBUGGING 0
      15             : 
      16             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      17             :     #define AP_AVOIDANCE_WARN_TIME_DEFAULT              30
      18             :     #define AP_AVOIDANCE_FAIL_TIME_DEFAULT              30
      19             :     #define AP_AVOIDANCE_WARN_DISTANCE_XY_DEFAULT       1000
      20             :     #define AP_AVOIDANCE_WARN_DISTANCE_Z_DEFAULT        300
      21             :     #define AP_AVOIDANCE_FAIL_DISTANCE_XY_DEFAULT       300
      22             :     #define AP_AVOIDANCE_FAIL_DISTANCE_Z_DEFAULT        100
      23             :     #define AP_AVOIDANCE_RECOVERY_DEFAULT               RecoveryAction::RESUME_IF_AUTO_ELSE_LOITER
      24             :     #define AP_AVOIDANCE_FAIL_ACTION_DEFAULT            MAV_COLLISION_ACTION_REPORT
      25             : #else // APM_BUILD_TYPE(APM_BUILD_ArduCopter),Heli, Rover, Boat
      26             :     #define AP_AVOIDANCE_WARN_TIME_DEFAULT              30
      27             :     #define AP_AVOIDANCE_FAIL_TIME_DEFAULT              30
      28             :     #define AP_AVOIDANCE_WARN_DISTANCE_XY_DEFAULT       300
      29             :     #define AP_AVOIDANCE_WARN_DISTANCE_Z_DEFAULT        300
      30             :     #define AP_AVOIDANCE_FAIL_DISTANCE_XY_DEFAULT       100
      31             :     #define AP_AVOIDANCE_FAIL_DISTANCE_Z_DEFAULT        100
      32             :     #define AP_AVOIDANCE_RECOVERY_DEFAULT               RecoveryAction::RTL
      33             :     #define AP_AVOIDANCE_FAIL_ACTION_DEFAULT            MAV_COLLISION_ACTION_REPORT
      34             : #endif
      35             : 
      36             : #if AVOIDANCE_DEBUGGING
      37             : #include <stdio.h>
      38             : #define debug(fmt, args ...)  do {::fprintf(stderr,"%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
      39             : #else
      40             : #define debug(fmt, args ...)
      41             : #endif
      42             : 
      43             : // table of user settable parameters
      44             : const AP_Param::GroupInfo AP_Avoidance::var_info[] = {
      45             : 
      46             :     // @Param: ENABLE
      47             :     // @DisplayName: Enable Avoidance using ADSB
      48             :     // @Description: Enable Avoidance using ADSB
      49             :     // @Values: 0:Disabled,1:Enabled
      50             :     // @User: Advanced
      51             :     AP_GROUPINFO_FLAGS("ENABLE", 1, AP_Avoidance, _enabled, 0, AP_PARAM_FLAG_ENABLE),
      52             : 
      53             :     // @Param: F_ACTION
      54             :     // @DisplayName: Collision Avoidance Behavior
      55             :     // @Description: Specifies aircraft behaviour when a collision is imminent
      56             :     // @Values: 0:None,1:Report,2:Climb Or Descend,3:Move Horizontally,4:Move Perpendicularly in 3D,5:RTL,6:Hover
      57             :     // @User: Advanced
      58             :     AP_GROUPINFO("F_ACTION",    2, AP_Avoidance, _fail_action, AP_AVOIDANCE_FAIL_ACTION_DEFAULT),
      59             : 
      60             :     // @Param: W_ACTION
      61             :     // @DisplayName: Collision Avoidance Behavior - Warn
      62             :     // @Description: Specifies aircraft behaviour when a collision may occur
      63             :     // @Values: 0:None,1:Report
      64             :     // @User: Advanced
      65             :     AP_GROUPINFO("W_ACTION",    3, AP_Avoidance, _warn_action, MAV_COLLISION_ACTION_REPORT),
      66             : 
      67             :     // @Param: F_RCVRY
      68             :     // @DisplayName: Recovery behaviour after a fail event
      69             :     // @Description: Determines what the aircraft will do after a fail event is resolved
      70             :     // @Values: 0:Remain in AVOID_ADSB,1:Resume previous flight mode,2:RTL,3:Resume if AUTO else Loiter
      71             :     // @User: Advanced
      72             :     AP_GROUPINFO("F_RCVRY",     4, AP_Avoidance, _fail_recovery, uint8_t(AP_AVOIDANCE_RECOVERY_DEFAULT)),
      73             : 
      74             :     // @Param: OBS_MAX
      75             :     // @DisplayName: Maximum number of obstacles to track
      76             :     // @Description: Maximum number of obstacles to track
      77             :     // @User: Advanced
      78             :     AP_GROUPINFO("OBS_MAX",     5, AP_Avoidance, _obstacles_max, 20),
      79             : 
      80             :     // @Param: W_TIME
      81             :     // @DisplayName: Time Horizon Warn
      82             :     // @Description: Aircraft velocity vectors are multiplied by this time to determine closest approach.  If this results in an approach closer than W_DIST_XY or W_DIST_Z then W_ACTION is undertaken (assuming F_ACTION is not undertaken)
      83             :     // @Units: s
      84             :     // @User: Advanced
      85             :     AP_GROUPINFO("W_TIME",      6, AP_Avoidance, _warn_time_horizon_s, AP_AVOIDANCE_WARN_TIME_DEFAULT),
      86             : 
      87             :     // @Param: F_TIME
      88             :     // @DisplayName: Time Horizon Fail
      89             :     // @Description: Aircraft velocity vectors are multiplied by this time to determine closest approach.  If this results in an approach closer than F_DIST_XY or F_DIST_Z then F_ACTION is undertaken
      90             :     // @Units: s
      91             :     // @User: Advanced
      92             :     AP_GROUPINFO("F_TIME",      7, AP_Avoidance, _fail_time_horizon_s, AP_AVOIDANCE_FAIL_TIME_DEFAULT),
      93             : 
      94             :     // @Param: W_DIST_XY
      95             :     // @DisplayName: Distance Warn XY
      96             :     // @Description: Closest allowed projected distance before W_ACTION is undertaken
      97             :     // @Units: m
      98             :     // @User: Advanced
      99             :     AP_GROUPINFO("W_DIST_XY",   8, AP_Avoidance, _warn_distance_ne_m, AP_AVOIDANCE_WARN_DISTANCE_XY_DEFAULT),
     100             : 
     101             :     // @Param: F_DIST_XY
     102             :     // @DisplayName: Distance Fail XY
     103             :     // @Description: Closest allowed projected distance before F_ACTION is undertaken
     104             :     // @Units: m
     105             :     // @User: Advanced
     106             :     AP_GROUPINFO("F_DIST_XY",   9, AP_Avoidance, _fail_distance_ne_m, AP_AVOIDANCE_FAIL_DISTANCE_XY_DEFAULT),
     107             : 
     108             :     // @Param: W_DIST_Z
     109             :     // @DisplayName: Distance Warn Z
     110             :     // @Description: Closest allowed projected distance before BEHAVIOUR_W is undertaken
     111             :     // @Units: m
     112             :     // @User: Advanced
     113             :     AP_GROUPINFO("W_DIST_Z",    10, AP_Avoidance, _warn_distance_d_m, AP_AVOIDANCE_WARN_DISTANCE_Z_DEFAULT),
     114             : 
     115             :     // @Param: F_DIST_Z
     116             :     // @DisplayName: Distance Fail Z
     117             :     // @Description: Closest allowed projected distance before BEHAVIOUR_F is undertaken
     118             :     // @Units: m
     119             :     // @User: Advanced
     120             :     AP_GROUPINFO("F_DIST_Z",    11, AP_Avoidance, _fail_distance_d_m, AP_AVOIDANCE_FAIL_DISTANCE_Z_DEFAULT),
     121             :     
     122             :     // @Param: F_ALT_MIN
     123             :     // @DisplayName: ADS-B avoidance minimum altitude
     124             :     // @Description: Minimum AMSL (above mean sea level) altitude for ADS-B avoidance. If the vehicle is below this altitude, no avoidance action will take place. Useful to prevent ADS-B avoidance from activating while below the tree line or around structures. Default of 0 is no minimum.
     125             :     // @Units: m
     126             :     // @User: Advanced
     127             :     AP_GROUPINFO("F_ALT_MIN",    12, AP_Avoidance, _fail_altitude_min_m, 0),
     128             : 
     129             :     AP_GROUPEND
     130             : };
     131             : 
     132        1693 : AP_Avoidance::AP_Avoidance(AP_ADSB &adsb) :
     133        1693 :     _adsb(adsb)
     134             : {
     135        1693 :     AP_Param::setup_object_defaults(this, var_info);
     136        1693 :     if (_singleton != nullptr) {
     137           0 :         AP_HAL::panic("AP_Avoidance must be singleton");
     138             :     }
     139        1693 :     _singleton = this;
     140        1693 : }
     141             : 
     142             : /*
     143             :  * Initialize variables and allocate memory for array
     144             :  */
     145           7 : void AP_Avoidance::init(void)
     146             : {
     147             :     debug("ADSB initialisation: %d obstacles", _obstacles_max.get());
     148           7 :     if (_obstacles == nullptr) {
     149         147 :         _obstacles = NEW_NOTHROW AP_Avoidance::Obstacle[_obstacles_max];
     150             : 
     151           7 :         if (_obstacles == nullptr) {
     152             :             // dynamic RAM allocation of _obstacles[] failed, disable gracefully
     153           0 :             DEV_PRINTF("Unable to initialize Avoidance obstacle list\n");
     154             :             // disable ourselves to avoid repeated allocation attempts
     155           0 :             _enabled.set(0);
     156           0 :             return;
     157             :         }
     158           7 :         _obstacles_allocated = _obstacles_max;
     159             :     }
     160           7 :     _obstacle_count = 0;
     161           7 :     _last_state_change_ms = 0;
     162           7 :     _threat_level = MAV_COLLISION_THREAT_LEVEL_NONE;
     163           7 :     _gcs_cleared_messages_first_sent = std::numeric_limits<uint32_t>::max();
     164           7 :     _current_most_serious_threat = -1;
     165             : }
     166             : 
     167             : /*
     168             :  * de-initialize and free up some memory
     169             :  */
     170           4 : void AP_Avoidance::deinit(void)
     171             : {
     172           4 :     if (_obstacles != nullptr) {
     173           4 :         delete [] _obstacles;
     174           4 :         _obstacles = nullptr;
     175           4 :         _obstacles_allocated = 0;
     176           4 :         handle_recovery(RecoveryAction::RTL);
     177             :     }
     178           4 :     _obstacle_count = 0;
     179           4 : }
     180             : 
     181     2452434 : bool AP_Avoidance::check_startup()
     182             : {
     183     2452434 :     if (!_enabled) {
     184     2446084 :         if (_obstacles != nullptr) {
     185           4 :             deinit();
     186             :         }
     187             :         // nothing to do
     188     2446084 :         return false;
     189             :     }
     190        6350 :     if (_obstacles == nullptr)  {
     191           7 :         init();
     192             :     }
     193        6350 :     return _obstacles != nullptr;
     194             : }
     195             : 
     196             : // vel_ned_ms is north/east/down
     197           6 : void AP_Avoidance::add_obstacle(const uint32_t obstacle_timestamp_ms,
     198             :                                 const MAV_COLLISION_SRC src,
     199             :                                 const uint32_t src_id,
     200             :                                 const Location &loc,
     201             :                                 const Vector3f &vel_ned_ms)
     202             : {
     203           6 :     if (! check_startup()) {
     204           0 :         return;
     205             :     }
     206           6 :     uint32_t oldest_timestamp = std::numeric_limits<uint32_t>::max();
     207           6 :     uint8_t oldest_index = 255; // avoid compiler warning with initialisation
     208           6 :     int16_t index = -1;
     209             :     uint8_t i;
     210           6 :     for (i=0; i<_obstacle_count; i++) {
     211           0 :         if (_obstacles[i].src_id == src_id &&
     212           0 :             _obstacles[i].src == src) {
     213             :             // pre-existing obstacle found; we will update its information
     214           0 :             index = i;
     215           0 :             break;
     216             :         }
     217           0 :         if (_obstacles[i].timestamp_ms < oldest_timestamp) {
     218           0 :             oldest_timestamp = _obstacles[i].timestamp_ms;
     219           0 :             oldest_index = i;
     220             :         }
     221             :     }
     222           6 :     WITH_SEMAPHORE(_rsem);
     223             :     
     224           6 :     if (index == -1) {
     225             :         // existing obstacle not found.  See if we can store it anyway:
     226           6 :         if (i <_obstacles_allocated) {
     227             :             // have room to store more vehicles...
     228           6 :             index = _obstacle_count++;
     229           0 :         } else if (oldest_timestamp < obstacle_timestamp_ms) {
     230             :             // replace this very old entry with this new data
     231           0 :             index = oldest_index;
     232             :         } else {
     233             :             // no room for this (old?!) data
     234           0 :             return;
     235             :         }
     236             : 
     237           6 :         _obstacles[index].src = src;
     238           6 :         _obstacles[index].src_id = src_id;
     239             :     }
     240             : 
     241           6 :     _obstacles[index]._location = loc;
     242           6 :     _obstacles[index]._velocity_ned_ms = vel_ned_ms;
     243           6 :     _obstacles[index].timestamp_ms = obstacle_timestamp_ms;
     244           6 : }
     245             : 
     246           6 : void AP_Avoidance::add_obstacle(const uint32_t obstacle_timestamp_ms,
     247             :                                 const MAV_COLLISION_SRC src,
     248             :                                 const uint32_t src_id,
     249             :                                 const Location &loc,
     250             :                                 const float cog,
     251             :                                 const float speed_ne_ms,
     252             :                                 const float speed_d_ms)
     253             : {
     254           6 :     Vector3f vel_ned_ms;
     255           6 :     vel_ned_ms[0] = speed_ne_ms * cosf(radians(cog));
     256           6 :     vel_ned_ms[1] = speed_ne_ms * sinf(radians(cog));
     257           6 :     vel_ned_ms[2] = speed_d_ms;
     258             :     // debug("cog=%f speed_ne_ms=%f veln=%f vele=%f", cog, speed_ne_ms, vel_ned_ms[0], vel[1]);
     259           6 :     return add_obstacle(obstacle_timestamp_ms, src, src_id, loc, vel_ned_ms);
     260             : }
     261             : 
     262           6 : uint32_t AP_Avoidance::src_id_for_adsb_vehicle(const AP_ADSB::adsb_vehicle_t &vehicle) const
     263             : {
     264             :     // TODO: need to include squawk code and callsign
     265           6 :     return vehicle.info.ICAO_address;
     266             : }
     267             : 
     268        2631 : void AP_Avoidance::get_adsb_samples()
     269             : {
     270             :     AP_ADSB::adsb_vehicle_t vehicle;
     271        2637 :     while (_adsb.next_sample(vehicle)) {
     272           6 :         uint32_t src_id = src_id_for_adsb_vehicle(vehicle);
     273           6 :         Location loc = _adsb.get_location(vehicle);
     274           6 :         add_obstacle(vehicle.last_update_ms,
     275             :                    MAV_COLLISION_SRC_ADSB,
     276             :                    src_id,
     277             :                    loc,
     278           6 :                    vehicle.info.heading * 0.01,
     279           6 :                    vehicle.info.hor_velocity * 0.01,
     280           6 :                    -vehicle.info.ver_velocity * 0.01); // convert cm-up to m-down
     281             :     }
     282        2631 : }
     283             : 
     284         191 : float closest_approach_NE_m(const Location &loc,
     285             :                           const Vector3f &vel_ned_ms,
     286             :                           const Location &obstacle_loc,
     287             :                           const Vector3f &obstacle_vel_ned_ms,
     288             :                           const uint8_t time_horizon_s)
     289             : {
     290             : 
     291         191 :     Vector2f delta_vel_ne_ms = Vector2f(obstacle_vel_ned_ms[0] - vel_ned_ms[0], obstacle_vel_ned_ms[1] - vel_ned_ms[1]);
     292         191 :     const Vector2f delta_pos_ne_m = obstacle_loc.get_distance_NE(loc);
     293             : 
     294         191 :     Vector2f line_segment_ne_m = delta_vel_ne_ms * time_horizon_s;
     295             : 
     296             :     float dist_ne_m = Vector2<float>::closest_distance_between_radial_and_point
     297         191 :         (line_segment_ne_m,
     298             :          delta_pos_ne_m);
     299             : 
     300             :     debug("   time_horizon: (%d)", time_horizon_s);
     301             :     debug("   delta pos: (y=%f,x=%f)", delta_pos_ne_m[0], delta_pos_ne_m[1]);
     302             :     debug("   delta vel: (y=%f,x=%f)", delta_vel_ne_ms[0], delta_vel_ne_ms[1]);
     303             :     debug("   line segment: (y=%f,x=%f)", line_segment_ne_m[0], line_segment_ne_m[1]);
     304             :     debug("   closest: (%f)", dist_ne_m);
     305             : 
     306         191 :     return dist_ne_m;
     307             : }
     308             : 
     309             : // returns the closest these objects will get in the body z axis (in metres)
     310         382 : float closest_approach_D_m(const Location &loc,
     311             :                          const Vector3f &vel_ned_ms,
     312             :                          const Location &obstacle_loc,
     313             :                          const Vector3f &obstacle_vel_ned_ms,
     314             :                          const uint8_t time_horizon_s)
     315             : {
     316             : 
     317         382 :     float delta_vel_d_ms = obstacle_vel_ned_ms[2] - vel_ned_ms[2];
     318         382 :     float delta_pos_d_cm = obstacle_loc.alt - loc.alt;
     319             : 
     320             :     float dist_d_cm;
     321         382 :     if (delta_pos_d_cm >= 0 && delta_vel_d_ms >= 0) {
     322         218 :         dist_d_cm = delta_pos_d_cm;
     323         164 :     } else if (delta_pos_d_cm <= 0 && delta_vel_d_ms <= 0) {
     324           0 :         dist_d_cm = fabsf(delta_pos_d_cm);
     325             :     } else {
     326         164 :         dist_d_cm = fabsf(delta_pos_d_cm - delta_vel_d_ms * time_horizon_s * 100.0);
     327             :     }
     328             : 
     329             :     debug("   time_horizon: (%d)", time_horizon_s);
     330             :     debug("   delta pos: (%f) metres", delta_pos_d_cm*0.01f);
     331             :     debug("   delta vel: (%f) m/s", delta_vel_d_ms);
     332             :     debug("   closest: (%f) metres", dist_d_cm*0.01f);
     333             : 
     334         382 :     return dist_d_cm * 0.01f;
     335             : }
     336             : 
     337         191 : void AP_Avoidance::update_threat_level(const Location &loc,
     338             :                                        const Vector3f &vel_ned_ms,
     339             :                                        AP_Avoidance::Obstacle &obstacle)
     340             : {
     341             : 
     342         191 :     Location &obstacle_loc = obstacle._location;
     343         191 :     Vector3f &obstacle_vel_ned_ms = obstacle._velocity_ned_ms;
     344             : 
     345         191 :     obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_NONE;
     346             : 
     347         191 :     const uint32_t obstacle_age_ms = AP_HAL::millis() - obstacle.timestamp_ms;
     348         191 :     float closest_ne_m = closest_approach_NE_m(loc, vel_ned_ms, obstacle_loc, obstacle_vel_ned_ms, _fail_time_horizon_s + obstacle_age_ms/1000);
     349         191 :     if (closest_ne_m < _fail_distance_ne_m) {
     350         191 :         obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_HIGH;
     351             :     } else {
     352           0 :         closest_ne_m = closest_approach_NE_m(loc, vel_ned_ms, obstacle_loc, obstacle_vel_ned_ms, _warn_time_horizon_s + obstacle_age_ms/1000);
     353           0 :         if (closest_ne_m < _warn_distance_ne_m) {
     354           0 :             obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_LOW;
     355             :         }
     356             :     }
     357             : 
     358             :     // check for vertical separation; our threat level is the minimum
     359             :     // of vertical and horizontal threat levels
     360         191 :     float closest_d_m = closest_approach_D_m(loc, vel_ned_ms, obstacle_loc, obstacle_vel_ned_ms, _warn_time_horizon_s + obstacle_age_ms/1000);
     361         191 :     if (obstacle.threat_level != MAV_COLLISION_THREAT_LEVEL_NONE) {
     362         191 :         if (closest_d_m > _warn_distance_d_m) {
     363           0 :             obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_NONE;
     364             :         } else {
     365         191 :             closest_d_m = closest_approach_D_m(loc, vel_ned_ms, obstacle_loc, obstacle_vel_ned_ms, _fail_time_horizon_s + obstacle_age_ms/1000);
     366         191 :             if (closest_d_m > _fail_distance_d_m) {
     367           3 :                 obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_LOW;
     368             :             }
     369             :         }
     370             :     }
     371             : 
     372             :     // If we haven't heard from a vehicle then assume it is no threat
     373         191 :     if (obstacle_age_ms > MAX_OBSTACLE_AGE_MS) {
     374           5 :         obstacle.threat_level = MAV_COLLISION_THREAT_LEVEL_NONE;
     375             :     }
     376             : 
     377             :     // could optimise this to not calculate a lot of this if threat
     378             :     // level is none - but only *once the GCS has been informed*!
     379         191 :     obstacle.closest_approach_ne_m = closest_ne_m;
     380         191 :     obstacle.closest_approach_d_m = closest_d_m;
     381         191 :     float current_distance_ne_m = loc.get_distance(obstacle_loc);
     382         191 :     obstacle.distance_to_closest_approach_ned_m = current_distance_ne_m - closest_ne_m;
     383         191 :     Vector2f net_velocity_ne_ms = Vector2f(vel_ned_ms[0] - obstacle_vel_ned_ms[0], vel_ned_ms[1] - obstacle_vel_ned_ms[1]);
     384         191 :     obstacle.time_to_closest_approach_s = 0.0f;
     385         214 :     if (!is_zero(obstacle.distance_to_closest_approach_ned_m) &&
     386          23 :         ! is_zero(net_velocity_ne_ms.length())) {
     387          23 :         obstacle.time_to_closest_approach_s = obstacle.distance_to_closest_approach_ned_m / net_velocity_ne_ms.length();
     388             :     }
     389         191 : }
     390             : 
     391           0 : MAV_COLLISION_THREAT_LEVEL AP_Avoidance::current_threat_level() const {
     392           0 :     if (_obstacles == nullptr) {
     393           0 :         return MAV_COLLISION_THREAT_LEVEL_NONE;
     394             :     }
     395           0 :     if (_current_most_serious_threat == -1) {
     396           0 :         return MAV_COLLISION_THREAT_LEVEL_NONE;
     397             :     }
     398           0 :     return _obstacles[_current_most_serious_threat].threat_level;
     399             : }
     400             : 
     401             : #if HAL_GCS_ENABLED
     402          19 : void AP_Avoidance::send_collision_all(const AP_Avoidance::Obstacle &threat, MAV_COLLISION_ACTION behaviour) const
     403             : {
     404          19 :     const mavlink_collision_t packet{
     405          19 :         id: threat.src_id,
     406          19 :         time_to_minimum_delta: threat.time_to_closest_approach_s,
     407          19 :         altitude_minimum_delta: threat.closest_approach_d_m,
     408          19 :         horizontal_minimum_delta: threat.closest_approach_ne_m,
     409             :         src: MAV_COLLISION_SRC_ADSB,
     410             :         action: (uint8_t)behaviour,
     411          19 :         threat_level: (uint8_t)threat.threat_level,
     412          19 :     };
     413          19 :     gcs().send_to_active_channels(MAVLINK_MSG_ID_COLLISION, (const char *)&packet);
     414          19 : }
     415             : #endif
     416             : 
     417        2631 : void AP_Avoidance::handle_threat_gcs_notify(AP_Avoidance::Obstacle *threat)
     418             : {
     419        2631 :     if (threat == nullptr) {
     420        2445 :         return;
     421             :     }
     422             : 
     423         186 :     uint32_t now = AP_HAL::millis();
     424         186 :     if (threat->threat_level == MAV_COLLISION_THREAT_LEVEL_NONE) {
     425             :         // only send cleared messages for a few seconds:
     426           0 :         if (_gcs_cleared_messages_first_sent == 0) {
     427           0 :             _gcs_cleared_messages_first_sent = now;
     428             :         }
     429           0 :         if (now - _gcs_cleared_messages_first_sent > _gcs_cleared_messages_duration * 1000) {
     430           0 :             return;
     431             :         }
     432             :     } else {
     433         186 :         _gcs_cleared_messages_first_sent = 0;
     434             :     }
     435         186 :     if (now - threat->last_gcs_report_time > _gcs_notify_interval * 1000) {
     436          19 :         send_collision_all(*threat, mav_avoidance_action());
     437          19 :         threat->last_gcs_report_time = now;
     438             :     }
     439             : 
     440             : }
     441             : 
     442         186 : bool AP_Avoidance::obstacle_is_more_serious_threat(const AP_Avoidance::Obstacle &obstacle) const
     443             : {
     444         186 :     if (_current_most_serious_threat == -1) {
     445             :         // any threat is more of a threat than no threat
     446         186 :         return true;
     447             :     }
     448           0 :     const AP_Avoidance::Obstacle &current = _obstacles[_current_most_serious_threat];
     449           0 :     if (obstacle.threat_level > current.threat_level) {
     450             :         // threat_level is updated by update_threat_level
     451           0 :         return true;
     452             :     }
     453           0 :     if (obstacle.threat_level == current.threat_level &&
     454           0 :         obstacle.time_to_closest_approach_s < current.time_to_closest_approach_s) {
     455           0 :         return true;
     456             :     }
     457           0 :     return false;
     458             : }
     459             : 
     460        2631 : void AP_Avoidance::check_for_threats()
     461             : {
     462        2631 :     const AP_AHRS &_ahrs = AP::ahrs();
     463             : 
     464        2631 :     Location loc;
     465        2631 :     if (!_ahrs.get_location(loc)) {
     466             :         // if we don't know our own location we can't determine any threat level
     467         246 :         return;
     468             :     }
     469             : 
     470        2385 :     Vector3f vel_ned_ms;
     471        2385 :     if (!_ahrs.get_velocity_NED(vel_ned_ms)) {
     472             :         // assuming our own velocity to be zero here may cause us to
     473             :         // fly into something.  Better not to attempt to avoid in this
     474             :         // case.
     475           0 :         return;
     476             :     }
     477             : 
     478             :     // we always check all obstacles to see if they are threats since it
     479             :     // is most likely our own position and/or velocity have changed
     480             :     // determine the current most-serious-threat
     481        2385 :     _current_most_serious_threat = -1;
     482        2576 :     for (uint8_t i=0; i<_obstacle_count; i++) {
     483             : 
     484         191 :         AP_Avoidance::Obstacle &obstacle = _obstacles[i];
     485         191 :         const uint32_t obstacle_age_ms = AP_HAL::millis() - obstacle.timestamp_ms;
     486             :         debug("i=%d src_id=%d timestamp=%u age=%d", i, obstacle.src_id, obstacle.timestamp_ms, obstacle_age_ms);
     487             : 
     488         191 :         update_threat_level(loc, vel_ned_ms, obstacle);
     489             :         debug("   threat-level=%d", obstacle.threat_level);
     490             : 
     491             :         // ignore any really old data:
     492         191 :         if (obstacle_age_ms > MAX_OBSTACLE_AGE_MS) {
     493             :             // shrink list if this is the last entry:
     494           5 :             if (i == _obstacle_count-1) {
     495           5 :                 _obstacle_count -= 1;
     496             :             }
     497           5 :             continue;
     498             :         }
     499             : 
     500         186 :         if (obstacle_is_more_serious_threat(obstacle)) {
     501         186 :             _current_most_serious_threat = i;
     502             :         }
     503             :     }
     504        2385 :     if (_current_most_serious_threat != -1) {
     505             :         debug("Current most serious threat: %d level=%d", _current_most_serious_threat, _obstacles[_current_most_serious_threat].threat_level);
     506             :     }
     507             : }
     508             : 
     509             : 
     510        5262 : AP_Avoidance::Obstacle *AP_Avoidance::most_serious_threat()
     511             : {
     512        5262 :     if (_current_most_serious_threat < 0) {
     513             :         // we *really_ should not have been called!
     514        4890 :         return nullptr;
     515             :     }
     516         372 :     return &_obstacles[_current_most_serious_threat];
     517             : }
     518             : 
     519             : 
     520     1306596 : void AP_Avoidance::update()
     521             : {
     522     1306596 :     if (!check_startup()) {
     523     1303965 :         return;
     524             :     }
     525             : 
     526        2631 :     if (_adsb.enabled()) {
     527        2631 :         get_adsb_samples();
     528             :     }
     529             : 
     530        2631 :     check_for_threats();
     531             : 
     532             :     // avoid object (if necessary)
     533        2631 :     handle_avoidance_local(most_serious_threat());
     534             : 
     535             :     // notify GCS of most serious thread
     536        2631 :     handle_threat_gcs_notify(most_serious_threat());
     537             : }
     538             : 
     539        2631 : void AP_Avoidance::handle_avoidance_local(AP_Avoidance::Obstacle *threat)
     540             : {
     541        2631 :     MAV_COLLISION_THREAT_LEVEL new_threat_level = MAV_COLLISION_THREAT_LEVEL_NONE;
     542        2631 :     MAV_COLLISION_ACTION action = MAV_COLLISION_ACTION_NONE;
     543             : 
     544        2631 :     if (threat != nullptr) {
     545         186 :         new_threat_level = threat->threat_level;
     546         186 :         if (new_threat_level == MAV_COLLISION_THREAT_LEVEL_HIGH) {
     547         183 :             action = (MAV_COLLISION_ACTION)_fail_action.get();
     548         183 :             Location loc;
     549         199 :             if (action != MAV_COLLISION_ACTION_NONE && _fail_altitude_min_m > 0 &&
     550         382 :                 AP::ahrs().get_location(loc) && ((loc.alt * 0.01f) < _fail_altitude_min_m)) {
     551             :                 // disable avoidance when close to ground, report only
     552          16 :                 action = MAV_COLLISION_ACTION_REPORT;
     553             :                         }
     554             :                 }
     555             :     }
     556             : 
     557        2631 :     uint32_t now = AP_HAL::millis();
     558             : 
     559        2631 :     if (new_threat_level != _threat_level) {
     560             :         // transition to higher states immediately, recovery to lower states more slowly
     561          25 :         if (((now - _last_state_change_ms) > AP_AVOIDANCE_STATE_RECOVERY_TIME_MS) || (new_threat_level > _threat_level)) {
     562             :             // handle recovery from high threat level
     563          11 :             if (_threat_level == MAV_COLLISION_THREAT_LEVEL_HIGH) {
     564           5 :                 handle_recovery(RecoveryAction(_fail_recovery.get()));
     565           5 :                 _latest_action = MAV_COLLISION_ACTION_NONE;
     566             :             }
     567             : 
     568             :             // update state
     569          11 :             _last_state_change_ms = now;
     570          11 :             _threat_level = new_threat_level;
     571             :         }
     572             :     }
     573             : 
     574             :     // handle ongoing threat by calling vehicle specific handler
     575        2631 :     if ((threat != nullptr) && (_threat_level == MAV_COLLISION_THREAT_LEVEL_HIGH) && (action > MAV_COLLISION_ACTION_REPORT)) {
     576         167 :         _latest_action = handle_avoidance(threat, action);
     577             :     }
     578        2631 : }
     579             : 
     580             : 
     581     1145832 : void AP_Avoidance::handle_msg(const mavlink_message_t &msg)
     582             : {
     583     1145832 :     if (!check_startup()) {
     584             :         // avoidance is not active / allocated
     585     1145832 :         return;
     586             :     }
     587             : 
     588        3713 :     if (msg.msgid != MAVLINK_MSG_ID_GLOBAL_POSITION_INT) {
     589             :         // we only take position from GLOBAL_POSITION_INT
     590        3713 :         return;
     591             :     }
     592             : 
     593           0 :     if (msg.sysid == mavlink_system.sysid) {
     594             :         // we do not obstruct ourselves....
     595           0 :         return;
     596             :     }
     597             : 
     598             :     // inform AP_Avoidance we have a new player
     599             :     mavlink_global_position_int_t packet;
     600           0 :     mavlink_msg_global_position_int_decode(&msg, &packet);
     601             :     const Location loc {
     602             :         packet.lat,
     603             :         packet.lon,
     604           0 :         int32_t(packet.alt * 0.1),  // mm -> cm
     605             :         Location::AltFrame::ABSOLUTE
     606           0 :     };
     607             :     const Vector3f vel_ned_ms {
     608           0 :         packet.vx * 0.01f, // cm to m
     609           0 :         packet.vy * 0.01f,
     610           0 :         packet.vz * 0.01f
     611           0 :     };
     612           0 :     add_obstacle(AP_HAL::millis(),
     613             :                  MAV_COLLISION_SRC_MAVLINK_GPS_GLOBAL_INT,
     614           0 :                  msg.sysid,
     615             :                  loc,
     616             :                  vel_ned_ms);
     617             : }
     618             : 
     619             : // get unit vector away from the nearest obstacle
     620          77 : bool AP_Avoidance::get_vector_perpendicular(const AP_Avoidance::Obstacle *obstacle, Vector3f &vec_neu_unit) const
     621             : {
     622          77 :     if (obstacle == nullptr) {
     623             :         // why where we called?!
     624           0 :         return false;
     625             :     }
     626             : 
     627          77 :     Location current_loc;
     628          77 :     if (!AP::ahrs().get_location(current_loc)) {
     629             :         // we should not get to here!  If we don't know our position
     630             :         // we can't know if there are any threats, for starters!
     631           0 :         return false;
     632             :     }
     633             : 
     634             :     // if their velocity is moving around close to zero then flying
     635             :     // perpendicular to that velocity may mean we do weird things.
     636             :     // Instead, we will fly directly away from them
     637          77 :     if (obstacle->_velocity_ned_ms.length() < _low_velocity_threshold) {
     638          77 :         const Vector2f delta_pos_ne_m =  obstacle->_location.get_distance_NE(current_loc);
     639          77 :         const float delta_pos_u_cm = current_loc.alt - obstacle->_location.alt;
     640          77 :         Vector3f delta_pos_neu_m = Vector3f{delta_pos_ne_m.x, delta_pos_ne_m.y, delta_pos_u_cm * 0.01};
     641             :         // avoid div by zero
     642          77 :         if (delta_pos_neu_m.is_zero()) {
     643           0 :             return false;
     644             :         }
     645          77 :         delta_pos_neu_m.normalize();
     646          77 :         vec_neu_unit = delta_pos_neu_m;
     647          77 :         return true;
     648             :     } else {
     649           0 :         vec_neu_unit = perpendicular_neu_m(obstacle->_location, obstacle->_velocity_ned_ms, current_loc);
     650             :         // avoid div by zero
     651           0 :         if (vec_neu_unit.is_zero()) {
     652           0 :             return false;
     653             :         }
     654           0 :         vec_neu_unit.normalize();
     655           0 :         return true;
     656             :     }
     657             : }
     658             : 
     659             : // helper functions to calculate 3D destination to get us away from obstacle
     660             : // v1_ned is NED
     661           0 : Vector3f AP_Avoidance::perpendicular_neu_m(const Location &p1, const Vector3f &v1_ned, const Location &p2)
     662             : {
     663           0 :     const Vector2f delta_p_ne_m = p1.get_distance_NE(p2);
     664           0 :     Vector3f delta_p_neu_m = Vector3f(delta_p_ne_m[0], delta_p_ne_m[1], (p2.alt - p1.alt) * 0.01f); //check this line
     665           0 :     Vector3f v1_neu = Vector3f(v1_ned[0], v1_ned[1], -v1_ned[2]);
     666           0 :     Vector3f ret_neu_m = Vector3f::perpendicular(delta_p_neu_m, v1_neu);
     667           0 :     return ret_neu_m;
     668             : }
     669             : 
     670             : // singleton instance
     671             : AP_Avoidance *AP_Avoidance::_singleton;
     672             : 
     673             : namespace AP {
     674             : 
     675           5 : AP_Avoidance *ap_avoidance()
     676             : {
     677           5 :     return AP_Avoidance::get_singleton();
     678             : }
     679             : 
     680             : }
     681             : 
     682             : #endif // AP_ADSB_AVOIDANCE_ENABLED

Generated by: LCOV version 1.14