View Javadoc
1   package org.opentrafficsim.road.gtu.perception.mental.channel;
2   
3   import java.util.Iterator;
4   
5   import org.djunits.value.vdouble.scalar.Length;
6   import org.djutils.exceptions.Throw;
7   import org.djutils.exceptions.Try;
8   import org.opentrafficsim.base.OtsRuntimeException;
9   import org.opentrafficsim.base.parameters.ParameterException;
10  import org.opentrafficsim.base.parameters.ParameterTypeLength;
11  import org.opentrafficsim.base.parameters.ParameterTypes;
12  import org.opentrafficsim.core.gtu.RelativePosition;
13  import org.opentrafficsim.core.gtu.perception.AbstractPerceptionCategory;
14  import org.opentrafficsim.core.network.LateralDirectionality;
15  import org.opentrafficsim.core.network.route.Route;
16  import org.opentrafficsim.road.gtu.LaneBasedGtu;
17  import org.opentrafficsim.road.gtu.perception.LanePerception;
18  import org.opentrafficsim.road.gtu.perception.PerceptionCollectable;
19  import org.opentrafficsim.road.gtu.perception.PerceptionReiterable;
20  import org.opentrafficsim.road.gtu.perception.RelativeLane;
21  import org.opentrafficsim.road.gtu.perception.categories.DirectIntersectionPerception;
22  import org.opentrafficsim.road.gtu.perception.categories.IntersectionPerception;
23  import org.opentrafficsim.road.gtu.perception.categories.neighbors.Anticipation;
24  import org.opentrafficsim.road.gtu.perception.categories.neighbors.Estimation;
25  import org.opentrafficsim.road.gtu.perception.categories.neighbors.PerceivedGtuType;
26  import org.opentrafficsim.road.gtu.perception.categories.neighbors.PerceivedGtuType.AnticipationPerceivedGtuType;
27  import org.opentrafficsim.road.gtu.perception.object.PerceivedConflict;
28  import org.opentrafficsim.road.gtu.perception.object.PerceivedTrafficLight;
29  import org.opentrafficsim.road.gtu.perception.object.PerceivedTrafficLightChannel;
30  import org.opentrafficsim.road.gtu.perception.structure.NavigatingIterable.Entry;
31  import org.opentrafficsim.road.network.conflict.Conflict;
32  import org.opentrafficsim.road.network.object.trafficlight.TrafficLight;
33  
34  /**
35   * This class is highly similar to {@link DirectIntersectionPerception} but takes perception delay for conflicting GTUs and
36   * traffic light colors from perception channels.
37   * <p>
38   * Copyright (c) 2024-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
39   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
40   * </p>
41   * @author Wouter Schakel
42   */
43  public class IntersectionPerceptionChannel extends AbstractPerceptionCategory<LaneBasedGtu, LanePerception>
44          implements IntersectionPerception
45  {
46  
47      /** Mental module. */
48      private final ChannelMental mental;
49  
50      /** Estimation. */
51      private final Estimation estimation;
52  
53      /** Anticipation. */
54      private final Anticipation anticipation;
55  
56      /** Look ahead parameter type. */
57      protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
58  
59      /**
60       * Constructor.
61       * @param perception perception.
62       * @param estimation estimation.
63       * @param anticipation anticipation.
64       */
65      public IntersectionPerceptionChannel(final LanePerception perception, final Estimation estimation,
66              final Anticipation anticipation)
67      {
68          super(perception);
69          Throw.when(!(getPerception().getMental().get() instanceof ChannelMental), IllegalArgumentException.class,
70                  "Mental module is not channel based.");
71          this.mental = (ChannelMental) getPerception().getMental().get();
72          this.estimation = estimation;
73          this.anticipation = anticipation;
74      }
75  
76      @Override
77      public final PerceptionCollectable<PerceivedTrafficLight, TrafficLight> getTrafficLights(final RelativeLane lane)
78      {
79          return computeIfAbsent("trafficLights", () -> computeTrafficLights(lane), lane);
80      }
81  
82      @Override
83      public final PerceptionCollectable<PerceivedConflict, Conflict> getConflicts(final RelativeLane lane)
84      {
85          return computeIfAbsent("conflicts", () -> computeConflicts(lane), lane);
86      }
87  
88      @Override
89      public final boolean isAlongsideConflictLeft()
90      {
91          return computeIfAbsent("alongside", () -> computeConflictAlongside(LateralDirectionality.LEFT),
92                  LateralDirectionality.LEFT);
93      }
94  
95      @Override
96      public final boolean isAlongsideConflictRight()
97      {
98          return computeIfAbsent("alongside", () -> computeConflictAlongside(LateralDirectionality.RIGHT),
99                  LateralDirectionality.RIGHT);
100     }
101 
102     /**
103      * Compute traffic lights.
104      * @param lane lane
105      * @return PerceptionCollectable of traffic lights
106      */
107     private PerceptionCollectable<PerceivedTrafficLight, TrafficLight> computeTrafficLights(final RelativeLane lane)
108     {
109         Iterable<Entry<TrafficLight>> iterable = Try.assign(() ->
110         {
111             return getPerception().getLaneStructure().getDownstreamObjects(lane, TrafficLight.class, RelativePosition.FRONT,
112                     true);
113         }, "Unable to get downstream traffic lights from LaneStructure");
114         Route route = getPerception().getGtu().getStrategicalPlanner().getRoute()
115                 .orElseThrow(() -> new OtsRuntimeException("Unable to obtain route"));
116         return new PerceptionReiterable<>(getGtu(), iterable, (trafficLight, distance) ->
117         {
118             return new PerceivedTrafficLightChannel(trafficLight, distance,
119                     trafficLight.canTurnOnRed(route, getGtu().getType()),
120                     () -> IntersectionPerceptionChannel.this.mental.getPerceptionDelay(ChannelTask.FRONT));
121         });
122     }
123 
124     /**
125      * Compute conflicts.
126      * @param lane lane
127      * @return PerceptionCollectable of conflicts
128      */
129     private PerceptionCollectable<PerceivedConflict, Conflict> computeConflicts(final RelativeLane lane)
130     {
131         Iterable<Entry<Conflict>> iterable = Try.assign(() -> getPerception().getLaneStructure().getDownstreamObjects(lane,
132                 Conflict.class, RelativePosition.FRONT, true), "Unable to get downstream conflicts from LaneStructure");
133         return new PerceptionReiterable<>(getGtu(), iterable, (conflict, distance) ->
134         {
135             Length lookAhead = getGtu().getParameters().getOptionalParameter(LOOKAHEAD)
136                     .orElseThrow(() -> new OtsRuntimeException("Parameter LOOKAHEAD not present."));
137             // TODO visibility
138             Length conflictingVisibility = lookAhead;
139             PerceivedConflict perceivedConflict;
140             PerceivedGtuType perceivedGtuType = new AnticipationPerceivedGtuType(IntersectionPerceptionChannel.this.estimation,
141                     IntersectionPerceptionChannel.this.anticipation,
142                     () -> IntersectionPerceptionChannel.this.mental.getPerceptionDelay(conflict));
143             perceivedConflict = PerceivedConflict.of(getGtu(), conflict, perceivedGtuType, distance, conflictingVisibility);
144             return perceivedConflict;
145         });
146     }
147 
148     /**
149      * Compute whether there is a conflict alongside.
150      * @param lat lateral directionality
151      * @return whether there is a conflict alongside
152      */
153     private boolean computeConflictAlongside(final LateralDirectionality lat)
154     {
155         try
156         {
157             RelativeLane lane = new RelativeLane(lat, 1);
158             if (getPerception().getLaneStructure().exists(lane))
159             {
160                 Iterator<Entry<Conflict>> conflicts = getPerception().getLaneStructure()
161                         .getUpstreamObjects(lane, Conflict.class, RelativePosition.FRONT).iterator();
162                 if (conflicts.hasNext())
163                 {
164                     Entry<Conflict> entry = conflicts.next();
165                     return entry.distance().si < entry.object().getLength().si + getGtu().getLength().si;
166                 }
167             }
168             return false;
169         }
170         catch (ParameterException exception)
171         {
172             throw new RuntimeException("Unexpected exception while computing conflict alongside.", exception);
173         }
174     }
175 
176     @Override
177     public final String toString()
178     {
179         return "IntersectionPerceptionChannel " + cacheAsString();
180     }
181 
182 }