use crate::{utils::units::Length, motion::pursuit::control::PursuitControl};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct BasicControl {
pub track_width: Length,
pub tolerance: Length,
}
impl BasicControl {
pub fn new(track_width: Length, tolerance: Length) -> Self {
Self {
track_width,
tolerance,
}
}
}
impl PursuitControl for BasicControl {
fn control(&self, x: Length, y: Length, lookahead: Length) -> ((f64, f64), bool) {
let x_in = x.as_inches();
let y_in = y.as_inches();
let track_w_in = self.track_width.as_inches();
let lookahead_in = lookahead.as_inches();
let d2 = x_in * x_in + y_in * y_in;
let dist = d2.sqrt();
let kappa = if d2 < 1e-12 { 0.0 } else { 2.0 * x_in / d2 };
let speed_scale = if lookahead_in <= 1e-9 {
1.0
} else {
(dist / lookahead_in).clamp(0.0, 1.0)
};
let mut left = speed_scale * (2.0 + kappa * track_w_in) / 2.0;
let mut right = speed_scale * (2.0 - kappa * track_w_in) / 2.0;
let mag = left.abs().max(right.abs());
if mag > 1.0 {
left /= mag;
right /= mag;
}
let max_voltage = 12.0;
let dir = y_in.signum();
(
(dir * left * max_voltage, dir * right * max_voltage),
dist > self.tolerance.as_inches(),
)
}
}
#[cfg(test)]
mod tests {
use crate::{
utils::units::{Length, assert_almost_eq},
motion::pursuit::control::{PursuitControl, basic::BasicControl},
};
#[test]
fn basic_normal() {
let basic_control = BasicControl {
track_width: Length::from_inches(12.0),
tolerance: Length::from_inches(0.1),
};
let ((left, right), _) = basic_control.control(
Length::from_inches(15.0),
Length::from_inches(15.0),
Length::from_inches(8.0),
);
assert_almost_eq(left, 12.0);
assert_almost_eq(right, 5.14285);
}
#[test]
fn basic_negative_x() {
let basic_control = BasicControl {
track_width: Length::from_inches(12.0),
tolerance: Length::from_inches(0.1),
};
let ((left, right), _) = basic_control.control(
Length::from_inches(-15.0),
Length::from_inches(15.0),
Length::from_inches(8.0),
);
assert_almost_eq(left, 5.14285);
assert_almost_eq(right, 12.0);
}
#[test]
fn basic_negative_y() {
let basic_control = BasicControl {
track_width: Length::from_inches(12.0),
tolerance: Length::from_inches(0.1),
};
let ((left, right), _) = basic_control.control(
Length::from_inches(15.0),
Length::from_inches(-15.0),
Length::from_inches(8.0),
);
assert_almost_eq(right, -5.14285);
assert_almost_eq(left, -12.0);
}
#[test]
fn basic_negative_both() {
let basic_control = BasicControl {
track_width: Length::from_inches(12.0),
tolerance: Length::from_inches(0.1),
};
let ((left, right), _) = basic_control.control(
Length::from_inches(-15.0),
Length::from_inches(-15.0),
Length::from_inches(8.0),
);
assert_almost_eq(left, -5.14285);
assert_almost_eq(right, -12.0);
}
#[test]
fn tolernace_check() {
let basic_control = BasicControl {
track_width: Length::from_inches(12.0),
tolerance: Length::from_inches(5.1),
};
let ((..), running) = basic_control.control(
Length::from_inches(4.0),
Length::from_inches(3.0),
Length::from_inches(8.0),
);
assert!(!running);
}
}