View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2   
3   import java.util.Iterator;
4   import java.util.LinkedHashSet;
5   import java.util.Map;
6   import java.util.SortedSet;
7   
8   import org.djunits.value.vdouble.scalar.Acceleration;
9   import org.djunits.value.vdouble.scalar.Duration;
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.djunits.value.vdouble.scalar.Speed;
12  import org.djunits.value.vdouble.scalar.Time;
13  import org.opentrafficsim.base.parameters.ParameterException;
14  import org.opentrafficsim.base.parameters.ParameterTypeAcceleration;
15  import org.opentrafficsim.base.parameters.ParameterTypeDuration;
16  import org.opentrafficsim.base.parameters.ParameterTypes;
17  import org.opentrafficsim.base.parameters.Parameters;
18  import org.opentrafficsim.core.gtu.GTUException;
19  import org.opentrafficsim.core.gtu.Try;
20  import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
21  import org.opentrafficsim.core.gtu.perception.EgoPerception;
22  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
23  import org.opentrafficsim.core.network.LateralDirectionality;
24  import org.opentrafficsim.core.network.NetworkException;
25  import org.opentrafficsim.road.gtu.animation.Synchronizable.State;
26  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
27  import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
28  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
30  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
31  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
32  import org.opentrafficsim.road.gtu.lane.perception.categories.IntersectionPerception;
33  import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
34  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
35  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
36  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayTrafficLight;
37  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
38  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
39  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
40  import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
41  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
42  import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
43  
44  /**
45   * <p>
46   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
47   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
48   * <p>
49   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br>
50   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
51   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
52   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
53   */
54  public final class LmrsUtil implements LmrsParameters
55  {
56  
57      /** Fixed model time step. */
58      public static final ParameterTypeDuration DT = ParameterTypes.DT;
59  
60      /** Minimum car-following headway. */
61      public static final ParameterTypeDuration TMIN = ParameterTypes.TMIN;
62  
63      /** Current car-following headway. */
64      public static final ParameterTypeDuration T = ParameterTypes.T;
65  
66      /** Maximum car-following headway. */
67      public static final ParameterTypeDuration TMAX = ParameterTypes.TMAX;
68  
69      /** Headway relaxation time. */
70      public static final ParameterTypeDuration TAU = ParameterTypes.TAU;
71  
72      /** Maximum critical deceleration, e.g. stop/go at traffic light. */
73      public static final ParameterTypeAcceleration BCRIT = ParameterTypes.BCRIT;
74  
75      /**
76       * Do not instantiate.
77       */
78      private LmrsUtil()
79      {
80          //
81      }
82  
83      /**
84       * Determines a simple representation of an operational plan.
85       * @param gtu gtu
86       * @param startTime start time
87       * @param carFollowingModel car-following model
88       * @param laneChange lane change status
89       * @param lmrsData LMRS data
90       * @param perception perception
91       * @param mandatoryIncentives set of mandatory lane change incentives
92       * @param voluntaryIncentives set of voluntary lane change incentives
93       * @return simple operational plan
94       * @throws GTUException gtu exception
95       * @throws NetworkException network exception
96       * @throws ParameterException parameter exception
97       * @throws OperationalPlanException operational plan exception
98       */
99      @SuppressWarnings("checkstyle:parameternumber")
100     public static SimpleOperationalPlan determinePlan(final LaneBasedGTU gtu, final Time startTime,
101             final CarFollowingModel carFollowingModel, final LaneChange laneChange, final LmrsData lmrsData,
102             final LanePerception perception, final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
103             final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
104             throws GTUException, NetworkException, ParameterException, OperationalPlanException
105     {
106 
107         // obtain objects to get info
108         InfrastructurePerception infra = perception.getPerceptionCategory(InfrastructurePerception.class);
109         SpeedLimitProspect slp = infra.getSpeedLimitProspect(RelativeLane.CURRENT);
110         SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
111         Parameters params = gtu.getParameters();
112         EgoPerception ego = perception.getPerceptionCategory(EgoPerception.class);
113         Speed speed = ego.getSpeed();
114         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
115         PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders = neighbors.getLeaders(RelativeLane.CURRENT);
116 
117         // regular car-following
118         lmrsData.getTailGating().tailgate(perception, params);
119         if (!leaders.isEmpty() && lmrsData.isNewLeader(leaders.first()))
120         {
121             initHeadwayRelaxation(params, leaders.first());
122         }
123         Acceleration a = gtu.getCarFollowingAcceleration();
124 
125         // during a lane change, both leaders are followed
126         LateralDirectionality initiatedLaneChange;
127         TurnIndicatorIntent turnIndicatorStatus = TurnIndicatorIntent.NONE;
128         if (laneChange.isChangingLane())
129         {
130             RelativeLane secondLane = laneChange.getSecondLane(gtu);
131             initiatedLaneChange = LateralDirectionality.NONE;
132             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> secondLeaders = neighbors.getLeaders(secondLane);
133             Acceleration aSecond = carFollowingModel.followingAcceleration(params, speed, sli, secondLeaders);
134             if (!secondLeaders.isEmpty() && lmrsData.isNewLeader(secondLeaders.first()))
135             {
136                 initHeadwayRelaxation(params, secondLeaders.first());
137             }
138             a = Acceleration.min(a, aSecond);
139         }
140         else
141         {
142 
143             // determine lane change desire based on incentives
144             Desire desire = getLaneChangeDesire(params, perception, carFollowingModel, mandatoryIncentives, voluntaryIncentives,
145                     lmrsData.desireMap);
146             
147             // lane change decision
148             double dFree = params.getParameter(DFREE);
149             initiatedLaneChange = LateralDirectionality.NONE;
150             turnIndicatorStatus = TurnIndicatorIntent.NONE;
151             if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dFree)
152             {
153                 if (acceptLaneChange(perception, params, sli, carFollowingModel, desire.getLeft(), speed, a,
154                         LateralDirectionality.LEFT, lmrsData.getGapAcceptance()))
155                 {
156                     // change left
157                     initiatedLaneChange = LateralDirectionality.LEFT;
158                     turnIndicatorStatus = TurnIndicatorIntent.LEFT;
159                     params.setParameter(DLC, desire.getLeft());
160                     setDesiredHeadway(params, desire.getLeft());
161                     leaders = neighbors.getLeaders(RelativeLane.LEFT);
162                     if (!leaders.isEmpty())
163                     {
164                         // don't respond on its lane change desire, but remember it such that it isn't a new leader in the next
165                         // step
166                         lmrsData.isNewLeader(leaders.first());
167                     }
168                     a = Acceleration.min(a, carFollowingModel.followingAcceleration(params, speed, sli,
169                             neighbors.getLeaders(RelativeLane.LEFT)));
170                 }
171             }
172             else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dFree)
173             {
174                 if (acceptLaneChange(perception, params, sli, carFollowingModel, desire.getRight(), speed, a,
175                         LateralDirectionality.RIGHT, lmrsData.getGapAcceptance()))
176                 {
177                     // change right
178                     initiatedLaneChange = LateralDirectionality.RIGHT;
179                     turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
180                     params.setParameter(DLC, desire.getRight());
181                     setDesiredHeadway(params, desire.getRight());
182                     leaders = neighbors.getLeaders(RelativeLane.RIGHT);
183                     if (!leaders.isEmpty())
184                     {
185                         // don't respond on its lane change desire, but remember it such that it isn't a new leader in the next
186                         // step
187                         lmrsData.isNewLeader(leaders.first());
188                     }
189                     a = Acceleration.min(a, carFollowingModel.followingAcceleration(params, speed, sli,
190                             neighbors.getLeaders(RelativeLane.RIGHT)));
191                 }
192             }
193             if (!initiatedLaneChange.isNone())
194             {
195                 SortedSet<InfrastructureLaneChangeInfo> set = infra.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT);
196                 if (!set.isEmpty())
197                 {
198                     Length boundary = null;
199                     for (InfrastructureLaneChangeInfo info : set)
200                     {
201                         int n = info.getRequiredNumberOfLaneChanges();
202                         if (n > 1)
203                         {
204                             Length thisBoundary = info.getRemainingDistance()
205                                     .minus(Synchronization.requiredBufferSpace(speed, info.getRequiredNumberOfLaneChanges(),
206                                             params.getParameter(ParameterTypes.LOOKAHEAD),
207                                             params.getParameter(ParameterTypes.T0), params.getParameter(ParameterTypes.LCDUR),
208                                             params.getParameter(DCOOP)));
209                             if (thisBoundary.le0())
210                             {
211                                 thisBoundary = info.getRemainingDistance().divideBy(info.getRequiredNumberOfLaneChanges());
212                             }
213                             boundary = boundary == null || thisBoundary.si < boundary.si ? thisBoundary : boundary;
214                         }
215                     }
216                     laneChange.setBoundary(boundary);
217                 }
218             }
219 
220             // take action if we cannot change lane
221             Acceleration aSync;
222             if (initiatedLaneChange.equals(LateralDirectionality.NONE))
223             {
224 
225                 // synchronize
226                 double dSync = params.getParameter(DSYNC);
227                 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dSync)
228                 {
229                     State state;
230                     if (desire.getLeft() >= params.getParameter(DCOOP))
231                     {
232                         // switch on left indicator
233                         turnIndicatorStatus = TurnIndicatorIntent.LEFT;
234                         state = State.INDICATING;
235                     }
236                     else
237                     {
238                         state = State.SYNCHRONIZING;
239                     }
240                     aSync = lmrsData.getSynchronization().synchronize(perception, params, sli, carFollowingModel,
241                             desire.getLeft(), LateralDirectionality.LEFT, lmrsData);
242                     a = applyAcceleration(a, aSync, lmrsData, state);
243                 }
244                 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dSync)
245                 {
246                     State state;
247                     if (desire.getRight() >= params.getParameter(DCOOP))
248                     {
249                         // switch on right indicator
250                         turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
251                         state = State.INDICATING;
252                     }
253                     else
254                     {
255                         state = State.SYNCHRONIZING;
256                     }
257                     aSync = lmrsData.getSynchronization().synchronize(perception, params, sli, carFollowingModel,
258                             desire.getRight(), LateralDirectionality.RIGHT, lmrsData);
259                     a = applyAcceleration(a, aSync, lmrsData, state);
260                 }
261                 else
262                 {
263                     lmrsData.synchronizationState = State.NONE;
264                 }
265                 params.setParameter(DLEFT, desire.getLeft());
266                 params.setParameter(DRIGHT, desire.getRight());
267             }
268             else
269             {
270                 params.setParameter(DLEFT, 0.0);
271                 params.setParameter(DRIGHT, 0.0);
272                 lmrsData.synchronizationState = State.NONE;
273             }
274 
275             // cooperate
276             aSync = lmrsData.getCooperation().cooperate(perception, params, sli, carFollowingModel, LateralDirectionality.LEFT,
277                     desire);
278             a = applyAcceleration(a, aSync, lmrsData, State.COOPERATING);
279             aSync = lmrsData.getCooperation().cooperate(perception, params, sli, carFollowingModel, LateralDirectionality.RIGHT,
280                     desire);
281             a = applyAcceleration(a, aSync, lmrsData, State.COOPERATING);
282 
283             // relaxation
284             exponentialHeadwayRelaxation(params);
285 
286         }
287         lmrsData.finalizeStep();
288 
289         SimpleOperationalPlan simplePlan = new SimpleOperationalPlan(a, params.getParameter(DT), initiatedLaneChange);
290         if (turnIndicatorStatus.isLeft())
291         {
292             simplePlan.setIndicatorIntentLeft();
293         }
294         else if (turnIndicatorStatus.isRight())
295         {
296             simplePlan.setIndicatorIntentRight();
297         }
298         return simplePlan;
299 
300     }
301 
302     /**
303      * Minimizes the acceleration and sets the synchronization state if applicable.
304      * @param a Acceleration; previous acceleration
305      * @param aNew Acceleration; new acceleration
306      * @param lmrsData LmrsData; lmrs data
307      * @param state State; synchronization state
308      * @return Acceleration; minimized acceleration
309      */
310     private static Acceleration applyAcceleration(final Acceleration a, final Acceleration aNew, final LmrsData lmrsData,
311             final State state)
312     {
313         if (a.si < aNew.si)
314         {
315             return a;
316         }
317         lmrsData.synchronizationState = state;
318         return aNew;
319     }
320 
321     /**
322      * Sets the headway as a response to a new leader.
323      * @param params parameters
324      * @param leader leader
325      * @throws ParameterException if DLC is not present
326      */
327     private static void initHeadwayRelaxation(final Parameters params, final HeadwayGTU leader) throws ParameterException
328     {
329         Double dlc = leader.getParameters().getParameterOrNull(DLC);
330         if (dlc != null)
331         {
332             setDesiredHeadway(params, dlc);
333         }
334         // else could not be perceived
335     }
336 
337     /**
338      * Updates the desired headway following an exponential shape approximated with fixed time step <tt>DT</tt>.
339      * @param params parameters
340      * @throws ParameterException in case of a parameter exception
341      */
342     private static void exponentialHeadwayRelaxation(final Parameters params) throws ParameterException
343     {
344         double ratio = params.getParameter(DT).si / params.getParameter(TAU).si;
345         params.setParameter(T,
346                 Duration.interpolate(params.getParameter(T), params.getParameter(TMAX), ratio <= 1.0 ? ratio : 1.0));
347     }
348 
349     /**
350      * Determines lane change desire for the given RSU. Mandatory desire is deduced as the maximum of a set of mandatory
351      * incentives, while voluntary desires are added. Depending on the level of mandatory lane change desire, voluntary desire
352      * may be included partially. If both are positive or negative, voluntary desire is fully included. Otherwise, voluntary
353      * desire is less considered within the range dSync &lt; |mandatory| &lt; dCoop. The absolute value is used as large
354      * negative mandatory desire may also dominate voluntary desire.
355      * @param parameters parameters
356      * @param perception perception
357      * @param carFollowingModel car-following model
358      * @param mandatoryIncentives mandatory incentives
359      * @param voluntaryIncentives voluntary incentives
360      * @param desireMap map where calculated desires are stored in
361      * @return lane change desire for gtu
362      * @throws ParameterException if a parameter is not defined
363      * @throws GTUException if there is no mandatory incentive, the model requires at least one
364      * @throws OperationalPlanException perception exception
365      */
366     public static Desire getLaneChangeDesire(final Parameters parameters, final LanePerception perception,
367             final CarFollowingModel carFollowingModel, final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
368             final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives,
369             final Map<Class<? extends Incentive>, Desire> desireMap)
370             throws ParameterException, GTUException, OperationalPlanException
371     {
372 
373         double dSync = parameters.getParameter(DSYNC);
374         double dCoop = parameters.getParameter(DCOOP);
375 
376         // Mandatory desire
377         double dLeftMandatory = 0.0;
378         double dRightMandatory = 0.0;
379         Desire mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
380         for (MandatoryIncentive incentive : mandatoryIncentives)
381         {
382             Desire d = incentive.determineDesire(parameters, perception, carFollowingModel, mandatoryDesire);
383             desireMap.put(incentive.getClass(), d);
384             dLeftMandatory = Math.abs(d.getLeft()) > Math.abs(dLeftMandatory) ? d.getLeft() : dLeftMandatory;
385             dRightMandatory = Math.abs(d.getRight()) > Math.abs(dRightMandatory) ? d.getRight() : dRightMandatory;
386             mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
387         }
388 
389         // Voluntary desire
390         double dLeftVoluntary = 0;
391         double dRightVoluntary = 0;
392         Desire voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
393         for (VoluntaryIncentive incentive : voluntaryIncentives)
394         {
395             Desire d = incentive.determineDesire(parameters, perception, carFollowingModel, mandatoryDesire, voluntaryDesire);
396             desireMap.put(incentive.getClass(), d);
397             dLeftVoluntary += d.getLeft();
398             dRightVoluntary += d.getRight();
399             voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
400         }
401 
402         // Total desire
403         double thetaLeft = 0;
404         double dLeftMandatoryAbs = Math.abs(dLeftMandatory);
405         double dRightMandatoryAbs = Math.abs(dRightMandatory);
406         if (dLeftMandatoryAbs <= dSync || dLeftMandatory * dLeftVoluntary >= 0)
407         {
408             // low mandatory desire, or same sign
409             thetaLeft = 1;
410         }
411         else if (dSync < dLeftMandatoryAbs && dLeftMandatoryAbs < dCoop && dLeftMandatory * dLeftVoluntary < 0)
412         {
413             // linear from 1 at dSync to 0 at dCoop
414             thetaLeft = (dCoop - dLeftMandatoryAbs) / (dCoop - dSync);
415         }
416         double thetaRight = 0;
417         if (dRightMandatoryAbs <= dSync || dRightMandatory * dRightVoluntary >= 0)
418         {
419             // low mandatory desire, or same sign
420             thetaRight = 1;
421         }
422         else if (dSync < dRightMandatoryAbs && dRightMandatoryAbs < dCoop && dRightMandatory * dRightVoluntary < 0)
423         {
424             // linear from 1 at dSync to 0 at dCoop
425             thetaRight = (dCoop - dRightMandatoryAbs) / (dCoop - dSync);
426         }
427         return new Desire(dLeftMandatory + thetaLeft * dLeftVoluntary, dRightMandatory + thetaRight * dRightVoluntary);
428 
429     }
430 
431     /**
432      * Determine whether a lane change is acceptable (gap, lane markings, etc.).
433      * @param perception perception
434      * @param params parameters
435      * @param sli speed limit info
436      * @param cfm car-following model
437      * @param desire level of lane change desire
438      * @param ownSpeed own speed
439      * @param ownAcceleration current car-following acceleration
440      * @param lat lateral direction for synchronization
441      * @param gapAcceptance gap-acceptance model
442      * @return whether a gap is acceptable
443      * @throws ParameterException if a parameter is not defined
444      * @throws OperationalPlanException perception exception
445      */
446     static boolean acceptLaneChange(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
447             final CarFollowingModel cfm, final double desire, final Speed ownSpeed, final Acceleration ownAcceleration,
448             final LateralDirectionality lat, final GapAcceptance gapAcceptance)
449             throws ParameterException, OperationalPlanException
450     {
451 
452         // beyond start distance
453         boolean beyond = Try.assign(() -> perception.getGtu().laneChangeAllowed(), "Cannot obtain GTU.");
454         if (!beyond)
455         {
456             return false;
457         }
458 
459         // legal?
460         InfrastructurePerception infra = perception.getPerceptionCategory(InfrastructurePerception.class);
461         if (Try.assign(() -> perception.getGtu(), "").getId().equals("1323") && lat.isLeft() && Try
462                 .assign(() -> perception.getGtu().getReferencePosition(), "").getLane().getParentLink().getId().equals("929_0"))
463         {
464             double q = infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, lat).si;
465             System.out.println("Legal distance is " + q);
466         }
467         if (infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, lat).si <= 0.0)
468         {
469             return false;
470         }
471 
472         // other causes for deceleration
473         IntersectionPerception intersection = perception.getPerceptionCategoryOrNull(IntersectionPerception.class);
474         if (intersection != null)
475         {
476             // conflicts alongside?
477             if ((lat.isLeft() && intersection.isAlongsideConflictLeft())
478                     || (lat.isRight() && intersection.isAlongsideConflictRight()))
479             {
480                 return false;
481             }
482             if (quickIntersectionScan(params, sli, cfm, ownSpeed, lat, intersection).lt(params.getParameter(BCRIT).neg()))
483             {
484                 return false;
485             }
486         }
487 
488         // safe regarding neighbors?
489         return gapAcceptance.acceptGap(perception, params, sli, cfm, desire, ownSpeed, ownAcceleration, lat);
490     }
491 
492     /**
493      * Returns a quickly determined acceleration to consider on an adjacent lane, following from conflicts and traffic lights.
494      * @param params parameters
495      * @param sli speed limit info
496      * @param cfm car-following model
497      * @param ownSpeed own speed
498      * @param lat lateral direction for synchronization
499      * @param intersection intersection perception
500      * @return a quickly determined acceleration to consider on an adjacent lane, following from conflicts and traffic lights
501      * @throws ParameterException if a parameter is not defined
502      */
503     private static Acceleration quickIntersectionScan(final Parameters params, final SpeedLimitInfo sli,
504             final CarFollowingModel cfm, final Speed ownSpeed, final LateralDirectionality lat,
505             final IntersectionPerception intersection) throws ParameterException
506     {
507         Acceleration a = Acceleration.POSITIVE_INFINITY;
508         if (intersection != null)
509         {
510             RelativeLane lane = lat.isRight() ? RelativeLane.RIGHT : RelativeLane.LEFT;
511             Iterable<HeadwayConflict> iterable = intersection.getConflicts(lane);
512             if (iterable != null)
513             {
514                 Iterator<HeadwayConflict> conflicts = iterable.iterator();
515                 if (conflicts.hasNext())
516                 {
517                     a = Acceleration.min(a, CarFollowingUtil.followSingleLeader(cfm, params, ownSpeed, sli,
518                             conflicts.next().getDistance(), Speed.ZERO));
519                 }
520                 Iterator<HeadwayTrafficLight> trafficLights = intersection.getTrafficLights(lane).iterator();
521                 if (trafficLights.hasNext())
522                 {
523                     HeadwayTrafficLight trafficLight = trafficLights.next();
524                     if (trafficLight.getTrafficLightColor().isRedOrYellow())
525                     {
526                         a = Acceleration.min(a, CarFollowingUtil.followSingleLeader(cfm, params, ownSpeed, sli,
527                                 trafficLight.getDistance(), Speed.ZERO));
528                     }
529                 }
530             }
531         }
532         return a;
533     }
534 
535     /**
536      * Sets value for T depending on level of lane change desire.
537      * @param params parameters
538      * @param desire lane change desire
539      * @throws ParameterException if T, TMIN or TMAX is not in the parameters
540      */
541     static void setDesiredHeadway(final Parameters params, final double desire) throws ParameterException
542     {
543         double limitedDesire = desire < 0 ? 0 : desire > 1 ? 1 : desire;
544         double tDes = limitedDesire * params.getParameter(TMIN).si + (1 - limitedDesire) * params.getParameter(TMAX).si;
545         double t = params.getParameter(T).si;
546         params.setParameterResettable(T, Duration.createSI(tDes < t ? tDes : t));
547     }
548 
549     /**
550      * Resets value for T depending on level of lane change desire.
551      * @param params parameters
552      * @throws ParameterException if T is not in the parameters
553      */
554     static void resetDesiredHeadway(final Parameters params) throws ParameterException
555     {
556         params.resetParameter(T);
557     }
558 
559     /**
560      * Determine acceleration from car-following with desire-adjusted headway.
561      * @param distance distance from follower to leader
562      * @param followerSpeed speed of follower
563      * @param leaderSpeed speed of leader
564      * @param desire level of lane change desire
565      * @param params parameters
566      * @param sli speed limit info
567      * @param cfm car-following model
568      * @return acceleration from car-following
569      * @throws ParameterException if a parameter is not defined
570      */
571     public static Acceleration singleAcceleration(final Length distance, final Speed followerSpeed, final Speed leaderSpeed,
572             final double desire, final Parameters params, final SpeedLimitInfo sli, final CarFollowingModel cfm)
573             throws ParameterException
574     {
575         // set T
576         setDesiredHeadway(params, desire);
577         // calculate acceleration
578         Acceleration a = CarFollowingUtil.followSingleLeader(cfm, params, followerSpeed, sli, distance, leaderSpeed);
579         // reset T
580         resetDesiredHeadway(params);
581         return a;
582     }
583 
584 }