use std::cell::Cell;
use serde::{Deserialize, Serialize};
const MIN_GAP: f64 = 2.5;
const MAX_DECEL: f64 = -6.0;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct AccelerationModel {
max_acc: f64,
comf_dec: f64,
acc: Cell<f64>,
}
pub struct ModelParams {
pub max_acceleration: f64,
pub comf_deceleration: f64,
}
impl AccelerationModel {
pub fn new(params: &ModelParams) -> Self {
AccelerationModel {
max_acc: params.max_acceleration,
comf_dec: params.comf_deceleration,
acc: Cell::new(params.max_acceleration),
}
}
pub fn reset(&self) {
self.acc.set(self.max_acc);
}
pub fn acc(&self) -> f64 {
f64::max(self.acc.get(), MAX_DECEL)
}
pub fn emergency_stop(&self) {
self.acc.set(MAX_DECEL);
}
pub fn min_reach_time(&self, vel: f64, dist: f64, max_vel: f64) -> f64 {
let discr = 2.0 * self.max_acc * dist + vel.powi(2);
if discr < max_vel.powi(2) {
(discr.sqrt() - vel) / self.max_acc
} else {
let t = (max_vel - vel) / self.max_acc; let d = 0.5 * (vel + max_vel) * t;
t + (dist - d) / max_vel
}
}
pub fn apply_current_speed_limit(&self, vel: f64, speed_limit: f64) {
let max_acc = self.max_acc;
let this_acc = max_acc * (1. - (vel / speed_limit).powi(4));
self.acc.set(f64::min(self.acc.get(), this_acc));
}
pub fn apply_speed_limit(&self, vel: f64, speed_limit: f64, distance: f64) {
if distance <= 0.0 {
return self.apply_current_speed_limit(vel, speed_limit);
}
let comf_acc = -self.comf_dec;
let this_acc = (speed_limit.powi(2) - vel.powi(2)) / (2. * distance);
if this_acc <= comf_acc {
let this_acc = f64::max(2.0 * comf_acc, this_acc);
self.acc.set(f64::min(self.acc.get(), this_acc));
}
}
pub fn stop_at_line(&self, net_dist: f64, my_vel: f64) {
let acc = self.idm(net_dist, my_vel, 0.0);
self.acc.set(f64::min(self.acc.get(), acc));
}
pub fn follow_vehicle(&self, net_dist: f64, my_vel: f64, their_vel: f64) {
let acc = self.idm(net_dist, my_vel, their_vel);
self.acc.set(f64::min(self.acc.get(), acc));
}
pub fn stopping_distance(&self, my_vel: f64) -> f64 {
let min_dist = 2.5; let t = my_vel / self.comf_dec;
0.5 * my_vel * t + min_dist
}
fn idm(&self, net_dist: f64, my_vel: f64, their_vel: f64) -> f64 {
let headway = 1.5; let comf_dec = self.comf_dec; let max_acc = self.max_acc;
if net_dist <= MIN_GAP {
-10. * max_acc
} else {
let appr = my_vel - their_vel;
let factor = 1. / (2. * (max_acc * comf_dec).sqrt());
let ss = MIN_GAP + (my_vel * headway) + (my_vel * appr * factor);
let term = ss / net_dist;
max_acc * (1. - (term * term))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn min_reach_time() {
let acc = AccelerationModel::new(&ModelParams {
max_acceleration: 2.0,
comf_deceleration: 2.0,
});
assert_approx_eq!(acc.min_reach_time(0.0, 25.0, 50.0), 5.0);
assert_approx_eq!(acc.min_reach_time(0.0, 25.0, 10.0), 5.0);
assert_approx_eq!(acc.min_reach_time(0.0, 25.0, 9.0), 5.027777777777);
assert_approx_eq!(acc.min_reach_time(5.0, 50.0, 50.0), 5.0);
assert_approx_eq!(acc.min_reach_time(5.0, 50.0, 15.0), 5.0);
assert_approx_eq!(acc.min_reach_time(5.0, 50.0, 14.0), 5.01785714285);
}
}