elevator_core/
movement.rs1#[must_use]
10pub fn braking_distance(velocity: f64, deceleration: f64) -> f64 {
11 if deceleration <= 0.0 {
12 return 0.0;
13 }
14 let speed = velocity.abs();
15 speed * speed / (2.0 * deceleration)
16}
17
18#[derive(Debug, Clone, Copy)]
20pub struct MovementResult {
21 pub position: f64,
23 pub velocity: f64,
25 pub arrived: bool,
27}
28
29#[must_use]
39pub fn tick_movement(
40 position: f64,
41 velocity: f64,
42 target_position: f64,
43 max_speed: f64,
44 acceleration: f64,
45 deceleration: f64,
46 dt: f64,
47) -> MovementResult {
48 const EPSILON: f64 = 1e-9;
49
50 let displacement = target_position - position;
51
52 if displacement.abs() < EPSILON && velocity.abs() < EPSILON {
54 return MovementResult {
55 position: target_position,
56 velocity: 0.0,
57 arrived: true,
58 };
59 }
60
61 let sign = displacement.signum();
62 let distance_remaining = displacement.abs();
63 let speed = velocity.abs();
64 let safe_decel = deceleration.max(EPSILON);
65 let stopping_distance = speed * speed / (2.0 * safe_decel);
66 let opposing = velocity * sign < 0.0;
70
71 let new_velocity = if opposing || stopping_distance >= distance_remaining - EPSILON {
72 let v = crate::fp::fma(-safe_decel * dt, velocity.signum(), velocity);
74 if velocity > 0.0 && v < 0.0 || velocity < 0.0 && v > 0.0 {
76 0.0
77 } else {
78 v
79 }
80 } else if speed < max_speed {
81 let v = crate::fp::fma(acceleration * dt, sign, velocity);
83 if v.abs() > max_speed {
85 sign * max_speed
86 } else {
87 v
88 }
89 } else {
90 sign * max_speed
92 };
93
94 let new_pos = crate::fp::fma(new_velocity, dt, position);
95
96 let new_displacement = target_position - new_pos;
98 if new_displacement.abs() < EPSILON || (new_displacement.signum() - sign).abs() > EPSILON {
99 return MovementResult {
100 position: target_position,
101 velocity: 0.0,
102 arrived: true,
103 };
104 }
105
106 MovementResult {
107 position: new_pos,
108 velocity: new_velocity,
109 arrived: false,
110 }
111}