#include "Rover.h"
#define AR_CIRCLE_ACCEL_DEFAULT 1.0
#define AR_CIRCLE_RADIUS_MIN 0.1
const AP_Param::GroupInfo ModeCircle::var_info[] = {
AP_GROUPINFO("_RADIUS", 1, ModeCircle, radius, 20),
AP_GROUPINFO("_SPEED", 2, ModeCircle, speed, 0),
AP_GROUPINFO("_DIR", 3, ModeCircle, direction, 0),
AP_GROUPEND
};
ModeCircle::ModeCircle() : Mode()
{
AP_Param::setup_object_defaults(this, var_info);
}
float ModeCircle::get_reached_distance() const
{
if (config.radius >= 0.5) {
return 0.2;
}
return 0.1;
}
bool ModeCircle::set_center(const Location& center_loc, float radius_m, bool dir_ccw)
{
Vector2f center_pos_cm;
if (!center_loc.get_vector_xy_from_origin_NE_cm(center_pos_cm)) {
return false;
}
if (!_enter()) {
return false;
}
config.center_pos = center_pos_cm * 0.01;
config.radius = MAX(fabsf(radius_m), AR_CIRCLE_RADIUS_MIN);
check_config_radius();
config.dir = dir_ccw ? Direction::CCW : Direction::CW;
init_target_yaw_rad();
config.center_loc = center_loc;
return true;
}
bool ModeCircle::_enter()
{
if (!AP::ahrs().get_relative_position_NE_origin_float(config.center_pos)) {
return false;
}
config.radius = MAX(fabsf(radius), AR_CIRCLE_RADIUS_MIN);
check_config_radius();
config.dir = (direction == 1) ? Direction::CCW : Direction::CW;
config.speed = is_positive(speed) ? speed : g2.wp_nav.get_default_speed();
target.yaw_rad = AP::ahrs().get_yaw_rad();
target.speed = 0;
config.center_loc = rover.current_loc;
check_config_speed();
tracking_back = false;
float atc_accel_max = MIN(g2.attitude_control.get_accel_max(), g2.attitude_control.get_decel_max());
if (!is_positive(atc_accel_max)) {
atc_accel_max = AR_CIRCLE_ACCEL_DEFAULT;
}
const float accel_max = is_positive(g2.wp_nav.get_default_accel()) ? MIN(g2.wp_nav.get_default_accel(), atc_accel_max) : atc_accel_max;
const float jerk_max = is_positive(g2.wp_nav.get_default_jerk()) ? g2.wp_nav.get_default_jerk() : accel_max;
g2.pos_control.set_limits(config.speed, accel_max, g2.attitude_control.get_turn_lat_accel_max(), jerk_max);
g2.pos_control.init();
angle_total_rad = 0;
reached_edge = false;
dist_to_edge_m = 0;
return true;
}
void ModeCircle::init_target_yaw_rad()
{
Vector2f curr_pos_NE;
if (!AP::ahrs().get_relative_position_NE_origin_float(curr_pos_NE)) {
target.yaw_rad = AP::ahrs().get_yaw_rad();
return;
}
Vector2f center_to_veh = (curr_pos_NE - config.center_pos);
float dist_m = center_to_veh.length();
if (is_zero(dist_m)) {
target.yaw_rad = AP::ahrs().get_yaw_rad();
} else {
target.yaw_rad = center_to_veh.angle();
}
}
void ModeCircle::update()
{
Vector2f curr_pos;
const bool position_ok = AP::ahrs().get_relative_position_NE_origin_float(curr_pos);
if (!position_ok) {
stop_vehicle();
return;
}
const Vector2f center_to_veh = curr_pos - config.center_pos;
_distance_to_destination = (target.pos.tofloat() - curr_pos).length();
dist_to_edge_m = fabsf(center_to_veh.length() - config.radius);
if (!reached_edge) {
update_drive_to_radius();
} else if (dist_to_edge_m > 2 * MAX(g2.turn_radius, get_reached_distance()) && !tracking_back) {
config.speed = 0.8 * config.speed;
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Circle: cannot keep up, slowing to %.1fm/s", config.speed);
tracking_back = true;
} else if (dist_to_edge_m < MAX(g2.turn_radius, get_reached_distance()) && tracking_back) {
tracking_back = false;
} else {
update_circling();
}
g2.pos_control.update(rover.G_Dt);
const float desired_speed = g2.pos_control.get_desired_speed();
const float desired_turn_rate = g2.pos_control.get_desired_turn_rate_rads();
calc_steering_from_turn_rate(desired_turn_rate);
calc_throttle(desired_speed, true);
}
void ModeCircle::update_drive_to_radius()
{
const float dist_thresh_m = MAX(g2.turn_radius, get_reached_distance());
reached_edge |= dist_to_edge_m <= dist_thresh_m;
target.pos = config.center_pos.topostype();
target.pos.offset_bearing(degrees(target.yaw_rad), config.radius);
g2.pos_control.input_pos_target(target.pos, rover.G_Dt);
}
void ModeCircle::update_circling()
{
const float speed_change_max = (g2.pos_control.get_accel_max() * 0.5 * rover.G_Dt);
const float accel_fb = constrain_float(config.speed - target.speed, -speed_change_max, speed_change_max);
target.speed += accel_fb;
if (!tracking_back) {
const float circumference = 2.0 * M_PI * config.radius;
const float angular_rate_rad = (target.speed / circumference) * M_2PI * (config.dir == Direction::CW ? 1.0 : -1.0);
const float angle_dt = angular_rate_rad * rover.G_Dt;
target.yaw_rad = wrap_PI(target.yaw_rad + angle_dt);
angle_total_rad += angle_dt;
} else {
init_target_yaw_rad();
}
target.pos = config.center_pos.topostype();
target.pos.offset_bearing(degrees(target.yaw_rad), config.radius);
target.vel = Vector2f(target.speed, 0);
target.vel.rotate(target.yaw_rad + radians(90));
target.accel = Vector2f(-sq(target.speed) / config.radius, accel_fb / rover.G_Dt);
target.accel.rotate(target.yaw_rad);
g2.pos_control.set_pos_vel_accel_target(target.pos, target.vel, target.accel);
}
float ModeCircle::wp_bearing() const
{
Vector2f curr_pos_NE;
if (!AP::ahrs().get_relative_position_NE_origin_float(curr_pos_NE)) {
return 0;
}
Vector2f veh_to_center = (target.pos.tofloat() - curr_pos_NE);
if (veh_to_center.is_zero()) {
return 0;
}
return degrees(veh_to_center.angle());
}
float ModeCircle::nav_bearing() const
{
const Vector2p pos_error = g2.pos_control.get_pos_error();
if (pos_error.is_zero()) {
return 0;
}
return degrees(pos_error.angle());
}
float ModeCircle::get_desired_lat_accel() const
{
return g2.pos_control.get_desired_lat_accel();
}
bool ModeCircle::set_desired_speed(float speed_ms)
{
if (is_positive(speed_ms)) {
config.speed = speed_ms;
check_config_speed();
if (config.speed > g2.pos_control.get_speed_max()) {
g2.pos_control.set_limits(config.speed, g2.pos_control.get_accel_max(), g2.pos_control.get_lat_accel_max(), g2.pos_control.get_jerk_max());
}
return true;
}
return false;
}
bool ModeCircle::get_desired_location(Location& destination) const
{
destination = config.center_loc;
destination.offset_bearing(degrees(target.yaw_rad), config.radius);
return true;
}
void ModeCircle::check_config_speed()
{
const float speed_max = MAX(safe_sqrt(g2.attitude_control.get_turn_lat_accel_max() * config.radius), 0);
if (config.speed > speed_max) {
config.speed = speed_max;
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Circle: max speed is %4.1f", (double)config.speed);
}
}
void ModeCircle::check_config_radius()
{
if (config.radius < g2.turn_radius) {
config.radius = g2.turn_radius;
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Circle: radius increased to TURN_RADIUS (%4.1f)", (double)g2.turn_radius);
}
}