1 package org.opentrafficsim.road.gtu.lane.tactical.util;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Optional;
8 import java.util.Set;
9 import java.util.UUID;
10 import java.util.function.Function;
11 import java.util.function.Supplier;
12
13 import org.djunits.unit.AccelerationUnit;
14 import org.djunits.unit.DurationUnit;
15 import org.djunits.unit.LengthUnit;
16 import org.djunits.value.vdouble.scalar.Acceleration;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.djutils.exceptions.Throw;
22 import org.opentrafficsim.base.OtsRuntimeException;
23 import org.opentrafficsim.base.logger.Logger;
24 import org.opentrafficsim.base.parameters.ParameterException;
25 import org.opentrafficsim.base.parameters.ParameterTypeAcceleration;
26 import org.opentrafficsim.base.parameters.ParameterTypeDouble;
27 import org.opentrafficsim.base.parameters.ParameterTypeDuration;
28 import org.opentrafficsim.base.parameters.ParameterTypeLength;
29 import org.opentrafficsim.base.parameters.ParameterTypes;
30 import org.opentrafficsim.base.parameters.Parameters;
31 import org.opentrafficsim.base.parameters.constraint.ConstraintInterface;
32 import org.opentrafficsim.core.definitions.DefaultsNl;
33 import org.opentrafficsim.core.gtu.GtuException;
34 import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
35 import org.opentrafficsim.core.network.Node;
36 import org.opentrafficsim.core.network.route.Route;
37 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
38 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
39 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable.PerceptionAccumulator;
40 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable.PerceptionCollector;
41 import org.opentrafficsim.road.gtu.lane.perception.PerceptionIterable;
42 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
43 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedConflict;
44 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu;
45 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu.Maneuver;
46 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu.Signals;
47 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtuBase;
48 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtuSimple;
49 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject;
50 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject.Kinematics;
51 import org.opentrafficsim.road.gtu.lane.tactical.Blockable;
52 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
53 import org.opentrafficsim.road.gtu.lane.tactical.pt.BusSchedule;
54 import org.opentrafficsim.road.network.lane.CrossSectionLink;
55 import org.opentrafficsim.road.network.lane.conflict.BusStopConflictRule;
56 import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
57 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public final class ConflictUtil
79 {
80
81
82 public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts",
83 new Duration(0.000001, DurationUnit.SECOND), ConstraintInterface.POSITIVE);
84
85
86 public static final ParameterTypeAcceleration B = ParameterTypes.B;
87
88
89 public static final ParameterTypeAcceleration BCRIT = ParameterTypes.BCRIT;
90
91
92 public static final ParameterTypeLength S0 = ParameterTypes.S0;
93
94
95 public static final ParameterTypeLength S0_CONF = new ParameterTypeLength("s0conf", "Stopping distance at conflicts",
96 new Length(1.5, LengthUnit.METER), ConstraintInterface.POSITIVE);
97
98
99 public static final ParameterTypeDouble TIME_FACTOR =
100 new ParameterTypeDouble("timeFactor", "Safety factor on estimated time", 1.25, ConstraintInterface.ATLEASTONE);
101
102
103 public static final ParameterTypeLength STOP_AREA =
104 new ParameterTypeLength("stopArea", "Area before stop line where one is considered arrived at the intersection",
105 new Length(4, LengthUnit.METER), ConstraintInterface.POSITIVE);
106
107
108 public static final ParameterTypeDuration TI = new ParameterTypeDuration("ti", "Indicator time before bus departure",
109 Duration.ofSI(3.0), ConstraintInterface.POSITIVE);
110
111
112 private static final Duration TIME_STEP = new Duration(0.5, DurationUnit.SI);
113
114
115 private static final boolean CROSSSTANDING = true;
116
117
118
119
120 private ConflictUtil()
121 {
122
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 @SuppressWarnings("checkstyle:parameternumber")
147
148 public static Acceleration approachConflicts(final Parameters parameters, final Iterable<PerceivedConflict> conflicts,
149 final PerceptionCollectable<PerceivedGtu, LaneBasedGtu> leaders, final CarFollowingModel carFollowingModel,
150 final Length vehicleLength, final Length vehicleWidth, final Speed speed, final Acceleration acceleration,
151 final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans, final LaneBasedGtu gtu,
152 final RelativeLane lane) throws GtuException, ParameterException
153 {
154 conflictPlans.cleanPlans();
155 boolean blocking = false;
156
157
158 Acceleration a = Acceleration.POS_MAXVALUE;
159 Length stoppingDistance = Length.ofSI(
160 parameters.getParameter(S0).si + vehicleLength.si + .5 * speed.si * speed.si / parameters.getParameter(B).si);
161 Iterator<PerceivedConflict> it = conflicts.iterator();
162 if (it.hasNext() && it.next().getDistance().gt(stoppingDistance))
163 {
164 conflictPlans.setBlocking(blocking);
165 return a;
166 }
167
168
169 List<Length> prevStarts = new ArrayList<>();
170 List<Length> prevEnds = new ArrayList<>();
171 List<Class<? extends ConflictRule>> conflictRuleTypes = new ArrayList<>();
172
173
174 Space space = leaders.collect(new AvailableSpace());
175 Length availableSpace = space.availableSpace().minus(passableDistance(vehicleLength, parameters));
176
177 for (PerceivedConflict conflict : conflicts)
178 {
179
180 if (conflict.isCrossing())
181 {
182
183 a = Acceleration.min(a, avoidCrossingCollision(parameters, conflict, carFollowingModel, speed, speedLimitInfo));
184 }
185 else
186 {
187 if (conflict.isMerge() && !lane.isCurrent() && conflict.getConflictPriority().isPriority())
188 {
189
190 a = Acceleration.min(a,
191 avoidMergeCollision(parameters, conflict, carFollowingModel, speed, speedLimitInfo));
192 }
193
194 a = Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, parameters, carFollowingModel, speed,
195 speedLimitInfo, vehicleWidth));
196 }
197
198
199 if (lane.isCurrent())
200 {
201
202 Optional<Route> route = gtu.getStrategicalPlanner().getRoute();
203 if (route.isPresent() && route.get() instanceof BusSchedule busSchedule
204 && gtu.getType().isOfType(DefaultsNl.BUS)
205 && conflict.getConflictRuleType().equals(BusStopConflictRule.class))
206 {
207 Optional<Duration> actualDeparture = busSchedule.getActualDepartureConflict(conflict.getId());
208 if (actualDeparture.isPresent() && actualDeparture.get().si < gtu.getSimulator().getSimulatorTime().si
209 + parameters.getParameter(TI).si)
210 {
211
212 conflictPlans.setIndicatorIntent(TurnIndicatorIntent.LEFT, conflict.getDistance());
213 }
214 }
215 }
216
217
218 if (conflict.getDistance().lt0() && lane.isCurrent())
219 {
220 if (conflict.getConflictType().isCrossing() && !conflict.getConflictPriority().isPriority())
221 {
222
223 blocking = true;
224 }
225
226 continue;
227 }
228
229
230 boolean stop = false;
231 if (conflict.isMerge() && conflict.getConflictPriority().isPriority())
232 {
233 if (conflict.getUpstreamConflictingGTUs().isEmpty()
234 || !conflictPlans.isZipGtu(conflict.getUpstreamConflictingGTUs().first().getId()))
235 {
236 conflictPlans.clearZipGtu();
237 }
238 else
239 {
240 stop = true;
241 }
242 }
243
244
245 if (!stop)
246 {
247 Length d = conflict.isCrossing() ? conflict.getDistance().plus(conflict.getLength()) : conflict.getDistance();
248 stop = !conflict.getConflictType().isSplit() && d.lt(space.firstStationary()) && availableSpace.lt(d);
249
250
251
252
253
254
255
256
257
258
259
260 if (stop && conflict.isMerge() && conflict.getConflictPriority().isPriority() && conflict.getDistance().gt0()
261 && !conflict.getUpstreamConflictingGTUs().isEmpty())
262 {
263 conflictPlans.setZipGtu(conflict.getUpstreamConflictingGTUs().first().getId());
264 }
265 }
266
267 if (!stop)
268 {
269 switch (conflict.getConflictPriority())
270 {
271 case PRIORITY:
272 {
273
274 break;
275 }
276 case YIELD:
277 {
278 Length prevEnd = prevEnds.isEmpty() ? null : prevEnds.get(prevEnds.size() - 1);
279 stop = stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters,
280 speedLimitInfo, carFollowingModel, blocking ? BCRIT : B, prevEnd);
281 break;
282 }
283 case STOP:
284 {
285 Length prevEnd = prevEnds.isEmpty() ? null : prevEnds.get(prevEnds.size() - 1);
286 stop = stopForStopConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters,
287 speedLimitInfo, carFollowingModel, blocking ? BCRIT : B, prevEnd);
288 break;
289 }
290 case ALL_STOP:
291 {
292 stop = stopForAllStopConflict(conflict, conflictPlans);
293 break;
294 }
295 case SPLIT:
296 {
297 continue;
298 }
299 default:
300 {
301 throw new GtuException("Unsupported conflict rule encountered while approaching conflicts.");
302 }
303 }
304 }
305
306
307 if (stop)
308 {
309 prevStarts.add(conflict.getDistance());
310 conflictRuleTypes.add(conflict.getConflictRuleType());
311
312
313 int j = 0;
314 for (int i = prevEnds.size() - 1; i >= 0; i--)
315 {
316
317 if (prevStarts.get(i + 1).minus(prevEnds.get(i)).gt(passableDistance(vehicleLength, parameters)))
318 {
319 j = i + 1;
320 break;
321 }
322 }
323 if (blocking && j == 0)
324 {
325
326 j = prevStarts.size() - 1;
327 }
328
329
330 parameters.setParameterResettable(S0, parameters.getParameter(S0_CONF));
331 Acceleration bCrit = parameters.getParameter(ParameterTypes.BCRIT).neg();
332 Acceleration aConflict = Acceleration.ofSI(-Double.MAX_VALUE);
333 while (aConflict.si < bCrit.si && j < prevStarts.size())
334 {
335 if (prevStarts.get(j).lt(parameters.getParameter(S0_CONF)))
336 {
337
338
339 aConflict = Acceleration.max(aConflict, bCrit);
340 }
341 else
342 {
343 Acceleration aStop =
344 CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo, prevStarts.get(j));
345 if (conflictRuleTypes.get(j).equals(BusStopConflictRule.class) && aStop.lt(bCrit))
346 {
347
348 aStop = Acceleration.POS_MAXVALUE;
349 }
350 aConflict = Acceleration.max(aConflict, aStop);
351 }
352 j++;
353 }
354 parameters.resetParameter(S0);
355 a = Acceleration.min(a, aConflict);
356 break;
357 }
358
359
360 if (conflict.isCrossing())
361 {
362 prevStarts.add(conflict.getDistance());
363 conflictRuleTypes.add(conflict.getConflictRuleType());
364 prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
365 }
366 }
367 conflictPlans.setBlocking(blocking);
368
369 if (a.si < -6.0 && speed.si > 5.0 / 3.6)
370 {
371 Logger.ots().info("Deceleration from conflict util stronger than 6m/s^2.");
372
373 }
374 return a;
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388 private static Acceleration followConflictingLeaderOnMergeOrSplit(final PerceivedConflict conflict,
389 final Parameters parameters, final CarFollowingModel carFollowingModel, final Speed speed,
390 final SpeedLimitInfo speedLimitInfo, final Length vehicleWidth) throws ParameterException
391 {
392
393 PerceptionIterable<PerceivedGtu> downstreamGTUs = conflict.getDownstreamConflictingGTUs();
394 if (downstreamGTUs.isEmpty() || downstreamGTUs.first().getKinematics().getOverlap().isAhead())
395 {
396 return Acceleration.POS_MAXVALUE;
397 }
398
399 PerceivedGtu c = null;
400 Length virtualHeadway = null;
401 if (conflict.getDistance().gt0())
402 {
403 c = downstreamGTUs.first();
404 virtualHeadway = conflict.getDistance().plus(c.getKinematics().getOverlap().getOverlapRear().get());
405 }
406 else
407 {
408 for (PerceivedGtu con : downstreamGTUs)
409 {
410 if (con.getKinematics().getOverlap().isAhead())
411 {
412
413 return Acceleration.POS_MAXVALUE;
414 }
415
416
417
418
419
420
421
422
423
424 virtualHeadway = conflict.getDistance().plus(con.getKinematics().getOverlap().getOverlapRear().get());
425 if (virtualHeadway.gt0())
426 {
427 if (conflict.isSplit())
428 {
429 double conflictWidth = conflict.getWidthAtFraction(
430 (-conflict.getDistance().si + virtualHeadway.si) / conflict.getConflictingLength().si).si;
431 double gtuWidth = con.getWidth().si + vehicleWidth.si;
432 if (conflictWidth > gtuWidth)
433 {
434 continue;
435 }
436 }
437
438 c = con;
439 break;
440 }
441 }
442 }
443 if (c == null)
444 {
445
446 return Acceleration.POS_MAXVALUE;
447 }
448
449 Acceleration a = CarFollowingUtil.followSingleLeader(carFollowingModel, parameters, speed, speedLimitInfo,
450 virtualHeadway, c.getSpeed());
451
452
453 if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
454 {
455
456
457
458
459
460
461
462
463
464
465 parameters.setParameterResettable(S0, parameters.getParameter(S0_CONF));
466 Acceleration aStop =
467 CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo, conflict.getDistance());
468 parameters.resetParameter(S0);
469 a = Acceleration.max(a, aStop);
470 }
471 return a;
472 }
473
474
475
476
477
478
479
480
481
482
483
484 private static Acceleration avoidCrossingCollision(final Parameters parameters, final PerceivedConflict conflict,
485 final CarFollowingModel carFollowingModel, final Speed speed, final SpeedLimitInfo speedLimitInfo)
486 throws ParameterException
487 {
488
489 List<PerceivedGtu> conflictingGTUs = new ArrayList<>();
490 for (PerceivedGtu gtu : conflict.getUpstreamConflictingGTUs())
491 {
492 if (conflict.getConflictingVisibility().lt(gtu.getDistance()))
493 {
494 break;
495 }
496 if (isOnRoute(conflict.getConflictingLink(), gtu))
497 {
498
499 conflictingGTUs.add(gtu);
500 break;
501 }
502 }
503 for (PerceivedGtu gtu : conflict.getDownstreamConflictingGTUs())
504 {
505 if (gtu.getKinematics().getOverlap().isParallel())
506 {
507 conflictingGTUs.add(gtu);
508 }
509 else
510 {
511
512 break;
513 }
514 }
515
516 if (conflictingGTUs.isEmpty())
517 {
518 return Acceleration.POS_MAXVALUE;
519 }
520
521 Acceleration a = Acceleration.POS_MAXVALUE;
522 for (PerceivedGtu conflictingGTU : conflictingGTUs)
523 {
524
525 AnticipationInfo tteCz;
526 Length distance;
527 if (conflictingGTU.getKinematics().getOverlap().isParallel())
528 {
529 tteCz = new AnticipationInfo(Duration.ZERO, conflictingGTU.getSpeed());
530 distance = conflictingGTU.getKinematics().getOverlap().getOverlapRear().get().abs()
531 .plus(conflictingGTU.getKinematics().getOverlap().getOverlap().get()).plus(Length.max(Length.ZERO,
532 conflictingGTU.getKinematics().getOverlap().getOverlapFront().get().neg()));
533 }
534 else
535 {
536 tteCz = AnticipationInfo.anticipateMovement(conflictingGTU.getDistance(), conflictingGTU.getSpeed(),
537 Acceleration.ZERO);
538 distance = conflictingGTU.getDistance().plus(conflict.getLength()).plus(conflictingGTU.getLength());
539 }
540
541 AnticipationInfo ttcCz =
542 AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
543
544 AnticipationInfo tteOa = AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
545 parameters, carFollowingModel, speedLimitInfo, TIME_STEP);
546
547
548 if (tteCz.duration().lt(tteOa.duration()) && tteOa.duration().lt(ttcCz.duration()))
549 {
550 if (!conflictingGTU.getSpeed().eq0() || !CROSSSTANDING)
551 {
552 double t = ttcCz.duration().si;
553
554 double acc = 2.0 * (conflict.getDistance().si - speed.si * t) / (t * t);
555
556 if (speed.si / -acc > ttcCz.duration().si)
557 {
558 a = Acceleration.min(a, new Acceleration(acc, AccelerationUnit.SI));
559 }
560 else
561 {
562
563 a = Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo,
564 conflict.getDistance()));
565 }
566 }
567
568 }
569 }
570 return a;
571 }
572
573
574
575
576
577
578
579
580
581
582
583 private static Acceleration avoidMergeCollision(final Parameters parameters, final PerceivedConflict conflict,
584 final CarFollowingModel carFollowingModel, final Speed speed, final SpeedLimitInfo speedLimitInfo)
585 throws ParameterException
586 {
587 PerceptionCollectable<PerceivedGtu, LaneBasedGtu> conflicting = conflict.getUpstreamConflictingGTUs();
588
589 if (conflicting.isEmpty() || conflicting.first().getKinematics().getOverlap().isParallel())
590 {
591 return Acceleration.POS_MAXVALUE;
592 }
593
594 PerceivedGtu conflictingGtu = conflicting.first();
595 double tteC = conflictingGtu.getDistance().si / conflictingGtu.getSpeed().si;
596 if (tteC < conflict.getDistance().si / speed.si + 3.0)
597 {
598 return CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo, conflict.getDistance());
599 }
600 return Acceleration.POS_MAXVALUE;
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618 @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
619 public static boolean stopForGiveWayConflict(final PerceivedConflict conflict,
620 final PerceptionCollectable<PerceivedGtu, LaneBasedGtu> leaders, final Speed speed, final Acceleration acceleration,
621 final Length vehicleLength, final Parameters parameters, final SpeedLimitInfo speedLimitInfo,
622 final CarFollowingModel carFollowingModel, final ParameterTypeAcceleration bType, final Length prevEnd)
623 throws ParameterException
624 {
625
626 PerceptionCollectable<PerceivedGtu, LaneBasedGtu> conflictingVehiclesCollectable =
627 conflict.getUpstreamConflictingGTUs();
628 Iterable<PerceivedGtu> conflictingVehicles;
629 if (conflictingVehiclesCollectable.isEmpty())
630 {
631 if (conflict.getConflictingTrafficLightDistance().isEmpty())
632 {
633
634 Length length = Length.ofSI(4.0);
635 PerceivedGtuSimple conflictGtu = new PerceivedGtuSimple("virtual " + UUID.randomUUID().toString(),
636 DefaultsNl.CAR, length, new Length(2.0, LengthUnit.SI),
637 Kinematics.dynamicBehind(conflict.getConflictingVisibility(), conflict.getConflictingSpeedLimit(),
638 Acceleration.ZERO, true, length, conflict.getLength()),
639 Signals.NONE, Maneuver.NONE);
640 conflictingVehicles = Set.of(conflictGtu);
641 }
642 else
643 {
644
645 return false;
646 }
647 }
648 else
649 {
650 PerceivedGtu conflicting = conflictingVehiclesCollectable.first();
651 Optional<Length> tlDistance = conflict.getConflictingTrafficLightDistance();
652 if (tlDistance.isPresent() && conflicting.getKinematics().getOverlap().isAhead()
653 && tlDistance.get().lt(conflicting.getDistance())
654 && (conflicting.getSpeed().eq0() || conflicting.getAcceleration().lt0()))
655 {
656
657 return false;
658 }
659 conflictingVehicles = conflictingVehiclesCollectable;
660 }
661
662
663 Acceleration b = parameters.getParameter(bType).neg();
664 double f = parameters.getParameter(TIME_FACTOR);
665 Duration gap = parameters.getParameter(MIN_GAP);
666 Length passable = passableDistance(vehicleLength, parameters);
667 Length distance = conflict.getDistance().plus(vehicleLength);
668 if (conflict.isCrossing())
669 {
670 distance = distance.plus(conflict.getLength());
671 }
672
673
674 AnticipationInfo ttcOa = AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, parameters,
675 carFollowingModel, speedLimitInfo, TIME_STEP);
676
677
678 boolean first = true;
679 for (PerceivedGtu conflictingVehicle : conflictingVehicles)
680 {
681
682 if (!isOnRoute(conflict.getConflictingLink(), conflictingVehicle))
683 {
684 continue;
685 }
686
687
688 if (first && conflictingVehicle.getSpeed().eq0() && conflictingVehicle.getKinematics().getOverlap().isAhead())
689 {
690 return false;
691 }
692
693
694 AnticipationInfo tteCa;
695 if (conflictingVehicle instanceof PerceivedGtuSimple)
696 {
697
698 tteCa = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
699 conflictingVehicle.getAcceleration());
700 }
701 else
702 {
703 Parameters params = conflictingVehicle.getBehavior().getParameters();
704 SpeedLimitInfo sli = conflictingVehicle.getBehavior().getSpeedLimitInfo();
705 CarFollowingModel cfm = conflictingVehicle.getBehavior().getCarFollowingModel();
706
707 if (conflictingVehicle.getKinematics().getOverlap().isAhead())
708 {
709 tteCa = AnticipationInfo.anticipateMovementFreeAcceleration(conflictingVehicle.getDistance(),
710 conflictingVehicle.getSpeed(), params, cfm, sli, TIME_STEP);
711 }
712 else
713 {
714 tteCa = new AnticipationInfo(Duration.ZERO, conflictingVehicle.getSpeed());
715 }
716 }
717
718
719 if (conflict.isMerge())
720 {
721
722
723
724
725
726
727
728
729
730
731
732
733
734 double vSelf = ttcOa.endSpeed().si;
735 double speedDiff = conflictingVehicle.getSpeed().si - vSelf;
736 speedDiff = speedDiff > 0 ? speedDiff : 0;
737 Duration additionalTime = Duration.ofSI(speedDiff / -b.si);
738 double followerFront = conflictingVehicle.getSpeed().si * (ttcOa.duration().si + additionalTime.si)
739 - conflictingVehicle.getDistance().si + 0.5 * b.si * additionalTime.si * additionalTime.si;
740 double ownRear = vSelf * additionalTime.si;
741 Duration tMax = parameters.getParameter(ParameterTypes.TMAX);
742 Length s0 = parameters.getParameter(S0);
743
744
745 if (ttcOa.duration().times(f).plus(gap).gt(tteCa.duration()) || (!Double.isInfinite(tteCa.duration().si)
746 && tteCa.duration().si > 0.0 && ownRear < (followerFront + (tMax.si + gap.si) * vSelf + s0.si) * f))
747 {
748 return true;
749 }
750 }
751 else if (conflict.isCrossing())
752 {
753
754 AnticipationInfo ttpDz = null;
755 if (!leaders.isEmpty())
756 {
757 distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength())
758 .plus(passable);
759 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
760 }
761 else
762 {
763
764 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
765 }
766
767
768 if (ttpDz.duration().times(f).plus(gap).gt(tteCa.duration())
769 || ttcOa.duration().times(f).plus(gap).gt(tteCa.duration()))
770 {
771 return true;
772 }
773 }
774 else
775 {
776 throw new RuntimeException(
777 "Conflict is of unknown type " + conflict.getConflictType() + ", which is not merge nor a crossing.");
778 }
779 first = false;
780 }
781
782
783 return false;
784 }
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801 @SuppressWarnings("checkstyle:parameternumber")
802 public static boolean stopForStopConflict(final PerceivedConflict conflict,
803 final PerceptionCollectable<PerceivedGtu, LaneBasedGtu> leaders, final Speed speed, final Acceleration acceleration,
804 final Length vehicleLength, final Parameters parameters, final SpeedLimitInfo speedLimitInfo,
805 final CarFollowingModel carFollowingModel, final ParameterTypeAcceleration bType, final Length prevEnd)
806 throws ParameterException
807 {
808
809 return stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters, speedLimitInfo,
810 carFollowingModel, bType, prevEnd);
811 }
812
813
814
815
816
817
818
819 public static boolean stopForAllStopConflict(final PerceivedConflict conflict, final ConflictPlans conflictPlans)
820 {
821
822 if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
823 {
824 return false;
825 }
826 return false;
827 }
828
829
830
831
832
833
834
835 private static boolean isOnRoute(final CrossSectionLink conflictingLink, final PerceivedGtu gtu)
836 {
837 try
838 {
839 Optional<Route> route = gtu.getBehavior().getRoute();
840 if (route.isEmpty())
841 {
842
843 return true;
844 }
845 Node startNode = conflictingLink.getStartNode();
846 Node endNode = conflictingLink.getEndNode();
847 return route.get().contains(startNode) && route.get().contains(endNode)
848 && Math.abs(route.get().indexOf(endNode) - route.get().indexOf(startNode)) == 1;
849 }
850 catch (UnsupportedOperationException uoe)
851 {
852
853 return true;
854 }
855 }
856
857
858
859
860
861
862
863
864 private static Length passableDistance(final Length vehicleLength, final Parameters parameters) throws ParameterException
865 {
866 return parameters.getParameter(S0).plus(vehicleLength);
867 }
868
869
870
871
872
873
874
875
876 public static final class ConflictPlans implements Blockable
877 {
878
879
880 private final LinkedHashMap<String, StopPhase> stopPhases = new LinkedHashMap<>();
881
882
883 private final LinkedHashMap<String, Time> arrivalTimes = new LinkedHashMap<>();
884
885
886 private TurnIndicatorIntent indicatorIntent = TurnIndicatorIntent.NONE;
887
888
889 private Length indicatorObjectDistance = null;
890
891
892 private boolean blocking;
893
894
895 private String zipGtuId;
896
897
898
899
900 public ConflictPlans()
901 {
902
903 }
904
905
906
907
908 void cleanPlans()
909 {
910 this.indicatorIntent = TurnIndicatorIntent.NONE;
911 this.indicatorObjectDistance = null;
912 }
913
914
915
916
917
918
919 void setArrivalTime(final PerceivedGtuBase gtu, final Time time)
920 {
921 this.arrivalTimes.put(gtu.getId(), time);
922 }
923
924
925
926
927
928
929 Time getArrivalTime(final PerceivedGtuBase gtu)
930 {
931 return this.arrivalTimes.get(gtu.getId());
932 }
933
934
935
936
937
938 void setStopPhaseApproach(final PerceivedObject stopLine)
939 {
940 this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
941 }
942
943
944
945
946
947
948 void setStopPhaseYield(final PerceivedObject stopLine)
949 {
950 Throw.when(
951 !this.stopPhases.containsKey(stopLine.getId())
952 || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH),
953 OtsRuntimeException.class, "Yield stop phase is set for stop line that was not approached.");
954 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
955 }
956
957
958
959
960
961
962 void setStopPhaseRun(final PerceivedObject stopLine)
963 {
964 Throw.when(!this.stopPhases.containsKey(stopLine.getId()), OtsRuntimeException.class,
965 "Run stop phase is set for stop line that was not approached.");
966 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
967 }
968
969
970
971
972
973
974 boolean isStopPhaseApproach(final PerceivedObject stopLine)
975 {
976 return this.stopPhases.containsKey(stopLine.getId())
977 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
978 }
979
980
981
982
983
984
985 boolean isStopPhaseYield(final PerceivedObject stopLine)
986 {
987 return this.stopPhases.containsKey(stopLine.getId())
988 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
989 }
990
991
992
993
994
995
996 boolean isStopPhaseRun(final PerceivedObject stopLine)
997 {
998 return this.stopPhases.containsKey(stopLine.getId()) && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
999 }
1000
1001
1002
1003
1004
1005 public TurnIndicatorIntent getIndicatorIntent()
1006 {
1007 return this.indicatorIntent;
1008 }
1009
1010
1011
1012
1013
1014 public Length getIndicatorObjectDistance()
1015 {
1016 return this.indicatorObjectDistance;
1017 }
1018
1019
1020
1021
1022
1023
1024 public void setIndicatorIntent(final TurnIndicatorIntent intent, final Length distance)
1025 {
1026 if (this.indicatorObjectDistance == null || this.indicatorObjectDistance.gt(distance))
1027 {
1028 this.indicatorIntent = intent;
1029 this.indicatorObjectDistance = distance;
1030 }
1031 }
1032
1033 @Override
1034 public boolean isBlocking()
1035 {
1036 return this.blocking;
1037 }
1038
1039
1040
1041
1042
1043 public void setBlocking(final boolean blocking)
1044 {
1045 this.blocking = blocking;
1046 }
1047
1048
1049
1050
1051
1052 void setZipGtu(final String zipGtuId)
1053 {
1054 this.zipGtuId = zipGtuId;
1055 }
1056
1057
1058
1059
1060 void clearZipGtu()
1061 {
1062 this.zipGtuId = null;
1063 }
1064
1065
1066
1067
1068
1069
1070 boolean isZipGtu(final String id)
1071 {
1072 return id.equals(this.zipGtuId);
1073 }
1074
1075 @Override
1076 public String toString()
1077 {
1078 return "ConflictPlans";
1079 }
1080
1081 }
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094 private enum StopPhase
1095 {
1096
1097 APPROACH,
1098
1099
1100 YIELD,
1101
1102
1103 RUN;
1104 }
1105
1106
1107
1108
1109 private static final class AvailableSpace implements PerceptionCollector<Space, LaneBasedGtu, Space>
1110 {
1111 @Override
1112 public Supplier<Space> getIdentity()
1113 {
1114 return () -> new Space();
1115 }
1116
1117 @Override
1118 public PerceptionAccumulator<LaneBasedGtu, Space> getAccumulator()
1119 {
1120 return (i, u, h) ->
1121 {
1122 if (i.getObject().addVehicle(u, h))
1123 {
1124 i.stop();
1125 }
1126 return i;
1127 };
1128 }
1129
1130 @Override
1131 public Function<Space, Space> getFinalizer()
1132 {
1133 return (l) -> l;
1134 }
1135 }
1136
1137
1138
1139
1140 private static final class Space
1141 {
1142
1143 private Length cumulativeRequiredSpace = Length.ZERO;
1144
1145
1146 private Length firstStationary;
1147
1148
1149
1150
1151
1152
1153
1154 public boolean addVehicle(final LaneBasedGtu gtu, final Length h)
1155 {
1156 if (gtu.getSpeed().eq0())
1157 {
1158 this.firstStationary = h;
1159 return true;
1160 }
1161 Length s0;
1162 try
1163 {
1164 s0 = gtu.getParameters().getParameter(ParameterTypes.S0);
1165 }
1166 catch (ParameterException ex)
1167 {
1168 s0 = Length.ofSI(3.0);
1169 }
1170 this.cumulativeRequiredSpace = this.cumulativeRequiredSpace.plus(gtu.getLength()).plus(s0);
1171 return false;
1172 }
1173
1174
1175
1176
1177
1178 public Length availableSpace()
1179 {
1180 return this.firstStationary == null ? Length.POSITIVE_INFINITY
1181 : this.firstStationary.minus(this.cumulativeRequiredSpace);
1182 }
1183
1184
1185
1186
1187
1188 public Length firstStationary()
1189 {
1190 return this.firstStationary == null ? Length.POSITIVE_INFINITY : this.firstStationary;
1191 }
1192 }
1193
1194 }