View Javadoc
1   package org.opentrafficsim.core.gtu;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   import static org.junit.Assert.fail;
6   
7   import java.awt.geom.Rectangle2D;
8   import java.rmi.RemoteException;
9   import java.util.HashMap;
10  import java.util.Map;
11  
12  import javax.naming.NamingException;
13  
14  import nl.tudelft.simulation.dsol.SimRuntimeException;
15  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
16  
17  import org.junit.Test;
18  import org.opentrafficsim.core.car.LaneBasedIndividualCar;
19  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
20  import org.opentrafficsim.core.dsol.OTSModelInterface;
21  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
22  import org.opentrafficsim.core.gtu.following.FixedAccelerationModel;
23  import org.opentrafficsim.core.gtu.following.GTUFollowingModel;
24  import org.opentrafficsim.core.gtu.lane.changing.AbstractLaneChangeModel;
25  import org.opentrafficsim.core.gtu.lane.changing.Egoistic;
26  import org.opentrafficsim.core.network.NetworkException;
27  import org.opentrafficsim.core.network.factory.LaneFactory;
28  import org.opentrafficsim.core.network.geotools.NodeGeotools;
29  import org.opentrafficsim.core.network.lane.Lane;
30  import org.opentrafficsim.core.network.lane.LaneType;
31  import org.opentrafficsim.core.unit.AccelerationUnit;
32  import org.opentrafficsim.core.unit.LengthUnit;
33  import org.opentrafficsim.core.unit.SpeedUnit;
34  import org.opentrafficsim.core.unit.TimeUnit;
35  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
36  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
37  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
38  import org.opentrafficsim.simulationengine.SimpleSimulator;
39  
40  import com.vividsolutions.jts.geom.Coordinate;
41  
42  /**
43   * Test the various methods of an AbstractLaneBasedGTU.<br/>
44   * As abstract classes cannot be directly
45   * <p>
46   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
47   * reserved. <br>
48   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
49   * <p>
50   * @version 14 jan. 2015 <br>
51   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
52   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
53   */
54  public class AbstractLaneBasedGTUTest
55  {
56  
57      /**
58       * Test that the constructor puts the supplied values in the correct fields.
59       * @throws SimRuntimeException
60       * @throws RemoteException
61       * @throws NamingException
62       * @throws NetworkException
63       * @throws GTUException
64       */
65      @Test
66      public void abstractLaneBasedGTUTest() throws RemoteException, SimRuntimeException, NamingException,
67              NetworkException, GTUException
68      {
69          // This initialization code should probably be moved to a helper method that will be used in several tests.
70          // First we need a set of Lanes
71          // To create Lanes we need Nodes and a LaneType
72          NodeGeotools.STR nodeAFrom = new NodeGeotools.STR("AFrom", new Coordinate(0, 0, 0));
73          NodeGeotools.STR nodeATo = new NodeGeotools.STR("ATo", new Coordinate(1000, 0, 0));
74          LaneType<String> laneType = new LaneType<String>("CarLane");
75          // And a simulator, but for that we first need something that implements OTSModelInterface
76          OTSModelInterface model = new DummyModelForTemplateGTUTest();
77          final SimpleSimulator simulator =
78                  new SimpleSimulator(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND),
79                          new DoubleScalar.Rel<TimeUnit>(0.0, TimeUnit.SECOND), new DoubleScalar.Rel<TimeUnit>(3600.0,
80                                  TimeUnit.SECOND), model, new Rectangle2D.Double(-1000, -1000, 2000, 2000));
81  
82          Lane[] lanesGroupA =
83                  LaneFactory.makeMultiLane("A", nodeAFrom, nodeATo, null, 3, laneType,
84                          (OTSDEVSSimulatorInterface) simulator.getSimulator());
85          // A GTU can exist on several lanes at once; create another lane group to test that
86          NodeGeotools.STR nodeBFrom = new NodeGeotools.STR("BFrom", new Coordinate(10, 0, 0));
87          NodeGeotools.STR nodeBTo = new NodeGeotools.STR("BTo", new Coordinate(1000, 100, 0));
88          Lane[] lanesGroupB =
89                  LaneFactory.makeMultiLane("B", nodeBFrom, nodeBTo, null, 3, laneType,
90                          (OTSDEVSSimulatorInterface) simulator.getSimulator());
91          Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions =
92                  new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
93  
94          DoubleScalar.Rel<LengthUnit> positionA = new DoubleScalar.Rel<LengthUnit>(100, LengthUnit.METER);
95          initialLongitudinalPositions.put(lanesGroupA[1], positionA);
96          DoubleScalar.Rel<LengthUnit> positionB = new DoubleScalar.Rel<LengthUnit>(90, LengthUnit.METER);
97          initialLongitudinalPositions.put(lanesGroupB[1], positionB);
98          // A Car needs a CarFollowingModel
99          DoubleScalar.Abs<AccelerationUnit> acceleration =
100                 new DoubleScalar.Abs<AccelerationUnit>(2, AccelerationUnit.METER_PER_SECOND_2);
101         DoubleScalar.Rel<TimeUnit> validFor = new DoubleScalar.Rel<TimeUnit>(10, TimeUnit.SECOND);
102         GTUFollowingModel gfm = new FixedAccelerationModel(acceleration, validFor);
103         // A Car needs a lane change model
104         AbstractLaneChangeModel laneChangeModel = new Egoistic();
105         // A Car needs a type
106         GTUType<String> gtuType = new GTUType<String>("Car");
107         // A Car needs an initial speed
108         DoubleScalar.Abs<SpeedUnit> initialSpeed = new DoubleScalar.Abs<SpeedUnit>(50, SpeedUnit.KM_PER_HOUR);
109         // Length of the Car
110         DoubleScalar.Rel<LengthUnit> carLength = new DoubleScalar.Rel<LengthUnit>(4, LengthUnit.METER);
111         // Width of the Car
112         DoubleScalar.Rel<LengthUnit> carWidth = new DoubleScalar.Rel<LengthUnit>(1.8, LengthUnit.METER);
113         // Maximum velocity of the Car
114         DoubleScalar.Abs<SpeedUnit> maximumVelocity = new DoubleScalar.Abs<SpeedUnit>(200, SpeedUnit.KM_PER_HOUR);
115         // ID of the Car
116         String carID = "theCar";
117         // Now we can make a GTU
118         LaneBasedIndividualCar<String> car =
119                 new LaneBasedIndividualCar<String>(carID, gtuType, gfm, laneChangeModel, initialLongitudinalPositions,
120                         initialSpeed, carLength, carWidth, maximumVelocity,
121                         (OTSDEVSSimulatorInterface) simulator.getSimulator());
122         // Now we can verify the various fields in the newly created Car
123         assertEquals("ID of the car should be identical to the provided one", carID, car.getId());
124         assertEquals("GTU following model should be identical to the provided one", gfm, car.getGTUFollowingModel());
125         assertEquals("GTU type should be identical to the provided one", gtuType, car.getGTUType());
126         assertEquals("front in lanesGroupA[1] is positionA", positionA.getSI(),
127                 car.position(lanesGroupA[1], car.getReference()).getSI(), 0.0001);
128         assertEquals("front in lanesGroupB[1] is positionB", positionB.getSI(),
129                 car.position(lanesGroupB[1], car.getReference()).getSI(), 0.0001);
130         assertEquals("acceleration is 0", 0, car.getAcceleration().getSI(), 0.00001);
131         assertEquals("longitudinal velocity is " + initialSpeed, initialSpeed.getSI(), car.getLongitudinalVelocity()
132                 .getSI(), 0.00001);
133         assertEquals("lastEvaluation time is 0", 0, car.getLastEvaluationTime().getSI(), 0.00001);
134         // Test the position(Lane, RelativePosition) method
135         try
136         {
137             car.position(null, car.getFront());
138             fail("position on null lane should have thrown a NetworkException");
139         }
140         catch (NetworkException ne)
141         {
142             // Ignore
143         }
144         for (Lane[] laneGroup : new Lane[][]{lanesGroupA, lanesGroupB})
145         {
146             for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
147             {
148                 Lane lane = laneGroup[laneIndex];
149                 boolean expectException = 1 != laneIndex;
150                 for (RelativePosition relativePosition : new RelativePosition[]{car.getFront(), car.getRear()})
151                 {
152                     // System.out.println("lane:" + lane + ", expectedException: " + expectException
153                     // + ", relativePostion: " + relativePosition);
154                     try
155                     {
156                         DoubleScalar.Rel<LengthUnit> position = car.position(lane, relativePosition);
157                         if (expectException)
158                         {
159                             // System.out.println("position: " + position);
160                             fail("Calling position on lane that the car is NOT on should have thrown a NetworkException");
161                         }
162                         else
163                         {
164                             DoubleScalar.Rel<LengthUnit> expectedPosition =
165                                     laneGroup == lanesGroupA ? positionA : positionB;
166                             // FIXME There should be a better way to check equality of RelativePosition
167                             if (relativePosition.getDx().getSI() != 0)
168                             {
169                                 expectedPosition = DoubleScalar.plus(expectedPosition, carLength).immutable();
170                             }
171                             // System.out.println("reported position: " + position);
172                             // System.out.println("expected position: " + expectedPosition);
173                             assertEquals("Position should match initial position", expectedPosition.getSI(),
174                                     position.getSI(), 0.0001);
175                         }
176                     }
177                     catch (NetworkException ne)
178                     {
179                         if (!expectException)
180                         {
181                             System.out.println(ne);
182                             fail("Calling position on lane that the car is on should NOT have thrown a NetworkException");
183                         }
184                     }
185                 }
186             }
187         }
188         // Assign a movement to the car (10 seconds of acceleration of 2 m/s/s)
189         // scheduled event that moves the car at t=0
190         assertEquals("lastEvaluation time is 0", 0, car.getLastEvaluationTime().getSI(), 0.00001);
191         assertEquals("nextEvaluation time is 0", 0, car.getNextEvaluationTime().getSI(), 0.00001);
192         // Increase the simulator clock in small steps and verify the both positions on all lanes at each step
193         double step = 0.01d;
194         for (int i = 0;; i++)
195         {
196             DoubleScalar.Abs<TimeUnit> stepTime = new DoubleScalar.Abs<TimeUnit>(i * step, TimeUnit.SECOND);
197             if (stepTime.getSI() > validFor.getSI())
198             {
199                 break;
200             }
201             if (stepTime.getSI() > 0.5)
202             {
203                 step = 0.1; // Reduce testing time by increasing the step size
204             }
205             // System.out.println("Simulating until " + stopTime.getSI());
206             simulator.runUpTo(stepTime);
207             if (stepTime.getSI() > 0)
208             {
209                 assertEquals("nextEvaluation time is " + validFor, validFor.getSI(), car.getNextEvaluationTime()
210                         .getSI(), 0.0001);
211                 assertEquals("acceleration is " + acceleration, acceleration.getSI(), car.getAcceleration().getSI(),
212                         0.00001);
213             }
214             DoubleScalar.Abs<SpeedUnit> longitudinalVelocity = car.getLongitudinalVelocity();
215             double expectedLongitudinalVelocity = initialSpeed.getSI() + stepTime.getSI() * acceleration.getSI();
216             assertEquals("longitudinal velocity is " + expectedLongitudinalVelocity, expectedLongitudinalVelocity,
217                     longitudinalVelocity.getSI(), 0.00001);
218             assertEquals("lateral velocity is 0", 0, car.getLateralVelocity().getSI(), 0.00001);
219             for (RelativePosition relativePosition : new RelativePosition[]{car.getFront(), car.getRear()})
220             {
221                 Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
222                 assertEquals("Car should be in two lanes", 2, positions.size());
223                 Double pos = positions.get(lanesGroupA[1]);
224                 assertTrue("Car should be in lane 1 of lane group A", null != pos);
225                 assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
226                         car.fractionalPosition(lanesGroupA[1], relativePosition), 0.0000001);
227                 pos = positions.get(lanesGroupB[1]);
228                 assertTrue("Car should be in lane 1 of lane group B", null != pos);
229                 assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
230                         car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
231             }
232             for (Lane[] laneGroup : new Lane[][]{lanesGroupA, lanesGroupB})
233             {
234                 for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
235                 {
236                     Lane lane = laneGroup[laneIndex];
237                     boolean expectException = 1 != laneIndex;
238                     for (RelativePosition relativePosition : new RelativePosition[]{car.getFront(), car.getRear()})
239                     {
240                         // System.out.println("lane:" + lane + ", expectedException: " + expectException
241                         // + ", relativePostion: " + relativePosition);
242                         try
243                         {
244                             DoubleScalar.Rel<LengthUnit> position = car.position(lane, relativePosition);
245                             if (expectException)
246                             {
247                                 // System.out.println("position: " + position);
248                                 fail("Calling position on lane that the car is NOT on should have thrown a "
249                                         + "NetworkException");
250                             }
251                             else
252                             {
253                                 DoubleScalar.Rel<LengthUnit> expectedPosition =
254                                         laneGroup == lanesGroupA ? positionA : positionB;
255                                 expectedPosition =
256                                         DoubleScalar.plus(
257                                                 expectedPosition,
258                                                 new DoubleScalar.Rel<LengthUnit>(stepTime.getSI()
259                                                         * initialSpeed.getSI(), LengthUnit.SI)).immutable();
260                                 expectedPosition =
261                                         DoubleScalar.plus(
262                                                 expectedPosition,
263                                                 new DoubleScalar.Rel<LengthUnit>(0.5 * acceleration.getSI()
264                                                         * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI))
265                                                 .immutable();
266                                 // FIXME There should be a (better) way to check equality of RelativePosition
267                                 if (relativePosition.getDx().getSI() != 0)
268                                 {
269                                     expectedPosition = DoubleScalar.plus(expectedPosition, carLength).immutable();
270                                 }
271                                 // System.out.println("reported position: " + position);
272                                 // System.out.println("expected position: " + expectedPosition);
273                                 assertEquals("Position should match initial position", expectedPosition.getSI(),
274                                         position.getSI(), 0.0001);
275                             }
276                         }
277                         catch (NetworkException ne)
278                         {
279                             if (!expectException)
280                             {
281                                 System.out.println(ne);
282                                 fail("Calling position on lane that the car is on should NOT have thrown a NetworkException");
283                             }
284                         }
285                         try
286                         {
287                             double fractionalPosition = car.fractionalPosition(lane, relativePosition);
288                             if (expectException)
289                             {
290                                 // System.out.println("position: " + position);
291                                 fail("Calling position on lane that the car is NOT on should have thrown a NetworkException");
292                             }
293                             else
294                             {
295                                 DoubleScalar.Rel<LengthUnit> expectedPosition =
296                                         laneGroup == lanesGroupA ? positionA : positionB;
297                                 expectedPosition =
298                                         DoubleScalar.plus(
299                                                 expectedPosition,
300                                                 new DoubleScalar.Rel<LengthUnit>(stepTime.getSI()
301                                                         * initialSpeed.getSI(), LengthUnit.SI)).immutable();
302                                 expectedPosition =
303                                         DoubleScalar.plus(
304                                                 expectedPosition,
305                                                 new DoubleScalar.Rel<LengthUnit>(0.5 * acceleration.getSI()
306                                                         * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI))
307                                                 .immutable();
308                                 // FIXME There should be a (better) way to check equality of RelativePosition
309                                 if (relativePosition.getDx().getSI() != 0)
310                                 {
311                                     expectedPosition = DoubleScalar.plus(expectedPosition, carLength).immutable();
312                                 }
313                                 // System.out.println("reported position: " + position);
314                                 // System.out.println("expected position: " + expectedPosition);
315                                 double expectedFractionalPosition = expectedPosition.getSI() / lane.getLength().getSI();
316                                 assertEquals("Position should match initial position", expectedFractionalPosition,
317                                         fractionalPosition, 0.000001);
318                             }
319                         }
320                         catch (NetworkException ne)
321                         {
322                             if (!expectException)
323                             {
324                                 System.out.println(ne);
325                                 fail("Calling fractionalPosition on lane that the car is on should NOT have thrown a "
326                                         + "NetworkException");
327                             }
328                         }
329                     }
330                 }
331             }
332         }
333         // A GTU can exist on several lanes at once; create another lane group to test that
334         NodeGeotools.STR nodeCFrom = new NodeGeotools.STR("CFrom", new Coordinate(10, 100, 0));
335         NodeGeotools.STR nodeCTo = new NodeGeotools.STR("CTo", new Coordinate(1000, 0, 0));
336         Lane[] lanesGroupC =
337                 LaneFactory.makeMultiLane("C", nodeCFrom, nodeCTo, null, 3, laneType,
338                         (OTSDEVSSimulatorInterface) simulator.getSimulator());
339         car.addLane(lanesGroupC[0], new DoubleScalar.Rel<LengthUnit>(0.0, LengthUnit.SI));
340         for (RelativePosition relativePosition : new RelativePosition[]{car.getFront(), car.getRear()})
341         {
342             Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
343             assertEquals("Car should be in three lanes", 3, positions.size());
344             Double pos = positions.get(lanesGroupA[1]);
345             assertTrue("Car should be in lane 1 of lane group A", null != pos);
346             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
347                     car.fractionalPosition(lanesGroupA[1], relativePosition), 0.0000001);
348             pos = positions.get(lanesGroupB[1]);
349             assertTrue("Car should be in lane 1 of lane group B", null != pos);
350             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
351                     car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
352             pos = positions.get(lanesGroupC[0]);
353             assertTrue("Car should be in lane 0 of lane group C", null != pos);
354             // The next one fails - maybe I don't understand something - PK
355             // assertEquals("fractional position should be 0", 0,
356             // car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
357             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
358                     car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
359         }
360         car.removeLane(lanesGroupA[1]);
361         for (RelativePosition relativePosition : new RelativePosition[]{car.getFront(), car.getRear()})
362         {
363             Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
364             assertEquals("Car should be in two lanes", 2, positions.size());
365             Double pos = positions.get(lanesGroupB[1]);
366             assertTrue("Car should be in lane 1 of lane group B", null != pos);
367             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
368                     car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
369             pos = positions.get(lanesGroupC[0]);
370             assertTrue("Car should be in lane 0 of lane group C", null != pos);
371             // The next one fails - maybe I don't understand something - PK
372             // assertEquals("fractional position should be 0", 0,
373             // car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
374             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
375                     car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
376         }
377         // TODO
378         // removeLane should throw an Error when the car is not on that lane (currently this is silently ignored)
379         // TODO
380         // figure out why the added lane has a non-zero position
381     }
382 
383 }
384 
385 /**
386  * Dummy OTSModelInterface.
387  * <p>
388  * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
389  * reserved. <br>
390  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
391  * <p>
392  * @version 14 jan. 2015 <br>
393  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
394  */
395 class DummyModel implements OTSModelInterface
396 {
397     /** */
398     private static final long serialVersionUID = 20150114L;
399 
400     /** The simulator. */
401     private SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> simulator;
402 
403     /**
404      * Register the simulator.
405      * @param simulator SimulatorInterface&lt;DoubleScalar.Abs&lt;TimeUnit&gt;, DoubleScalar.Rel&lt;TimeUnit&gt;,
406      *            OTSSimTimeDouble&gt;; the simulator
407      */
408     public void setSimulator(
409             SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> simulator)
410     {
411         this.simulator = simulator;
412     }
413 
414     /** {@inheritDoc} */
415     @Override
416     public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> arg0)
417             throws SimRuntimeException, RemoteException
418     {
419         // Nothing happens here
420     }
421 
422     /** {@inheritDoc} */
423     @Override
424     public SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
425             throws RemoteException
426     {
427         if (null == this.simulator)
428         {
429             throw new Error("getSimulator called, but simulator field is null");
430         }
431         return this.simulator;
432     }
433 
434 }