View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception.object;
2   
3   import java.util.Optional;
4   
5   import org.djunits.value.vdouble.scalar.Acceleration;
6   import org.djunits.value.vdouble.scalar.Duration;
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djunits.value.vdouble.scalar.Speed;
9   import org.djutils.exceptions.Throw;
10  import org.djutils.exceptions.Try;
11  import org.opentrafficsim.base.parameters.ParameterSet;
12  import org.opentrafficsim.base.parameters.Parameters;
13  import org.opentrafficsim.core.gtu.GtuType;
14  import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
15  import org.opentrafficsim.core.network.LateralDirectionality;
16  import org.opentrafficsim.core.network.route.Route;
17  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
18  import org.opentrafficsim.road.gtu.lane.perception.GtuTypeAssumptions;
19  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
20  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
21  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
22  import org.opentrafficsim.road.network.speed.SpeedLimitTypes;
23  
24  /**
25   * Interface for perceived surrounding GTU's, adding signals, maneuver and behavioral information.
26   * <p>
27   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
29   * </p>
30   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
32   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
33   */
34  public interface PerceivedGtu extends PerceivedObject
35  {
36  
37      /**
38       * Returns the GTU type.
39       * @return gtuType
40       */
41      GtuType getGtuType();
42  
43      /**
44       * Returns the width of the GTU.
45       * @return width of the GTU
46       */
47      Length getWidth();
48  
49      /**
50       * Returns information on the signals. This includes indicators and braking lights.
51       * @return information on the signals
52       */
53      Signals getSignals();
54  
55      /**
56       * Returns information on the maneuver. This includes lane changing and lateral deviation.
57       * @return information on the maneuver
58       */
59      Maneuver getManeuver();
60  
61      /**
62       * Returns information on the behavior. This includes the car-following model, parameters, desired speed, route, lane change
63       * desire and social pressure.
64       * @return information on the behavior
65       */
66      Behavior getBehavior();
67  
68      /**
69       * Signal information.
70       */
71      interface Signals
72      {
73          /** Instance with no signals. */
74          Signals NONE = new Record(TurnIndicatorStatus.NONE, false);
75  
76          /**
77           * Returns indicator status.
78           * @param lat direction of indicator.
79           * @return indicator status
80           * @throws IllegalArgumentException when the direction is not LEFT or RIGHT
81           */
82          default boolean isIndicatorOn(final LateralDirectionality lat)
83          {
84              Throw.when(lat == null || lat.equals(LateralDirectionality.NONE), IllegalArgumentException.class,
85                      "Lateral direction should be LEFT or RIGHT.");
86              return lat.isLeft() ? getTurnIndicatorStatus().isLeft() : getTurnIndicatorStatus().isRight();
87          }
88  
89          /**
90           * Returns the indicator status.
91           * @return the indicator status
92           */
93          TurnIndicatorStatus getTurnIndicatorStatus();
94  
95          /**
96           * Returns whether the braking lights are on.
97           * @return whether the braking lights are on
98           */
99          boolean isBrakingLightsOn();
100 
101         /**
102          * Wraps a GTU and returns its signals.
103          * @param gtu GTU
104          * @return signals view of the GTU
105          */
106         static Signals of(final LaneBasedGtu gtu)
107         {
108             return of(gtu, gtu.getSimulator().getSimulatorTime());
109         }
110 
111         /**
112          * Wraps a GTU and returns its signals.
113          * @param gtu GTU
114          * @param time simulation time of the signals
115          * @return signals view of the GTU
116          */
117         static Signals of(final LaneBasedGtu gtu, final Duration time)
118         {
119             return new Signals()
120             {
121                 @Override
122                 public TurnIndicatorStatus getTurnIndicatorStatus()
123                 {
124                     return gtu.getTurnIndicatorStatus(time);
125                 }
126 
127                 @Override
128                 public boolean isBrakingLightsOn()
129                 {
130                     return gtu.isBrakingLightsOn(time);
131                 }
132             };
133         }
134 
135         /**
136          * Record storing signals information.
137          * @param getTurnIndicatorStatus the indicator status
138          * @param isBrakingLightsOn whether the braking lights are on
139          */
140         record Record(TurnIndicatorStatus getTurnIndicatorStatus, boolean isBrakingLightsOn) implements Signals
141         {
142 
143             /**
144              * Constructor.
145              * @param getTurnIndicatorStatus the indicator status
146              * @param isBrakingLightsOn whether the braking lights are on
147              * @throws NullPointerException when getTurnIndicatorStatus is {@code null}
148              */
149             public Record
150             {
151                 Throw.whenNull(getTurnIndicatorStatus, "getTurnIndicatorStatus");
152             }
153 
154         };
155     }
156 
157     /**
158      * Information on the maneuver.
159      */
160     interface Maneuver
161     {
162         /** Instance with no signals. */
163         Maneuver NONE = new Record(false, false, Length.ZERO);
164 
165         /**
166          * Returns whether the GTU is changing either left or right.
167          * @param lat lateral lane change direction
168          * @return whether the GTU is changing either left or right
169          * @throws IllegalArgumentException when the direction is not LEFT or RIGHT
170          */
171         default boolean isChangingLane(final LateralDirectionality lat)
172         {
173             Throw.when(lat == null || lat.equals(LateralDirectionality.NONE), IllegalArgumentException.class,
174                     "Lateral direction should be LEFT or RIGHT.");
175             return lat.isLeft() ? isChangingLeft() : isChangingRight();
176         }
177 
178         /**
179          * Returns whether the GTU is changing lanes to the left.
180          * @return whether the GTU is changing lanes to the left
181          */
182         boolean isChangingLeft();
183 
184         /**
185          * Returns whether the GTU is changing lanes to the right.
186          * @return whether the GTU is changing lanes to the right
187          */
188         boolean isChangingRight();
189 
190         /**
191          * Returns the lateral deviation from the lane center line. Positive values are left, negative values are right.
192          * @return lateral deviation from the lane center line
193          */
194         Length getDeviation();
195 
196         /**
197          * Wraps a GTU and returns its signals.
198          * @param gtu GTU
199          * @return signals view of the GTU
200          */
201         static Maneuver of(final LaneBasedGtu gtu)
202         {
203             return of(gtu, gtu.getSimulator().getSimulatorTime());
204         }
205 
206         /**
207          * Wraps a GTU and returns its maneuver at given time.
208          * @param gtu GTU
209          * @param time time of the maneuver
210          * @return maneuver view of the GTU
211          */
212         static Maneuver of(final LaneBasedGtu gtu, final Duration time)
213         {
214             Throw.whenNull(gtu, "gtu");
215             Throw.whenNull(time, "time");
216             return new Maneuver()
217             {
218                 @Override
219                 public boolean isChangingLeft()
220                 {
221                     return gtu.getLaneChangeDirection(time).isLeft();
222                 }
223 
224                 @Override
225                 public boolean isChangingRight()
226                 {
227                     return gtu.getLaneChangeDirection(time).isRight();
228                 }
229 
230                 @Override
231                 public Length getDeviation()
232                 {
233                     return gtu.getDeviation(time);
234                 }
235             };
236         }
237 
238         /**
239          * Record storing signals information.
240          * @param isChangingLeft whether the GTU is changing lanes to the left
241          * @param isChangingRight whether the GTU is changing lanes to the right
242          * @param getDeviation lateral deviation from the lane center line
243          */
244         record Record(boolean isChangingLeft, boolean isChangingRight, Length getDeviation) implements Maneuver
245         {
246 
247             /**
248              * Constructor.
249              * @param isChangingLeft whether the GTU is changing lanes to the left
250              * @param isChangingRight whether the GTU is changing lanes to the right
251              * @param getDeviation lateral deviation from the lane center line
252              * @throws IllegalArgumentException when both {@code isChangingLeft} and {@code isChangingRight} are true
253              * @throws NullPointerException when {@code getDeviation} is {@code null}
254              */
255             public Record
256             {
257                 Throw.when(isChangingLeft && isChangingRight, IllegalArgumentException.class,
258                         "Both isChangingLeft and isChangingRight are true.");
259                 Throw.whenNull(getDeviation, "getDeviation");
260             }
261         };
262     }
263 
264     /**
265      * Information on the behavior.
266      */
267     interface Behavior
268     {
269         /**
270          * Many models that observe a GTU need to predict the imminent behavior of that GTU. Having a car following model of the
271          * observed GTU can help with that. The car following model that is returned can be on a continuum between the actual
272          * car following model of the observed GTU and the own car following model of the observing GTU, not making any
273          * assumptions about the observed GTU. When successive observations of the GTU take place, parameters about its behavior
274          * can be estimated more accurately. Another interesting easy-to-implement solution is to return a car following model
275          * per GTU type, where the following model of a truck can differ from that of a car.
276          * @return a car following model that represents the expected behavior of the observed GTU
277          */
278         CarFollowingModel getCarFollowingModel();
279 
280         /**
281          * Many models that observe a GTU need to predict the imminent behavior of that GTU. Having an estimate of the
282          * behavioral characteristics of the observed GTU can help with that. The parameters that are returned can be on a
283          * continuum between the actual parameters of the observed GTU and the own parameters of the observing GTU, not making
284          * any assumptions about the observed GTU. When successive observations of the GTU take place, parameters about its
285          * behavior can be estimated more accurately. Another interesting easy-to-implement solution is to return a set of
286          * parameters per GTU type, where the parameters of a truck can differ from that of a car.
287          * @return the parameters that represent the expected behavior of the observed GTU, in case of exact values a safe copy
288          *         is returned
289          */
290         Parameters getParameters();
291 
292         /**
293          * Many models that observe a GTU need to predict the imminent behavior of that GTU. Having a model of the speed info
294          * profile for the observed GTU can help with predicting its future behavior. The speed limit info that is returned can
295          * be on a continuum between the actual speed limit model of the observed GTU and the own speed limit model of the
296          * observing GTU, not making any assumptions about the observed GTU. When successive observations of the GTU take place,
297          * parameters about its behavior, such as the maximum speed it accepts, can be estimated more accurately. Another
298          * interesting easy-to-implement solution is to return a speed limit info object per GTU type, where the returned
299          * information of a truck -- with a maximum allowed speed on 80 km/h -- can differ from that of a car -- which can have
300          * a maximum allowed speed of 100 km/h on the same road.
301          * @return a speed limit model that helps in determining the expected behavior of the observed GTU
302          */
303         SpeedLimitInfo getSpeedLimitInfo();
304 
305         /**
306          * Returns the perceived desired speed of the neighbor.
307          * @return perceived desired speed of the neighbor
308          */
309         Speed getDesiredSpeed();
310 
311         /**
312          * Models responding to other GTU may assume a route of the vehicle, for instance at intersections. The route may be
313          * short, i.e. only over the next intersection. Implementations may return anything from the actual route, a route based
314          * on indicators and other assumptions, or empty if simply not known/estimated.
315          * @return route of GTU, empty if there is no route
316          */
317         Optional<Route> getRoute();
318 
319         /**
320          * Returns the perceived left lane change desire, a value between -1 and 1.
321          * @return the perceived left lane change desire, a value between -1 and 1
322          */
323         double leftLaneChangeDesire();
324 
325         /**
326          * Returns the perceived right lane change desire, a value between -1 and 1.
327          * @return the perceived right lane change desire, a value between -1 and 1
328          */
329         double rightLaneChangeDesire();
330 
331         /**
332          * Returns the perceived social pressure, a value between 0 and 1.
333          * @return the perceived social pressure, a value between 0 and 1
334          */
335         double socialPressure();
336 
337         /**
338          * Wraps a GTU and returns its behavior. The given time only applies to the parameters, lane change desire and social
339          * pressure.
340          * @param gtu GTU
341          * @return behavior view of the GTU
342          * @throws NullPointerException when GTU is {@code null}
343          */
344         static Behavior of(final LaneBasedGtu gtu)
345         {
346             Throw.whenNull(gtu, "gtu");
347             return of0(gtu, gtu.getSimulator().getSimulatorTime(), null);
348         }
349 
350         /**
351          * Wraps a GTU and returns its behavior. The given time only applies to lane change desire and social pressure.
352          * @param gtu GTU
353          * @param gtuTypeAssumptions assumptions on the GTU type
354          * @return behavior view of the GTU
355          * @throws NullPointerException when any input argument is {@code null}
356          */
357         static Behavior of(final LaneBasedGtu gtu, final GtuTypeAssumptions gtuTypeAssumptions)
358         {
359             Throw.whenNull(gtu, "gtu");
360             Throw.whenNull(gtuTypeAssumptions, "gtuTypeAssumptions");
361             return of0(gtu, gtu.getSimulator().getSimulatorTime(), gtuTypeAssumptions);
362         }
363 
364         /**
365          * Wraps a GTU and returns its behavior. The given time only applies to the parameters, lane change desire and social
366          * pressure.
367          * @param gtu GTU
368          * @param time simulation time of the behavior
369          * @return behavior view of the GTU
370          * @throws NullPointerException when any input argument is {@code null}
371          */
372         static Behavior of(final LaneBasedGtu gtu, final Duration time)
373         {
374             return of0(gtu, time, null);
375         }
376 
377         /**
378          * Wraps a GTU and returns its behavior. The given time only applies to the lane change desire and social pressure.
379          * @param gtu GTU
380          * @param time simulation time of the behavior
381          * @param gtuTypeAssumptions assumptions on the GTU type
382          * @return behavior view of the GTU
383          * @throws NullPointerException when any input argument is {@code null}
384          */
385         static Behavior of(final LaneBasedGtu gtu, final Duration time, final GtuTypeAssumptions gtuTypeAssumptions)
386         {
387             Throw.whenNull(gtuTypeAssumptions, "gtuTypeAssumptions");
388             return of0(gtu, time, gtuTypeAssumptions);
389         }
390 
391         /**
392          * Wraps a GTU and returns its behavior. The given time only applies to the parameters, lane change desire and social
393          * pressure.
394          * @param gtu GTU
395          * @param time simulation time of the behavior
396          * @param gtuTypeAssumptions assumptions on the GTU type, can be {@code null}
397          * @return behavior view of the GTU
398          */
399         private static Behavior of0(final LaneBasedGtu gtu, final Duration time, final GtuTypeAssumptions gtuTypeAssumptions)
400         {
401             Throw.whenNull(gtu, "gtu");
402             Throw.whenNull(time, "time");
403             // parameters are not historical, they could be, but that's really slow
404             Parameters parameters = new ParameterSet(gtu.getParameters());
405             return new Behavior()
406             {
407                 /** Speed limit info. */
408                 private SpeedLimitInfo speedLimitInfo;
409 
410                 @Override
411                 public CarFollowingModel getCarFollowingModel()
412                 {
413                     return gtuTypeAssumptions == null ? gtu.getTacticalPlanner().getCarFollowingModel()
414                             : gtuTypeAssumptions.getCarFollowingModel(gtu.getType());
415                 }
416 
417                 @Override
418                 public Parameters getParameters()
419                 {
420                     return gtuTypeAssumptions == null ? parameters : gtuTypeAssumptions.getParameters(gtu.getType());
421                 }
422 
423                 @Override
424                 public SpeedLimitInfo getSpeedLimitInfo()
425                 {
426                     if (this.speedLimitInfo == null)
427                     {
428                         this.speedLimitInfo = new SpeedLimitInfo();
429                         this.speedLimitInfo.addSpeedInfo(SpeedLimitTypes.MAX_VEHICLE_SPEED, gtu.getMaximumSpeed());
430                         this.speedLimitInfo.addSpeedInfo(SpeedLimitTypes.FIXED_SIGN,
431                                 gtuTypeAssumptions == null
432                                         ? Try.assign(() -> gtu.getLane().getSpeedLimit(gtu.getType()),
433                                                 "Unable to obtain speed limit for GTU on lane where it is at.")
434                                         : gtuTypeAssumptions.getLaneTypeMaxSpeed(gtu.getType(), gtu.getLane().getType()));
435                     }
436                     return this.speedLimitInfo;
437                 }
438 
439                 @Override
440                 public Speed getDesiredSpeed()
441                 {
442                     return gtu.getDesiredSpeed();
443                 }
444 
445                 @Override
446                 public Optional<Route> getRoute()
447                 {
448                     return gtu.getStrategicalPlanner().getRoute();
449                 }
450 
451                 @Override
452                 public double leftLaneChangeDesire()
453                 {
454                     return parameters.getOptionalParameter(LmrsParameters.DLEFT).orElse(0.0);
455                 }
456 
457                 @Override
458                 public double rightLaneChangeDesire()
459                 {
460                     return parameters.getOptionalParameter(LmrsParameters.DRIGHT).orElse(0.0);
461                 }
462 
463                 @Override
464                 public double socialPressure()
465                 {
466                     return parameters.getOptionalParameter(LmrsParameters.SOCIO).orElse(0.0);
467                 }
468             };
469         }
470     }
471 
472     /**
473      * Returns a view of this perceived headway GTU that returns different values for the headway, speed and acceleration.
474      * @param headway headway
475      * @param speed speed
476      * @param acceleration acceleration
477      * @return copy with different headway, speed and acceleration
478      * @throws NullPointerException when any input argument is {@code null}
479      * @throws IllegalStateException when this perceived GTU is parallel
480      */
481     default PerceivedGtu moved(final Length headway, final Speed speed, final Acceleration acceleration)
482     {
483         Throw.whenNull(headway, "headyway");
484         Throw.whenNull(speed, "speed");
485         Throw.whenNull(acceleration, "acceleration");
486         Throw.when(getKinematics().getOverlap().isParallel(), IllegalStateException.class,
487                 "GTU {} is moved in perception, but it is parallel.", getId());
488         return new PerceivedGtu()
489         {
490             @Override
491             public ObjectType getObjectType()
492             {
493                 return PerceivedGtu.this.getObjectType();
494             }
495 
496             @Override
497             public Length getLength()
498             {
499                 return PerceivedGtu.this.getLength();
500             }
501 
502             @Override
503             public Kinematics getKinematics()
504             {
505                 return new Kinematics()
506                 {
507                     @Override
508                     public Speed getSpeed()
509                     {
510                         return speed;
511                     }
512 
513                     @Override
514                     public Length getDistance()
515                     {
516                         return headway;
517                     }
518 
519                     @Override
520                     public Acceleration getAcceleration()
521                     {
522                         return acceleration;
523                     }
524 
525                     @Override
526                     public boolean isFacingSameDirection()
527                     {
528                         return PerceivedGtu.this.getKinematics().isFacingSameDirection();
529                     }
530 
531                     @Override
532                     public Overlap getOverlap()
533                     {
534                         return PerceivedGtu.this.getKinematics().getOverlap();
535                     }
536                 };
537             }
538 
539             @Override
540             public String getId()
541             {
542                 return PerceivedGtu.this.getId();
543             }
544 
545             @Override
546             public GtuType getGtuType()
547             {
548                 return PerceivedGtu.this.getGtuType();
549             }
550 
551             @Override
552             public Length getWidth()
553             {
554                 return PerceivedGtu.this.getWidth();
555             }
556 
557             @Override
558             public Signals getSignals()
559             {
560                 return PerceivedGtu.this.getSignals();
561             }
562 
563             @Override
564             public Maneuver getManeuver()
565             {
566                 return PerceivedGtu.this.getManeuver();
567             }
568 
569             @Override
570             public Behavior getBehavior()
571             {
572                 return PerceivedGtu.this.getBehavior();
573             }
574         };
575     }
576 
577     /**
578      * Returns perceived GTU with given kinematics.
579      * @param gtu GTU that is perceived
580      * @param kinematics kinematics for the vehicle
581      * @return perceived view of the GTU
582      * @throws NullPointerException when {@code gtu} is null
583      */
584     static PerceivedGtu of(final LaneBasedGtu gtu, final Kinematics kinematics)
585     {
586         Throw.whenNull(gtu, "gtu");
587         return new PerceivedGtuBase(gtu.getId(), gtu.getType(), gtu.getLength(), gtu.getWidth(), kinematics, Signals.of(gtu),
588                 Maneuver.of(gtu), Behavior.of(gtu));
589     }
590 
591     /**
592      * Returns perceived GTU at the given time with given kinematics.
593      * @param gtu GTU that is perceived
594      * @param kinematics kinematics for the vehicle
595      * @param time simulation time at which the GTU is perceived
596      * @return perceived view of the GTU
597      * @throws NullPointerException when {@code gtu} is null
598      */
599     static PerceivedGtu of(final LaneBasedGtu gtu, final Kinematics kinematics, final Duration time)
600     {
601         Throw.whenNull(gtu, "gtu");
602         return new PerceivedGtuBase(gtu.getId(), gtu.getType(), gtu.getLength(), gtu.getWidth(), kinematics,
603                 Signals.of(gtu, time), Maneuver.of(gtu, time), Behavior.of(gtu, time));
604     }
605 
606 }