#include <stdio.h>
#include "Plane.h"
#if HAL_ADSB_ENABLED || AP_ADSB_AVOIDANCE_ENABLED
void Plane::avoidance_adsb_update(void)
{
#if HAL_ADSB_ENABLED
adsb.update();
#endif
#if AP_ADSB_AVOIDANCE_ENABLED
avoidance_adsb.update();
#endif
}
#endif
#if AP_ADSB_AVOIDANCE_ENABLED
MAV_COLLISION_ACTION AP_Avoidance_Plane::handle_avoidance(const AP_Avoidance::Obstacle *obstacle, MAV_COLLISION_ACTION requested_action)
{
MAV_COLLISION_ACTION actual_action = requested_action;
bool failsafe_state_change = false;
if (!plane.failsafe.adsb) {
plane.failsafe.adsb = true;
failsafe_state_change = true;
prev_control_mode_number = plane.control_mode->mode_number();
}
bool flightmode_prohibits_action = false;
if (plane.control_mode == &plane.mode_manual ||
(plane.control_mode == &plane.mode_auto && !plane.auto_state.takeoff_complete) ||
(plane.flight_stage == AP_FixedWing::FlightStage::LAND) ||
plane.control_mode == &plane.mode_autotune) {
flightmode_prohibits_action = true;
}
#if HAL_QUADPLANE_ENABLED
if (plane.control_mode == &plane.mode_qland) {
flightmode_prohibits_action = true;
}
#endif
if (flightmode_prohibits_action) {
actual_action = MAV_COLLISION_ACTION_NONE;
}
switch (actual_action) {
case MAV_COLLISION_ACTION_RTL:
if (failsafe_state_change) {
plane.set_mode(plane.mode_rtl, ModeReason::AVOIDANCE);
}
break;
case MAV_COLLISION_ACTION_HOVER:
if (failsafe_state_change) {
#if HAL_QUADPLANE_ENABLED
if (plane.quadplane.is_flying()) {
plane.set_mode(plane.mode_qloiter, ModeReason::AVOIDANCE);
break;
}
#endif
plane.set_mode(plane.mode_loiter, ModeReason::AVOIDANCE);
}
break;
case MAV_COLLISION_ACTION_ASCEND_OR_DESCEND: {
Location loc = plane.next_WP_loc;
if (handle_avoidance_vertical(obstacle, failsafe_state_change, loc)) {
plane.set_guided_WP(loc);
} else {
actual_action = MAV_COLLISION_ACTION_NONE;
}
break;
}
case MAV_COLLISION_ACTION_MOVE_HORIZONTALLY: {
Location loc = plane.next_WP_loc;
if (handle_avoidance_horizontal(obstacle, failsafe_state_change, loc)) {
plane.set_guided_WP(loc);
} else {
actual_action = MAV_COLLISION_ACTION_NONE;
}
break;
}
case MAV_COLLISION_ACTION_MOVE_PERPENDICULAR:
{
Location loc = plane.next_WP_loc;
const bool success_vert = handle_avoidance_vertical(obstacle, failsafe_state_change, loc);
const bool success_hor = handle_avoidance_horizontal(obstacle, failsafe_state_change, loc);
if (success_vert || success_hor) {
plane.set_guided_WP(loc);
} else {
actual_action = MAV_COLLISION_ACTION_NONE;
}
}
break;
case MAV_COLLISION_ACTION_NONE:
return actual_action;
case MAV_COLLISION_ACTION_REPORT:
default:
break;
}
if (failsafe_state_change) {
gcs().send_text(MAV_SEVERITY_ALERT, "Avoid: Performing action: %d", actual_action);
}
return actual_action;
}
void AP_Avoidance_Plane::handle_recovery(RecoveryAction recovery_action)
{
if (plane.failsafe.adsb) {
plane.failsafe.adsb = false;
gcs().send_text(MAV_SEVERITY_INFO, "Avoid: Resuming with action: %u", (unsigned)recovery_action);
if (plane.control_mode_reason == ModeReason::AVOIDANCE) {
switch (recovery_action) {
case RecoveryAction::REMAIN_IN_AVOID_ADSB:
break;
case RecoveryAction::RESUME_PREVIOUS_FLIGHTMODE:
plane.set_mode_by_number(prev_control_mode_number, ModeReason::AVOIDANCE_RECOVERY);
break;
case RecoveryAction::RTL:
plane.set_mode(plane.mode_rtl, ModeReason::AVOIDANCE_RECOVERY);
break;
case RecoveryAction::RESUME_IF_AUTO_ELSE_LOITER:
if (prev_control_mode_number == Mode::Number::AUTO) {
plane.set_mode(plane.mode_auto, ModeReason::AVOIDANCE_RECOVERY);
} else {
plane.set_guided_WP(plane.current_loc);
}
break;
default:
plane.set_guided_WP(plane.current_loc);
break;
}
}
}
}
bool AP_Avoidance_Plane::check_flightmode(bool allow_mode_change)
{
if (allow_mode_change && plane.control_mode != &plane.mode_avoidADSB) {
plane.set_mode(plane.mode_avoidADSB, ModeReason::AVOIDANCE);
}
return (plane.control_mode == &plane.mode_avoidADSB);
}
bool AP_Avoidance_Plane::handle_avoidance_vertical(const AP_Avoidance::Obstacle *obstacle, bool allow_mode_change, Location &new_loc)
{
if (!check_flightmode(allow_mode_change)) {
return false;
}
if (plane.current_loc.alt > obstacle->_location.alt) {
new_loc.set_alt_cm(plane.current_loc.alt + 1000, Location::AltFrame::ABSOLUTE);
return true;
} else if (plane.current_loc.alt > plane.g.RTL_altitude*100) {
new_loc.set_alt_cm(plane.current_loc.alt - 1000, Location::AltFrame::ABSOLUTE);
return true;
}
return false;
}
bool AP_Avoidance_Plane::handle_avoidance_horizontal(const AP_Avoidance::Obstacle *obstacle, bool allow_mode_change, Location &new_loc)
{
if (!check_flightmode(allow_mode_change)) {
return false;
}
Vector3f velocity_neu;
if (get_vector_perpendicular(obstacle, velocity_neu)) {
velocity_neu.z = 0.0f;
if (is_zero(velocity_neu.x) && is_zero(velocity_neu.y)) {
return false;
}
velocity_neu.normalize();
velocity_neu *= 10000;
new_loc.offset(velocity_neu.x, velocity_neu.y);
return true;
}
return false;
}
#endif