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