#include "Copter.h"
Mode::AutoYaw Mode::auto_yaw;
float Mode::AutoYaw::roi_yaw_rad() const
{
Vector2f pos_ne_m;
if (AP::ahrs().get_relative_position_NE_origin_float(pos_ne_m)){
return get_bearing_rad(pos_ne_m, roi_ned_m.xy());
}
return copter.attitude_control->get_att_target_euler_rad().z;
}
float Mode::AutoYaw::look_ahead_yaw_rad()
{
Vector3f vel_ned_ms;
if (copter.position_ok() && AP::ahrs().get_velocity_NED(vel_ned_ms)) {
const float speed_ms_sq = vel_ned_ms.xy().length_squared();
if (speed_ms_sq > (YAW_LOOK_AHEAD_MIN_SPEED_MS * YAW_LOOK_AHEAD_MIN_SPEED_MS)) {
_look_ahead_yaw_rad = atan2f(vel_ned_ms.y,vel_ned_ms.x);
}
}
return _look_ahead_yaw_rad;
}
void Mode::AutoYaw::set_mode_to_default(bool rtl)
{
set_mode(default_mode(rtl));
}
Mode::AutoYaw::Mode Mode::AutoYaw::default_mode(bool rtl) const
{
switch (copter.g.wp_yaw_behavior) {
case WP_YAW_BEHAVIOR_NONE:
return Mode::HOLD;
case WP_YAW_BEHAVIOR_LOOK_AT_NEXT_WP_EXCEPT_RTL:
if (rtl) {
return Mode::HOLD;
} else {
return Mode::LOOK_AT_NEXT_WP;
}
case WP_YAW_BEHAVIOR_LOOK_AHEAD:
return Mode::LOOK_AHEAD;
case WP_YAW_BEHAVIOR_LOOK_AT_NEXT_WP:
default:
return Mode::LOOK_AT_NEXT_WP;
}
}
void Mode::AutoYaw::set_mode(Mode yaw_mode)
{
if (_mode == yaw_mode) {
return;
}
_last_mode = _mode;
_mode = yaw_mode;
switch (_mode) {
case Mode::HOLD:
break;
case Mode::LOOK_AT_NEXT_WP:
break;
case Mode::ROI:
break;
case Mode::FIXED:
break;
case Mode::LOOK_AHEAD:
_look_ahead_yaw_rad = copter.ahrs.get_yaw_rad();
break;
case Mode::RESET_TO_ARMED_YAW:
break;
case Mode::ANGLE_RATE:
break;
case Mode::RATE:
_yaw_rate_rads = 0.0;
break;
case Mode::CIRCLE:
case Mode::PILOT_RATE:
case Mode::WEATHERVANE:
break;
}
}
void Mode::AutoYaw::set_fixed_yaw_rad(float yaw_rad, float yaw_rate_rads, int8_t direction, bool relative_angle)
{
_last_update_ms = millis();
const float angle_rad = yaw_rad;
if (relative_angle) {
if (_mode == Mode::HOLD) {
_yaw_angle_rad = copter.ahrs.get_yaw_rad();
}
_fixed_yaw_offset_rad = angle_rad * (direction >= 0 ? 1.0 : -1.0);
} else {
_fixed_yaw_offset_rad = wrap_PI(angle_rad - _yaw_angle_rad);
if (direction < 0 && is_positive(_fixed_yaw_offset_rad)) {
_fixed_yaw_offset_rad -= M_2PI;
} else if (direction > 0 && is_negative(_fixed_yaw_offset_rad)) {
_fixed_yaw_offset_rad += M_2PI;
}
}
if (!is_positive(yaw_rate_rads)) {
_fixed_yaw_slewrate_rads = copter.attitude_control->get_slew_yaw_max_rads();
} else {
_fixed_yaw_slewrate_rads = MIN(copter.attitude_control->get_slew_yaw_max_rads(), yaw_rate_rads);
}
set_mode(Mode::FIXED);
}
void Mode::AutoYaw::set_yaw_angle_and_rate_rad(float yaw_angle_rad, float yaw_rate_rads)
{
_last_update_ms = millis();
_yaw_angle_rad = yaw_angle_rad;
_yaw_rate_rads = yaw_rate_rads;
set_mode(Mode::ANGLE_RATE);
}
void Mode::AutoYaw::set_yaw_angle_offset_deg(const float yaw_angle_offset_deg)
{
_last_update_ms = millis();
_yaw_angle_rad = wrap_2PI(_yaw_angle_rad + radians(yaw_angle_offset_deg));
_yaw_rate_rads = 0.0f;
set_mode(Mode::ANGLE_RATE);
}
void Mode::AutoYaw::set_roi(const Location &roi_location)
{
if (!roi_location.initialised()) {
auto_yaw.set_mode_to_default(false);
#if HAL_MOUNT_ENABLED
copter.camera_mount.clear_roi_target();
#endif
} else {
#if HAL_MOUNT_ENABLED
if (!copter.camera_mount.has_pan_control()) {
if (roi_location.get_vector_from_origin_NED_m(roi_ned_m)) {
auto_yaw.set_mode(Mode::ROI);
}
}
copter.camera_mount.set_roi_target(roi_location);
#else
if (roi_location.get_vector_from_origin_NED_m(roi_ned_m)) {
auto_yaw.set_mode(Mode::ROI);
}
#endif
}
}
void Mode::AutoYaw::set_rate_rad(float turn_rate_rads)
{
set_mode(Mode::RATE);
_yaw_rate_rads = turn_rate_rads;
}
bool Mode::AutoYaw::reached_fixed_yaw_target()
{
if (mode() != Mode::FIXED) {
return true;
}
if (!is_zero(_fixed_yaw_offset_rad)) {
return false;
}
return (fabsf(wrap_PI(_yaw_angle_rad - copter.ahrs.get_yaw_rad())) <= radians(2));
}
float Mode::AutoYaw::yaw_rad()
{
switch (_mode) {
case Mode::ROI:
_yaw_angle_rad = roi_yaw_rad();
break;
case Mode::FIXED: {
const uint32_t now_ms = millis();
float dt = (now_ms - _last_update_ms) * 0.001;
_last_update_ms = now_ms;
float yaw_angle_step_rad = constrain_float(_fixed_yaw_offset_rad, - dt * _fixed_yaw_slewrate_rads, dt * _fixed_yaw_slewrate_rads);
_fixed_yaw_offset_rad -= yaw_angle_step_rad;
_yaw_angle_rad += yaw_angle_step_rad;
break;
}
case Mode::LOOK_AHEAD:
_yaw_angle_rad = look_ahead_yaw_rad();
break;
case Mode::RESET_TO_ARMED_YAW:
_yaw_angle_rad = copter.initial_armed_bearing_rad;
break;
case Mode::CIRCLE:
#if MODE_CIRCLE_ENABLED
if (copter.circle_nav->is_active()) {
_yaw_angle_rad = copter.circle_nav->get_yaw_rad();
}
#endif
break;
case Mode::ANGLE_RATE:{
const uint32_t now_ms = millis();
float dt = (now_ms - _last_update_ms) * 0.001;
_last_update_ms = now_ms;
_yaw_angle_rad += _yaw_rate_rads * dt;
break;
}
case Mode::RATE:
case Mode::WEATHERVANE:
case Mode::PILOT_RATE:
_yaw_angle_rad = copter.attitude_control->get_att_target_euler_rad().z;
break;
case Mode::LOOK_AT_NEXT_WP:
default:
_yaw_angle_rad = copter.pos_control->get_yaw_rad();
break;
}
return _yaw_angle_rad;
}
float Mode::AutoYaw::rate_rads()
{
switch (_mode) {
case Mode::HOLD:
case Mode::ROI:
case Mode::FIXED:
case Mode::LOOK_AHEAD:
case Mode::RESET_TO_ARMED_YAW:
case Mode::CIRCLE:
_yaw_rate_rads = 0.0f;
break;
case Mode::LOOK_AT_NEXT_WP:
_yaw_rate_rads = copter.pos_control->get_yaw_rate_rads();
break;
case Mode::PILOT_RATE:
_yaw_rate_rads = _pilot_yaw_rate_rads;
break;
case Mode::ANGLE_RATE:
case Mode::RATE:
case Mode::WEATHERVANE:
break;
}
return _yaw_rate_rads;
}
AC_AttitudeControl::HeadingCommand Mode::AutoYaw::get_heading()
{
_pilot_yaw_rate_rads = 0.0;
if (rc().has_valid_input() && copter.flightmode->use_pilot_yaw()) {
_pilot_yaw_rate_rads = copter.flightmode->get_pilot_desired_yaw_rate_rads();
if (!is_zero(_pilot_yaw_rate_rads)) {
auto_yaw.set_mode(AutoYaw::Mode::PILOT_RATE);
}
} else if (auto_yaw.mode() == AutoYaw::Mode::PILOT_RATE) {
auto_yaw.set_mode(AutoYaw::Mode::HOLD);
}
#if WEATHERVANE_ENABLED
update_weathervane(_pilot_yaw_rate_rads);
#endif
AC_AttitudeControl::HeadingCommand heading;
heading.yaw_angle_rad = auto_yaw.yaw_rad();
heading.yaw_rate_rads = auto_yaw.rate_rads();
switch (auto_yaw.mode()) {
case Mode::HOLD:
case Mode::RATE:
case Mode::PILOT_RATE:
case Mode::WEATHERVANE:
heading.heading_mode = AC_AttitudeControl::HeadingMode::Rate_Only;
break;
case Mode::LOOK_AT_NEXT_WP:
case Mode::ROI:
case Mode::FIXED:
case Mode::LOOK_AHEAD:
case Mode::RESET_TO_ARMED_YAW:
case Mode::ANGLE_RATE:
case Mode::CIRCLE:
heading.heading_mode = AC_AttitudeControl::HeadingMode::Angle_And_Rate;
break;
}
return heading;
}
#if WEATHERVANE_ENABLED
void Mode::AutoYaw::update_weathervane(const float pilot_yaw_rads)
{
if (!copter.flightmode->allows_weathervaning()) {
return;
}
float yaw_rate_cds;
if (copter.g2.weathervane.get_yaw_out(yaw_rate_cds, rad_to_cd(pilot_yaw_rads), copter.flightmode->get_alt_above_ground_m(),
copter.pos_control->get_roll_cd()-copter.attitude_control->get_roll_trim_cd(),
copter.pos_control->get_pitch_cd(),
copter.flightmode->is_taking_off(),
copter.flightmode->is_landing())) {
set_mode(Mode::WEATHERVANE);
_yaw_rate_rads = cd_to_rad(yaw_rate_cds);
return;
}
if (mode() == Mode::WEATHERVANE) {
_yaw_rate_rads = 0.0;
if (_last_mode == Mode::HOLD) {
set_mode_to_default(false);
} else {
set_mode(_last_mode);
}
}
}
#endif