#include "Plane.h"
#include <AP_Stats/AP_Stats.h>
#define CRASH_DETECTION_DELAY_MS 500
#define IS_FLYING_IMPACT_TIMER_MS 3000
#define GPS_IS_FLYING_SPEED_MS 1.5
void Plane::update_is_flying_5Hz(void)
{
float aspeed=0;
bool is_flying_bool = false;
uint32_t now_ms = AP_HAL::millis();
const float ground_speed_thresh_ms = (aparm.min_groundspeed > 0) ? (aparm.min_groundspeed*0.9) : GPS_IS_FLYING_SPEED_MS;
bool gps_confirmed_movement = (gps.status() >= AP_GPS::GPS_OK_FIX_3D) &&
(gps.ground_speed() >= ground_speed_thresh_ms);
const float airspeed_threshold = MAX(aparm.airspeed_min,2)*0.75f;
bool airspeed_movement = ahrs.airspeed_EAS(aspeed) && (aspeed >= airspeed_threshold);
if (gps.status() < AP_GPS::GPS_OK_FIX_2D && arming.is_armed() && !airspeed_movement && isFlyingProbability > 0.3) {
airspeed_movement = aspeed >= airspeed_threshold;
}
#if HAL_QUADPLANE_ENABLED
is_flying_bool = quadplane.is_flying();
#endif
if (is_flying_bool) {
} else if(arming.is_armed()) {
bool gps_lost_recently = (gps.last_fix_time_ms() > 0) &&
(gps.status() < AP_GPS::GPS_OK_FIX_2D) &&
(now_ms - gps.last_fix_time_ms() < 5000);
if ((auto_state.last_flying_ms > 0) && gps_lost_recently) {
is_flying_bool = airspeed_movement;
} else {
is_flying_bool = (airspeed_movement && !AP::ins().is_still()) ||
gps_confirmed_movement;
}
if (control_mode == &mode_auto) {
if (g.crash_accel_threshold == 0) {
crash_state.impact_detected = false;
} else if (ins.get_accel_peak_hold_neg_x() < -(g.crash_accel_threshold)) {
crash_state.impact_detected = true;
crash_state.impact_timer_ms = now_ms;
if (isFlyingProbability > 0.2f) {
isFlyingProbability = 0.2f;
}
} else if (crash_state.impact_detected &&
(now_ms - crash_state.impact_timer_ms > IS_FLYING_IMPACT_TIMER_MS)) {
crash_state.impact_detected = false;
}
switch (flight_stage)
{
case AP_FixedWing::FlightStage::TAKEOFF:
break;
case AP_FixedWing::FlightStage::NORMAL:
if (in_preLaunch_flight_stage()) {
is_flying_bool = false;
crash_state.is_crashed = false;
auto_state.started_flying_in_auto_ms = 0;
}
break;
case AP_FixedWing::FlightStage::VTOL:
break;
case AP_FixedWing::FlightStage::LAND:
if (landing.is_on_approach() && auto_state.sink_rate > 0.2f) {
is_flying_bool = true;
}
break;
case AP_FixedWing::FlightStage::ABORT_LANDING:
if (auto_state.sink_rate < -0.5f) {
is_flying_bool = true;
}
break;
default:
break;
}
}
} else {
is_flying_bool = airspeed_movement && gps_confirmed_movement;
if ((flight_stage == AP_FixedWing::FlightStage::TAKEOFF) || landing.is_flaring()) {
is_flying_bool = false;
}
}
if (!crash_state.impact_detected || !is_flying_bool) {
isFlyingProbability = (0.85f * isFlyingProbability) + (0.15f * (float)is_flying_bool);
}
bool new_is_flying = is_flying();
if (new_is_flying) {
auto_state.last_flying_ms = now_ms;
if (!previous_is_flying) {
started_flying_ms = now_ms;
}
if ((control_mode == &mode_auto) &&
((auto_state.started_flying_in_auto_ms == 0) || !previous_is_flying) ) {
auto_state.started_flying_in_auto_ms = now_ms;
}
}
previous_is_flying = new_is_flying;
#if HAL_ADSB_ENABLED
adsb.set_is_flying(new_is_flying);
#endif
#if HAL_PARACHUTE_ENABLED
parachute.set_is_flying(new_is_flying);
#endif
#if AP_STATS_ENABLED
AP::stats()->set_flying(new_is_flying);
#endif
AP_Notify::flags.flying = new_is_flying;
crash_detection_update();
#if HAL_LOGGING_ENABLED
Log_Write_Status();
#endif
set_likely_flying(new_is_flying);
ground_mode = !is_flying() && !arming.is_armed_and_safety_off();
}
bool Plane::is_flying(void)
{
if (arming.is_armed_and_safety_off()) {
#if HAL_QUADPLANE_ENABLED
if (quadplane.is_flying_vtol()) {
return true;
}
#endif
return (isFlyingProbability >= 0.1f);
}
return (isFlyingProbability >= 0.9f);
}
void Plane::crash_detection_update(void)
{
if (control_mode != &mode_auto || !aparm.crash_detection_enable)
{
crash_state.debounce_timer_ms = 0;
crash_state.is_crashed = false;
return;
}
uint32_t now_ms = AP_HAL::millis();
bool crashed_near_land_waypoint = false;
bool crashed = false;
bool been_auto_flying = (auto_state.started_flying_in_auto_ms > 0) &&
(now_ms - auto_state.started_flying_in_auto_ms >= 2500);
if (!is_flying() && arming.is_armed())
{
if (landing.is_expecting_impact()) {
if (!crash_state.checkedHardLanding &&
been_auto_flying &&
(fabsf(ahrs.get_roll_deg()) > 60 || fabsf(ahrs.get_pitch_deg()) > 60)) {
crashed = true;
crashed_near_land_waypoint =
current_loc.get_distance(mission.get_current_nav_cmd().content.location) < 75;
crash_state.debounce_timer_ms = now_ms;
crash_state.debounce_time_total_ms = 0;
}
crash_state.checkedHardLanding = true;
} else if (landing.is_on_approach()) {
if (been_auto_flying) {
crashed = true;
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
}
} else {
switch (flight_stage)
{
case AP_FixedWing::FlightStage::TAKEOFF:
if (g2.takeoff_throttle_accel_count == 1 && g.takeoff_throttle_min_accel > 0 &&
!throttle_suppressed) {
crashed = true;
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
}
break;
case AP_FixedWing::FlightStage::NORMAL:
if (!in_preLaunch_flight_stage() && been_auto_flying) {
crashed = true;
crash_state.debounce_time_total_ms = CRASH_DETECTION_DELAY_MS;
}
break;
case AP_FixedWing::FlightStage::VTOL:
crashed = false;
break;
default:
break;
}
}
} else {
crash_state.checkedHardLanding = false;
}
if (gps.status() < AP_GPS::GPS_OK_FIX_3D) {
#if AP_AIRSPEED_ENABLED
if (!airspeed.use() || !airspeed.healthy()) {
crashed = false;
}
#else
crashed = false;
#endif
}
if (!crashed) {
crash_state.debounce_timer_ms = 0;
} else if (crash_state.debounce_timer_ms == 0) {
crash_state.debounce_timer_ms = now_ms;
} else if ((now_ms - crash_state.debounce_timer_ms >= crash_state.debounce_time_total_ms) && !crash_state.is_crashed) {
crash_state.is_crashed = true;
if (aparm.crash_detection_enable & CRASH_DETECT_ACTION_BITMASK_DISARM) {
arming.disarm(AP_Arming::Method::CRASH);
}
if (crashed_near_land_waypoint) {
gcs().send_text(MAV_SEVERITY_CRITICAL, "Hard landing detected");
} else {
gcs().send_text(MAV_SEVERITY_EMERGENCY, "Crash detected");
}
}
}
bool Plane::in_preLaunch_flight_stage(void)
{
if (control_mode == &mode_takeoff && throttle_suppressed) {
return true;
}
#if HAL_QUADPLANE_ENABLED
if (quadplane.is_vtol_takeoff(mission.get_current_nav_cmd().id)) {
return false;
}
#endif
return (control_mode == &mode_auto &&
throttle_suppressed &&
flight_stage == AP_FixedWing::FlightStage::NORMAL &&
mission.get_current_nav_cmd().id == MAV_CMD_NAV_TAKEOFF);
}