View Javadoc
1   package org.opentrafficsim.road.gtu.perception.mental.channel;
2   
3   import java.util.SortedSet;
4   import java.util.TreeSet;
5   
6   import org.djutils.exceptions.Throw;
7   import org.djutils.exceptions.Try;
8   import org.opentrafficsim.base.parameters.ParameterException;
9   import org.opentrafficsim.core.gtu.RelativePosition;
10  import org.opentrafficsim.core.gtu.perception.AbstractPerceptionCategory;
11  import org.opentrafficsim.core.network.LateralDirectionality;
12  import org.opentrafficsim.road.gtu.LaneBasedGtu;
13  import org.opentrafficsim.road.gtu.perception.LanePerception;
14  import org.opentrafficsim.road.gtu.perception.PerceptionCollectable;
15  import org.opentrafficsim.road.gtu.perception.PerceptionReiterable;
16  import org.opentrafficsim.road.gtu.perception.RelativeLane;
17  import org.opentrafficsim.road.gtu.perception.categories.neighbors.Anticipation;
18  import org.opentrafficsim.road.gtu.perception.categories.neighbors.DirectNeighborsPerception;
19  import org.opentrafficsim.road.gtu.perception.categories.neighbors.Estimation;
20  import org.opentrafficsim.road.gtu.perception.categories.neighbors.NeighborsPerception;
21  import org.opentrafficsim.road.gtu.perception.categories.neighbors.PerceivedGtuType;
22  import org.opentrafficsim.road.gtu.perception.categories.neighbors.PerceivedGtuType.AnticipationPerceivedGtuType;
23  import org.opentrafficsim.road.gtu.perception.object.PerceivedGtu;
24  import org.opentrafficsim.road.gtu.perception.structure.NavigatingIterable.Entry;
25  
26  /**
27   * This class is highly similar to {@link DirectNeighborsPerception}, but creates and uses 4 different perceived GTU types for
28   * the 4 standard channels. These headway GTU types dynamically obtain a perception delay from the mental module.
29   * <p>
30   * Copyright (c) 2024-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
31   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
32   * </p>
33   * @author Wouter Schakel
34   */
35  public class NeighborsPerceptionChannel extends AbstractPerceptionCategory<LaneBasedGtu, LanePerception>
36          implements NeighborsPerception
37  {
38  
39      /** Left headway GTU type. */
40      private final PerceivedGtuType perceivedGtuTypeLeft;
41  
42      /** Right headway GTU type. */
43      private final PerceivedGtuType perceivedGtuTypeRight;
44  
45      /** Front headway GTU type. */
46      private final PerceivedGtuType perceivedGtuTypeFront;
47  
48      /** Rear headway GTU type. */
49      private final PerceivedGtuType perceivedGtuTypeRear;
50  
51      /**
52       * Constructor.
53       * @param perception perception.
54       * @param estimation estimation.
55       * @param anticipation anticipation.
56       */
57      public NeighborsPerceptionChannel(final LanePerception perception, final Estimation estimation,
58              final Anticipation anticipation)
59      {
60          super(perception);
61          Throw.when(!(getPerception().getMental().get() instanceof ChannelMental), IllegalArgumentException.class,
62                  "Mental module is not channel based.");
63          ChannelMental mental = (ChannelMental) getPerception().getMental().get();
64          this.perceivedGtuTypeLeft =
65                  new AnticipationPerceivedGtuType(estimation, anticipation, () -> mental.getPerceptionDelay(ChannelTask.LEFT));
66          this.perceivedGtuTypeRight =
67                  new AnticipationPerceivedGtuType(estimation, anticipation, () -> mental.getPerceptionDelay(ChannelTask.RIGHT));
68          this.perceivedGtuTypeFront =
69                  new AnticipationPerceivedGtuType(estimation, anticipation, () -> mental.getPerceptionDelay(ChannelTask.FRONT));
70          this.perceivedGtuTypeRear =
71                  new AnticipationPerceivedGtuType(estimation, anticipation, () -> mental.getPerceptionDelay(ChannelTask.REAR));
72      }
73  
74      /** {@inheritDoc} */
75      @Override
76      public SortedSet<PerceivedGtu> getFirstLeaders(final LateralDirectionality lat)
77              throws ParameterException, NullPointerException, IllegalArgumentException
78      {
79          checkLateralDirectionality(lat);
80          return computeIfAbsent("firstLeaders", () -> computeFirstLeaders(lat), lat);
81      }
82  
83      /**
84       * Computes the first leaders regarding splits.
85       * @param lat lateral directionality
86       * @return first leaders
87       */
88      private SortedSet<PerceivedGtu> computeFirstLeaders(final LateralDirectionality lat)
89      {
90          try
91          {
92              SortedSet<PerceivedGtu> set = new TreeSet<>();
93              PerceivedGtuType perceivedGtuType = lat.isLeft() ? this.perceivedGtuTypeLeft : this.perceivedGtuTypeRight;
94              for (Entry<LaneBasedGtu> entry : getPerception().getLaneStructure().getFirstDownstreamGtus(new RelativeLane(lat, 1),
95                      RelativePosition.FRONT, RelativePosition.REAR, RelativePosition.FRONT, RelativePosition.REAR))
96              {
97                  set.add(perceivedGtuType.createPerceivedGtu(getGtu(), getGtu(), entry.object(), entry.distance(), true));
98              }
99              return set;
100         }
101         catch (ParameterException | IllegalArgumentException exception)
102         {
103             throw new RuntimeException("Unexpected exception while computing first leaders.", exception);
104         }
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public SortedSet<PerceivedGtu> getFirstFollowers(final LateralDirectionality lat)
110             throws ParameterException, NullPointerException, IllegalArgumentException
111     {
112         checkLateralDirectionality(lat);
113         return computeIfAbsent("firstFollowers", () -> computeFirstFollowers(lat), lat);
114     }
115 
116     /**
117      * Computes the first followers regarding splits.
118      * @param lat lateral directionality
119      * @return first followers
120      */
121     private SortedSet<PerceivedGtu> computeFirstFollowers(final LateralDirectionality lat)
122     {
123         try
124         {
125             SortedSet<PerceivedGtu> set = new TreeSet<>();
126             PerceivedGtuType perceivedGtuType = lat.isLeft() ? this.perceivedGtuTypeLeft : this.perceivedGtuTypeRight;
127             for (Entry<LaneBasedGtu> entry : getPerception().getLaneStructure().getFirstUpstreamGtus(new RelativeLane(lat, 1),
128                     RelativePosition.REAR, RelativePosition.FRONT, RelativePosition.REAR, RelativePosition.FRONT))
129             {
130                 set.add(perceivedGtuType.createPerceivedGtu(getGtu(), getGtu(), entry.object(), entry.distance(), false));
131             }
132             return set;
133         }
134         catch (ParameterException | IllegalArgumentException exception)
135         {
136             throw new RuntimeException("Unexpected exception while computing first followers.", exception);
137         }
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public boolean isGtuAlongside(final LateralDirectionality lat)
143             throws ParameterException, NullPointerException, IllegalArgumentException
144     {
145         checkLateralDirectionality(lat);
146         return computeIfAbsent("gtuAlongside", () -> computeGtuAlongside(lat), lat);
147     }
148 
149     /**
150      * Computes whether there is a GTU alongside.
151      * @param lat lateral directionality
152      * @return whether there is a GTU alongside
153      */
154     public boolean computeGtuAlongside(final LateralDirectionality lat)
155     {
156         try
157         {
158             // check if any GTU is downstream of the rear, within the vehicle length
159             for (Entry<LaneBasedGtu> entry : getPerception().getLaneStructure().getFirstDownstreamGtus(new RelativeLane(lat, 1),
160                     RelativePosition.REAR, RelativePosition.FRONT, RelativePosition.FRONT, RelativePosition.REAR))
161             {
162                 if (entry.distance().le0())
163                 {
164                     return true;
165                 }
166             }
167 
168             // check if any GTU is upstream of the front, within the vehicle length
169             for (Entry<LaneBasedGtu> entry : getPerception().getLaneStructure().getFirstUpstreamGtus(new RelativeLane(lat, 1),
170                     RelativePosition.FRONT, RelativePosition.REAR, RelativePosition.REAR, RelativePosition.FRONT))
171             {
172                 if (entry.distance().le0())
173                 {
174                     return true;
175                 }
176             }
177         }
178         catch (ParameterException | IllegalArgumentException exception) // | GtuException
179         {
180             throw new RuntimeException("Unexpected exception while computing gtu alongside.", exception);
181         }
182         // no such GTU
183         return false;
184     }
185 
186     /** {@inheritDoc} */
187     @Override
188     public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getLeaders(final RelativeLane lane)
189     {
190         Throw.whenNull(lane, "Lane may not be null.");
191         return computeIfAbsent("leaders", () -> computeLeaders(lane), lane);
192     }
193 
194     /**
195      * Computes leaders.
196      * @param lane lane
197      * @return perception iterable for leaders
198      */
199     private PerceptionCollectable<PerceivedGtu, LaneBasedGtu> computeLeaders(final RelativeLane lane)
200     {
201         Iterable<Entry<LaneBasedGtu>> iterable = Try.assign(() ->
202         {
203             return getPerception().getLaneStructure().getDownstreamGtus(lane, RelativePosition.FRONT, RelativePosition.FRONT,
204                     RelativePosition.FRONT, RelativePosition.REAR);
205         }, "Unable to get leaders from LaneStructure");
206         PerceivedGtuType perceivedGtuType = lane.getLateralDirectionality().isNone() ? this.perceivedGtuTypeFront
207                 : (lane.getLateralDirectionality().isLeft() ? this.perceivedGtuTypeLeft : this.perceivedGtuTypeRight);
208         return new PerceptionReiterable<>(getGtu(), iterable, (perceivedGtu, distance) ->
209         {
210             return Try.assign(() -> perceivedGtuType.createPerceivedGtu(getGtu(), getGtu(), perceivedGtu, distance, true),
211                     "Exception during perception of leader.");
212         });
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public PerceptionCollectable<PerceivedGtu, LaneBasedGtu> getFollowers(final RelativeLane lane)
218     {
219         Throw.whenNull(lane, "Lane may not be null.");
220         return computeIfAbsent("followers", () -> computeFollowers(lane), lane);
221     }
222 
223     /**
224      * Computes followers.
225      * @param lane lane
226      * @return perception iterable for followers
227      */
228     private PerceptionCollectable<PerceivedGtu, LaneBasedGtu> computeFollowers(final RelativeLane lane)
229     {
230         Iterable<Entry<LaneBasedGtu>> iterable = Try.assign(() ->
231         {
232             return getPerception().getLaneStructure().getUpstreamGtus(lane, RelativePosition.FRONT, RelativePosition.FRONT,
233                     RelativePosition.REAR, RelativePosition.FRONT);
234         }, "Unable to get followers from LaneStructure");
235         PerceivedGtuType perceivedGtuType = lane.getLateralDirectionality().isNone() ? this.perceivedGtuTypeRear
236                 : (lane.getLateralDirectionality().isLeft() ? this.perceivedGtuTypeLeft : this.perceivedGtuTypeRight);
237         return new PerceptionReiterable<>(getGtu(), iterable, (perceivedGtu, distance) ->
238         {
239             return Try.assign(() -> perceivedGtuType.createPerceivedGtu(getGtu(), getGtu(), perceivedGtu, distance, false),
240                     "ParameterException during perception of leader.");
241         });
242     }
243 
244     /**
245      * Checks that lateral directionality is either left or right and an existing lane.
246      * @param lat LEFT or RIGHT
247      * @throws ParameterException if parameter is not defined
248      * @throws NullPointerException if {@code lat} is {@code null}
249      * @throws IllegalArgumentException if {@code lat} is {@code NONE}
250      */
251     private void checkLateralDirectionality(final LateralDirectionality lat)
252             throws ParameterException, NullPointerException, IllegalArgumentException
253     {
254         Throw.whenNull(lat, "Lateral directionality may not be null.");
255         Throw.when(lat.equals(LateralDirectionality.NONE), IllegalArgumentException.class,
256                 "Lateral directionality may not be NONE.");
257         Throw.when(!getPerception().getLaneStructure().exists(lat.isLeft() ? RelativeLane.LEFT : RelativeLane.RIGHT),
258                 IllegalArgumentException.class, "Lateral directionality may only point to an existing adjacent lane.");
259     }
260 
261     /** {@inheritDoc} */
262     @Override
263     public final String toString()
264     {
265         return "NeighborsPerceptionChannel";
266     }
267 
268 }