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 }