use crate::dt::model::{Arcade, CurvatureDrive, Drivetrain, Tank};
use crate::util::utils::GroupErrors;
use crate::{MotorGroup, OmniWheel, QLength, Vector2};
pub struct DifferentialDrive {
left: MotorGroup,
right: MotorGroup,
wheel: OmniWheel,
pub(crate) width: QLength,
ratio: f64,
expo: ExpoDrive
}
pub struct ExpoDrive {
n: f64,
k: f64,
eps: f64,
}
impl ExpoDrive {
pub fn new(n: f64, k: f64, eps: Option<f64>) -> Self {
Self { n, k, eps: eps.unwrap_or(0.0) }
}
pub fn calculate(&self, x: f64, y: f64) -> Vector2<f64> {
{
assert!(-1.0 <= x && x <= 1.0, "x must be between [-1, 1]");
}
{
assert!(-1.0 <= y && y <= 1.0, "y must be between [-1, 1]");
}
if x == 0. && y == 0. {
return Vector2::<f64>::new(0.0, 0.0);
}
let m = libm::pow(libm::pow(libm::fabs(x), self.n+2.) + libm::pow(libm::fabs(y), self.n+2.), 1./(self.n+2.));
let f = libm::pow(m, self.k)/libm::sqrt(x*x + y*y);
let mut fx = f*x;
let mut fy = f*y;
if fx <= self.eps {
fx = 0.;
}
if fy <= self.eps {
fy = 0.;
}
Vector2::<f64>::new(fx, fy)
}
}
impl DifferentialDrive {
#[inline]
pub fn new(left: MotorGroup, right: MotorGroup, expo: ExpoDrive, wheel: OmniWheel, width: QLength, ratio: f64) -> Self {
Self { left, right, expo, wheel, width, ratio }
}
pub fn width(&self) -> QLength {
self.width
}
pub fn wheel(&self) -> &OmniWheel {
&self.wheel
}
}
impl Arcade for DifferentialDrive {
async fn drive_arcade(&mut self, left: f64, right: f64) -> Result<(), GroupErrors> {
let maximum = libm::fmax(libm::fabs(left), libm::fabs(right));
let (x, y) = self.expo.calculate(left, right).as_tuple();
let total = x + y;
let difference = x - y;
if x >= 0. {
if y >= 0. {
self.left.set_voltage(maximum).await?;
self.right.set_voltage(difference).await?;
} else {
self.left.set_voltage(total).await?;
self.right.set_voltage(maximum).await?;
}
} else if y >= 0. {
self.left.set_voltage(total).await?;
self.right.set_voltage(-maximum).await?;
} else {
self.left.set_voltage(-maximum).await?;
self.right.set_voltage(difference).await?;
}
Ok(())
}
}
impl Tank for DifferentialDrive {
async fn drive_tank(&mut self, left: f64, right: f64) -> Result<(), GroupErrors> {
let (x, y) = self.expo.calculate(left, right).as_tuple();
self.left.set_voltage(x * 12.0).await?;
self.right.set_voltage(y * 12.0).await?;
Ok(())
}
}
impl CurvatureDrive for DifferentialDrive {
async fn drive_curvature(&mut self, throttle: f64, curvature: f64) -> Result<(), GroupErrors> {
let left_speed = throttle + curvature;
let right_speed = throttle - curvature;
let max_magnitude = libm::fmax(libm::fabs(left_speed), libm::fabs(right_speed));
let (left_normalized, right_normalized) = if max_magnitude > 1.0 {
(left_speed / max_magnitude, right_speed / max_magnitude)
} else {
(left_speed, right_speed)
};
self.left.set_voltage(left_normalized * 12.0).await?;
self.right.set_voltage(right_normalized * 12.0).await?;
Ok(())
}
}
impl Drivetrain for DifferentialDrive {
async fn linear_velocity(&self) -> Result<f64, GroupErrors> {
let left = self.left.velocity().await? as f64;
let right = self.right.velocity().await? as f64;
let avg = (left+right)/2.;
let vel = avg * self.ratio * (std::f64::consts::TAU / 60.) * self.wheel.size().as_meters();
Ok(vel)
}
async fn angular_velocity(&self) -> Result<f64, GroupErrors> {
let left_rpm = self.left.velocity().await? as f64;
let right_rpm = self.right.velocity().await? as f64;
let vel = |rpm: f64| -> f64 {
let wheel_rpm = rpm * self.ratio;
let angular_vel = wheel_rpm * (2.0 * core::f64::consts::PI / 60.0);
angular_vel * self.wheel.size().as_meters()
};
let left = vel(left_rpm);
let right = vel(right_rpm);
let angular_vel = (right - left) / self.width.as_meters();
Ok(angular_vel)
}
}