2019-01-15 13:46:13 -04:00
|
|
|
#include "Plane.h"
|
|
|
|
|
2023-02-12 09:33:27 -04:00
|
|
|
Mode::Mode() :
|
|
|
|
ahrs(plane.ahrs)
|
2021-09-10 03:28:21 -03:00
|
|
|
#if HAL_QUADPLANE_ENABLED
|
2023-02-12 09:33:27 -04:00
|
|
|
, quadplane(plane.quadplane),
|
2021-07-24 11:06:30 -03:00
|
|
|
pos_control(plane.quadplane.pos_control),
|
|
|
|
attitude_control(plane.quadplane.attitude_control),
|
|
|
|
loiter_nav(plane.quadplane.loiter_nav),
|
|
|
|
poscontrol(plane.quadplane.poscontrol)
|
2021-09-10 03:28:21 -03:00
|
|
|
#endif
|
2019-01-15 13:46:13 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mode::exit()
|
|
|
|
{
|
|
|
|
// call sub-classes exit
|
|
|
|
_exit();
|
2023-01-26 16:59:58 -04:00
|
|
|
// stop autotuning if not AUTOTUNE mode
|
|
|
|
if (plane.control_mode != &plane.mode_autotune){
|
|
|
|
plane.autotune_restore();
|
|
|
|
}
|
|
|
|
|
2019-01-15 13:46:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Mode::enter()
|
|
|
|
{
|
2022-02-06 22:23:17 -04:00
|
|
|
#if AP_SCRIPTING_ENABLED
|
|
|
|
// reset nav_scripting.enabled
|
|
|
|
plane.nav_scripting.enabled = false;
|
|
|
|
#endif
|
|
|
|
|
2019-01-15 13:46:13 -04:00
|
|
|
// cancel inverted flight
|
|
|
|
plane.auto_state.inverted_flight = false;
|
|
|
|
|
|
|
|
// don't cross-track when starting a mission
|
|
|
|
plane.auto_state.next_wp_crosstrack = false;
|
|
|
|
|
|
|
|
// reset landing check
|
|
|
|
plane.auto_state.checked_for_autoland = false;
|
|
|
|
|
|
|
|
// zero locked course
|
|
|
|
plane.steer_state.locked_course_err = 0;
|
2021-12-03 03:27:16 -04:00
|
|
|
plane.steer_state.locked_course = false;
|
2019-01-15 13:46:13 -04:00
|
|
|
|
|
|
|
// reset crash detection
|
|
|
|
plane.crash_state.is_crashed = false;
|
|
|
|
plane.crash_state.impact_detected = false;
|
|
|
|
|
|
|
|
// reset external attitude guidance
|
|
|
|
plane.guided_state.last_forced_rpy_ms.zero();
|
|
|
|
plane.guided_state.last_forced_throttle_ms = 0;
|
|
|
|
|
2020-05-16 01:02:01 -03:00
|
|
|
#if OFFBOARD_GUIDED == ENABLED
|
|
|
|
plane.guided_state.target_heading = -4; // radians here are in range -3.14 to 3.14, so a default value needs to be outside that range
|
|
|
|
plane.guided_state.target_heading_type = GUIDED_HEADING_NONE;
|
|
|
|
plane.guided_state.target_airspeed_cm = -1; // same as above, although an airspeed of -1 is rare on plane.
|
|
|
|
plane.guided_state.target_alt = -1; // same as above, although a target alt of -1 is rare on plane.
|
2023-08-05 14:54:35 -03:00
|
|
|
plane.guided_state.target_alt_time_ms = 0;
|
2020-05-16 01:02:01 -03:00
|
|
|
plane.guided_state.last_target_alt = 0;
|
|
|
|
#endif
|
|
|
|
|
2022-06-02 05:28:26 -03:00
|
|
|
#if AP_CAMERA_ENABLED
|
2019-01-15 13:46:13 -04:00
|
|
|
plane.camera.set_is_auto_mode(this == &plane.mode_auto);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// zero initial pitch and highest airspeed on mode change
|
|
|
|
plane.auto_state.highest_airspeed = 0;
|
2023-02-12 09:33:27 -04:00
|
|
|
plane.auto_state.initial_pitch_cd = ahrs.pitch_sensor;
|
2019-01-15 13:46:13 -04:00
|
|
|
|
|
|
|
// disable taildrag takeoff on mode change
|
|
|
|
plane.auto_state.fbwa_tdrag_takeoff_mode = false;
|
|
|
|
|
|
|
|
// start with previous WP at current location
|
|
|
|
plane.prev_WP_loc = plane.current_loc;
|
|
|
|
|
|
|
|
// new mode means new loiter
|
|
|
|
plane.loiter.start_time_ms = 0;
|
|
|
|
|
|
|
|
// record time of mode change
|
|
|
|
plane.last_mode_change_ms = AP_HAL::millis();
|
|
|
|
|
2021-09-04 16:22:35 -03:00
|
|
|
// set VTOL auto state
|
|
|
|
plane.auto_state.vtol_mode = is_vtol_mode();
|
2019-01-15 13:46:13 -04:00
|
|
|
plane.auto_state.vtol_loiter = false;
|
|
|
|
|
2021-02-23 12:32:06 -04:00
|
|
|
// initialize speed variable used in AUTO and GUIDED for DO_CHANGE_SPEED commands
|
|
|
|
plane.new_airspeed_cm = -1;
|
|
|
|
|
2022-03-08 22:16:32 -04:00
|
|
|
#if HAL_QUADPLANE_ENABLED
|
|
|
|
quadplane.mode_enter();
|
|
|
|
#endif
|
|
|
|
|
2023-10-03 03:33:54 -03:00
|
|
|
#if AP_TERRAIN_AVAILABLE
|
|
|
|
plane.target_altitude.terrain_following_pending = false;
|
|
|
|
#endif
|
|
|
|
|
2019-01-15 13:46:13 -04:00
|
|
|
bool enter_result = _enter();
|
|
|
|
|
|
|
|
if (enter_result) {
|
|
|
|
// -------------------
|
|
|
|
// these must be done AFTER _enter() because they use the results to set more flags
|
|
|
|
|
|
|
|
// start with throttle suppressed in auto_throttle modes
|
2020-12-23 01:25:35 -04:00
|
|
|
plane.throttle_suppressed = does_auto_throttle();
|
2020-09-19 05:38:20 -03:00
|
|
|
#if HAL_ADSB_ENABLED
|
2020-12-23 00:56:54 -04:00
|
|
|
plane.adsb.set_is_auto_mode(does_auto_navigation());
|
2020-09-19 05:38:20 -03:00
|
|
|
#endif
|
2019-03-22 00:59:00 -03:00
|
|
|
|
|
|
|
// reset steering integrator on mode change
|
|
|
|
plane.steerController.reset_I();
|
|
|
|
|
|
|
|
// update RC failsafe, as mode change may have necessitated changing the failsafe throttle
|
|
|
|
plane.control_failsafe();
|
2022-08-06 17:43:59 -03:00
|
|
|
|
|
|
|
#if AP_FENCE_ENABLED
|
|
|
|
// pilot requested flight mode change during a fence breach indicates pilot is attempting to manually recover
|
|
|
|
// this flight mode change could be automatic (i.e. fence, battery, GPS or GCS failsafe)
|
|
|
|
// but it should be harmless to disable the fence temporarily in these situations as well
|
|
|
|
plane.fence.manual_recovery_start();
|
|
|
|
#endif
|
2019-01-15 13:46:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return enter_result;
|
|
|
|
}
|
|
|
|
|
2020-08-24 13:44:50 -03:00
|
|
|
bool Mode::is_vtol_man_throttle() const
|
|
|
|
{
|
2021-09-10 03:28:21 -03:00
|
|
|
#if HAL_QUADPLANE_ENABLED
|
2021-07-14 17:15:49 -03:00
|
|
|
if (plane.quadplane.tailsitter.is_in_fw_flight() &&
|
2020-08-24 13:44:50 -03:00
|
|
|
plane.quadplane.assisted_flight) {
|
2020-10-13 13:59:30 -03:00
|
|
|
// We are a tailsitter that has fully transitioned to Q-assisted forward flight.
|
|
|
|
// In this case the forward throttle directly drives the vertical throttle so
|
2020-08-24 13:44:50 -03:00
|
|
|
// set vertical throttle state to match the forward throttle state. Confusingly the booleans are inverted,
|
2020-12-23 01:25:35 -04:00
|
|
|
// forward throttle uses 'does_auto_throttle' whereas vertical uses 'is_vtol_man_throttle'.
|
|
|
|
return !does_auto_throttle();
|
2020-08-24 13:44:50 -03:00
|
|
|
}
|
2021-09-10 03:28:21 -03:00
|
|
|
#endif
|
2020-08-24 13:44:50 -03:00
|
|
|
return false;
|
|
|
|
}
|
2022-12-22 15:02:17 -04:00
|
|
|
|
|
|
|
void Mode::update_target_altitude()
|
|
|
|
{
|
|
|
|
Location target_location;
|
|
|
|
|
|
|
|
if (plane.landing.is_flaring()) {
|
|
|
|
// during a landing flare, use TECS_LAND_SINK as a target sink
|
|
|
|
// rate, and ignores the target altitude
|
|
|
|
plane.set_target_altitude_location(plane.next_WP_loc);
|
|
|
|
} else if (plane.landing.is_on_approach()) {
|
|
|
|
plane.landing.setup_landing_glide_slope(plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.target_altitude.offset_cm);
|
|
|
|
plane.landing.adjust_landing_slope_for_rangefinder_bump(plane.rangefinder_state, plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.auto_state.wp_distance, plane.target_altitude.offset_cm);
|
|
|
|
} else if (plane.landing.get_target_altitude_location(target_location)) {
|
|
|
|
plane.set_target_altitude_location(target_location);
|
|
|
|
#if HAL_SOARING_ENABLED
|
|
|
|
} else if (plane.g2.soaring_controller.is_active() && plane.g2.soaring_controller.get_throttle_suppressed()) {
|
|
|
|
// Reset target alt to current alt, to prevent large altitude errors when gliding.
|
|
|
|
plane.set_target_altitude_location(plane.current_loc);
|
|
|
|
plane.reset_offset_altitude();
|
|
|
|
#endif
|
|
|
|
} else if (plane.reached_loiter_target()) {
|
|
|
|
// once we reach a loiter target then lock to the final
|
|
|
|
// altitude target
|
|
|
|
plane.set_target_altitude_location(plane.next_WP_loc);
|
|
|
|
} else if (plane.target_altitude.offset_cm != 0 &&
|
|
|
|
!plane.current_loc.past_interval_finish_line(plane.prev_WP_loc, plane.next_WP_loc)) {
|
|
|
|
// control climb/descent rate
|
|
|
|
plane.set_target_altitude_proportion(plane.next_WP_loc, 1.0f-plane.auto_state.wp_proportion);
|
|
|
|
|
|
|
|
// stay within the range of the start and end locations in altitude
|
|
|
|
plane.constrain_target_altitude_location(plane.next_WP_loc, plane.prev_WP_loc);
|
|
|
|
} else {
|
|
|
|
plane.set_target_altitude_location(plane.next_WP_loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
plane.altitude_error_cm = plane.calc_altitude_error_cm();
|
|
|
|
}
|
2023-02-14 08:18:09 -04:00
|
|
|
|
|
|
|
// returns true if the vehicle can be armed in this mode
|
|
|
|
bool Mode::pre_arm_checks(size_t buflen, char *buffer) const
|
|
|
|
{
|
|
|
|
if (!_pre_arm_checks(buflen, buffer)) {
|
|
|
|
if (strlen(buffer) == 0) {
|
|
|
|
// If no message is provided add a generic one
|
|
|
|
hal.util->snprintf(buffer, buflen, "mode not armable");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Auto and Guided do not call this to bypass the q-mode check.
|
|
|
|
bool Mode::_pre_arm_checks(size_t buflen, char *buffer) const
|
|
|
|
{
|
|
|
|
#if HAL_QUADPLANE_ENABLED
|
|
|
|
if (plane.quadplane.enabled() && !is_vtol_mode() &&
|
|
|
|
plane.quadplane.option_is_set(QuadPlane::OPTION::ONLY_ARM_IN_QMODE_OR_AUTO)) {
|
|
|
|
hal.util->snprintf(buffer, buflen, "not Q mode");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|