1 package org.opentrafficsim.road.gtu.lane.plan.operational;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.djunits.unit.AccelerationUnit;
10 import org.djunits.unit.DurationUnit;
11 import org.djunits.unit.LengthUnit;
12 import org.djunits.unit.SpeedUnit;
13 import org.djunits.value.ValueException;
14 import org.djunits.value.vdouble.scalar.Acceleration;
15 import org.djunits.value.vdouble.scalar.Duration;
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Speed;
18 import org.djunits.value.vdouble.scalar.Time;
19 import org.djutils.exceptions.Throw;
20 import org.opentrafficsim.base.parameters.ParameterException;
21 import org.opentrafficsim.core.geometry.OTSGeometryException;
22 import org.opentrafficsim.core.geometry.OTSLine3D;
23 import org.opentrafficsim.core.geometry.OTSPoint3D;
24 import org.opentrafficsim.core.gtu.GTUException;
25 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
27 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
28 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
29 import org.opentrafficsim.core.math.Solver;
30 import org.opentrafficsim.core.network.LateralDirectionality;
31 import org.opentrafficsim.core.network.NetworkException;
32 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
33 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
34 import org.opentrafficsim.road.network.lane.Lane;
35 import org.opentrafficsim.road.network.lane.LaneDirection;
36 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
37 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
38
39 import nl.tudelft.simulation.dsol.SimRuntimeException;
40 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
41 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
42 import nl.tudelft.simulation.language.d3.DirectedPoint;
43
44 /**
45 * Builder for several often used operational plans. E.g., decelerate to come to a full stop at the end of a shape; accelerate
46 * to reach a certain speed at the end of a curve; drive constant on a curve; decelerate or accelerate to reach a given end
47 * speed at the end of a curve, etc.<br>
48 * TODO driving with negative speeds (backward driving) is not yet supported.
49 * <p>
50 * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
51 * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
52 * </p>
53 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
54 * initial version Nov 15, 2015 <br>
55 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
56 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
57 */
58 public final class LaneOperationalPlanBuilder // class package private for scheduling static method on an instance
59 {
60
61 /** Use instant lane changes. */
62 public static boolean INSTANT_LANE_CHANGES = true;
63
64 /** Maximum acceleration for unbounded accelerations: 1E12 m/s2. */
65 private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);
66
67 /** Maximum deceleration for unbounded accelerations: -1E12 m/s2. */
68 private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);
69
70 /**
71 * Minimum distance of an operational plan path; anything shorter will be truncated to 0. <br>
72 * If objects related to e.g. molecular movements are simulated using this code, a setter for this parameter will be needed.
73 */
74 private static final Length MINIMUM_CREDIBLE_PATH_LENGTH = new Length(0.001, LengthUnit.METER);
75
76 /** Constructor. */
77 LaneOperationalPlanBuilder()
78 {
79 // class should not be instantiated
80 }
81
82 /**
83 * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
84 * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
85 * that the end speed is actually reached by this plan.
86 * @param gtu LaneBasedGTU; the GTU for debugging purposes
87 * @param distance Length; distance to drive for reaching the end speed
88 * @param startTime Time; the current time or a time in the future when the plan should start
89 * @param startSpeed Speed; the speed at the start of the path
90 * @param endSpeed Speed; the required end speed
91 * @param maxAcceleration Acceleration; the maximum acceleration that can be applied, provided as a POSITIVE number
92 * @param maxDeceleration Acceleration; the maximum deceleration that can be applied, provided as a NEGATIVE number
93 * @return the operational plan to accomplish the given end speed
94 * @throws OperationalPlanException when the plan cannot be generated, e.g. because of a path that is too short
95 * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
96 * constructed segment list differ more than a given threshold
97 * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
98 * first lane
99 */
100 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
101 final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration maxAcceleration,
102 final Acceleration maxDeceleration) throws OperationalPlanException, OTSGeometryException
103 {
104 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
105 Segment segment;
106 if (startSpeed.eq(endSpeed))
107 {
108 segment = new SpeedSegment(distance.divideBy(startSpeed));
109 }
110 else
111 {
112 try
113 {
114 // t = 2x / (vt + v0); a = (vt - v0) / t
115 Duration duration = distance.multiplyBy(2.0).divideBy(endSpeed.plus(startSpeed));
116 Acceleration acceleration = endSpeed.minus(startSpeed).divideBy(duration);
117 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
118 {
119 acceleration = maxDeceleration;
120 duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
121 DurationUnit.SI);
122 }
123 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
124 {
125 acceleration = maxAcceleration;
126 duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
127 DurationUnit.SI);
128 }
129 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
130 }
131 catch (ValueException ve)
132 {
133 throw new OperationalPlanException(ve);
134 }
135 }
136 ArrayList<Segment> segmentList = new ArrayList<>();
137 segmentList.add(segment);
138 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
139 }
140
141 /**
142 * Build a plan with a path and a given start speed to reach a provided end speed, exactly at the end of the curve.
143 * Acceleration and deceleration are virtually unbounded (1E12 m/s2) to reach the end speed (e.g., to come to a complete
144 * stop).
145 * @param gtu LaneBasedGTU; the GTU for debugging purposes
146 * @param distance Length; distance to drive for reaching the end speed
147 * @param startTime Time; the current time or a time in the future when the plan should start
148 * @param startSpeed Speed; the speed at the start of the path
149 * @param endSpeed Speed; the required end speed
150 * @return the operational plan to accomplish the given end speed
151 * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
152 * constructed segment list differ more than a given threshold
153 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
154 * first lane
155 */
156 public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
157 final Time startTime, final Speed startSpeed, final Speed endSpeed)
158 throws OperationalPlanException, OTSGeometryException
159 {
160 return buildGradualAccelerationPlan(gtu, distance, startTime, startSpeed, endSpeed, MAX_ACCELERATION, MAX_DECELERATION);
161 }
162
163 /**
164 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
165 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
166 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
167 * before completing the path, a truncated path that ends where the GTU stops is used instead.
168 * @param gtu LaneBasedGTU; the GTU for debugging purposes
169 * @param distance Length; distance to drive for reaching the end speed
170 * @param startTime Time; the current time or a time in the future when the plan should start
171 * @param startSpeed Speed; the speed at the start of the path
172 * @param endSpeed Speed; the required end speed
173 * @param acceleration Acceleration; the acceleration to use if endSpeed > startSpeed, provided as a POSITIVE number
174 * @param deceleration Acceleration; the deceleration to use if endSpeed < startSpeed, provided as a NEGATIVE number
175 * @return the operational plan to accomplish the given end speed
176 * @throws OperationalPlanException when the construction of the operational path fails
177 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
178 * first lane
179 */
180 public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
181 final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration acceleration,
182 final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
183 {
184 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
185 ArrayList<Segment> segmentList = new ArrayList<>();
186 if (startSpeed.eq(endSpeed))
187 {
188 segmentList.add(new OperationalPlan.SpeedSegment(distance.divideBy(startSpeed)));
189 }
190 else
191 {
192 try
193 {
194 if (endSpeed.gt(startSpeed))
195 {
196 Duration t = endSpeed.minus(startSpeed).divideBy(acceleration);
197 Length x = startSpeed.multiplyBy(t).plus(acceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
198 if (x.ge(distance))
199 {
200 // we cannot reach the end speed in the given distance with the given acceleration
201 Duration duration =
202 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
203 DurationUnit.SI);
204 segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
205 }
206 else
207 {
208 // we reach the (higher) end speed before the end of the segment. Make two segments.
209 segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
210 Duration duration = distance.minus(x).divideBy(endSpeed);
211 segmentList.add(new OperationalPlan.SpeedSegment(duration));
212 }
213 }
214 else
215 {
216 Duration t = endSpeed.minus(startSpeed).divideBy(deceleration);
217 Length x = startSpeed.multiplyBy(t).plus(deceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
218 if (x.ge(distance))
219 {
220 // we cannot reach the end speed in the given distance with the given deceleration
221 Duration duration =
222 new Duration(Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si),
223 DurationUnit.SI);
224 segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
225 }
226 else
227 {
228 if (endSpeed.si == 0.0)
229 {
230 // if endSpeed == 0, we cannot reach the end of the path. Therefore, build a partial path.
231 OTSLine3D partialPath = path.truncate(x.si);
232 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
233 return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, false);
234 }
235 // we reach the (lower) end speed, larger than zero, before the end of the segment. Make two segments.
236 segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
237 Duration duration = distance.minus(x).divideBy(endSpeed);
238 segmentList.add(new OperationalPlan.SpeedSegment(duration));
239 }
240 }
241 }
242 catch (ValueException ve)
243 {
244 throw new OperationalPlanException(ve);
245 }
246
247 }
248 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
249 }
250
251 /**
252 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
253 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
254 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
255 * before completing the path, a truncated path that ends where the GTU stops is used instead.
256 * @param gtu LaneBasedGTU; the GTU for debugging purposes
257 * @param startTime Time; the current time or a time in the future when the plan should start
258 * @param startSpeed Speed; the speed at the start of the path
259 * @param acceleration Acceleration; the acceleration to use
260 * @param timeStep Duration; time step for the plan
261 * @return the operational plan to accomplish the given end speed
262 * @throws OperationalPlanException when the construction of the operational path fails
263 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
264 * first lane
265 */
266 public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final Time startTime,
267 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep)
268 throws OperationalPlanException, OTSGeometryException
269 {
270 if (startSpeed.si <= OperationalPlan.DRIFTING_SPEED_SI && acceleration.le(Acceleration.ZERO))
271 {
272 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, false);
273 }
274
275 Acceleration acc = gtuCapabilities(acceleration, gtu);
276 Duration brakingTime = brakingTime(acc, startSpeed, timeStep);
277 Length distance = Length.createSI(startSpeed.si * brakingTime.si + .5 * acc.si * brakingTime.si * brakingTime.si);
278 List<Segment> segmentList = createAccelerationSegments(startSpeed, acc, brakingTime, timeStep);
279 if (distance.le(MINIMUM_CREDIBLE_PATH_LENGTH))
280 {
281 return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, false);
282 }
283 OTSLine3D path = createPathAlongCenterLine(gtu, distance);
284 return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
285 }
286
287 /**
288 * Creates a path along lane center lines.
289 * @param gtu LaneBasedGTU; gtu
290 * @param distance Length; minimum distance
291 * @return OTSLine3D; path along lane center lines
292 * @throws OTSGeometryException when any of the OTSLine3D operations fails
293 */
294 public static OTSLine3D createPathAlongCenterLine(final LaneBasedGTU gtu, final Length distance) throws OTSGeometryException
295 {
296 OTSLine3D path = null;
297 try
298 {
299 DirectedLanePosition ref = gtu.getReferencePosition();
300 double f = ref.getLane().fraction(ref.getPosition());
301 if (ref.getGtuDirection().isPlus() && f < 1.0)
302 {
303 path = ref.getLane().getCenterLine().extractFractional(f, 1.0);
304 }
305 else if (ref.getGtuDirection().isMinus() && f > 0.0)
306 {
307 path = ref.getLane().getCenterLine().extractFractional(0.0, f).reverse();
308 }
309 LaneDirection prevFrom = null;
310 LaneDirection from = ref.getLaneDirection();
311 int n = 1;
312 while (path == null || path.getLength().si < distance.si + n * Lane.MARGIN.si)
313 {
314 n++;
315 prevFrom = from;
316 from = from.getNextLaneDirection(gtu);
317 if (from == null)
318 {
319 // check sink sensor
320 Length pos = prevFrom.getDirection().isPlus() ? prevFrom.getLength() : Length.ZERO;
321 for (SingleSensor sensor : prevFrom.getLane().getSensors(pos, pos, gtu.getGTUType(),
322 prevFrom.getDirection()))
323 {
324 if (sensor instanceof SinkSensor)
325 {
326 // just add some length so the GTU is happy to go to the sink
327 DirectedPoint end = path.getLocationExtendedSI(distance.si + n * Lane.MARGIN.si);
328 List<OTSPoint3D> points = new ArrayList<>(Arrays.asList(path.getPoints()));
329 points.add(new OTSPoint3D(end));
330 return new OTSLine3D(points);
331 }
332 }
333 }
334 if (path == null)
335 {
336 path = from.getDirection().isPlus() ? from.getLane().getCenterLine()
337 : from.getLane().getCenterLine().reverse();
338 }
339 else
340 {
341 path = OTSLine3D.concatenate(Lane.MARGIN.si, path, from.getDirection().isPlus()
342 ? from.getLane().getCenterLine() : from.getLane().getCenterLine().reverse());
343 }
344 }
345 }
346 catch (GTUException exception)
347 {
348 throw new RuntimeException("Error during creation of path.", exception);
349 }
350 return path;
351 }
352
353 /**
354 * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
355 * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
356 * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
357 * before completing the path, a truncated path that ends where the GTU stops is used instead.
358 * @param gtu LaneBasedGTU; the GTU for debugging purposes
359 * @param laneChangeDirectionality LateralDirectionality; direction of lane change (on initiation only, after that not
360 * important)
361 * @param startPosition DirectedPoint; current position
362 * @param startTime Time; the current time or a time in the future when the plan should start
363 * @param startSpeed Speed; the speed at the start of the path
364 * @param acceleration Acceleration; the acceleration to use
365 * @param timeStep Duration; time step for the plan
366 * @param laneChange LaneChange; lane change status
367 * @return the operational plan to accomplish the given end speed
368 * @throws OperationalPlanException when the construction of the operational path fails
369 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
370 * first lane
371 */
372 @SuppressWarnings("checkstyle:parameternumber")
373 public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
374 final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
375 final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
376 throws OperationalPlanException, OTSGeometryException
377 {
378
379 // on first call during lane change, use laneChangeDirectionality as laneChange.getDirection() is NONE
380 // on successive calls, use laneChange.getDirection() as laneChangeDirectionality is NONE (i.e. no LC initiated)
381 LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality;
382
383 Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
384 Length planDistance =
385 Length.createSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
386 List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
387
388 try
389 {
390 // get position on from lane
391 Map<Lane, Length> positions = gtu.positions(gtu.getReference());
392 DirectedLanePosition ref = gtu.getReferencePosition();
393 Iterator<Lane> iterator = ref.getLane()
394 .accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection()).iterator();
395 Lane adjLane = iterator.hasNext() ? iterator.next() : null;
396 DirectedLanePosition from = null;
397 if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
398 {
399 // reference lane is from lane, this is ok
400 from = ref;
401 }
402 else
403 {
404 // reference lane is to lane, this should be accounted for
405 for (Lane lane : positions.keySet())
406 {
407 if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection())
408 .contains(ref.getLane()))
409 {
410 from = new DirectedLanePosition(lane, positions.get(lane), ref.getGtuDirection());
411 break;
412 }
413 }
414 }
415 Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");
416
417 // get path and make plan
418 OTSLine3D path = laneChange.getPath(timeStep, gtu, from, startPosition, planDistance, direction);
419 LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, true);
420 return plan;
421 }
422 catch (GTUException exception)
423 {
424 throw new RuntimeException("Error during creation of lane change plan.", exception);
425 }
426 }
427
428 /**
429 * Makes the acceleration adhere to GTU capabilities.
430 * @param acceleration Acceleration; desired acceleration
431 * @param gtu LaneBasedGTU; gtu
432 * @return Acceleration; possible acceleration
433 */
434 private static Acceleration gtuCapabilities(final Acceleration acceleration, final LaneBasedGTU gtu)
435 {
436 return acceleration; // .gt(gtu.getMaximumDeceleration())
437 // ? (acceleration.lt(gtu.getMaximumAcceleration()) ? acceleration : gtu.getMaximumAcceleration())
438 // : gtu.getMaximumDeceleration();
439 }
440
441 /**
442 * Returns the effective braking time, which stops if stand-still is reached.
443 * @param acceleration Acceleration; acceleration
444 * @param startSpeed Speed; start speed
445 * @param time Duration; intended time step
446 * @return Duration; effective braking time
447 */
448 public static Duration brakingTime(final Acceleration acceleration, final Speed startSpeed, final Duration time)
449 {
450 if (acceleration.ge0())
451 {
452 return time;
453 }
454 double t = startSpeed.si / -acceleration.si;
455 if (t >= time.si)
456 {
457 return time;
458 }
459 return Duration.createSI(t);
460 }
461
462 /**
463 * Creates 1 or 2 segments in an operational plan. Two segments are returned of stand-still is reached within the time step.
464 * @param startSpeed Speed; start speed
465 * @param acceleration Acceleration; acceleration
466 * @param brakingTime Duration; braking time until stand-still
467 * @param timeStep Duration; time step
468 * @return 1 or 2 segments in an operational plan
469 */
470 private static List<Segment> createAccelerationSegments(final Speed startSpeed, final Acceleration acceleration,
471 final Duration brakingTime, final Duration timeStep)
472 {
473 List<Segment> segmentList = new ArrayList<>();
474 if (brakingTime.si < timeStep.si)
475 {
476 if (brakingTime.si > 0.0)
477 {
478 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
479 }
480 segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
481 }
482 else
483 {
484 segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
485 }
486 return segmentList;
487 }
488
489 /**
490 * Build an operational plan based on a simple operational plan and status info.
491 * @param gtu LaneBasedGTU; gtu
492 * @param startTime Time; start time for plan
493 * @param simplePlan SimpleOperationalPlan; simple operational plan
494 * @param laneChange LaneChange; lane change status
495 * @return operational plan
496 * @throws ParameterException if parameter is not defined
497 * @throws GTUException gtu exception
498 * @throws NetworkException network exception
499 * @throws OperationalPlanException operational plan exeption
500 */
501 public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
502 final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
503 throws ParameterException, GTUException, NetworkException, OperationalPlanException
504 {
505 Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);
506
507 if (INSTANT_LANE_CHANGES)
508 {
509 if (simplePlan.isLaneChange())
510 {
511 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
512 }
513 try
514 {
515 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
516 simplePlan.getDuration());
517 }
518 catch (OTSGeometryException exception)
519 {
520 throw new OperationalPlanException(exception);
521 }
522 }
523
524 // gradual lane change
525 try
526 {
527 if ((!simplePlan.isLaneChange() && !laneChange.isChangingLane()) || (gtu.getSpeed().si == 0.0 && acc.si <= 0.0))
528 {
529 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
530 simplePlan.getDuration());
531 }
532 return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
533 gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
534 }
535 catch (OTSGeometryException exception)
536 {
537 throw new OperationalPlanException(exception);
538 }
539 }
540
541 /**
542 * Schedules a lane change finalization after the given distance is covered. This distance is known as the plan is created,
543 * but at that point no time can be derived as the plan is required for that. Hence, this method can be scheduled at the
544 * same time (sequentially after creation of the plan) to then schedule the actual finalization by deriving time from
545 * distance with the plan.
546 * @param gtu LaneBasedGTU; gtu
547 * @param distance Length; distance
548 * @param laneChangeDirection LateralDirectionality; lane change direction
549 * @throws SimRuntimeException on bad time
550 */
551 public static void scheduleLaneChangeFinalization(final LaneBasedGTU gtu, final Length distance,
552 final LateralDirectionality laneChangeDirection) throws SimRuntimeException
553 {
554 Time time = gtu.getOperationalPlan().timeAtDistance(distance);
555 if (Double.isNaN(time.si))
556 {
557 // rounding...
558 time = gtu.getOperationalPlan().getEndTime();
559 }
560 SimEventInterface<SimTimeDoubleUnit> event = gtu.getSimulator().scheduleEventAbs(time, (short) 6, gtu, gtu,
561 "finalizeLaneChange", new Object[] { laneChangeDirection });
562 gtu.setFinalizeLaneChangeEvent(event);
563 }
564
565 /**
566 * Build a plan with a path and a given start speed to try to come to a stop with a given deceleration. If the GTU can stop
567 * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee
568 * that the OperationalPlan will lead to a complete stop.
569 * @param gtu LaneBasedGTU; the GTU for debugging purposes
570 * @param distance Length; distance to drive for reaching the end speed
571 * @param startTime Time; the current time or a time in the future when the plan should start
572 * @param startSpeed Speed; the speed at the start of the path
573 * @param deceleration Acceleration; the deceleration to use if endSpeed < startSpeed, provided as a NEGATIVE number
574 * @return the operational plan to accomplish the given end speed
575 * @throws OperationalPlanException when construction of the operational path fails
576 * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
577 * first lane
578 */
579 public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final Length distance, final Time startTime,
580 final Speed startSpeed, final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
581 {
582 return buildMaximumAccelerationPlan(gtu, distance, startTime, startSpeed, new Speed(0.0, SpeedUnit.SI),
583 new Acceleration(1.0, AccelerationUnit.SI), deceleration);
584 }
585
586 }