1use super::simdrive_impl::*;
4use super::*;
5use crate::cycle::{
6 accel_array_for_constant_jerk, accel_for_constant_jerk, calc_constant_jerk_trajectory,
7 create_dist_and_target_speeds_by_microtrip, detect_passing, extend_cycle,
8 trapz_distance_for_step, trapz_step_distances, trapz_step_start_distance,
9};
10use crate::simdrive::RustSimDrive;
11use crate::utils::{add_from, max, min, ndarrcumsum, ndarrunique};
12
13impl RustSimDrive {
14 pub fn gap_to_lead_vehicle_m(&self) -> Array1<f64> {
16 let mut gaps_m = ndarrcumsum(&trapz_step_distances(&self.cyc0))
18 - ndarrcumsum(&trapz_step_distances(&self.cyc));
19 if self.sim_params.idm_allow {
20 gaps_m += self.sim_params.idm_minimum_gap_m;
21 }
22 gaps_m
23 }
24
25 pub fn activate_eco_cruise_rust(
36 &mut self,
37 by_microtrip: bool, extend_fraction: f64, blend_factor: f64, min_target_speed_m_per_s: f64, ) -> anyhow::Result<()> {
42 self.sim_params.idm_allow = true;
43 if !by_microtrip {
44 self.sim_params.idm_v_desired_m_per_s =
45 if !self.cyc0.time_s.is_empty() && self.cyc0.time_s.last().unwrap() > &0.0 {
46 self.cyc0
47 .dist_m()
48 .slice(s![0..self.cyc0.time_s.len()])
49 .sum()
50 / self.cyc0.time_s.last().unwrap()
51 } else {
52 0.0
53 };
54 } else {
55 ensure!(
56 (0.0..=1.0).contains(&blend_factor),
57 "blend_factor must be between 0 and 1 but got {}",
58 blend_factor
59 );
60 ensure!(
61 min_target_speed_m_per_s >= 0.0,
62 "min_target_speed_m_per_s must be >= 0.0 but got {}",
63 min_target_speed_m_per_s
64 );
65 self.sim_params.idm_v_desired_in_m_per_s_by_distance_m =
66 Some(create_dist_and_target_speeds_by_microtrip(
67 &self.cyc0,
68 blend_factor,
69 min_target_speed_m_per_s,
70 ));
71 }
72 ensure!(
74 extend_fraction >= 0.0,
75 "extend_fraction must be >= 0.0 but got {}",
76 extend_fraction
77 );
78 if extend_fraction > 0.0 {
79 self.cyc0 = extend_cycle(&self.cyc0, None, Some(extend_fraction));
80 self.cyc = self.cyc0.clone();
81 }
82 Ok(())
83 }
84
85 #[allow(clippy::too_many_arguments)]
99 pub fn next_speed_by_idm(
100 &mut self,
101 i: usize,
102 a_m_per_s2: f64,
103 b_m_per_s2: f64,
104 dt_headway_s: f64,
105 s0_m: f64,
106 v_desired_m_per_s: f64,
107 delta: f64,
108 ) -> f64 {
109 if v_desired_m_per_s <= 0.0 {
110 return 0.0;
111 }
112 let a_m_per_s2 = a_m_per_s2.abs();
113 let b_m_per_s2 = b_m_per_s2.abs();
114 let dt_headway_s = max(dt_headway_s, 0.0);
115 let s0_m = max(0.0, s0_m);
117 let sqrt_ab = (a_m_per_s2 * b_m_per_s2).powf(0.5);
119 let v0_m_per_s = self.mps_ach[i - 1];
120 let v0_lead_m_per_s = self.cyc0.mps[i - 1];
121 let dv0_m_per_s = v0_m_per_s - v0_lead_m_per_s;
122 let d0_lead_m = self.cyc0_cache.trapz_distances_m[(i - 1).max(0)] + s0_m;
123 let d0_m = trapz_step_start_distance(&self.cyc, i);
124 let s_m = max(d0_lead_m - d0_m, 0.01);
125 let s_target_m = s0_m
127 + max(
128 0.0,
129 (v0_m_per_s * dt_headway_s) + ((v0_m_per_s * dv0_m_per_s) / (2.0 * sqrt_ab)),
130 );
131 let accel_target_m_per_s2 = a_m_per_s2
132 * (1.0 - ((v0_m_per_s / v_desired_m_per_s).powf(delta)) - ((s_target_m / s_m).powi(2)));
133 max(
134 v0_m_per_s + (accel_target_m_per_s2 * self.cyc.dt_s_at_i(i)),
135 0.0,
136 )
137 }
138
139 pub fn set_speed_for_target_gap_using_idm(&mut self, i: usize) {
158 let v_desired_m_per_s = if self.idm_target_speed_m_per_s[i] > 0.0 {
160 self.idm_target_speed_m_per_s[i]
161 } else {
162 *self.cyc0.mps.max().unwrap()
163 };
164 self.cyc.mps[i] = self.next_speed_by_idm(
166 i,
167 self.sim_params.idm_accel_m_per_s2,
168 self.sim_params.idm_decel_m_per_s2,
169 self.sim_params.idm_dt_headway_s,
170 self.sim_params.idm_minimum_gap_m,
171 v_desired_m_per_s,
172 self.sim_params.idm_delta,
173 );
174 }
175
176 pub fn set_speed_for_target_gap(&mut self, i: usize) {
181 self.set_speed_for_target_gap_using_idm(i);
182 }
183
184 pub fn estimate_grade_for_step(&self, i: usize) -> anyhow::Result<f64> {
192 if self.cyc0_cache.grade_all_zero {
193 return Ok(0.0);
194 }
195 if !self.sim_params.coast_allow && !self.sim_params.idm_allow {
196 return Ok(self.cyc.grade[i]);
197 }
198 self.cyc0_cache
199 .interp_grade(trapz_step_start_distance(&self.cyc, i))
200 }
201
202 pub fn lookup_grade_for_step(&self, i: usize, mps_ach: Option<f64>) -> anyhow::Result<f64> {
212 if self.cyc0_cache.grade_all_zero {
213 return Ok(0.0);
214 }
215 if !self.sim_params.coast_allow && !self.sim_params.idm_allow {
216 return Ok(self.cyc.grade[i]);
217 }
218 match mps_ach {
219 Some(mps_ach) => self.cyc0.average_grade_over_range(
220 trapz_step_start_distance(&self.cyc, i),
221 0.5 * (mps_ach + self.mps_ach[i - 1]) * self.cyc.dt_s_at_i(i),
222 Some(&self.cyc0_cache),
223 ),
224 None => self.cyc0.average_grade_over_range(
225 trapz_step_start_distance(&self.cyc, i),
226 trapz_distance_for_step(&self.cyc, i),
227 Some(&self.cyc0_cache),
228 ),
229 }
230 }
231 pub fn set_time_dilation(&mut self, i: usize) -> anyhow::Result<()> {
232 let mut trace_met = (self.cyc.dist_m().slice(s![0..(i + 1)]).sum()
234 - self.dist_m.slice(s![0..(i + 1)]).sum())
235 .abs()
236 / self.cyc0.dist_m().slice(s![0..(i + 1)]).sum()
237 < self.sim_params.time_dilation_tol
238 || self.cyc.mps[i] == 0.0;
239
240 let mut d_short = vec![];
241 let mut t_dilation = vec![0.0]; if !trace_met {
243 self.trace_miss_iters[i] += 1;
244
245 d_short.push(
246 self.cyc0.dist_m().slice(s![0..i + 1]).sum()
247 - self.dist_m.slice(s![0..i + 1]).sum(),
248 ); t_dilation.push(min(
250 max(
251 d_short.last().unwrap() / self.cyc0.dt_s_at_i(i) / self.mps_ach[i], self.sim_params.min_time_dilation,
253 ),
254 self.sim_params.max_time_dilation,
255 ));
256
257 self.cyc.time_s = add_from(
259 &self.cyc.time_s,
260 i,
261 self.cyc.dt_s_at_i(i) * t_dilation.last().unwrap(),
262 );
263 self.solve_step(i)?;
264
265 trace_met =
266 (self.cyc0.dist_m().slice(s![0..i+1]).sum() - self.dist_m.slice(s![0..i+1]).sum()).abs() / self.cyc0.dist_m().slice(s![0..i+1]).sum()
268 < self.sim_params.time_dilation_tol
269 || t_dilation.last().unwrap() >= &self.sim_params.max_time_dilation
271 || t_dilation.last().unwrap() <= &self.sim_params.min_time_dilation;
273 }
274 while !trace_met {
275 d_short.push(
279 self.cyc0.dist_m().slice(s![0..i + 1]).sum()
280 - self.dist_m.slice(s![0..i + 1]).sum(),
281 );
282 t_dilation.push(min(
283 max(
284 t_dilation.last().unwrap()
285 - (t_dilation.last().unwrap() - t_dilation[t_dilation.len() - 2])
286 / (d_short.last().unwrap() - d_short[d_short.len() - 2])
287 * d_short.last().unwrap(),
288 self.sim_params.min_time_dilation,
289 ),
290 self.sim_params.max_time_dilation,
291 ));
292 self.cyc.time_s = add_from(
293 &self.cyc.time_s,
294 i,
295 self.cyc.dt_s_at_i(i) * t_dilation.last().unwrap(),
296 );
297
298 self.solve_step(i)?;
299
300 self.trace_miss_iters[i] += 1;
301
302 trace_met =
303 (self.cyc0.dist_m().slice(s![0..i+1]).sum() - self.dist_m.slice(s![0..i+1]).sum()).abs() / self.cyc0.dist_m().slice(s![0..i+1]).sum()
305 < self.sim_params.time_dilation_tol
306 || self.trace_miss_iters[i] >= self.sim_params.max_trace_miss_iters
308 || t_dilation.last().unwrap() >= &self.sim_params.max_time_dilation
310 || t_dilation.last().unwrap() <= &self.sim_params.min_time_dilation;
312 }
313 Ok(())
314 }
315
316 fn calc_dvdd(&self, v: f64, grade: f64) -> f64 {
321 if v <= 0.0 {
322 0.0
323 } else {
324 let (atan_grade_sin, atan_grade_cos) = if grade == 0.0 {
325 (0.0, 1.0)
326 } else {
327 let atan_g = grade.atan();
328 (atan_g.sin(), atan_g.cos())
329 };
330 let g = self.props.a_grav_mps2;
331 let m = self.veh.veh_kg;
332 let rho_cdfa =
333 self.props.air_density_kg_per_m3 * self.veh.drag_coef * self.veh.frontal_area_m2;
334 let rrc = self.veh.wheel_rr_coef;
335 -1.0 * ((g / v) * (atan_grade_sin + rrc * atan_grade_cos)
336 + (0.5 * rho_cdfa * (1.0 / m) * v))
337 }
338 }
339
340 fn apply_coast_trajectory(&mut self, coast_traj: CoastTrajectory) -> anyhow::Result<()> {
341 if coast_traj.found_trajectory {
342 let num_speeds = match coast_traj.speeds_m_per_s {
343 Some(speeds_m_per_s) => {
344 for (di, &new_speed) in speeds_m_per_s.iter().enumerate() {
345 let idx = coast_traj.start_idx + di;
346 if idx >= self.mps_ach.len() {
347 break;
348 }
349 self.cyc.mps[idx] = new_speed;
350 }
351 speeds_m_per_s.len()
352 }
353 None => 0,
354 };
355 let (_, n) = self.cyc.modify_with_braking_trajectory(
356 self.sim_params.coast_brake_accel_m_per_s2,
357 coast_traj.start_idx + num_speeds,
358 coast_traj.distance_to_brake_m,
359 )?;
360 for di in 0..(self.cyc0.mps.len() - coast_traj.start_idx) {
361 let idx = coast_traj.start_idx + di;
362 self.impose_coast[idx] = di < num_speeds + n;
363 }
364 }
365 Ok(())
366 }
367
368 fn generate_coast_trajectory(&self, i: usize) -> anyhow::Result<CoastTrajectory> {
372 let v0 = self.mps_ach[i - 1];
373 let v_brake = self.sim_params.coast_brake_start_speed_m_per_s;
374 let a_brake = self.sim_params.coast_brake_accel_m_per_s2;
375 assert![a_brake <= 0.0];
376 let ds = &self.cyc0_cache.trapz_distances_m;
377 let d0 = trapz_step_start_distance(&self.cyc, i);
378 let mut distances_m = Vec::with_capacity(ds.len());
379 let mut grade_by_distance = Vec::with_capacity(ds.len());
380 for idx in 0..ds.len() {
381 if ds[idx] >= d0 {
382 distances_m.push(ds[idx] - d0);
383 grade_by_distance.push(self.cyc0.grade[idx])
384 }
385 }
386 if distances_m.is_empty() {
387 return Ok(CoastTrajectory {
388 found_trajectory: false,
389 distance_to_stop_via_coast_m: 0.0,
390 start_idx: 0,
391 speeds_m_per_s: None,
392 distance_to_brake_m: None,
393 });
394 }
395 let distances_m = Array::from_vec(distances_m);
396 let grade_by_distance = Array::from_vec(grade_by_distance);
397 if v0 <= v_brake {
399 return Ok(CoastTrajectory {
400 found_trajectory: true,
401 distance_to_stop_via_coast_m: -0.5 * v0 * v0 / a_brake,
402 start_idx: i,
403 speeds_m_per_s: None,
404 distance_to_brake_m: None,
405 });
406 }
407 let dtb = -0.5 * v_brake * v_brake / a_brake;
408 let mut d = 0.0;
409 let d_max = distances_m.last().unwrap() - dtb;
410 let unique_grades = ndarrunique(&grade_by_distance);
411 let unique_grade = if unique_grades.len() == 1 {
412 Some(unique_grades[0])
413 } else {
414 None
415 };
416 let has_unique_grade = unique_grade.is_some();
417 let max_iter = 180;
418 let iters_per_step = if self.sim_params.favor_grade_accuracy {
419 2
420 } else {
421 1
422 };
423 let mut new_speeds_m_per_s = Vec::with_capacity(max_iter as usize);
424 let mut v = v0;
425 let mut iter = 0;
426 let mut idx = i;
427 let dts0 = self
428 .cyc0
429 .calc_distance_to_next_stop_from(d0, Some(&self.cyc0_cache));
430 while v > v_brake && v >= 0.0 && d <= d_max && iter < max_iter && idx < self.mps_ach.len() {
431 let dt_s = self.cyc0.dt_s_at_i(idx);
432 let mut gr = match unique_grade {
433 Some(g) => g,
434 None => self.cyc0_cache.interp_grade(d + d0)?,
435 };
436 let mut k = self.calc_dvdd(v, gr);
437 let mut v_next = v * (1.0 + 0.5 * k * dt_s) / (1.0 - 0.5 * k * dt_s);
438 let mut vavg = 0.5 * (v + v_next);
439 let mut dd: f64;
440 for _ in 0..iters_per_step {
441 k = self.calc_dvdd(vavg, gr);
442 v_next = v * (1.0 + 0.5 * k * dt_s) / (1.0 - 0.5 * k * dt_s);
443 vavg = 0.5 * (v + v_next);
444 dd = vavg * dt_s;
445 if self.sim_params.favor_grade_accuracy {
446 gr = match unique_grade {
447 Some(g) => g,
448 None => self.cyc0.average_grade_over_range(
449 d + d0,
450 dd,
451 Some(&self.cyc0_cache),
452 )?,
453 };
454 }
455 }
456 if k >= 0.0 && has_unique_grade {
457 return Ok(CoastTrajectory {
459 found_trajectory: false,
460 distance_to_stop_via_coast_m: 0.0,
461 start_idx: 0,
462 speeds_m_per_s: None,
463 distance_to_brake_m: None,
464 });
465 }
466 if v_next <= v_brake {
467 break;
468 }
469 vavg = 0.5 * (v + v_next);
470 dd = vavg * dt_s;
471 let dtb = -0.5 * v_next * v_next / a_brake;
472 d += dd;
473 new_speeds_m_per_s.push(v_next);
474 v = v_next;
475 if d + dtb > dts0 {
476 break;
477 }
478 iter += 1;
479 idx += 1;
480 }
481 if iter < max_iter && idx < self.mps_ach.len() {
482 let dtb = -0.5 * v * v / a_brake;
483 let dtb_target = min(max(dts0 - d, 0.5 * dtb), 2.0 * dtb);
484 let dtsc = d + dtb_target;
485 return Ok(CoastTrajectory {
486 found_trajectory: true,
487 distance_to_stop_via_coast_m: dtsc,
488 start_idx: i,
489 speeds_m_per_s: Some(new_speeds_m_per_s),
490 distance_to_brake_m: Some(dtb_target),
491 });
492 }
493 Ok(CoastTrajectory {
494 found_trajectory: false,
495 distance_to_stop_via_coast_m: 0.0,
496 start_idx: 0,
497 speeds_m_per_s: None,
498 distance_to_brake_m: None,
499 })
500 }
501
502 fn calc_distance_to_stop_coast_v2(&self, i: usize) -> anyhow::Result<f64> {
512 let not_found = -1.0;
513 let v0 = self.cyc.mps[i - 1];
514 let v_brake = self.sim_params.coast_brake_start_speed_m_per_s;
515 let a_brake = self.sim_params.coast_brake_accel_m_per_s2;
516 let ds = &self.cyc0_cache.trapz_distances_m;
517 let gs = &self.cyc0.grade;
518 assert!(
519 ds.len() == gs.len(),
520 "Assumed length of ds and gs the same but actually ds.len():{} and gs.len():{}",
521 ds.len(),
522 gs.len()
523 );
524 let d0 = trapz_step_start_distance(&self.cyc, i);
525 let mut grade_by_distance = Vec::with_capacity(ds.len());
526 for idx in 0..ds.len() {
527 if ds[idx] >= d0 {
528 grade_by_distance.push(gs[idx]);
529 }
530 }
531 let grade_by_distance = Array::from_vec(grade_by_distance);
532 let veh_mass_kg = self.veh.veh_kg;
533 let air_density_kg_per_m3 = self.props.air_density_kg_per_m3;
534 let cdfa_m2 = self.veh.drag_coef * self.veh.frontal_area_m2;
535 let rrc = self.veh.wheel_rr_coef;
536 let gravity_m_per_s2 = self.props.a_grav_mps2;
537 let dtb = -0.5 * v_brake * v_brake / a_brake;
539 if v0 <= v_brake {
540 return Ok(-0.5 * v0 * v0 / a_brake);
541 }
542 let unique_grades = ndarrunique(&grade_by_distance);
543 if unique_grades.len() == 1 {
544 let unique_grade = unique_grades[0];
546 let theta = unique_grade.atan();
547 let c1 = gravity_m_per_s2 * (theta.sin() + rrc * theta.cos());
548 let c2 = (air_density_kg_per_m3 * cdfa_m2) / (2.0 * veh_mass_kg);
549 let v02 = v0 * v0;
550 let vb2 = v_brake * v_brake;
551 let mut d = not_found;
552 let a1 = c1 + c2 * v02;
553 let b1 = c1 + c2 * vb2;
554 if c2 == 0.0 {
555 if c1 > 0.0 {
556 d = (1.0 / (2.0 * c1)) * (v02 - vb2);
557 }
558 } else if a1 > 0.0 && b1 > 0.0 {
559 d = (1.0 / (2.0 * c2)) * (a1.ln() - b1.ln());
560 }
561 if d != not_found {
562 return Ok(d + dtb);
563 }
564 }
565 let ct = self.generate_coast_trajectory(i)?;
566 if ct.found_trajectory {
567 Ok(ct.distance_to_stop_via_coast_m)
568 } else {
569 Ok(not_found)
570 }
571 }
572
573 fn should_impose_coast(&self, i: usize) -> anyhow::Result<bool> {
581 if self.sim_params.coast_start_speed_m_per_s > 0.0 {
582 return Ok(self.cyc.mps[i] >= self.sim_params.coast_start_speed_m_per_s);
583 }
584 let v0 = self.mps_ach[i - 1];
585 if v0 < self.sim_params.coast_brake_start_speed_m_per_s {
586 return Ok(false);
587 }
588 let dtsc0 = self.calc_distance_to_stop_coast_v2(i)?;
590 if dtsc0 < 0.0 {
591 return Ok(false);
592 }
593 let d0 = trapz_step_start_distance(&self.cyc, i);
595 let dts0 = self
596 .cyc0
597 .calc_distance_to_next_stop_from(d0, Some(&self.cyc0_cache));
598 let dtb = -0.5 * v0 * v0 / self.sim_params.coast_brake_accel_m_per_s2;
599 Ok(dtsc0 >= dts0 && dts0 >= (4.0 * dtb))
600 }
601
602 fn calc_next_rendezvous_trajectory(
616 &self,
617 i: usize,
618 min_accel_m_per_s2: f64,
619 max_accel_m_per_s2: f64,
620 ) -> anyhow::Result<(bool, usize, f64, f64)> {
621 let tol = 1e-6;
622 let v0 = self.cyc.mps[i - 1];
624 let brake_start_speed_m_per_s = self.sim_params.coast_brake_start_speed_m_per_s;
625 let brake_accel_m_per_s2 = self.sim_params.coast_brake_accel_m_per_s2;
626 let time_horizon_s = max(self.sim_params.coast_time_horizon_for_adjustment_s, 1.0);
627 let not_found_n = 0;
629 let not_found_jerk_m_per_s3 = 0.0;
630 let not_found_accel_m_per_s2 = 0.0;
631 let not_found = (
632 false,
633 not_found_n,
634 not_found_jerk_m_per_s3,
635 not_found_accel_m_per_s2,
636 );
637 if v0 < (brake_start_speed_m_per_s + tol) {
638 return Ok(not_found);
640 }
641 let (min_accel_m_per_s2, max_accel_m_per_s2) = if min_accel_m_per_s2 > max_accel_m_per_s2 {
642 (max_accel_m_per_s2, min_accel_m_per_s2)
643 } else {
644 (min_accel_m_per_s2, max_accel_m_per_s2)
645 };
646 let num_samples = self.cyc.mps.len();
647 let d0 = trapz_step_start_distance(&self.cyc, i);
648 let dts0 = self
651 .cyc0
652 .calc_distance_to_next_stop_from(d0, Some(&self.cyc0_cache));
653 if dts0 < 0.0 {
654 return Ok(not_found);
656 }
657 let dt = self.cyc.dt_s_at_i(i);
658 let dtb =
660 -0.5 * brake_start_speed_m_per_s * brake_start_speed_m_per_s / brake_accel_m_per_s2;
661 let dtbi0 = dts0 - dtb;
663 if dtbi0 < 0.0 {
664 return Ok(not_found);
665 }
666 let mut step_idx = i;
668 let mut dt_plan = 0.0;
669 let mut r_best_found = false;
670 let mut r_best_n = 0;
671 let mut r_best_jerk_m_per_s3 = 0.0;
672 let mut r_best_accel_m_per_s2 = 0.0;
673 let mut r_best_accel_spread_m_per_s2 = 0.0;
674 while dt_plan <= time_horizon_s && step_idx < num_samples {
675 dt_plan += self.cyc0.dt_s_at_i(step_idx);
676 let step_ahead = step_idx - (i - 1);
677 if step_ahead == 1 {
678 let accel = (brake_start_speed_m_per_s - v0) / dt;
680 let v1 = max(0.0, v0 + accel * dt);
681 let dd_proposed = ((v0 + v1) / 2.0) * dt;
682 if (v1 - brake_start_speed_m_per_s).abs() < tol && (dtbi0 - dd_proposed).abs() < tol
683 {
684 r_best_found = true;
685 r_best_n = 1;
686 r_best_jerk_m_per_s3 = 0.0;
687 r_best_accel_m_per_s2 = accel;
688 break;
689 }
690 } else {
691 if dtbi0 > 0.0 {
693 let (r_bi_jerk_m_per_s3, r_bi_accel_m_per_s2) = calc_constant_jerk_trajectory(
694 step_ahead,
695 0.0,
696 v0,
697 dtbi0,
698 brake_start_speed_m_per_s,
699 dt,
700 )?;
701 if r_bi_accel_m_per_s2 < max_accel_m_per_s2
702 && r_bi_accel_m_per_s2 > min_accel_m_per_s2
703 && r_bi_jerk_m_per_s3 >= 0.0
704 {
705 let as_bi = accel_array_for_constant_jerk(
706 step_ahead,
707 r_bi_accel_m_per_s2,
708 r_bi_jerk_m_per_s3,
709 dt,
710 );
711 let as_bi_min = as_bi.to_vec().into_iter().reduce(f64::min).unwrap_or(0.0);
712 let as_bi_max = as_bi.to_vec().into_iter().reduce(f64::max).unwrap_or(0.0);
713 let accel_spread = (as_bi_max - as_bi_min).abs();
714 let flag = (as_bi_max < (max_accel_m_per_s2 + 1e-6)
715 && as_bi_min > (min_accel_m_per_s2 - 1e-6))
716 && (!r_best_found || (accel_spread < r_best_accel_spread_m_per_s2));
717 if flag {
718 r_best_found = true;
719 r_best_n = step_ahead;
720 r_best_accel_m_per_s2 = r_bi_accel_m_per_s2;
721 r_best_jerk_m_per_s3 = r_bi_jerk_m_per_s3;
722 r_best_accel_spread_m_per_s2 = accel_spread;
723 }
724 }
725 }
726 }
727 step_idx += 1;
728 }
729 if r_best_found {
730 return Ok((
731 r_best_found,
732 r_best_n,
733 r_best_jerk_m_per_s3,
734 r_best_accel_m_per_s2,
735 ));
736 }
737 Ok(not_found)
738 }
739
740 fn set_coast_delay(&mut self, i: usize) {
753 let speed_tol = 0.01; let dist_tol = 0.1; for idx in i..self.cyc.len() {
756 self.coast_delay_index[idx] = 0; }
758 let mut coast_delay = None;
759 if !self.sim_params.idm_allow && self.cyc.mps[i] < speed_tol {
760 let d0 = trapz_step_start_distance(&self.cyc, i);
761 let d0_lv = self.cyc0_cache.trapz_distances_m[i - 1];
762 let dtlv0 = d0_lv - d0;
763 if dtlv0.abs() > dist_tol {
764 let mut d_lv = 0.0;
765 let mut min_dtlv = None;
766 for (idx, (&dd, &v)) in trapz_step_distances(&self.cyc0)
767 .iter()
768 .zip(self.cyc0.mps.iter())
769 .enumerate()
770 {
771 d_lv += dd;
772 let dtlv = (d_lv - d0).abs();
773 if v < speed_tol && (min_dtlv.is_none() || dtlv <= min_dtlv.unwrap()) {
774 if min_dtlv.is_none()
775 || dtlv < min_dtlv.unwrap()
776 || (d0 < d0_lv && min_dtlv.unwrap() == dtlv)
777 {
778 let i_i32 = i32::try_from(i).unwrap();
779 let idx_i32 = i32::try_from(idx).unwrap();
780 coast_delay = Some(i_i32 - idx_i32);
781 }
782 min_dtlv = Some(dtlv);
783 }
784 if min_dtlv.is_some() && dtlv > min_dtlv.unwrap() {
785 break;
786 }
787 }
788 }
789 }
790 if let Some(cd) = coast_delay {
791 if cd < 0 {
792 let mut new_cd = cd;
793 for idx in i..self.cyc0.mps.len() {
794 self.coast_delay_index[idx] = new_cd;
795 new_cd += 1;
796 if new_cd == 0 {
797 break;
798 }
799 }
800 } else {
801 for idx in i..self.cyc0.mps.len() {
802 self.coast_delay_index[idx] = cd;
803 }
804 }
805 }
806 }
807
808 fn prevent_collisions(&mut self, i: usize, passing_tol_m: Option<f64>) -> anyhow::Result<bool> {
815 let passing_tol_m = passing_tol_m.unwrap_or(1.0);
816 let collision = detect_passing(&self.cyc, &self.cyc0, i, Some(passing_tol_m));
817 if !collision.has_collision {
818 return Ok(false);
819 }
820 let mut best = RendezvousTrajectory {
821 found_trajectory: false,
822 idx: 0,
823 n: 0,
824 full_brake_steps: 0,
825 jerk_m_per_s3: 0.0,
826 accel0_m_per_s2: 0.0,
827 accel_spread: 0.0,
828 };
829 let a_brake_m_per_s2 = self.sim_params.coast_brake_accel_m_per_s2;
830 assert!(
831 a_brake_m_per_s2 < 0.0,
832 "brake acceleration must be negative; got {} m/s2",
833 a_brake_m_per_s2
834 );
835 for full_brake_steps in 0..4 {
836 for di in 0..(self.mps_ach.len() - i) {
837 let idx = i + di;
838 if !self.impose_coast[idx] {
839 if idx == i {
840 break;
841 } else {
842 continue;
843 }
844 }
845 let n = collision.idx - idx + 1 - full_brake_steps;
846 if n < 2 {
847 break;
848 }
849 if (idx - 1 + full_brake_steps) >= self.cyc.len() {
850 break;
851 }
852 let dt = collision.time_step_duration_s;
853 let v_start_m_per_s = self.cyc.mps[idx - 1];
854 let dt_full_brake =
855 self.cyc.time_s[idx - 1 + full_brake_steps] - self.cyc.time_s[idx - 1];
856 let dv_full_brake = dt_full_brake * a_brake_m_per_s2;
857 let v_start_jerk_m_per_s = max(v_start_m_per_s + dv_full_brake, 0.0);
858 let dd_full_brake = 0.5 * (v_start_m_per_s + v_start_jerk_m_per_s) * dt_full_brake;
859 let d_start_m = trapz_step_start_distance(&self.cyc, idx) + dd_full_brake;
860 if collision.distance_m <= d_start_m {
861 continue;
862 }
863 let (jerk_m_per_s3, accel0_m_per_s2) = calc_constant_jerk_trajectory(
864 n,
865 d_start_m,
866 v_start_jerk_m_per_s,
867 collision.distance_m,
868 collision.speed_m_per_s,
869 dt,
870 )?;
871 let mut accels_m_per_s2 = vec![];
872 let mut trace_accels_m_per_s2 = vec![];
873 for ni in 0..n {
874 if (ni + idx + full_brake_steps) >= self.cyc.len() {
875 break;
876 }
877 accels_m_per_s2.push(accel_for_constant_jerk(
878 ni,
879 accel0_m_per_s2,
880 jerk_m_per_s3,
881 dt,
882 ));
883 trace_accels_m_per_s2.push(
884 (self.cyc.mps[ni + idx + full_brake_steps]
885 - self.cyc.mps[ni + idx - 1 + full_brake_steps])
886 / self.cyc.dt_s()[ni + idx + full_brake_steps],
887 );
888 }
889 let all_sub_coast = trace_accels_m_per_s2
890 .iter()
891 .copied()
892 .zip(accels_m_per_s2.iter().copied())
893 .fold(
894 true,
895 |all_sc_flag: bool, (trace_accel, accel): (f64, f64)| {
896 if !all_sc_flag {
897 return all_sc_flag;
898 }
899 trace_accel >= accel
900 },
901 );
902 let accels_ndarr = Array1::from(accels_m_per_s2.clone());
903 let min_accel_m_per_s2 = accels_ndarr.min()?;
904 let max_accel_m_per_s2 = accels_ndarr.max()?;
905 let accept = all_sub_coast;
906 let accel_spread = (max_accel_m_per_s2 - min_accel_m_per_s2).abs();
907 if accept && (!best.found_trajectory || accel_spread < best.accel_spread) {
908 best = RendezvousTrajectory {
909 found_trajectory: true,
910 idx,
911 n,
912 full_brake_steps,
913 jerk_m_per_s3,
914 accel0_m_per_s2,
915 accel_spread,
916 };
917 }
918 }
919 if best.found_trajectory {
920 break;
921 }
922 }
923 if !best.found_trajectory {
924 let new_passing_tol_m = if passing_tol_m < 10.0 {
925 10.0
926 } else {
927 passing_tol_m + 5.0
928 };
929 if new_passing_tol_m > 60.0 {
930 return Ok(false);
931 }
932 return self.prevent_collisions(i, Some(new_passing_tol_m));
933 }
934 for fbs in 0..best.full_brake_steps {
935 if (best.idx + fbs) >= self.cyc.len() {
936 break;
937 }
938 let dt = self.cyc.time_s[best.idx + fbs] - self.cyc.time_s[best.idx - 1];
939 let dv = a_brake_m_per_s2 * dt;
940 let v_start = self.cyc.mps[best.idx - 1];
941 self.cyc.mps[best.idx + fbs] = max(v_start + dv, 0.0);
942 self.impose_coast[best.idx + fbs] = true;
943 self.coast_delay_index[best.idx + fbs] = 0;
944 }
945 self.cyc.modify_by_const_jerk_trajectory(
946 best.idx + best.full_brake_steps,
947 best.n,
948 best.jerk_m_per_s3,
949 best.accel0_m_per_s2,
950 );
951 for idx in (best.idx + best.n)..self.cyc0.mps.len() {
952 self.impose_coast[idx] = false;
953 self.coast_delay_index[idx] = 0;
954 }
955 Ok(true)
956 }
957
958 pub fn set_coast_speed(&mut self, i: usize) -> anyhow::Result<()> {
962 let tol = 1e-6;
963 let v0 = self.mps_ach[i - 1];
964 if v0 > tol && !self.impose_coast[i] && self.should_impose_coast(i)? {
965 let ct = self.generate_coast_trajectory(i)?;
966 if ct.found_trajectory {
967 let d = ct.distance_to_stop_via_coast_m;
968 if d < 0.0 {
969 for idx in i..self.cyc0.mps.len() {
970 self.impose_coast[idx] = false;
971 }
972 } else {
973 self.apply_coast_trajectory(ct)?;
974 }
975 if !self.sim_params.coast_allow_passing {
976 self.prevent_collisions(i, None)?;
977 }
978 }
979 }
980 if !self.impose_coast[i] {
981 if !self.sim_params.idm_allow {
982 let i_i32 = i32::try_from(i).ok();
983 let target_idx = match i_i32 {
984 Some(v) => Some(v - self.coast_delay_index[i]),
985 None => None,
986 };
987 let target_idx = match target_idx {
988 Some(ti) => {
989 if ti < 0 {
990 Some(0)
991 } else {
992 usize::try_from(ti).ok()
993 }
994 }
995 None => None,
996 };
997 if let Some(ti) = target_idx {
998 self.cyc.mps[i] = self.cyc0.mps[cmp::min(ti, self.cyc0.mps.len() - 1)];
999 }
1000 }
1001 return Ok(());
1002 }
1003 let v1_traj = self.cyc.mps[i];
1004 if v0 > self.sim_params.coast_brake_start_speed_m_per_s {
1005 if self.sim_params.coast_allow_passing {
1006 self.cyc.mps[i] = self.sim_params.coast_max_speed_m_per_s;
1010 } else {
1011 self.cyc.mps[i] = min(v1_traj, self.sim_params.coast_max_speed_m_per_s);
1012 }
1013 }
1014 self.solve_step(i)?;
1016 self.newton_iters[i] = 0; self.cyc.mps[i] = self.mps_ach[i];
1018 let accel_proposed = (self.cyc.mps[i] - self.cyc.mps[i - 1]) / self.cyc.dt_s_at_i(i);
1019 if self.cyc.mps[i] < tol {
1020 for idx in i..self.cyc0.mps.len() {
1021 self.impose_coast[idx] = false;
1022 }
1023 self.set_coast_delay(i);
1024 self.cyc.mps[i] = 0.0;
1025 return Ok(());
1026 }
1027 if (self.cyc.mps[i] - v1_traj).abs() > tol {
1028 let mut adjusted_current_speed = false;
1029 let brake_speed_start_tol_m_per_s = 0.1;
1030 if self.cyc.mps[i]
1031 < (self.sim_params.coast_brake_start_speed_m_per_s - brake_speed_start_tol_m_per_s)
1032 {
1033 let (_, num_steps) = self.cyc.modify_with_braking_trajectory(
1034 self.sim_params.coast_brake_accel_m_per_s2,
1035 i,
1036 None,
1037 )?;
1038 for idx in i..self.cyc.len() {
1039 self.impose_coast[idx] = idx < (i + num_steps);
1040 }
1041 adjusted_current_speed = true;
1042 } else {
1043 let (traj_found, traj_n, traj_jerk_m_per_s3, traj_accel_m_per_s2) = self
1044 .calc_next_rendezvous_trajectory(
1045 i,
1046 self.sim_params.coast_brake_accel_m_per_s2,
1047 min(accel_proposed, 0.0),
1048 )?;
1049 if traj_found {
1050 let final_speed_m_per_s = self.cyc.modify_by_const_jerk_trajectory(
1052 i,
1053 traj_n,
1054 traj_jerk_m_per_s3,
1055 traj_accel_m_per_s2,
1056 );
1057 for idx in i..self.cyc0.mps.len() {
1058 self.impose_coast[idx] = idx < (i + traj_n);
1059 }
1060 adjusted_current_speed = true;
1061 let i_for_brake = i + traj_n;
1062 if (final_speed_m_per_s - self.sim_params.coast_brake_start_speed_m_per_s).abs()
1063 < 0.1
1064 {
1065 let (_, num_steps) = self.cyc.modify_with_braking_trajectory(
1066 self.sim_params.coast_brake_accel_m_per_s2,
1067 i_for_brake,
1068 None,
1069 )?;
1070 for idx in i_for_brake..self.cyc0.mps.len() {
1071 self.impose_coast[idx] = idx < i_for_brake + num_steps;
1072 }
1073 adjusted_current_speed = true;
1074 } else {
1075 #[cfg(feature = "logging")]
1076 log::warn!(
1077 "final_speed_m_per_s={} not close to coast_brake_start_speed={} for i={}; i_for_brake={}, traj_n={}",
1078 final_speed_m_per_s,
1079 self.sim_params.coast_brake_start_speed_m_per_s,
1080 i,
1081 i_for_brake,
1082 traj_n
1083 );
1084 }
1085 }
1086 }
1087 if adjusted_current_speed {
1088 if !self.sim_params.coast_allow_passing {
1089 self.prevent_collisions(i, None)?;
1090 }
1091 self.solve_step(i)?;
1092 self.newton_iters[i] = 0; self.cyc.mps[i] = self.mps_ach[i];
1094 }
1095 }
1096 Ok(())
1097 }
1098}