Eclipse SUMO - Simulation of Urban MObility
MSDevice_SSM.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2013-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
23 // An SSM-device logs encounters / conflicts of the carrying vehicle with other surrounding vehicles
24 // XXX: Preliminary implementation. Use with care. Especially rerouting vehicles could be problematic.
25 // TODO: implement SSM time-gap (estimated conflict entry and exit times are already calculated for PET calculation)
26 /****************************************************************************/
27 #include <config.h>
28 
29 #include <iostream>
30 #include <algorithm>
35 #include <utils/geom/GeomHelper.h>
36 #include <utils/geom/Position.h>
40 #include <microsim/MSNet.h>
41 #include <microsim/MSJunction.h>
42 #include <microsim/MSLane.h>
43 #include <microsim/MSLink.h>
44 #include <microsim/MSEdge.h>
45 #include <microsim/MSVehicle.h>
49 #include "MSDevice_SSM.h"
50 
51 // ===========================================================================
52 // Debug constants
53 // ===========================================================================
54 //#define DEBUG_SSM
55 //#define DEBUG_SSM_OPPOSITE
56 //#define DEBUG_ENCOUNTER
57 //#define DEBUG_SSM_SURROUNDING
58 //#define DEBUG_SSM_DRAC
59 //#define DEBUG_SSM_NOTIFICATIONS
60 //#define DEBUG_COND(ego) MSNet::getInstance()->getCurrentTimeStep() > 308000
61 //
62 //#define DEBUG_EGO_ID ""
63 //#define DEBUG_FOE_ID ""
64 //#define DEBUG_COND_FIND(ego) (ego.getID() == DEBUG_EGO_ID)
65 //#define DEBUG_COND(ego) ((ego)!=nullptr && (ego)->getID() == DEBUG_EGO_ID)
66 //#define DEBUG_COND_ENCOUNTER(e) ((DEBUG_EGO_ID == std::string("") || e->egoID == DEBUG_EGO_ID) && (DEBUG_FOE_ID == std::string("") || e->foeID == DEBUG_FOE_ID))
67 
68 //#define DEBUG_COND(ego) (ego!=nullptr && ego->isSelected())
69 //#define DEBUG_COND_FIND(ego) (ego.isSelected())
70 //#define DEBUG_COND_ENCOUNTER(e) (e->ego != nullptr && e->ego->isSelected() && e->foe != nullptr && e->foe->isSelected())
71 
72 
73 // ===========================================================================
74 // Constants
75 // ===========================================================================
76 // list of implemented SSMs (NOTE: To add more SSMs, identifiers are added to AVAILABLE_SSMS
77 // and a default threshold must be defined. A corresponding
78 // case should be added to the switch in buildVehicleDevices,
79 // and in computeSSMs(), the SSM-value should be computed.)
80 #define AVAILABLE_SSMS "TTC DRAC PET BR SGAP TGAP PPET MDRAC"
81 #define DEFAULT_THRESHOLD_TTC 3. // in [s.], events get logged if time to collision is below threshold (1.5s. is an appropriate criticality threshold according to Van der Horst, A. R. A. (1991). Time-to-collision as a Cue for Decision-making in Braking [also see Guido et al. 2011])
82 #define DEFAULT_THRESHOLD_DRAC 3. // in [m/s^2], events get logged if "deceleration to avoid a crash" is above threshold (3.4s. is an appropriate criticality threshold according to American Association of State Highway and Transportation Officials (2004). A Policy on Geometric Design of Highways and Streets [also see Guido et al. 2011])
83 #define DEFAULT_THRESHOLD_MDRAC 3.4 //in [m/s^2], events get logged if "deceleration to avoid a crash" is above threshold (MDRAC considers reaction time of follower)
84 
85 #define DEFAULT_THRESHOLD_PET 2. // in seconds, events get logged if post encroachment time is below threshold
86 #define DEFAULT_THRESHOLD_PPET 2. // in seconds, events get logged if predicted post encroachment time is below threshold
87 
88 #define DEFAULT_THRESHOLD_BR 0.0 // in [m/s^2], events get logged if brake rate is above threshold
89 #define DEFAULT_THRESHOLD_SGAP 0.2 // in [m.], events get logged if the space headway is below threshold.
90 #define DEFAULT_THRESHOLD_TGAP 0.5 // in [m.], events get logged if the time headway is below threshold.
91 
92 #define DEFAULT_EXTRA_TIME 5. // in seconds, events get logged for extra time even if encounter is over
93 
94 // ===========================================================================
95 // static members
96 // ===========================================================================
97 std::set<const MSEdge*> MSDevice_SSM::myEdgeFilter;
100 
101 // ===========================================================================
102 // method definitions
103 // ===========================================================================
104 
106 std::ostream& operator<<(std::ostream& out, MSDevice_SSM::EncounterType type) {
107  switch (type) {
109  out << "NOCONFLICT_AHEAD";
110  break;
112  out << "FOLLOWING";
113  break;
115  out << "FOLLOWING_FOLLOWER";
116  break;
118  out << "FOLLOWING_LEADER";
119  break;
121  out << "ON_ADJACENT_LANES";
122  break;
124  out << "MERGING";
125  break;
127  out << "MERGING_LEADER";
128  break;
130  out << "MERGING_FOLLOWER";
131  break;
133  out << "MERGING_ADJACENT";
134  break;
136  out << "CROSSING";
137  break;
139  out << "CROSSING_LEADER";
140  break;
142  out << "CROSSING_FOLLOWER";
143  break;
145  out << "EGO_ENTERED_CONFLICT_AREA";
146  break;
148  out << "FOE_ENTERED_CONFLICT_AREA";
149  break;
151  out << "BOTH_ENTERED_CONFLICT_AREA";
152  break;
154  out << "EGO_LEFT_CONFLICT_AREA";
155  break;
157  out << "FOE_LEFT_CONFLICT_AREA";
158  break;
160  out << "BOTH_LEFT_CONFLICT_AREA";
161  break;
163  out << "FOLLOWING_PASSED";
164  break;
166  out << "MERGING_PASSED";
167  break;
168  // Collision (currently unused, might be differentiated further)
170  out << "COLLISION";
171  break;
173  out << "ONCOMING";
174  break;
175  default:
176  out << "unknown type (" << int(type) << ")";
177  break;
178  }
179  return out;
180 }
181 
182 
183 // ---------------------------------------------------------------------------
184 // static initialisation methods
185 // ---------------------------------------------------------------------------
186 
187 std::set<MSDevice_SSM*, ComparatorNumericalIdLess>* MSDevice_SSM::myInstances = new std::set<MSDevice_SSM*, ComparatorNumericalIdLess>();
188 
189 std::set<std::string> MSDevice_SSM::myCreatedOutputFiles;
190 
192 
193 const std::set<int> MSDevice_SSM::FOE_ENCOUNTERTYPES({
194  ENCOUNTER_TYPE_FOLLOWING_LEADER, ENCOUNTER_TYPE_MERGING_FOLLOWER,
195  ENCOUNTER_TYPE_CROSSING_FOLLOWER, ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA,
196  ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
197 });
198 const std::set<int> MSDevice_SSM::EGO_ENCOUNTERTYPES({
199  ENCOUNTER_TYPE_FOLLOWING_FOLLOWER, ENCOUNTER_TYPE_MERGING_LEADER,
200  ENCOUNTER_TYPE_CROSSING_LEADER, ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA,
201  ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
202 });
203 
204 
205 const std::set<MSDevice_SSM*, ComparatorNumericalIdLess>&
207  return *myInstances;
208 }
209 
210 void
212  // Close current encounters and flush conflicts to file for all existing devices
213  if (myInstances != nullptr) {
214  for (MSDevice_SSM* device : *myInstances) {
215  device->resetEncounters();
216  device->flushConflicts(true);
217  device->flushGlobalMeasures();
218  }
219  myInstances->clear();
220  }
221  for (const std::string& fn : myCreatedOutputFiles) {
223  }
224  myCreatedOutputFiles.clear();
225  myEdgeFilter.clear();
226  myEdgeFilterInitialized = false;
227  myEdgeFilterActive = false;
228 }
229 
230 
231 void
233  oc.addOptionSubTopic("SSM Device");
234  insertDefaultAssignmentOptions("ssm", "SSM Device", oc);
235 
236  // custom options
237  oc.doRegister("device.ssm.measures", new Option_String(""));
238  oc.addDescription("device.ssm.measures", "SSM Device", TL("Specifies which measures will be logged (as a space or comma-separated sequence of IDs in ('TTC', 'DRAC', 'PET', 'PPET', 'MDRAC'))"));
239  oc.doRegister("device.ssm.thresholds", new Option_String(""));
240  oc.addDescription("device.ssm.thresholds", "SSM Device", TL("Specifies space or comma-separated thresholds corresponding to the specified measures (see documentation and watch the order!). Only events exceeding the thresholds will be logged."));
241  oc.doRegister("device.ssm.trajectories", new Option_Bool(false));
242  oc.addDescription("device.ssm.trajectories", "SSM Device", TL("Specifies whether trajectories will be logged (if false, only the extremal values and times are reported)."));
243  oc.doRegister("device.ssm.range", new Option_Float(50.));
244  oc.addDescription("device.ssm.range", "SSM Device", TL("Specifies the detection range in meters. For vehicles below this distance from the equipped vehicle, SSM values are traced."));
245  oc.doRegister("device.ssm.extratime", new Option_Float(DEFAULT_EXTRA_TIME));
246  oc.addDescription("device.ssm.extratime", "SSM Device", TL("Specifies the time in seconds to be logged after a conflict is over. Required >0 if PET is to be calculated for crossing conflicts."));
247  oc.doRegister("device.ssm.mdrac.prt", new Option_Float(1.));
248  oc.addDescription("device.ssm.mdrac.prt", "SSM Device", TL("Specifies the perception reaction time for MDRAC computation."));
249  oc.doRegister("device.ssm.file", new Option_String(""));
250  oc.addDescription("device.ssm.file", "SSM Device", TL("Give a global default filename for the SSM output"));
251  oc.doRegister("device.ssm.geo", new Option_Bool(false));
252  oc.addDescription("device.ssm.geo", "SSM Device", TL("Whether to use coordinates of the original reference system in output"));
253  oc.doRegister("device.ssm.write-positions", new Option_Bool(false));
254  oc.addDescription("device.ssm.write-positions", "SSM Device", TL("Whether to write positions (coordinates) for each timestep"));
255  oc.doRegister("device.ssm.write-lane-positions", new Option_Bool(false));
256  oc.addDescription("device.ssm.write-lane-positions", "SSM Device", TL("Whether to write lanes and their positions for each timestep"));
257  oc.doRegister("device.ssm.exclude-conflict-types", new Option_String(""));
258  oc.addDescription("device.ssm.exclude-conflict-types", "SSM Device", TL("Which conflicts will be excluded from the log according to the conflict type they have been classified (combination of values in 'ego', 'foe' , '', any numerical valid conflict type code). An empty value will log all and 'ego'/'foe' refer to a certain conflict type subset."));
259 }
260 
261 
262 void
265  if (OptionsCont::getOptions().isSet("device.ssm.filter-edges.input-file")) {
266  const std::string file = OptionsCont::getOptions().getString("device.ssm.filter-edges.input-file");
267  std::ifstream strm(file.c_str());
268  if (!strm.good()) {
269  throw ProcessError(TLF("Could not load names of edges for filtering SSM device output from '%'.", file));
270  }
271  myEdgeFilterActive = true;
272  while (strm.good()) {
273  std::string line;
274  strm >> line;
275  // maybe we're loading an edge-selection
276  if (StringUtils::startsWith(line, "edge:")) {
277  std::string edgeID = line.substr(5);
278  MSEdge* edge = MSEdge::dictionary(edgeID);
279  if (edge != nullptr) {
280  myEdgeFilter.insert(edge);
281  } else {
282  WRITE_WARNING("Unknown edge ID '" + edgeID + "' in SSM device edge filter (" + file + "): " + line);
283  }
284  } else if (StringUtils::startsWith(line, "junction:")) {
285  // get the internal edge(s) of a junction
286  std::string junctionID = line.substr(9);
287  MSJunction* junction = MSNet::getInstance()->getJunctionControl().get(junctionID);
288  if (junction != nullptr) {
289  for (MSLane* const internalLane : junction->getInternalLanes()) {
290  myEdgeFilter.insert(&(internalLane->getEdge()));
291  }
292  } else {
293  WRITE_WARNING("Unknown junction ID '" + junctionID + "' in SSM device edge filter (" + file + "): " + line);
294  }
295  } else if (line == "") { // ignore empty lines (mostly last line)
296  } else {
297  WRITE_WARNING("Cannot interpret line in SSM device edge filter (" + file + "): " + line);
298  }
299  }
300  }
301 }
302 
303 void
304 MSDevice_SSM::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
307  WRITE_WARNINGF("SSM Device for vehicle '%' will not be built. (SSMs not supported in MESO)", v.getID());
308  return;
309  }
310  // ID for the device
311  std::string deviceID = "ssm_" + v.getID();
312 
313  // Load parameters:
314 
315  // Measures and thresholds
316  std::map<std::string, double> thresholds;
317  bool success = getMeasuresAndThresholds(v, deviceID, thresholds);
318  if (!success) {
319  return;
320  }
321 
322  // TODO: modify trajectory option: "all", "conflictPoints", ("position" && "speed" == "vehState"), "SSMs"!
323  // Trajectories
324  bool trajectories = requestsTrajectories(v);
325 
326  // detection range
327  double range = getDetectionRange(v);
328 
329  // extra time
330  double extraTime = getExtraTime(v);
331 
332  // File
333  std::string file = getOutputFilename(v, deviceID);
334 
335  const bool useGeo = useGeoCoords(v);
336 
337  const bool writePos = writePositions(v);
338 
339  const bool writeLanesPos = writeLanesPositions(v);
340 
341  std::vector<int> conflictTypeFilter;
342  success = filterByConflictType(v, deviceID, conflictTypeFilter);
343  if (!success) {
344  return;
345  }
346 
347  // Build the device (XXX: who deletes it?)
348  MSDevice_SSM* device = new MSDevice_SSM(v, deviceID, file, thresholds, trajectories, range, extraTime, useGeo, writePos, writeLanesPos, conflictTypeFilter);
349  into.push_back(device);
350 
351  // Init spatial filter (once)
353  initEdgeFilter();
354  }
355  }
356 }
357 
358 
359 MSDevice_SSM::Encounter::Encounter(const MSVehicle* _ego, const MSVehicle* const _foe, double _begin, double extraTime) :
360  ego(_ego),
361  foe(_foe),
362  egoID(_ego->getID()),
363  foeID(_foe->getID()),
364  begin(_begin),
365  end(-INVALID_DOUBLE),
366  currentType(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
367  remainingExtraTime(extraTime),
368  egoConflictEntryTime(INVALID_DOUBLE),
369  egoConflictExitTime(INVALID_DOUBLE),
370  foeConflictEntryTime(INVALID_DOUBLE),
371  foeConflictExitTime(INVALID_DOUBLE),
372  minTTC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
373  maxDRAC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
374  maxMDRAC(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
375  PET(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
376  minPPET(INVALID_DOUBLE, Position::INVALID, ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID_DOUBLE, INVALID_DOUBLE),
377  closingRequested(false) {
378 #ifdef DEBUG_ENCOUNTER
379  if (DEBUG_COND_ENCOUNTER(this)) {
380  std::cout << "\n" << SIMTIME << " Constructing encounter of '" << ego->getID() << "' and '" << foe->getID() << "'" << std::endl;
381  }
382 #endif
383 }
384 
386 #ifdef DEBUG_ENCOUNTER
387  if (DEBUG_COND_ENCOUNTER(this)) {
388  std::cout << "\n" << SIMTIME << " Destroying encounter of '" << egoID << "' and '" << foeID << "' (begin was " << begin << ")" << std::endl;
389  }
390 #endif
391 }
392 
393 
394 void
395 MSDevice_SSM::Encounter::add(double time, const EncounterType type, Position egoX, std::string egoLane, double egoLanePos, Position egoV,
396  Position foeX, std::string foeLane, double foeLanePos, Position foeV,
397  Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair<double, double> pet, double ppet, double mdrac) {
398 #ifdef DEBUG_ENCOUNTER
399  if (DEBUG_COND_ENCOUNTER(this))
400  std::cout << time << " Adding data point for encounter of '" << egoID << "' and '" << foeID << "':\n"
401  << "type=" << type << ", egoDistToConflict=" << (egoDistToConflict == INVALID_DOUBLE ? "NA" : ::toString(egoDistToConflict))
402  << ", foeDistToConflict=" << (foeDistToConflict == INVALID_DOUBLE ? "NA" : ::toString(foeDistToConflict))
403  << ",\nttc=" << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc))
404  << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac))
405  << ", pet=" << (pet.second == INVALID_DOUBLE ? "NA" : ::toString(pet.second))
406  << std::endl;
407 #endif
408  currentType = type;
409 
410  timeSpan.push_back(time);
411  typeSpan.push_back(type);
412  egoTrajectory.x.push_back(egoX);
413  egoTrajectory.lane.push_back(egoLane);
414  egoTrajectory.lanePos.push_back(egoLanePos);
415  egoTrajectory.v.push_back(egoV);
416  foeTrajectory.x.push_back(foeX);
417  foeTrajectory.lane.push_back(foeLane);
418  foeTrajectory.lanePos.push_back(foeLanePos);
419  foeTrajectory.v.push_back(foeV);
420  conflictPointSpan.push_back(conflictPoint);
421  egoDistsToConflict.push_back(egoDistToConflict);
422  foeDistsToConflict.push_back(foeDistToConflict);
423 
424  TTCspan.push_back(ttc);
425  if (ttc != INVALID_DOUBLE && (ttc < minTTC.value || minTTC.value == INVALID_DOUBLE)) {
426  minTTC.value = ttc;
427  minTTC.time = time;
428  minTTC.pos = conflictPoint;
429  minTTC.type = ttc <= 0 ? ENCOUNTER_TYPE_COLLISION : type;
430  minTTC.speed = egoV.distanceTo(Position(0, 0));
431  }
432 
433  DRACspan.push_back(drac);
434  if (drac != INVALID_DOUBLE && (drac > maxDRAC.value || maxDRAC.value == INVALID_DOUBLE)) {
435  maxDRAC.value = drac;
436  maxDRAC.time = time;
437  maxDRAC.pos = conflictPoint;
438  maxDRAC.type = type;
439  maxDRAC.speed = egoV.distanceTo(Position(0, 0));
440  }
441 
442  if (pet.first != INVALID_DOUBLE && (PET.value >= pet.second || PET.value == INVALID_DOUBLE)) {
443  PET.value = pet.second;
444  PET.time = pet.first;
445  PET.pos = conflictPoint;
446  PET.type = PET.value <= 0 ? ENCOUNTER_TYPE_COLLISION : type;
447  PET.speed = egoV.distanceTo(Position(0, 0));
448  }
449  PPETspan.push_back(ppet);
450  if (ppet != INVALID_DOUBLE && (ppet < minPPET.value || minPPET.value == INVALID_DOUBLE)) {
451  minPPET.value = ppet;
452  minPPET.time = time;
453  minPPET.pos = conflictPoint;
454  minPPET.type = ppet <= 0 ? ENCOUNTER_TYPE_COLLISION : type;
455  minPPET.speed = egoV.distanceTo(Position(0, 0));
456  }
457  MDRACspan.push_back(mdrac);
458  if (mdrac != INVALID_DOUBLE && (mdrac > maxMDRAC.value || maxMDRAC.value == INVALID_DOUBLE)) {
459  maxMDRAC.value = mdrac;
460  maxMDRAC.time = time;
461  maxMDRAC.pos = conflictPoint;
462  maxMDRAC.type = type;
463  maxMDRAC.speed = egoV.distanceTo(Position(0, 0));
464  }
465 }
466 
467 
468 void
470  remainingExtraTime = value;
471 }
472 
473 
474 void
476  remainingExtraTime -= amount;
477 }
478 
479 
480 double
482  return remainingExtraTime;
483 }
484 
485 
487  encounter(e),
489  conflictPoint(Position::INVALID),
490  egoConflictEntryDist(INVALID_DOUBLE),
491  foeConflictEntryDist(INVALID_DOUBLE),
492  egoConflictExitDist(INVALID_DOUBLE),
493  foeConflictExitDist(INVALID_DOUBLE),
494  egoEstimatedConflictEntryTime(INVALID_DOUBLE),
495  foeEstimatedConflictEntryTime(INVALID_DOUBLE),
496  egoEstimatedConflictExitTime(INVALID_DOUBLE),
497  foeEstimatedConflictExitTime(INVALID_DOUBLE),
498  egoConflictAreaLength(INVALID_DOUBLE),
499  foeConflictAreaLength(INVALID_DOUBLE),
500  ttc(INVALID_DOUBLE),
501  drac(INVALID_DOUBLE),
502  mdrac(INVALID_DOUBLE),
503  pet(std::make_pair(INVALID_DOUBLE, INVALID_DOUBLE)),
504  ppet(INVALID_DOUBLE) {
505 }
506 
507 
508 void
510  if (myHolder.isOnRoad()) {
511  update();
512  // Write out past conflicts
513  flushConflicts();
514  } else {
515 #ifdef DEBUG_SSM
516  if (DEBUG_COND(myHolderMS))
517  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' updateAndWriteOutput()\n"
518  << " Holder is off-road! Calling resetEncounters()."
519  << std::endl;
520 #endif
521  resetEncounters();
522  // Write out past conflicts
523  flushConflicts(true);
524  }
525 }
526 
527 void
529 #ifdef DEBUG_SSM
530  if (DEBUG_COND(myHolderMS))
531  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' update()\n"
532  << "Size of myActiveEncounters: " << myActiveEncounters.size()
533  << "\nSize of myPastConflicts: " << myPastConflicts.size()
534  << std::endl;
535 #endif
536  // Scan surroundings for other vehicles
537  FoeInfoMap foes;
538  bool scan = true;
539  if (myEdgeFilterActive) {
540  // Is the ego vehicle inside the filtered edge subset?
541  const MSEdge* egoEdge = &((*myHolderMS).getLane()->getEdge());
542  scan = myEdgeFilter.find(egoEdge) != myEdgeFilter.end();
543  }
544  if (scan) {
546  }
547 
548 #ifdef DEBUG_SSM
549  if (DEBUG_COND(myHolderMS)) {
550  if (foes.size() > 0) {
551  std::cout << "Scanned surroundings: Found potential foes:\n";
552  for (FoeInfoMap::const_iterator i = foes.begin(); i != foes.end(); ++i) {
553  std::cout << i->first->getID() << " ";
554  }
555  std::cout << std::endl;
556  } else {
557  std::cout << "Scanned surroundings: No potential conflict could be identified." << std::endl;
558  }
559  }
560 #endif
561 
562  // Update encounters and conflicts -> removes all foes (and deletes corresponding FoeInfos) for which already a corresponding encounter exists
563  processEncounters(foes);
564 
565  // Make new encounters for all foes, which were not removed by processEncounters (and deletes corresponding FoeInfos)
566  createEncounters(foes);
567  foes.clear();
568 
569  // Compute "global SSMs" (only computed once per time-step)
571 
572 }
573 
574 
575 void
579  if (myWritePositions) {
581  }
582  if (myWriteLanesPositions) {
585  }
586  if (myComputeBR) {
587  double br = MAX2(-myHolderMS->getAcceleration(), 0.0);
588  if (br > myMaxBR.second) {
589  myMaxBR = std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), br);
590  }
591  myBRspan.push_back(br);
592  }
593 
594  double leaderSearchDist = 0;
595  std::pair<const MSVehicle*, double> leader(nullptr, 0.);
596  if (myComputeSGAP) {
597  leaderSearchDist = myThresholds["SGAP"];
598  }
599  if (myComputeTGAP) {
600  leaderSearchDist = MAX2(leaderSearchDist, myThresholds["TGAP"] * myHolderMS->getSpeed());
601  }
602 
603  if (leaderSearchDist > 0.) {
604  leader = myHolderMS->getLeader(leaderSearchDist);
605  }
606 
607  // negative gap indicates theoretical car-following relationship for paths that cross at an intersection
608  if (myComputeSGAP) {
609  if (leader.first == nullptr || leader.second < 0) {
610  mySGAPspan.push_back(INVALID_DOUBLE);
611  } else {
612  double sgap = leader.second + myHolder.getVehicleType().getMinGap();
613  mySGAPspan.push_back(sgap);
614  if (sgap < myMinSGAP.first.second) {
615  myMinSGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), sgap), leader.first->getID());
616  }
617  }
618  }
619 
620  if (myComputeTGAP) {
621  if (leader.first == nullptr || myHolderMS->getSpeed() == 0. || leader.second < 0) {
622  myTGAPspan.push_back(INVALID_DOUBLE);
623  } else {
624  const double tgap = (leader.second + myHolder.getVehicleType().getMinGap()) / myHolderMS->getSpeed();
625  myTGAPspan.push_back(tgap);
626  if (tgap < myMinTGAP.first.second) {
627  myMinTGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), tgap), leader.first->getID());
628  }
629  }
630  }
631 
632  }
633 }
634 
635 
636 void
638 #ifdef DEBUG_SSM
639  if (DEBUG_COND(myHolderMS)) {
640  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' createEncounters()" << std::endl;
641  std::cout << "New foes:\n";
642  for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
643  std::cout << vi->first->getID() << "\n";
644  }
645  std::cout << std::endl;
646  }
647 #endif
648 
649  for (FoeInfoMap::const_iterator foe = foes.begin(); foe != foes.end(); ++foe) {
650  Encounter* e = new Encounter(myHolderMS, foe->first, SIMTIME, myExtraTime);
651  if (updateEncounter(e, foe->second)) {
653  assert(myActiveEncounters.empty());
655  }
656  assert(myOldestActiveEncounterBegin <= e->begin);
657  myActiveEncounters.push_back(e);
658  } else {
659  // Discard encounters, where one vehicle already left the conflict area
660  delete e;
661  }
662  // free foeInfo
663  delete foe->second;
664  }
665 }
666 
667 
668 void
670  // Call processEncounters() with empty vehicle set
671  FoeInfoMap foes;
672  // processEncounters with empty argument closes all encounters
673  processEncounters(foes, true);
674 }
675 
676 
677 void
679 #ifdef DEBUG_SSM
680  if (DEBUG_COND(myHolderMS)) {
681  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' processEncounters(forceClose = " << forceClose << ")" << std::endl;
682  std::cout << "Currently present foes:\n";
683  for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
684  std::cout << vi->first->getID() << "\n";
685  }
686  std::cout << std::endl;
687  }
688 #endif
689 
690  // Run through active encounters. If corresponding foe is still present in foes update and
691  // remove foe from foes. If the foe has disappeared close the encounter (check if it qualifies
692  // as a conflict and in case transfer it to myPastConflicts).
693  // Afterwards run through remaining elements in foes and create new encounters for them.
694  EncounterVector::iterator ei = myActiveEncounters.begin();
695  while (ei != myActiveEncounters.end()) {
696  Encounter* e = *ei;
697  // check whether foe is still on net
698  bool foeExists = !(MSNet::getInstance()->getVehicleControl().getVehicle(e->foeID) == nullptr);
699  if (!foeExists) {
700  e->foe = nullptr;
701  }
702  if (foes.find(e->foe) != foes.end()) {
703  FoeInfo* foeInfo = foes[e->foe];
704  EncounterType prevType = e->currentType;
705  // Update encounter
706  updateEncounter(e, foeInfo);
709  // The encounter classification switched from BOTH_LEFT to another
710  // => Start new encounter (i.e. don't erase the foe, don't delete the foeInfo and request closing)
711  // Note that updateEncounter did not add another trajectory point in this case.
712 #ifdef DEBUG_SSM
713  if (DEBUG_COND(myHolderMS)) {
714  std::cout << " Requesting encounter closure because both left conflict area of previous encounter but another encounter lies ahead." << std::endl;
715  }
716 #endif
717  e->closingRequested = true;
718  } else {
719  // Erase foes which were already encountered and should not be used to open a new conflict
720  delete foeInfo;
721  foes.erase(e->foe);
722  }
723  } else {
724  if (e->getRemainingExtraTime() <= 0. || forceClose || !foeExists) {
725  // Close encounter, extra time has expired (deletes e if it does not qualify as conflict)
726 #ifdef DEBUG_SSM
727  if (DEBUG_COND(myHolderMS)) {
728  std::cout << " Requesting encounter closure because..." << std::endl;
729  if (e->getRemainingExtraTime() <= 0.) {
730  std::cout << " ... extra time elapsed." << std::endl;
731  } else if (forceClose) {
732  std::cout << " ... closing was forced." << std::endl;
733  } else {
734  std::cout << " ... foe disappeared." << std::endl;
735  }
736  }
737 #endif
738  e->closingRequested = true;
739  } else {
740  updateEncounter(e, nullptr); // counts down extra time
741  }
742  }
743 
744  if (e->closingRequested) {
745  double eBegin = e->begin;
746  closeEncounter(e);
747  ei = myActiveEncounters.erase(ei);
748  if (myActiveEncounters.empty()) {
750  } else if (eBegin == myOldestActiveEncounterBegin) {
751  // Erased the oldest encounter, update myOldestActiveEncounterBegin
752  auto i = myActiveEncounters.begin();
753  myOldestActiveEncounterBegin = (*i++)->begin;
754  while (i != myActiveEncounters.end()) {
756  }
757  }
758  } else {
759  ++ei;
760  }
761  }
762 }
763 
764 
765 bool
767  // Check if conflict measure thresholds are exceeded (to decide whether to keep the encounter for writing out)
768 #ifdef DEBUG_SSM
769  if (DEBUG_COND(myHolderMS))
770  std::cout << SIMTIME << " qualifiesAsConflict() for encounter of vehicles '"
771  << e->egoID << "' and '" << e->foeID
772  << "'" << std::endl;
773 #endif
774 
775  if (myComputePET && e->PET.value != INVALID_DOUBLE && e->PET.value <= myThresholds["PET"]) {
776  return true;
777  }
778  if (myComputeTTC && e->minTTC.value != INVALID_DOUBLE && e->minTTC.value <= myThresholds["TTC"]) {
779  return true;
780  }
781  if (myComputeDRAC && e->maxDRAC.value != INVALID_DOUBLE && e->maxDRAC.value >= myThresholds["DRAC"]) {
782  return true;
783  }
784  if (myComputePPET && e->minPPET.value != INVALID_DOUBLE && e->minPPET.value <= myThresholds["PPET"]) {
785  return true;
786  }
787  if (myComputeMDRAC && e->maxMDRAC.value != INVALID_DOUBLE && e->maxMDRAC.value >= myThresholds["MDRAC"]) {
788  return true;
789  }
790  return false;
791 }
792 
793 
794 void
796  assert(e->size() > 0);
797  // erase pointers (encounter is stored before being destroyed and pointers could become invalid)
798  e->ego = nullptr;
799  e->foe = nullptr;
800  e->end = e->timeSpan.back();
801  bool wasConflict = qualifiesAsConflict(e);
802 #ifdef DEBUG_SSM
803  if (DEBUG_COND(myHolderMS)) {
804  std::cout << SIMTIME << " closeEncounter() of vehicles '"
805  << e->egoID << "' and '" << e->foeID
806  << "' (was ranked as " << (wasConflict ? "conflict" : "non-conflict") << ")" << std::endl;
807  }
808 #endif
809  if (wasConflict) {
810  myPastConflicts.push(e);
811 #ifdef DEBUG_SSM
812  if (!myPastConflicts.empty()) {
813  if (DEBUG_COND(myHolderMS)) {
814  std::cout << "pastConflictsQueue of veh '" << myHolderMS->getID() << "':\n";
815  }
816  auto myPastConflicts_bak = myPastConflicts;
817  double lastBegin = myPastConflicts.top()->begin;
818  while (!myPastConflicts.empty()) {
819  auto c = myPastConflicts.top();
820  myPastConflicts.pop();
821  if (DEBUG_COND(myHolderMS)) {
822  std::cout << " Conflict with foe '" << c->foe << "' (time=" << c->begin << "-" << c->end << ")\n";
823  }
824  if (c->begin < lastBegin) {
825  std::cout << " Queue corrupt...\n";
826  assert(false);
827  }
828  lastBegin = c->begin;
829  }
830  std::cout << std::endl;
831  myPastConflicts = myPastConflicts_bak;
832  }
833 #endif
834  } else {
835  delete e;
836  }
837  return;
838 }
839 
840 
841 bool
843 #ifdef DEBUG_ENCOUNTER
844  if (DEBUG_COND_ENCOUNTER(e)) {
845  std::cout << SIMTIME << " updateEncounter() of vehicles '" << e->egoID << "' and '" << e->foeID << "'\n";
846  }
847 #endif
848  assert(e->foe != 0);
849 
850  // Struct storing distances (determined in classifyEncounter()) and times to potential conflict entry / exit (in estimateConflictTimes())
851  EncounterApproachInfo eInfo(e);
852 
853  // Classify encounter type based on the present information
854  // More details on follower/lead relation are determined in a second step below, see estimateConflictTimes()
855  // If a crossing situation is ongoing (i.e. one of the vehicles entered the conflict area already in the last step,
856  // this is handled by passedEncounter by only tracing the vehicle's movements)
857  // The further development of the encounter type is done in checkConflictEntryAndExit()
858  eInfo.type = classifyEncounter(foeInfo, eInfo);
859 
860  // Discard new encounters, where one vehicle has already left the conflict area
861  if (eInfo.encounter->size() == 0) {
864  // Signalize to discard
865  return false;
866  }
867  }
868 
869  if (eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
870  // At this state, eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD implies that the foe
871  // is either out of the device's range or its route does not interfere with the ego's route.
872 #ifdef DEBUG_ENCOUNTER
873  if (DEBUG_COND_ENCOUNTER(e)) {
874  std::cout << SIMTIME << " Encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' does not imply any conflict.\n";
875  }
876 #endif
877  updatePassedEncounter(e, foeInfo, eInfo);
878 // return;
885  // Ongoing encounter. Treat with update passed encounter (trace covered distances)
886  // eInfo.type only holds the previous type
887  updatePassedEncounter(e, foeInfo, eInfo);
888 
889  // Estimate times until a possible conflict / collision
890  estimateConflictTimes(eInfo);
891 
892  } else {
893  // Estimate times until a possible conflict / collision
894  // Not all are used for all types of encounters:
895  // Follow/lead situation doesn't need them at all, currently (might change if more SSMs are implemented).
896  // Crossing / Merging calculates entry times to determine leader/follower and calculates the exit time for the leader.
897  estimateConflictTimes(eInfo);
898 
899  // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
901  }
902 
903  // update entry/exit times for conflict area
905  if (e->size() == 0) {
906 #ifdef DEBUG_ENCOUNTER
907  if (DEBUG_COND_ENCOUNTER(e)) {
908  std::cout << SIMTIME << " type when creating encounter: " << eInfo.type << "\n";
909  }
910 #endif
916  return false;
917  }
918  }
919 
920  // update (x,y)-coords of conflict point
921  determineConflictPoint(eInfo);
922 
923  // Compute SSMs
924  computeSSMs(eInfo);
925 
928  // Don't add a point which switches back to a different encounter type from a passed encounter.
929  // For this situation this encounter will be closed and a new encounter will be created,
930  // @see correspondingly conditionalized code in processEncounters()
931  e->currentType = eInfo.type;
932  } else {
933  // Add current states to trajectories and update type
934  e->add(SIMTIME, eInfo.type, e->ego->getPosition(), e->ego->getLane()->getID(), e->ego->getPositionOnLane(), e->ego->getVelocityVector(),
935  e->foe->getPosition(), e->foe->getLane()->getID(), e->foe->getPositionOnLane(), e->foe->getVelocityVector(),
936  eInfo.conflictPoint, eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, eInfo.ttc, eInfo.drac, eInfo.pet, eInfo.ppet, eInfo.mdrac);
937  }
938  // Keep encounter
939  return true;
940 }
941 
942 
943 void
945  /* Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in
946  * eInfo.conflictPoint. In case of MERGING and CROSSING, this is the entry point to conflict area for follower
947  * In case of FOLLOWING it is the position of leader's back. */
948 
949 #ifdef DEBUG_SSM
950  if (DEBUG_COND(eInfo.encounter->ego)) {
951  std::cout << SIMTIME << " determineConflictPoint()" << std::endl;
952  }
953 #endif
954 
955  const EncounterType& type = eInfo.type;
956  const Encounter* e = eInfo.encounter;
959  || type == ENCOUNTER_TYPE_COLLISION) {
960  // Both vehicles have already past the conflict entry.
961  if (e->size() == 0) {
962  eInfo.conflictPoint = e->ego->getPosition();
963  WRITE_WARNINGF(TL("SSM device of vehicle '%' encountered an unexpected conflict with foe % at time=%. Please review your vehicle behavior settings."), e->egoID, e->foeID, time2string(SIMSTEP));
964  return;
965  }
966  assert(e->size() > 0); // A new encounter should not be created if both vehicles already entered the conflict area
967  eInfo.conflictPoint = e->conflictPointSpan.back();
968  } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
973  } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
978  } else if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
979  eInfo.conflictPoint = e->foe->getPosition(-e->foe->getLength());
980  } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
981  eInfo.conflictPoint = e->ego->getPosition(-e->ego->getLength());
982  } else if (type == ENCOUNTER_TYPE_ONCOMING) {
983  eInfo.conflictPoint = (e->ego->getPosition() + e->foe->getPosition()) * 0.5;
984  } else {
985 #ifdef DEBUG_SSM
986  if (DEBUG_COND(eInfo.encounter->ego)) {
987  std::cout << "No conflict point associated with encounter type " << type << std::endl;
988  }
989 #endif
990  return;
991  }
992 
993 #ifdef DEBUG_SSM
994  if (DEBUG_COND(eInfo.encounter->ego)) {
995  std::cout << " Conflict at " << eInfo.conflictPoint << std::endl;
996  }
997 #endif
998 }
999 
1000 
1001 void
1003 
1004  EncounterType& type = eInfo.type;
1005  Encounter* e = eInfo.encounter;
1006 
1007  assert(type != ENCOUNTER_TYPE_NOCONFLICT_AHEAD); // arrival times not defined, if no conflict is ahead.
1008 #ifdef DEBUG_SSM
1009  if (DEBUG_COND(e->ego))
1010  std::cout << SIMTIME << " estimateConflictTimes() for ego '" << e->egoID << "' and foe '" << e->foeID << "'\n"
1011  << " encounter type: " << eInfo.type << "\n"
1012  << " egoConflictEntryDist=" << writeNA(eInfo.egoConflictEntryDist)
1013  << " foeConflictEntryDist=" << writeNA(eInfo.foeConflictEntryDist)
1014  << " egoConflictExitDist=" << writeNA(eInfo.egoConflictExitDist)
1015  << " foeConflictExitDist=" << writeNA(eInfo.foeConflictExitDist)
1016  << "\n ego speed=" << e->ego->getSpeed()
1017  << ", foe speed=" << e->foe->getSpeed()
1018  << std::endl;
1019 #endif
1020  if (type == ENCOUNTER_TYPE_COLLISION) {
1021 #ifdef DEBUG_SSM
1024  if (DEBUG_COND(e->ego))
1025  std::cout << " encouter type " << type << " -> no exit times to be calculated."
1026  << std::endl;
1027 #endif
1028  return;
1029  }
1030 
1032  // No need to know the times until ...ConflictDistEntry, currently. They would correspond to an estimated time headway or similar.
1033  // TTC must take into account the movement of the leader, as would DRAC, PET doesn't need the time either, since it uses aposteriori
1034  // values.
1035 #ifdef DEBUG_SSM
1036  if (DEBUG_COND(e->ego))
1037  std::cout << " encouter type " << type << " -> no entry/exit times to be calculated."
1038  << std::endl;
1039 #endif
1040  return;
1041  }
1042 
1043  assert(type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_CROSSING
1050  || type == ENCOUNTER_TYPE_ONCOMING);
1051 
1052  // Determine exit distances
1053  if (type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_ONCOMING) {
1056  } else {
1059  }
1060 
1061  // Estimate entry times to stipulate a leader / follower relation for the encounter.
1062  if (eInfo.egoConflictEntryDist > NUMERICAL_EPS) {
1064  assert(eInfo.egoEstimatedConflictEntryTime > 0.);
1065  } else {
1066  // ego already entered conflict area
1067  eInfo.egoEstimatedConflictEntryTime = 0.;
1068  }
1069  if (eInfo.foeConflictEntryDist > NUMERICAL_EPS) {
1071  assert(eInfo.foeEstimatedConflictEntryTime > 0.);
1072  } else {
1073  // foe already entered conflict area
1074  eInfo.foeEstimatedConflictEntryTime = 0.;
1075  }
1076 
1077  if (type == ENCOUNTER_TYPE_ONCOMING) {
1080  }
1081 
1082 #ifdef DEBUG_SSM
1083  if (DEBUG_COND(e->ego))
1084  std::cout << " Conflict type: " << encounterToString(type) << "\n"
1085  << " egoConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
1086  << ", foeConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
1087  << std::endl;
1088 #endif
1089 
1090  // Estimate exit times from conflict area for leader / follower.
1091  // assume vehicles start to accelerate after entring the intersection
1092  if (eInfo.egoConflictExitDist >= 0.) {
1094  //eInfo.egoEstimatedConflictExitTime = eInfo.egoEstimatedConflictEntryTime + e->ego->getCarFollowModel().estimateArrivalTime(
1095  // eInfo.egoConflictExitDist - MAX2(0.0, eInfo.egoConflictEntryDist),
1096  // e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(),
1097  // e->ego->getCarFollowModel().getMaxAccel());
1098  } else {
1099  eInfo.egoEstimatedConflictExitTime = 0.;
1100  }
1101  if (eInfo.foeConflictExitDist >= 0.) {
1103  //eInfo.foeEstimatedConflictExitTime = eInfo.foeEstimatedConflictEntryTime + e->foe->getCarFollowModel().estimateArrivalTime(
1104  // eInfo.foeConflictExitDist - MAX2(0.0, eInfo.foeConflictEntryDist),
1105  // e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(),
1106  // e->foe->getCarFollowModel().getMaxAccel());
1107  } else {
1108  eInfo.foeEstimatedConflictExitTime = 0.;
1109  }
1110 
1111  if (type == ENCOUNTER_TYPE_ONCOMING) {
1114  }
1115 
1116  if (type != ENCOUNTER_TYPE_MERGING && type != ENCOUNTER_TYPE_CROSSING) {
1117  // this call is issued in context of an ongoing conflict, therefore complete type is already known for the encounter
1118  // (One of EGO_ENTERED_CONFLICT_AREA, FOE_ENTERED_CONFLICT_AREA, EGO_LEFT_CONFLICT_AREA, FOE_LEFT_CONFLICT_AREA, BOTH_ENTERED_CONFLICT_AREA)
1119  // --> no need to specify incomplete encounter type
1120  return;
1121  }
1122 
1123  // For merging and crossing situation, the leader/follower relation not determined by classifyEncounter()
1124  // This is done below based on the estimated conflict entry times
1125  if (eInfo.egoEstimatedConflictEntryTime == 0. && eInfo.foeEstimatedConflictEntryTime == 0. &&
1126  eInfo.egoConflictExitDist >= 0 && eInfo.foeConflictExitDist >= 0) {
1127  type = ENCOUNTER_TYPE_COLLISION;
1128  WRITE_WARNINGF(TL("SSM device of vehicle '%' detected collision with vehicle '%' at time=%."), e->egoID, e->foeID, time2string(SIMSTEP));
1132  // ego is estimated first at conflict point
1133 #ifdef DEBUG_SSM
1134  if (DEBUG_COND(e->ego))
1135  std::cout << " -> ego is estimated leader at conflict entry."
1136  << " egoConflictExitTime=" << (eInfo.egoEstimatedConflictExitTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictExitTime))
1137  << " egoEstimatedConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
1138  << " foeEstimatedConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
1139  << std::endl;
1140 #endif
1142  } else {
1143  // ego is estimated second at conflict point
1144 #ifdef DEBUG_SSM
1145  if (DEBUG_COND(e->ego))
1146  std::cout << " -> foe is estimated leader at conflict entry."
1147  << " foeConflictExitTime=" << (eInfo.foeEstimatedConflictExitTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictExitTime))
1148  << " egoEstimatedConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.egoEstimatedConflictEntryTime))
1149  << " foeEstimatedConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID_DOUBLE ? "NA" : ::toString(eInfo.foeEstimatedConflictEntryTime))
1150  << std::endl;
1151 #endif
1153  }
1154 
1155 }
1156 
1157 
1158 
1159 void
1161 #ifdef DEBUG_SSM
1162  if (DEBUG_COND(myHolderMS)) {
1163  Encounter* e = eInfo.encounter;
1164  std::cout << SIMTIME << " computeSSMs() for vehicles '"
1165  << e->ego->getID() << "' and '" << e->foe->getID()
1166  << "'" << std::endl;
1167  }
1168 #endif
1169 
1170  const EncounterType& type = eInfo.type;
1171 
1176  || type == ENCOUNTER_TYPE_ONCOMING) {
1179  }
1180  determinePET(eInfo);
1181  } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
1182  determinePET(eInfo);
1183  } else if (type == ENCOUNTER_TYPE_COLLISION) {
1184  // TODO: handle collision
1187  // No conflict measures apply for these states, which correspond to intermediate times between
1188  // one vehicle leaving the conflict area and the arrival time for the other (difference corresponds to the PET)
1189  } else if (type == ENCOUNTER_TYPE_ON_ADJACENT_LANES || type == ENCOUNTER_TYPE_MERGING_ADJACENT) {
1190  // No conflict measures apply for this state
1191  } else if (type == ENCOUNTER_TYPE_MERGING_PASSED || type == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
1192  // No conflict measures apply for this state
1193  } else if (type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
1194  // No conflict measures apply for this state
1195  } else {
1196  std::stringstream ss;
1197  ss << "'" << type << "'";
1198  WRITE_WARNING("Unknown or undetermined encounter type at computeSSMs(): " + ss.str());
1199  }
1200 
1201 #ifdef DEBUG_SSM
1202  if (DEBUG_COND(myHolderMS)) {
1203  Encounter* e = eInfo.encounter;
1204  std::cout << "computeSSMs() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "':\n"
1205  << " ttc=" << (eInfo.ttc == INVALID_DOUBLE ? "NA" : ::toString(eInfo.ttc))
1206  << ", drac=" << (eInfo.drac == INVALID_DOUBLE ? "NA" : ::toString(eInfo.drac))
1207  << ", pet=" << (eInfo.pet.second == INVALID_DOUBLE ? "NA" : ::toString(eInfo.pet.second))
1208  << std::endl;
1209  }
1210 #endif
1211 }
1212 
1213 
1214 void
1216  Encounter* e = eInfo.encounter;
1217  if (e->size() == 0) {
1218  return;
1219  }
1220  const EncounterType& type = eInfo.type;
1221  std::pair<double, double>& pet = eInfo.pet;
1222 
1223 #ifdef DEBUG_SSM
1224  if (DEBUG_COND(myHolderMS))
1225  std::cout << SIMTIME << " determinePET() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
1226  << "(type: " << encounterToString(static_cast<EncounterType>(e->typeSpan.back())) << ")" << std::endl;
1227 #endif
1228 
1230  // For a following situation, the corresponding PET-value is merely the time-headway.
1231  // Determining these could be done by comparison of memorized gaps with memorized covered distances
1232  // Implementation is postponed. Tracing the time gaps (in contrast to crossing PET) corresponds to
1233  // a vector of values not a single value.
1234  // pass
1235  } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
1236  EncounterType prevType = static_cast<EncounterType>(e->typeSpan.back());
1237  if (prevType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
1238 #ifdef DEBUG_SSM
1239  if (DEBUG_COND(myHolderMS))
1240  std::cout << "PET for crossing encounter already calculated as " << e->PET.value
1241  << std::endl;
1242 #endif
1243  // pet must have been calculated already
1244  assert(e->PET.value != INVALID_DOUBLE);
1245  return;
1246  }
1247 
1248  // this situation should have emerged from one of the following
1249  assert(prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1250  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER
1256 
1257 
1258 #ifdef DEBUG_SSM
1259  if (DEBUG_COND(myHolderMS))
1260  std::cout << "e->egoDistsToConflict.back() = " << e->egoDistsToConflict.back()
1261  << "\ne->egoConflictEntryTime = " << e->egoConflictEntryTime
1262  << "\ne->egoConflictExitTime = " << e->egoConflictExitTime
1263  << "\ne->foeDistsToConflict.back() = " << e->foeDistsToConflict.back()
1264  << "\ne->foeConflictEntryTime = " << e->foeConflictEntryTime
1265  << "\ne->foeConflictExitTime = " << e->foeConflictExitTime
1266  << std::endl;
1267 #endif
1268 
1269  // But both have passed the conflict area
1271 
1272  // Both have left the conflict region
1273  // (Conflict may have started as one was already within the conflict area - thus the check for invalid entry times)
1275  pet.first = e->egoConflictEntryTime;
1276  pet.second = e->egoConflictEntryTime - e->foeConflictExitTime;
1278  pet.first = e->foeConflictEntryTime;
1279  pet.second = e->foeConflictEntryTime - e->egoConflictExitTime;
1280  } else {
1281 #ifdef DEBUG_SSM
1282  if (DEBUG_COND(myHolderMS))
1283  std::cout << "determinePET: Both passed conflict area in the same step. Assume collision"
1284  << std::endl;
1285 #endif
1286  pet.first = e->egoConflictEntryTime;
1287  pet.second = 0;
1288  }
1289 
1290  // Reset entry and exit times two allow an eventual subsequent re-use
1295 
1296 #ifdef DEBUG_SSM
1297  if (DEBUG_COND(myHolderMS))
1298  std::cout << "Calculated PET = " << pet.second << " (at t=" << pet.first << ")"
1299  << std::endl;
1300 #endif
1301  } else {
1302  // other cases (merging and pre-crossing situations) do not correspond to a PET calculation.
1303 #ifdef DEBUG_SSM
1304  if (DEBUG_COND(myHolderMS))
1305  std::cout << "PET unappropriate for merging and pre-crossing situations. No calculation performed."
1306  << std::endl;
1307 #endif
1308  return;
1309  }
1310 }
1311 
1312 
1313 void
1315  Encounter* e = eInfo.encounter;
1316  const EncounterType& type = eInfo.type;
1317  double& ttc = eInfo.ttc;
1318  double& drac = eInfo.drac;
1319  double& ppet = eInfo.ppet;
1320  double& mdrac = eInfo.mdrac;
1321 
1322 #ifdef DEBUG_SSM
1323  if (DEBUG_COND(myHolderMS))
1324  std::cout << SIMTIME << " determineTTCandDRAC() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' (type = " << eInfo.type << ")"
1325  << std::endl;
1326 #endif
1327 
1328  // Dependent on the actual encounter situation (eInfo.type) calculate the TTC.
1329  // For merging and crossing, different cases occur when a collision during the merging / crossing process is predicted.
1330  if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
1331  double gap = eInfo.egoConflictEntryDist;
1332  if (myComputeTTC) {
1333  ttc = computeTTC(gap, e->ego->getSpeed(), e->foe->getSpeed());
1334  }
1335  if (myComputeDRAC) {
1336  drac = computeDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed());
1337  }
1338  if (myComputeMDRAC) {
1339  mdrac = computeMDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed(), myMDRACPRT);
1340  }
1341  } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
1342  double gap = eInfo.foeConflictEntryDist;
1343  if (myComputeTTC) {
1344  ttc = computeTTC(gap, e->foe->getSpeed(), e->ego->getSpeed());
1345  }
1346  if (myComputeDRAC) {
1347  drac = computeDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed());
1348  }
1349  if (myComputeMDRAC) {
1350  mdrac = computeMDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed(), myMDRACPRT);
1351  }
1352  } else if (type == ENCOUNTER_TYPE_ONCOMING) {
1353  if (myComputeTTC) {
1354  const double dv = e->ego->getSpeed() + e->foe->getSpeed();
1355  if (dv > 0) {
1356  ttc = eInfo.egoConflictEntryDist / dv;
1357  }
1358  }
1359  } else if (type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER) {
1360  // TODO: calculate more specifically whether a following situation in the merge conflict area
1361  // is predicted when assuming constant speeds or whether a side collision is predicted.
1362  // Currently, we ignore any conflict area before the actual merging point of the lanes.
1363 
1364  // linearly extrapolated arrival times at the conflict
1365  // NOTE: These differ from the estimated times stored in eInfo
1366  double egoEntryTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictEntryDist / e->ego->getSpeed() : INVALID_DOUBLE;
1367  double egoExitTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictExitDist / e->ego->getSpeed() : INVALID_DOUBLE;
1368  double foeEntryTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictEntryDist / e->foe->getSpeed() : INVALID_DOUBLE;
1369  double foeExitTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictExitDist / e->foe->getSpeed() : INVALID_DOUBLE;
1370 
1371 #ifdef DEBUG_SSM
1372  if (DEBUG_COND(myHolderMS))
1373  std::cout << " Conflict times with constant speed extrapolation for merging situation:\n "
1374  << " egoEntryTime=" << (egoEntryTime == INVALID_DOUBLE ? "NA" : ::toString(egoEntryTime))
1375  << ", egoExitTime=" << (egoExitTime == INVALID_DOUBLE ? "NA" : ::toString(egoExitTime))
1376  << ", foeEntryTime=" << (foeEntryTime == INVALID_DOUBLE ? "NA" : ::toString(foeEntryTime))
1377  << ", foeExitTime=" << (foeExitTime == INVALID_DOUBLE ? "NA" : ::toString(foeExitTime))
1378  << std::endl;
1379 #endif
1380 
1381  // based on that, we obtain
1382  if (egoEntryTime == INVALID_DOUBLE || foeEntryTime == INVALID_DOUBLE) {
1383  // at least one vehicle is stopped
1384  ttc = INVALID_DOUBLE;
1385  drac = INVALID_DOUBLE;
1386  mdrac = INVALID_DOUBLE;
1387 #ifdef DEBUG_SSM
1388  if (DEBUG_COND(myHolderMS)) {
1389  std::cout << " No TTC and DRAC computed as one vehicle is stopped." << std::endl;
1390  }
1391 #endif
1392  return;
1393  }
1394  double leaderEntryTime = MIN2(egoEntryTime, foeEntryTime);
1395  double followerEntryTime = MAX2(egoEntryTime, foeEntryTime);
1396  double leaderExitTime = leaderEntryTime == egoEntryTime ? egoExitTime : foeExitTime;
1397  //double followerExitTime = leaderEntryTime==egoEntryTime?foeExitTime:egoExitTime;
1398  double leaderSpeed = leaderEntryTime == egoEntryTime ? e->ego->getSpeed() : e->foe->getSpeed();
1399  double followerSpeed = leaderEntryTime == egoEntryTime ? e->foe->getSpeed() : e->ego->getSpeed();
1400  double leaderConflictDist = leaderEntryTime == egoEntryTime ? eInfo.egoConflictEntryDist : eInfo.foeConflictEntryDist;
1401  double followerConflictDist = leaderEntryTime == egoEntryTime ? eInfo.foeConflictEntryDist : eInfo.egoConflictEntryDist;
1402  double leaderLength = leaderEntryTime == egoEntryTime ? e->ego->getLength() : e->foe->getLength();
1403  if (myComputePPET && followerEntryTime != INVALID_DOUBLE && leaderEntryTime != INVALID_DOUBLE) {
1404  ppet = followerEntryTime - leaderExitTime;
1405  //std::cout << " debug1 ppet=" << ppet << "\n";
1406  }
1407  if (leaderExitTime >= followerEntryTime) {
1408  // collision would occur at merge area
1409  if (myComputeTTC) {
1410  ttc = computeTTC(followerConflictDist, followerSpeed, 0.);
1411  }
1412  // TODO: Calculate more specific drac for merging case here (complete stop is not always necessary -> see calculation for crossing case)
1413  // Rather the
1414  if (myComputeDRAC) {
1415  drac = computeDRAC(followerConflictDist, followerSpeed, 0.);
1416  }
1417  if (myComputeMDRAC) {
1418  mdrac = computeMDRAC(followerConflictDist, followerSpeed, 0., myMDRACPRT);
1419  }
1420 
1421 #ifdef DEBUG_SSM
1422  if (DEBUG_COND(myHolderMS))
1423  std::cout << " Extrapolation predicts collision *at* merge point with TTC=" << ttc
1424  << ", drac=" << drac << std::endl;
1425 #endif
1426 
1427  } else {
1428  // -> No collision at the merge area
1429  if (myComputeTTC) {
1430  // Check if after merge a collision would occur if speeds are hold constant.
1431  double gapAfterMerge = followerConflictDist - leaderExitTime * followerSpeed;
1432  assert(gapAfterMerge >= 0);
1433 
1434  // ttc as for following situation (assumes no collision until leader merged)
1435  double ttcAfterMerge = computeTTC(gapAfterMerge, followerSpeed, leaderSpeed);
1436  ttc = ttcAfterMerge == INVALID_DOUBLE ? INVALID_DOUBLE : leaderExitTime + ttcAfterMerge;
1437  }
1438  if (myComputeDRAC) {
1439  // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
1440  double g0 = followerConflictDist - leaderConflictDist - leaderLength;
1441  if (g0 < 0) {
1442  // Speed difference must be positive if g0<0.
1443  assert(leaderSpeed - followerSpeed > 0);
1444  // no deceleration needed for dv>0 and gap after merge >= 0
1445  drac = INVALID_DOUBLE;
1446  } else {
1447  // compute drac as for a following situation
1448  drac = computeDRAC(g0, followerSpeed, leaderSpeed);
1449  }
1450  }
1451  if (myComputeMDRAC) {
1452  // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
1453  double g0 = followerConflictDist - leaderConflictDist - leaderLength;
1454  if (g0 < 0) {
1455  // Speed difference must be positive if g0<0.
1456  assert(leaderSpeed - followerSpeed > 0);
1457  // no deceleration needed for dv>0 and gap after merge >= 0
1458  mdrac = INVALID_DOUBLE;
1459  } else {
1460  // compute drac as for a following situation
1461  mdrac = computeMDRAC(g0, followerSpeed, leaderSpeed, myMDRACPRT);
1462  }
1463  }
1464 #ifdef DEBUG_SSM
1465  if (DEBUG_COND(myHolderMS)) {
1466  if (ttc == INVALID_DOUBLE) {
1467  // assert(dv >= 0);
1468  assert(drac == INVALID_DOUBLE || drac == 0.0);
1469  std::cout << " Extrapolation does not predict any collision." << std::endl;
1470  } else {
1471  std::cout << " Extrapolation predicts collision *after* merge point with TTC="
1472  << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc))
1473  << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac)) << std::endl;
1474  }
1475  }
1476 #endif
1477 
1478  }
1479 
1480  } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1482  if (myComputeDRAC) {
1483  drac = computeDRAC(eInfo);
1484  }
1486  // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1487  double gap = eInfo.egoConflictEntryDist;
1488  if (myComputeTTC) {
1489  ttc = computeTTC(gap, e->ego->getSpeed(), 0.);
1490  }
1491  if (myComputeMDRAC) {
1492  mdrac = computeMDRAC(gap, e->ego->getSpeed(), 0., myMDRACPRT);
1493  }
1494  } else {
1495  // encounter is expected to happen without collision
1496  ttc = INVALID_DOUBLE;
1497  mdrac = INVALID_DOUBLE;
1498  }
1499  if (myComputePPET) {
1500  const double entryTime = eInfo.egoEstimatedConflictEntryTime;
1501  const double exitTime = (e->foeConflictExitTime == INVALID_DOUBLE
1503  if (entryTime != INVALID_DOUBLE && exitTime != INVALID_DOUBLE) {
1504  ppet = entryTime - exitTime;
1505  }
1506  //std::cout << " debug2 ppet=" << ppet << "\n";
1507  }
1508 
1509  } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
1511  if (myComputeDRAC) {
1512  drac = computeDRAC(eInfo);
1513  }
1515  // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1516  double gap = eInfo.foeConflictEntryDist;
1517  if (myComputeTTC) {
1518  ttc = computeTTC(gap, e->foe->getSpeed(), 0.);
1519  }
1520  if (myComputeMDRAC) {
1521  mdrac = computeMDRAC(gap, e->foe->getSpeed(), 0., myMDRACPRT);
1522  }
1523  } else {
1524  // encounter is expected to happen without collision
1525  ttc = INVALID_DOUBLE;
1526  mdrac = INVALID_DOUBLE;
1527  }
1528  if (myComputePPET) {
1529  const double entryTime = eInfo.foeEstimatedConflictEntryTime;
1530  const double exitTime = (e->egoConflictExitTime == INVALID_DOUBLE
1532  if (entryTime != INVALID_DOUBLE && exitTime != INVALID_DOUBLE) {
1533  ppet = entryTime - exitTime;
1534  }
1535  //std::cout << SIMTIME << " debug3 ppet=" << writeNA(ppet)
1536  // << " fecet=" << writeNA(eInfo.foeEstimatedConflictEntryTime)
1537  // << " ecet=" << writeNA(e->egoConflictExitTime)
1538  // << " eecec=" << writeNA(eInfo.egoEstimatedConflictExitTime)
1539  // << "\n";
1540  }
1541  } else {
1542 #ifdef DEBUG_SSM
1543  if (DEBUG_COND(myHolderMS)) {
1544  std::stringstream ss;
1545  ss << "'" << type << "'";
1546  WRITE_WARNING("Underspecified or unknown encounter type in MSDevice_SSM::determineTTCandDRAC(): " + ss.str());
1547  }
1548 #endif
1549  }
1550 
1551 #ifdef DEBUG_SSM
1552  if (DEBUG_COND(myHolderMS))
1553  std::cout << "ttc=" << (ttc == INVALID_DOUBLE ? "NA" : ::toString(ttc)) << ", drac=" << (drac == INVALID_DOUBLE ? "NA" : ::toString(drac))
1554  << std::endl;
1555 #endif
1556 }
1557 
1558 
1559 double
1560 MSDevice_SSM::computeTTC(double gap, double followerSpeed, double leaderSpeed) const {
1561  // TODO: in merging situations, the TTC may be lower than the one computed here for following situations
1562  // (currently only the cross section corresponding to the target lane's begin is considered)
1563  // More specifically, the minimum has to be taken from the two if a collision at merge was predicted.
1564 #ifdef DEBUG_SSM
1565  if (DEBUG_COND(myHolderMS))
1566  std::cout << "computeTTC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1567  << std::endl;
1568 #endif
1569  if (gap <= 0.) {
1570  return 0.; // collision already happend
1571  }
1572  double dv = followerSpeed - leaderSpeed;
1573  if (dv <= 0.) {
1574  return INVALID_DOUBLE; // no collision
1575  }
1576 
1577  return gap / dv;
1578 }
1579 
1580 
1581 double
1582 MSDevice_SSM::computeDRAC(double gap, double followerSpeed, double leaderSpeed) {
1583 //#ifdef DEBUG_SSM_DRAC
1584 // if (DEBUG_COND)
1585 // std::cout << "computeDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1586 // << std::endl;
1587 //#endif
1588  if (gap <= 0.) {
1589  return INVALID_DOUBLE; // collision!
1590  }
1591  double dv = followerSpeed - leaderSpeed;
1592  if (dv <= 0.) {
1593  return 0.0; // no need to break
1594  }
1595  assert(followerSpeed > 0.);
1596  return 0.5 * dv * dv / gap; // following Guido et al. (2011)
1597 }
1598 
1599 double
1600 MSDevice_SSM::computeMDRAC(double gap, double followerSpeed, double leaderSpeed, double prt) {
1601 //#ifdef DEBUG_SSM_DRAC
1602 // if (DEBUG_COND)
1603 // std::cout << "computeMDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1604 // << std::endl;
1605 //#endif
1606  if (gap <= 0.) {
1607  return INVALID_DOUBLE; // collision!
1608  }
1609  double dv = followerSpeed - leaderSpeed;
1610  if (dv <= 0.) {
1611  return 0.0; // no need to brake
1612  }
1613  if (gap / dv == prt) {
1614  return INVALID_DOUBLE;
1615  }
1616  assert(followerSpeed > 0.);
1617  return 0.5 * dv / (gap / dv - prt);
1618 }
1619 
1620 
1621 double
1623  // Introduce concise variable names
1624  double dEntry1 = eInfo.egoConflictEntryDist;
1625  double dEntry2 = eInfo.foeConflictEntryDist;
1626  double dExit1 = eInfo.egoConflictExitDist;
1627  double dExit2 = eInfo.foeConflictExitDist;
1628  double v1 = eInfo.encounter->ego->getSpeed();
1629  double v2 = eInfo.encounter->foe->getSpeed();
1630  double tEntry1 = eInfo.egoEstimatedConflictEntryTime;
1631  double tEntry2 = eInfo.foeEstimatedConflictEntryTime;
1632  double tExit1 = eInfo.egoEstimatedConflictExitTime;
1633  double tExit2 = eInfo.foeEstimatedConflictExitTime;
1634 #ifdef DEBUG_SSM_DRAC
1635  if (DEBUG_COND(eInfo.encounter->ego))
1636  std::cout << SIMTIME << "computeDRAC() with"
1637  << "\ndEntry1=" << dEntry1 << ", dEntry2=" << dEntry2
1638  << ", dExit1=" << dExit1 << ", dExit2=" << dExit2
1639  << ",\nv1=" << v1 << ", v2=" << v2
1640  << "\ntEntry1=" << (tEntry1 == INVALID_DOUBLE ? "NA" : ::toString(tEntry1)) << ", tEntry2=" << (tEntry2 == INVALID_DOUBLE ? "NA" : ::toString(tEntry2))
1641  << ", tExit1=" << (tExit1 == INVALID_DOUBLE ? "NA" : ::toString(tExit1)) << ", tExit2=" << (tExit2 == INVALID_DOUBLE ? "NA" : ::toString(tExit2))
1642  << std::endl;
1643 #endif
1644  if (dExit1 <= 0. || dExit2 <= 0.) {
1645  // At least one vehicle already left or is not about to enter conflict area at all => no breaking needed.
1646 #ifdef DEBUG_SSM_DRAC
1647  if (DEBUG_COND(eInfo.encounter->ego)) {
1648  std::cout << "One already left conflict area -> drac == 0." << std::endl;
1649  }
1650 #endif
1651  return 0.;
1652  }
1653  if (dEntry1 <= 0. && dEntry2 <= 0.) {
1654  // collision... (both already entered conflict area but none left)
1655 #ifdef DEBUG_SSM_DRAC
1656  if (DEBUG_COND(eInfo.encounter->ego)) {
1657  std::cout << "Both entered conflict area but neither left. -> collision!" << std::endl;
1658  }
1659 #endif
1660  return INVALID_DOUBLE;
1661  }
1662 
1663  double drac = std::numeric_limits<double>::max();
1664  if (dEntry1 > 0.) {
1665  // vehicle 1 could break
1666 #ifdef DEBUG_SSM_DRAC
1667  if (DEBUG_COND(eInfo.encounter->ego)) {
1668  std::cout << "Ego could break..." << std::endl;
1669  }
1670 #endif
1671  if (tExit2 != INVALID_DOUBLE) {
1672  // Vehicle 2 is expected to leave conflict area at t2
1673  drac = MIN2(drac, 2 * (v1 - dEntry1 / tExit2) / tExit2);
1674 #ifdef DEBUG_SSM_DRAC
1675  if (DEBUG_COND(eInfo.encounter->ego)) {
1676  std::cout << " Foe expected to leave in " << tExit2 << "-> Ego needs drac=" << drac << std::endl;
1677  }
1678 #endif
1679  } else {
1680  // Vehicle 2 is expected to stop on conflict area or earlier
1681  if (tEntry2 != INVALID_DOUBLE) {
1682  // ... on conflict area => veh1 has to stop before entry
1683  drac = MIN2(drac, computeDRAC(dEntry1, v1, 0));
1684 #ifdef DEBUG_SSM_DRAC
1685  if (DEBUG_COND(eInfo.encounter->ego)) {
1686  std::cout << " Foe is expected stop on conflict area -> Ego needs drac=" << drac << std::endl;
1687  }
1688 #endif
1689  } else {
1690  // ... before conflict area
1691 #ifdef DEBUG_SSM_DRAC
1692  if (DEBUG_COND(eInfo.encounter->ego)) {
1693  std::cout << " Foe is expected stop before conflict area -> no drac computation for ego (will be done for foe if applicable)" << std::endl;
1694  }
1695 #endif
1696  }
1697  }
1698  }
1699 
1700  if (dEntry2 > 0.) {
1701  // vehicle 2 could break
1702 #ifdef DEBUG_SSM_DRAC
1703  if (DEBUG_COND(eInfo.encounter->ego)) {
1704  std::cout << "Foe could break..." << std::endl;
1705  }
1706 #endif
1707  if (tExit1 != INVALID_DOUBLE) {
1708  // Vehicle 1 is expected to leave conflict area at t1
1709 #ifdef DEBUG_SSM_DRAC
1710  if (DEBUG_COND(eInfo.encounter->ego)) {
1711  std::cout << " Ego expected to leave in " << tExit1 << "-> Foe needs drac=" << (2 * (v2 - dEntry2 / tExit1) / tExit1) << std::endl;
1712  }
1713 #endif
1714  drac = MIN2(drac, 2 * (v2 - dEntry2 / tExit1) / tExit1);
1715  } else {
1716  // Vehicle 1 is expected to stop on conflict area or earlier
1717  if (tEntry1 != INVALID_DOUBLE) {
1718  // ... on conflict area => veh2 has to stop before entry
1719 #ifdef DEBUG_SSM_DRAC
1720  if (DEBUG_COND(eInfo.encounter->ego)) {
1721  std::cout << " Ego is expected stop on conflict area -> Foe needs drac=" << computeDRAC(dEntry2, v2, 0) << std::endl;
1722  }
1723 #endif
1724  drac = MIN2(drac, computeDRAC(dEntry2, v2, 0));
1725  } else {
1726  // ... before conflict area
1727 #ifdef DEBUG_SSM_DRAC
1728  if (DEBUG_COND(eInfo.encounter->ego)) {
1729  std::cout << " Ego is expected stop before conflict area -> no drac computation for foe (done for ego if applicable)" << std::endl;
1730  }
1731 #endif
1732  }
1733  }
1734  }
1735 
1736  return drac > 0 ? drac : INVALID_DOUBLE;
1737 }
1738 
1739 void
1741  // determine exact entry and exit times
1742  Encounter* e = eInfo.encounter;
1743 
1744  const bool foePastConflictEntry = eInfo.foeConflictEntryDist < 0.0 && eInfo.foeConflictEntryDist != INVALID_DOUBLE;
1745  const bool egoPastConflictEntry = eInfo.egoConflictEntryDist < 0.0 && eInfo.egoConflictEntryDist != INVALID_DOUBLE;
1746  const bool foePastConflictExit = eInfo.foeConflictExitDist < 0.0 && eInfo.foeConflictExitDist != INVALID_DOUBLE;
1747  const bool egoPastConflictExit = eInfo.egoConflictExitDist < 0.0 && eInfo.egoConflictExitDist != INVALID_DOUBLE;
1748 
1749 #ifdef DEBUG_ENCOUNTER
1750  if (DEBUG_COND_ENCOUNTER(e)) {
1751  std::cout << SIMTIME << " checkConflictEntryAndExit() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
1752  << " foeEntryDist=" << eInfo.foeConflictEntryDist
1753  << " egoEntryDist=" << eInfo.egoConflictEntryDist
1754  << " foeExitDist=" << eInfo.foeConflictExitDist
1755  << " egoExitDist=" << eInfo.egoConflictExitDist
1756  << "\n";
1757  }
1758 #endif
1759 
1760 
1761  if (e->size() == 0) {
1762  // This is a new conflict (or a conflict that was considered earlier
1763  // but disregarded due to being 'over')
1764 
1765  if (egoPastConflictExit) {
1766  if (foePastConflictExit) {
1768  } else if (foePastConflictEntry) {
1770  } else {
1772  }
1773  } else if (foePastConflictExit) {
1774  if (egoPastConflictEntry) {
1776  } else {
1778  }
1779  } else {
1780  // No one left conflict area
1781  if (egoPastConflictEntry) {
1782  if (foePastConflictEntry) {
1784  } else {
1786  }
1787  } else if (foePastConflictEntry) {
1789  }
1790  // else: both before conflict, keep current type
1791  }
1792  return;
1793  }
1794 
1795  // Distances to conflict area boundaries in previous step
1796  double prevEgoConflictEntryDist = eInfo.egoConflictEntryDist + e->ego->getLastStepDist();
1797  double prevFoeConflictEntryDist = eInfo.foeConflictEntryDist + e->foe->getLastStepDist();
1798  double prevEgoConflictExitDist = prevEgoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
1799  double prevFoeConflictExitDist = prevFoeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
1800  EncounterType prevType = e->currentType;
1801 
1802 //#ifdef DEBUG_ENCOUNTER
1803 // if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
1804 // std::cout << "\nEgo's prev distance to conflict entry: " << prevEgoConflictEntryDist
1805 // << "\nEgo's prev distance to conflict exit: " << prevEgoConflictExitDist
1806 // << "\nFoe's prev distance to conflict entry: " << prevFoeConflictEntryDist
1807 // << "\nFoe's prev distance to conflict exit: " << prevFoeConflictExitDist
1808 // << std::endl;
1809 //#endif
1810 
1811  // Check if ego entered in last step
1812  if (e->egoConflictEntryTime == INVALID_DOUBLE && egoPastConflictEntry && prevEgoConflictEntryDist >= 0) {
1813  // ego must have entered the conflict in the last step. Determine exact entry time
1814  e->egoConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictEntryDist, 0., -eInfo.egoConflictEntryDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1815 #ifdef DEBUG_ENCOUNTER
1816  if (DEBUG_COND_ENCOUNTER(e)) {
1817  std::cout << " ego entered conflict area at t=" << e->egoConflictEntryTime << std::endl;
1818  }
1819 #endif
1820  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1821  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1822  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1824  }
1825  }
1826 
1827  // Check if foe entered in last step
1828  if (e->foeConflictEntryTime == INVALID_DOUBLE && foePastConflictEntry && prevFoeConflictEntryDist >= 0) {
1829  // foe must have entered the conflict in the last step. Determine exact entry time
1830  e->foeConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictEntryDist, 0., -eInfo.foeConflictEntryDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1831 #ifdef DEBUG_ENCOUNTER
1832  if (DEBUG_COND_ENCOUNTER(e)) {
1833  std::cout << " foe entered conflict area at t=" << e->foeConflictEntryTime << std::endl;
1834  }
1835 #endif
1836  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1837  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1838  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1840  }
1841  }
1842 
1843  // Check if ego left conflict area
1844  if (e->egoConflictExitTime == INVALID_DOUBLE && eInfo.egoConflictExitDist < 0 && prevEgoConflictExitDist >= 0) {
1845  // ego must have left the conflict area in the last step. Determine exact exit time
1846  e->egoConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictExitDist, 0., -eInfo.egoConflictExitDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1847  // Add cross section to calculate PET for foe
1848 // e->foePETCrossSections.push_back(std::make_pair(eInfo.foeConflictEntryCrossSection, e->egoConflictExitTime));
1849 #ifdef DEBUG_ENCOUNTER
1850  if (DEBUG_COND_ENCOUNTER(e)) {
1851  std::cout << " ego left conflict area at t=" << e->egoConflictExitTime << std::endl;
1852  }
1853 #endif
1854  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1855  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1856  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1858  }
1859  }
1860 
1861  // Check if foe left conflict area
1862  if (e->foeConflictExitTime == INVALID_DOUBLE && eInfo.foeConflictExitDist < 0 && prevFoeConflictExitDist >= 0) {
1863  // foe must have left the conflict area in the last step. Determine exact exit time
1864  e->foeConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictExitDist, 0., -eInfo.foeConflictExitDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1865  // Add cross section to calculate PET for ego
1866 // e->egoPETCrossSections.push_back(std::make_pair(eInfo.egoConflictEntryCrossSection, e->foeConflictExitTime));
1867 #ifdef DEBUG_ENCOUNTER
1868  if (DEBUG_COND_ENCOUNTER(e)) {
1869  std::cout << " foe left conflict area at t=" << e->foeConflictExitTime << std::endl;
1870  }
1871 #endif
1872  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1873  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1874  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1876  }
1877  }
1878 }
1879 
1880 
1881 void
1883 
1884 #ifdef DEBUG_ENCOUNTER
1885  if (DEBUG_COND_ENCOUNTER(e)) {
1886  std::cout << SIMTIME << " updatePassedEncounter() for vehicles '" << e->egoID << "' and '" << e->foeID << "'\n";
1887  }
1888 #endif
1889 
1890  if (foeInfo == nullptr) {
1891  // the foe is out of the device's range, proceed counting down the remaining extra time to trace
1892  e->countDownExtraTime(TS);
1893 #ifdef DEBUG_ENCOUNTER
1894  if (DEBUG_COND_ENCOUNTER(e)) std::cout << " Foe is out of range. Counting down extra time."
1895  << " Remaining seconds before closing encounter: " << e->getRemainingExtraTime() << std::endl;
1896 #endif
1897 
1898  } else {
1899  // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
1901  }
1902 
1903  // Check, whether this was really a potential conflict at some time:
1904  // Search through typeSpan for a type other than no conflict
1905  EncounterType lastPotentialConflictType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1906 
1907  if (lastPotentialConflictType == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
1908  // This encounter was no conflict in the last step -> remains so
1909 #ifdef DEBUG_ENCOUNTER
1910  if (DEBUG_COND_ENCOUNTER(e)) {
1911  std::cout << " This encounter wasn't classified as a potential conflict lately.\n";
1912  }
1913 #endif
1914  if (foeInfo == nullptr) {
1915  // Encounter was either never a potential conflict and foe is out of range
1916  // or the foe has left the network
1917  // -> no use in further tracing this encounter
1918 #ifdef DEBUG_SSM
1919  if (DEBUG_COND(myHolderMS)) {
1920  std::cout << " Requesting encounter closure because foeInfo==nullptr" << std::endl;
1921  }
1922 #endif
1923  e->closingRequested = true;
1924 #ifdef DEBUG_ENCOUNTER
1925  if (DEBUG_COND_ENCOUNTER(e)) {
1926  std::cout << " Closing encounter.\n";
1927  }
1928 #endif
1930  }
1931  } else if (lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER
1932  || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_LEADER
1933  || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
1934  // if a following situation leads to a no-conflict situation this encounter switches no-conflict, since no further computations (PET) are needed.
1936 #ifdef DEBUG_ENCOUNTER
1937  if (DEBUG_COND_ENCOUNTER(e)) {
1938  std::cout << " Encounter was previously classified as a follow/lead situation.\n";
1939  }
1940 #endif
1941  } else if (lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_FOLLOWER
1942  || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_LEADER
1943  || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_PASSED) {
1944  // if a merging situation leads to a no-conflict situation the leader was either removed from the net (we disregard special treatment)
1945  // or route- or lane-changes removed the conflict.
1947 #ifdef DEBUG_ENCOUNTER
1948  if (DEBUG_COND_ENCOUNTER(e)) {
1949  std::cout << " Encounter was previously classified as a merging situation.\n";
1950  }
1951 #endif
1952  }
1953  if (lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1954  || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER
1955  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
1956  || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
1957  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
1958  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1959  || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1960  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
1961  || lastPotentialConflictType == ENCOUNTER_TYPE_COLLISION) {
1962  // Encounter has been a crossing situation.
1963 
1964 #ifdef DEBUG_ENCOUNTER
1965  if (DEBUG_COND_ENCOUNTER(e)) {
1966  std::cout << " Encounter was previously classified as a crossing situation of type " << lastPotentialConflictType << ".\n";
1967  }
1968 #endif
1969  // For passed encounters, the xxxConflictAreaLength variables are not determined before -> we use the stored values.
1970 
1971  // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
1972  if (eInfo.egoConflictAreaLength == INVALID_DOUBLE) {
1973  eInfo.egoConflictAreaLength = e->foe->getWidth();
1974  }
1975  if (eInfo.foeConflictAreaLength == INVALID_DOUBLE) {
1976  eInfo.foeConflictAreaLength = e->ego->getWidth();
1977  }
1978 
1979  eInfo.egoConflictEntryDist = e->egoDistsToConflict.back() - e->ego->getLastStepDist();
1981  eInfo.foeConflictEntryDist = e->foeDistsToConflict.back() - e->foe->getLastStepDist();
1983 
1984 #ifdef DEBUG_ENCOUNTER
1985  if (DEBUG_COND_ENCOUNTER(e))
1986  std::cout << " egoConflictEntryDist = " << eInfo.egoConflictEntryDist
1987  << ", egoConflictExitDist = " << eInfo.egoConflictExitDist
1988  << "\n foeConflictEntryDist = " << eInfo.foeConflictEntryDist
1989  << ", foeConflictExitDist = " << eInfo.foeConflictExitDist
1990  << std::endl;
1991 #endif
1992 
1993  // Determine actual encounter type
1994  bool egoEnteredConflict = eInfo.egoConflictEntryDist < 0.;
1995  bool foeEnteredConflict = eInfo.foeConflictEntryDist < 0.;
1996  bool egoLeftConflict = eInfo.egoConflictExitDist < 0.;
1997  bool foeLeftConflict = eInfo.foeConflictExitDist < 0.;
1998  if ((!egoEnteredConflict) && !foeEnteredConflict) {
1999  // XXX: do we need to recompute the follow/lead order, here?
2000  assert(lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
2001  || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER);
2002  eInfo.type = lastPotentialConflictType;
2003  } else if (egoEnteredConflict && !foeEnteredConflict) {
2005  } else if ((!egoEnteredConflict) && foeEnteredConflict) {
2007  } else { // (egoEnteredConflict && foeEnteredConflict) {
2009  }
2010 
2011  if ((!egoLeftConflict) && !foeLeftConflict) {
2014  }
2015  } else if (egoLeftConflict && !foeLeftConflict) {
2018  }
2019  } else if ((!egoLeftConflict) && foeLeftConflict) {
2022  }
2023  } else {
2025  // It should not occur that both leave the conflict at the same step
2026  assert(lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
2027  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
2028  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
2029  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA);
2030  }
2031 
2032  // TODO: adjust the conflict distances according to lateral movement for single ENTERED-cases
2033 
2034 #ifdef DEBUG_ENCOUNTER
2035  if (DEBUG_COND_ENCOUNTER(e)) {
2036  std::cout << " Updated classification: " << eInfo.type << "\n";
2037  }
2038 #endif
2039  }
2040 }
2041 
2042 
2045 #ifdef DEBUG_ENCOUNTER
2046  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2047  std::cout << "classifyEncounter() called.\n";
2048  }
2049 #endif
2050  if (foeInfo == nullptr) {
2051  // foeInfo == 0 signalizes, that no corresponding foe info was returned by findSurroundingVehicles(),
2052  // i.e. the foe is actually out of range (This may also mean that it has left the network)
2054  }
2055  const Encounter* e = eInfo.encounter;
2056 
2057  // previous classification (if encounter was not just created)
2058  EncounterType prevType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2059  if (e->typeSpan.size() > 0
2065  // This is an ongoing crossing situation with at least one of the vehicles not
2066  // having passed the conflict area.
2067  // -> Merely trace the change of distances to the conflict entry / exit
2068  // -> Derefer this to updatePassedEncounter, where this is done anyhow.
2069 #ifdef DEBUG_ENCOUNTER
2070  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2071  std::cout << " Ongoing crossing conflict will be traced by passedEncounter().\n";
2072  }
2073 #endif
2074  return prevType;
2075  }
2076 
2077 
2078  // Ego's current Lane
2079  const MSLane* egoLane = e->ego->getLane();
2080  // Foe's current Lane
2081  const MSLane* foeLane = e->foe->getLane();
2082 
2083  // Ego's conflict lane is memorized in foeInfo
2084  const MSLane* egoConflictLane = foeInfo->egoConflictLane;
2085  double egoDistToConflictLane = foeInfo->egoDistToConflictLane;
2086  // Find conflicting lane and the distance to its entry link for the foe
2087  double foeDistToConflictLane;
2088  const MSLane* foeConflictLane = findFoeConflictLane(e->foe, foeInfo->egoConflictLane, foeDistToConflictLane);
2089 
2090 #ifdef DEBUG_ENCOUNTER
2091  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2092  std::cout << " egoConflictLane='" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
2093  << " foeConflictLane='" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
2094  << " egoDistToConflictLane=" << egoDistToConflictLane
2095  << " foeDistToConflictLane=" << foeDistToConflictLane
2096  << std::endl;
2097 #endif
2098 
2099  // Treat different cases for foeConflictLane and egoConflictLane (internal or non-internal / equal to egoLane or to foeLane),
2100  // and thereby determine encounterType and the ego/foeEncounterDistance.
2101  // The encounter distance has a different meaning for different types of encounters:
2102  // 1) For rear-end conflicts (lead/follow situations) the follower's encounter distance is the distance to the actual back position of the leader. The leaders's distance is undefined.
2103  // 2) For merging encounters the encounter distance is the distance until the begin of the common target edge/lane.
2104  // (XXX: Perhaps this should be adjusted to include the entry point to the region where a simultaneous occupancy of
2105  // both merging lanes could imply a collision)
2106  // 3) For crossing encounters the encounter distances is the distance until the entry point to the conflicting lane.
2107 
2108  EncounterType type;
2109 
2110  if (foeConflictLane == nullptr) {
2111  // foe vehicle is not on course towards the ego's route (see findFoeConflictLane)
2113 #ifdef DEBUG_ENCOUNTER
2114  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2115  std::cout << "-> Encounter type: No conflict.\n";
2116  }
2117 #endif
2118  } else if (!egoConflictLane->isInternal()) {
2119  // The conflict lane is non-internal, therefore we either have no potential conflict or a lead/follow situation (i.e., no crossing or merging)
2120  if (egoConflictLane == egoLane) {
2121  const bool egoOpposite = e->ego->getLaneChangeModel().isOpposite();
2122  const bool foeOpposite = e->foe->getLaneChangeModel().isOpposite();
2123 #ifdef DEBUG_ENCOUNTER
2124  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2125  std::cout << "-> ego " << e->ego->getID() << " isOpposite " << egoOpposite << " foe " << e->foe->getID() << " isOpposite " << foeOpposite << " .\n";
2126  }
2127 #endif
2128  // The conflict point is on the ego's current lane.
2129  if (foeLane == egoLane) {
2130  // Foe is on the same non-internal lane
2131  if (!egoOpposite && !foeOpposite) {
2132  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2135  } else {
2138  }
2139 #ifdef DEBUG_ENCOUNTER
2140  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2141  std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'\n";
2142  }
2143 #endif
2144  } else if (egoOpposite && foeOpposite) {
2145  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2148  } else {
2151  }
2152 #ifdef DEBUG_ENCOUNTER
2153  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2154  std::cout << "-> Encounter type: Lead/follow-situation while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
2155  }
2156 #endif
2157  } else {
2158  type = ENCOUNTER_TYPE_ONCOMING;
2159  const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
2160  if (egoOpposite) {
2161  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2162  eInfo.egoConflictEntryDist = gap;
2163  eInfo.foeConflictEntryDist = gap;
2164  } else {
2166  }
2167  } else {
2168  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2169  eInfo.egoConflictEntryDist = -gap;
2170  eInfo.foeConflictEntryDist = -gap;
2171  } else {
2173  }
2174  }
2175 #ifdef DEBUG_ENCOUNTER
2176  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2177  std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
2178  }
2179 #endif
2180 
2181  }
2182  } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
2183  // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
2184  // XXX: this disregards conflicts for vehicles on adjacent lanes
2185  if (foeOpposite != egoOpposite) {
2187  } else {
2189  }
2190 #ifdef DEBUG_ENCOUNTER
2191  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2192  std::cout << "-> Encounter type: " << type << std::endl;
2193  }
2194 #endif
2195  } else {
2196 
2197  if (!egoOpposite && !foeOpposite) {
2198 
2199  assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
2200  assert(egoDistToConflictLane <= 0);
2201  // Foe must be on a route leading into the ego's edge
2202  if (foeConflictLane == egoLane) {
2204  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
2205 
2206 #ifdef DEBUG_ENCOUNTER
2207  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2208  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
2209  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2210  << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
2211 #endif
2212  } else {
2213  // Foe's route leads to an adjacent lane of the current lane of the ego
2215 #ifdef DEBUG_ENCOUNTER
2216  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2217  std::cout << "-> Encounter type: " << type << std::endl;
2218  }
2219 #endif
2220  }
2221 
2222  } else if (egoOpposite && foeOpposite) {
2223  // XXX determine follower relationship by searching for the foe lane in the opposites of ego bestlanes
2225  /*
2226  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2227  type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
2228  eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
2229  } else {
2230  type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
2231  eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
2232  }
2233  */
2234 #ifdef DEBUG_ENCOUNTER
2235  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2236  std::cout << "-> Encounter type: Lead/follow-situation while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
2237  }
2238 #endif
2239  } else {
2240  type = ENCOUNTER_TYPE_ONCOMING;
2241  // XXX determine distance by searching for the foe lane in the opposites of ego bestlanes
2242  /*
2243  const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
2244  if (egoOpposite) {
2245  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2246  eInfo.egoConflictEntryDist = gap;
2247  eInfo.foeConflictEntryDist = gap;
2248  } else {
2249  type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2250  }
2251  } else {
2252  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2253  eInfo.egoConflictEntryDist = -gap;
2254  eInfo.foeConflictEntryDist = -gap;
2255  } else {
2256  type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2257  }
2258  }
2259  */
2260 #ifdef DEBUG_ENCOUNTER
2261  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2262  std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
2263  }
2264 #endif
2265 
2266  }
2267  }
2268  } else {
2269  // The egoConflictLane is a non-internal lane which is not the ego's current lane. Thus it must lie ahead of the ego vehicle and
2270  // is located on the foe's current edge see findSurroundingVehicles()
2271  // (otherwise the foe would have had to enter the ego's route along a junction and the corresponding
2272  // conflict lane would be internal)
2273  assert(&(foeLane->getEdge()) == &(egoConflictLane->getEdge()));
2274  assert(foeDistToConflictLane <= 0);
2275  if (foeLane == egoConflictLane) {
2277  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
2278 #ifdef DEBUG_ENCOUNTER
2279  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2280  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
2281  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2282  << " (gap = " << eInfo.egoConflictEntryDist << ", case1)\n";
2283 #endif
2284  } else {
2285  // Ego's route leads to an adjacent lane of the current lane of the foe
2287 #ifdef DEBUG_ENCOUNTER
2288  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2289  std::cout << "-> Encounter type: " << type << std::endl;
2290  }
2291 #endif
2292  }
2293  }
2294  } else {
2295  // egoConflictLane is internal, i.e., lies on a junction. Besides the lead/follow situation (which may stretch over different lanes of a connection),
2296  // merging or crossing of the conflict lanes is possible.
2297  assert(foeConflictLane->isInternal());
2298  const MSLink* egoEntryLink = egoConflictLane->getEntryLink();
2299  const MSLink* foeEntryLink = foeConflictLane->getEntryLink();
2300  const bool egoOpposite = e->ego->getLaneChangeModel().isOpposite();
2301  const bool foeOpposite = e->foe->getLaneChangeModel().isOpposite();
2302 
2303  if (((!egoOpposite && foeOpposite) || (egoOpposite && !foeOpposite)) && egoConflictLane == foeConflictLane) {
2304  // oncoming on junction
2305 #ifdef DEBUG_ENCOUNTER
2306  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2307 
2308  std::cout << "-> egoConflictLane: " << egoConflictLane->getID() << " foeConflictLane: " << foeConflictLane->getID() << " egoOpposite " << egoOpposite << " foeOpposite " << foeOpposite << std::endl;
2309  }
2310 #endif
2311  // The conflict point is on the ego's current lane.
2312  if (foeLane == egoLane) {
2313  // Foe is on the same non-internal lane
2314  if (!egoOpposite && !foeOpposite) {
2315  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2318  } else {
2321  }
2322 #ifdef DEBUG_ENCOUNTER
2323  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2324  std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'\n";
2325  }
2326 #endif
2327  } else if (egoOpposite && foeOpposite) {
2328  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2331  } else {
2334  }
2335 #ifdef DEBUG_ENCOUNTER
2336  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2337  std::cout << "-> Encounter type: Lead/follow-situation while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
2338  }
2339 #endif
2340  } else {
2341  type = ENCOUNTER_TYPE_ONCOMING;
2342  const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
2343  if (egoOpposite) {
2344  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2345  eInfo.egoConflictEntryDist = gap;
2346  eInfo.foeConflictEntryDist = gap;
2347  } else {
2349  }
2350  } else {
2351  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2352  eInfo.egoConflictEntryDist = -gap;
2353  eInfo.foeConflictEntryDist = -gap;
2354  } else {
2356  }
2357  }
2358 #ifdef DEBUG_ENCOUNTER
2359  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2360  std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
2361  }
2362 #endif
2363 
2364  }
2365  } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
2366  // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
2367  // XXX: this disregards conflicts for vehicles on adjacent lanes
2368  if (foeOpposite != egoOpposite) {
2370  } else {
2372  }
2373 #ifdef DEBUG_ENCOUNTER
2374  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2375  std::cout << "-> Encounter type: " << type << std::endl;
2376  }
2377 #endif
2378  } else {
2379  if (!egoOpposite && !foeOpposite) {
2380  assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
2381  assert(egoDistToConflictLane <= 0);
2382  // Foe must be on a route leading into the ego's edge
2383  if (foeConflictLane == egoLane) {
2385  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
2386 
2387 #ifdef DEBUG_ENCOUNTER
2388  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2389  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
2390  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2391  << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
2392 #endif
2393  } else {
2394  // Foe's route leads to an adjacent lane of the current lane of the ego
2396 #ifdef DEBUG_ENCOUNTER
2397  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2398  std::cout << "-> Encounter type: " << type << std::endl;
2399  }
2400 #endif
2401  }
2402  } else if (egoOpposite && foeOpposite) {
2403  // XXX determine follower relationship by searching for the foe lane in the opposites of ego bestlanes
2405  /*
2406  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2407  type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
2408  eInfo.foeConflictEntryDist = -(e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane());
2409  } else {
2410  type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
2411  eInfo.egoConflictEntryDist = -(e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane());
2412  }
2413  */
2414 #ifdef DEBUG_ENCOUNTER
2415  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2416  std::cout << "-> Encounter type: Lead/follow-situation while both are driving in the opposite direction on non-internal lane '" << egoLane->getID() << "'\n";
2417  }
2418 #endif
2419  } else {
2420  type = ENCOUNTER_TYPE_ONCOMING;
2421  // XXX determine distance by searching for the foe lane in the opposites of ego bestlanes
2422  /*
2423  const double gap = e->ego->getPositionOnLane() - e->foe->getPositionOnLane();
2424  if (egoOpposite) {
2425  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2426  eInfo.egoConflictEntryDist = gap;
2427  eInfo.foeConflictEntryDist = gap;
2428  } else {
2429  type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2430  }
2431  } else {
2432  if (e->ego->getPositionOnLane() < e->foe->getPositionOnLane()) {
2433  eInfo.egoConflictEntryDist = -gap;
2434  eInfo.foeConflictEntryDist = -gap;
2435  } else {
2436  type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2437  }
2438  }
2439  */
2440 #ifdef DEBUG_ENCOUNTER
2441  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2442  std::cout << "-> Encounter type: oncoming on non-internal lane '" << egoLane->getID() << "'\n";
2443  }
2444 #endif
2445  }
2446  }
2447  } else if (&(egoEntryLink->getViaLane()->getEdge()) == &(foeEntryLink->getViaLane()->getEdge())) {
2448  if (egoEntryLink != foeEntryLink) {
2449  // XXX: this disregards conflicts for vehicles on adjacent internal lanes
2451 #ifdef DEBUG_ENCOUNTER
2452  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2453  std::cout << "-> Encounter type: " << type << std::endl;
2454  }
2455 #endif
2456  } else {
2457  // Lead / follow situation on connection
2458  if (egoLane == egoConflictLane && foeLane != foeConflictLane) {
2459  // ego on junction, foe not yet
2461  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
2462  if (e->ego->getLane()->getIncomingLanes()[0].lane->isInternal()) {
2463  eInfo.foeConflictEntryDist += e->ego->getLane()->getIncomingLanes()[0].lane->getLength();
2464  }
2465 #ifdef DEBUG_ENCOUNTER
2466  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2467  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
2468  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2469  << " (gap = " << eInfo.foeConflictEntryDist << ")\n";
2470 #endif
2471  } else if (egoLane != egoConflictLane && foeLane == foeConflictLane) {
2472  // foe on junction, ego not yet
2474  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
2475  if (e->foe->getLane()->getIncomingLanes()[0].lane->isInternal()) {
2476  eInfo.egoConflictEntryDist += e->foe->getLane()->getIncomingLanes()[0].lane->getLength();
2477  }
2478 #ifdef DEBUG_ENCOUNTER
2479  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2480  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
2481  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2482  << " (gap = " << eInfo.egoConflictEntryDist << ", case2)\n";
2483 #endif
2484  } else if (e->ego->getLaneChangeModel().isOpposite() || e->foe->getLaneChangeModel().isOpposite()) {
2485  type = ENCOUNTER_TYPE_MERGING;
2486  eInfo.foeConflictEntryDist = foeDistToConflictLane;
2487  eInfo.egoConflictEntryDist = egoDistToConflictLane;
2488 #ifdef DEBUG_ENCOUNTER
2489  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2490  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' merges with foe '"
2491  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2492  << " (gap = " << eInfo.egoConflictEntryDist << ", case5)\n";
2493 #endif
2494 
2495  } else {
2496  // Both must be already on the junction in a lead / follow situation on a connection
2497  // (since they approach via the same link, findSurroundingVehicles() would have determined a
2498  // different conflictLane if both are not on the junction)
2499  if (egoLane != egoConflictLane || foeLane != foeConflictLane) {
2500  WRITE_WARNINGF(TL("Cannot classify SSM encounter between ego vehicle % and foe vehicle % at time=%\n"), e->ego->getID(), e->foe->getID(), SIMTIME);
2502  }
2503  if (egoLane == foeLane) {
2504  // both on the same internal lane
2505  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
2507  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
2508 #ifdef DEBUG_ENCOUNTER
2509  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2510  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
2511  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2512  << " (gap = " << eInfo.foeConflictEntryDist << ")"
2513  << std::endl;
2514 #endif
2515  } else {
2517  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
2518 #ifdef DEBUG_ENCOUNTER
2519  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2520  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
2521  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2522  << " (gap = " << eInfo.egoConflictEntryDist << ", case3)"
2523  << std::endl;
2524 #endif
2525  }
2526  } else {
2527  // ego and foe on distinct, consecutive internal lanes
2528 #ifdef DEBUG_ENCOUNTER
2529  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2530  std::cout << " Lead/follow situation on consecutive internal lanes." << std::endl;
2531  }
2532 #endif
2533  MSLane* lane = egoEntryLink->getViaLane();
2534  while (true) {
2535  // Find first of egoLane and foeLane while crossing the junction (this dertermines who's the follower)
2536  // Then set the conflict lane to the lane of the leader and adapt the follower's distance to conflict
2537  if (egoLane == lane) {
2538  // ego is follower
2540  // adapt conflict dist
2541  eInfo.egoConflictEntryDist = egoDistToConflictLane;
2542  while (lane != foeLane) {
2543  eInfo.egoConflictEntryDist += lane->getLength();
2544  lane = lane->getLinkCont()[0]->getViaLane();
2545  assert(lane != 0);
2546  }
2548  egoConflictLane = lane;
2549 #ifdef DEBUG_ENCOUNTER
2550  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2551  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
2552  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2553  << " (gap = " << eInfo.egoConflictEntryDist << ", case4)"
2554  << std::endl;
2555 #endif
2556  break;
2557  } else if (foeLane == lane) {
2558  // ego is leader
2560  // adapt conflict dist
2561  eInfo.foeConflictEntryDist = foeDistToConflictLane;
2562  while (lane != egoLane) {
2563  eInfo.foeConflictEntryDist += lane->getLength();
2564  lane = lane->getLinkCont()[0]->getViaLane();
2565  assert(lane != 0);
2566  }
2568  foeConflictLane = lane;
2569 #ifdef DEBUG_ENCOUNTER
2570  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2571  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
2572  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2573  << " (gap = " << eInfo.foeConflictEntryDist << ")"
2574  << std::endl;
2575 #endif
2576  break;
2577  }
2578  lane = lane->getLinkCont()[0]->getViaLane();
2579  assert(lane != 0);
2580  }
2581  }
2582 #ifdef DEBUG_ENCOUNTER
2583  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2584  std::cout << "-> Encounter type: Lead/follow-situation on connection from '" << egoEntryLink->getLaneBefore()->getID()
2585  << "' to '" << egoEntryLink->getLane()->getID() << "'" << std::endl;
2586 #endif
2587  }
2588  }
2589  } else {
2590  // Entry links to junctions lead to different internal edges.
2591  // There are three possibilities, either the edges cross, merge or have no conflict
2592  const std::vector<MSLink*>& egoFoeLinks = egoEntryLink->getFoeLinks();
2593  const std::vector<MSLink*>& foeFoeLinks = foeEntryLink->getFoeLinks();
2594  // Determine whether ego and foe links are foes
2595  bool crossOrMerge = (find(egoFoeLinks.begin(), egoFoeLinks.end(), foeEntryLink) != egoFoeLinks.end()
2596  || std::find(foeFoeLinks.begin(), foeFoeLinks.end(), egoEntryLink) != foeFoeLinks.end());
2597  if (!crossOrMerge) {
2598  // if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
2599  // // XXX: the situation of merging into adjacent lanes is disregarded for now <- the alleged situation appears to imply crossOrMerge!!!
2600  // type = ENCOUNTER_TYPE_MERGING_ADJACENT;
2601  //#ifdef DEBUG_SSM
2602  // std::cout << "-> Encounter type: No conflict (adjacent lanes)." << std::endl;
2603  //#endif
2604  // } else {
2606 #ifdef DEBUG_ENCOUNTER
2607  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2608  std::cout << "-> Encounter type: No conflict.\n";
2609  }
2610 #endif
2611  // }
2612  } else if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
2613  if (foeEntryLink->getLane() == egoEntryLink->getLane()) {
2614  type = ENCOUNTER_TYPE_MERGING;
2615  assert(egoConflictLane->isInternal());
2616  assert(foeConflictLane->isInternal());
2617  eInfo.egoConflictEntryDist = egoDistToConflictLane + egoEntryLink->getInternalLengthsAfter();
2618  eInfo.foeConflictEntryDist = foeDistToConflictLane + foeEntryLink->getInternalLengthsAfter();
2619 
2620  MSLink* egoEntryLinkSucc = egoEntryLink->getViaLane()->getLinkCont().front();
2621  if (egoEntryLinkSucc->isInternalJunctionLink() && e->ego->getLane() == egoEntryLinkSucc->getViaLane()) {
2622  // ego is already past the internal junction
2623  eInfo.egoConflictEntryDist -= egoEntryLink->getViaLane()->getLength();
2624  eInfo.egoConflictExitDist -= egoEntryLink->getViaLane()->getLength();
2625  }
2626  MSLink* foeEntryLinkSucc = foeEntryLink->getViaLane()->getLinkCont().front();
2627  if (foeEntryLinkSucc->isInternalJunctionLink() && e->foe->getLane() == foeEntryLinkSucc->getViaLane()) {
2628  // foe is already past the internal junction
2629  eInfo.foeConflictEntryDist -= foeEntryLink->getViaLane()->getLength();
2630  eInfo.foeConflictExitDist -= foeEntryLink->getViaLane()->getLength();
2631  }
2632 
2633 #ifdef DEBUG_ENCOUNTER
2634  if (DEBUG_COND_ENCOUNTER(eInfo.encounter))
2635  std::cout << "-> Encounter type: Merging situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
2636  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2637  << "\nDistances to merge-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
2638  << std::endl;
2639 #endif
2640  } else {
2641  // Links leading to the same edge but different lanes. XXX: Disregards conflicts on adjacent lanes
2643 #ifdef DEBUG_ENCOUNTER
2644  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2645  std::cout << "-> Encounter type: No conflict: " << type << std::endl;
2646  }
2647 #endif
2648  }
2649  } else {
2650  type = ENCOUNTER_TYPE_CROSSING;
2651 
2652  assert(egoConflictLane->isInternal());
2653  assert(foeConflictLane->getEdge().getToJunction() == egoConflictLane->getEdge().getToJunction());
2654 
2655  // If the conflict lanes are internal, they may not correspond to the
2656  // actually crossing parts of the corresponding connections.
2657  // Adjust the conflict lanes accordingly.
2658  // set back both to the first parts of the corresponding connections
2659  double offset = 0.;
2660  egoConflictLane = egoConflictLane->getFirstInternalInConnection(offset);
2661  egoDistToConflictLane -= offset;
2662  foeConflictLane = foeConflictLane->getFirstInternalInConnection(offset);
2663  foeDistToConflictLane -= offset;
2664  // find the distances to the conflict from the junction entry for both vehicles
2665  // Here we also determine the real crossing lanes (before the conflict lane is the first lane of the connection)
2666  // for the ego
2667  double egoDistToConflictFromJunctionEntry = INVALID_DOUBLE;
2668  while (foeConflictLane != nullptr && foeConflictLane->isInternal()) {
2669  egoDistToConflictFromJunctionEntry = egoEntryLink->getLengthsBeforeCrossing(foeConflictLane);
2670  if (egoDistToConflictFromJunctionEntry != INVALID_DOUBLE) {
2671  // found correct foeConflictLane
2672  egoDistToConflictFromJunctionEntry += 0.5 * (foeConflictLane->getWidth() - e->foe->getVehicleType().getWidth());
2673  break;
2674  }
2675  if (!foeConflictLane->getCanonicalSuccessorLane()->isInternal()) {
2676  // intersection has wierd geometry and the intersection was found
2677  egoDistToConflictFromJunctionEntry = 0;
2678  WRITE_WARNINGF(TL("Cannot compute SSM due to bad internal lane geometry at junction '%'. Crossing point between traffic from links % and % not found."),
2679  egoEntryLink->getJunction()->getID(),
2680  egoEntryLink->getIndex(),
2681  foeEntryLink->getIndex());
2682  break;
2683  }
2684  foeConflictLane = foeConflictLane->getCanonicalSuccessorLane();
2685  assert(foeConflictLane != nullptr && foeConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
2686  }
2687  assert(egoDistToConflictFromJunctionEntry != INVALID_DOUBLE);
2688 
2689  // for the foe
2690  double foeDistToConflictFromJunctionEntry = INVALID_DOUBLE;
2691  foeDistToConflictFromJunctionEntry = INVALID_DOUBLE;
2692  while (egoConflictLane != nullptr && egoConflictLane->isInternal()) {
2693  foeDistToConflictFromJunctionEntry = foeEntryLink->getLengthsBeforeCrossing(egoConflictLane);
2694  if (foeDistToConflictFromJunctionEntry != INVALID_DOUBLE) {
2695  // found correct egoConflictLane
2696  foeDistToConflictFromJunctionEntry += 0.5 * (egoConflictLane->getWidth() - e->ego->getVehicleType().getWidth());
2697  break;
2698  }
2699  if (!egoConflictLane->getCanonicalSuccessorLane()->isInternal()) {
2700  // intersection has wierd geometry and the intersection was found
2701  foeDistToConflictFromJunctionEntry = 0;
2702  WRITE_WARNINGF(TL("Cannot compute SSM due to bad internal lane geometry at junction '%'. Crossing point between traffic from links % and % not found."),
2703  foeEntryLink->getJunction()->getID(),
2704  foeEntryLink->getIndex(),
2705  egoEntryLink->getIndex());
2706  break;
2707  }
2708  egoConflictLane = egoConflictLane->getCanonicalSuccessorLane();
2709  assert(egoConflictLane != nullptr && egoConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
2710  }
2711  assert(foeDistToConflictFromJunctionEntry != INVALID_DOUBLE);
2712 
2713  // store conflict entry information in eInfo
2714 
2715  // // TO-DO: equip these with exit times to store relevant PET sections in encounter
2716  // eInfo.egoConflictEntryCrossSection = std::make_pair(egoConflictLane, egoDistToConflictFromJunctionEntry - egoInternalLaneLengthsBeforeCrossing);
2717  // eInfo.foeConflictEntryCrossSection = std::make_pair(foeConflictLane, foeDistToConflictFromJunctionEntry - foeInternalLaneLengthsBeforeCrossing);
2718 
2719  // Take into account the lateral position for the exact determination of the conflict point
2720  // whether lateral position increases or decreases conflict distance depends on lane angles at conflict
2721  // -> conflictLaneOrientation in {-1,+1}
2722  // First, measure the angle between the two connection lines (straight lines from junction entry point to junction exit point)
2723  Position egoEntryPos = egoEntryLink->getViaLane()->getShape().front();
2724  Position egoExitPos = egoEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
2725  PositionVector egoConnectionLine(egoEntryPos, egoExitPos);
2726  Position foeEntryPos = foeEntryLink->getViaLane()->getShape().front();
2727  Position foeExitPos = foeEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
2728  PositionVector foeConnectionLine(foeEntryPos, foeExitPos);
2729  double angle = std::fmod(egoConnectionLine.rotationAtOffset(0.) - foeConnectionLine.rotationAtOffset(0.), (2 * M_PI));
2730  if (angle < 0) {
2731  angle += 2 * M_PI;
2732  }
2733  assert(angle >= 0);
2734  assert(angle <= 2 * M_PI);
2735  if (angle > M_PI) {
2736  angle -= 2 * M_PI;
2737  }
2738  assert(angle >= -M_PI);
2739  assert(angle <= M_PI);
2740  // Determine orientation of the connection lines. (Positive values mean that the ego vehicle approaches from the foe's left side.)
2741  double crossingOrientation = (angle < 0) - (angle > 0);
2742 
2743  // Adjust conflict dist to lateral positions
2744  // TODO: This could more precisely be calculated wrt the angle of the crossing *at the conflict point*
2745  egoDistToConflictFromJunctionEntry -= crossingOrientation * e->foe->getLateralPositionOnLane();
2746  foeDistToConflictFromJunctionEntry += crossingOrientation * e->ego->getLateralPositionOnLane();
2747 
2748  // Complete entry distances
2749  eInfo.egoConflictEntryDist = egoDistToConflictLane + egoDistToConflictFromJunctionEntry;
2750  eInfo.foeConflictEntryDist = foeDistToConflictLane + foeDistToConflictFromJunctionEntry;
2751 
2752 
2753  // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
2754  eInfo.egoConflictAreaLength = e->foe->getWidth();
2755  eInfo.foeConflictAreaLength = e->ego->getWidth();
2756 
2757  // resulting exit distances
2760 
2761 #ifdef DEBUG_ENCOUNTER
2762  if (DEBUG_COND_ENCOUNTER(eInfo.encounter)) {
2763  std::cout << " Determined exact conflict distances for crossing conflict."
2764  << "\n crossingOrientation=" << crossingOrientation
2765  << ", egoCrossingAngle=" << egoConnectionLine.rotationAtOffset(0.)
2766  << ", foeCrossingAngle=" << foeConnectionLine.rotationAtOffset(0.)
2767  << ", relativeAngle=" << angle
2768  << " (foe from " << (crossingOrientation > 0 ? "right)" : "left)")
2769  << "\n resulting offset for conflict entry distance:"
2770  << "\n ego=" << crossingOrientation* e->foe->getLateralPositionOnLane()
2771  << ", foe=" << crossingOrientation* e->ego->getLateralPositionOnLane()
2772  << "\n distToConflictLane:"
2773  << "\n ego=" << egoDistToConflictLane
2774  << ", foe=" << foeDistToConflictLane
2775  << "\n distToConflictFromJunctionEntry:"
2776  << "\n ego=" << egoDistToConflictFromJunctionEntry
2777  << ", foe=" << foeDistToConflictFromJunctionEntry
2778  << "\n resulting entry distances:"
2779  << "\n ego=" << eInfo.egoConflictEntryDist
2780  << ", foe=" << eInfo.foeConflictEntryDist
2781  << "\n resulting exit distances:"
2782  << "\n ego=" << eInfo.egoConflictExitDist
2783  << ", foe=" << eInfo.foeConflictExitDist
2784  << std::endl;
2785 
2786  std::cout << "real egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
2787  << "real foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
2788  << "-> Encounter type: Crossing situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
2789  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2790  << "\nDistances to crossing-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
2791  << std::endl;
2792  }
2793 #endif
2794  }
2795  }
2796  }
2797  return type;
2798 }
2799 
2800 
2801 
2802 const MSLane*
2803 MSDevice_SSM::findFoeConflictLane(const MSVehicle* foe, const MSLane* egoConflictLane, double& distToConflictLane) const {
2804 
2805 #ifdef DEBUG_SSM
2806  if (DEBUG_COND(myHolderMS))
2807  std::cout << SIMTIME << " findFoeConflictLane() for foe '"
2808  << foe->getID() << "' on lane '" << foe->getLane()->getID()
2809  << "' (with egoConflictLane=" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID())
2810  << ")\nfoeBestLanes: " << ::toString(foe->getBestLanesContinuation())
2811  << std::endl;
2812 #endif
2813  if (foe->getLaneChangeModel().isOpposite()) {
2814  // distinguish three cases
2815  // 1) foe is driving in the same direction as ego and ego is driving in lane direction -> ENCOUNTER_TYPE_ON_ADJACENT_LANES
2816  // 2) foe is driving in the same direction as ego and ego is also driving in the opposite direction -> ENCOUNTER_TYPE_FOLLOWING
2817  // 3) foe is driving in the opposite direction as ego and both are driving way from each other -> ENCOUNTER_TYPE_NOCONFLICT_AHEAD
2818  // 3) foe is driving in the opposite direction as ego and both are driving towards each other -> ENCOUNTER_TYPE_ONCOMING
2819 #ifdef DEBUG_SSM_OPPOSITE
2820 #endif
2821  auto egoIt = std::find(myHolder.getCurrentRouteEdge(), myHolder.getRoute().end(), foe->getEdge());
2822  if (egoIt != myHolder.getRoute().end()) {
2823  // same direction, foe is leader
2825  if (egoConflictLane->isInternal() && !foe->getLane()->isInternal()) {
2826  // lead/follow situation resolved elsewhere
2827  return nullptr;
2828  }
2829  return foe->getLane();
2830  } else {
2831  // adjacent
2832  return nullptr;
2833  }
2834  }
2835  auto foeIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), myHolder.getEdge());
2836  if (foeIt != foe->getRoute().end()) {
2837  // same direction, ego is leader
2839  return egoConflictLane;
2840  } else {
2841  // adjacent
2842  return nullptr;
2843  }
2844  }
2845  auto egoIt2 = std::find(myHolder.getCurrentRouteEdge(), myHolder.getRoute().end(), foe->getEdge()->getOppositeEdge());
2846  if (egoIt2 != myHolder.getRoute().end()) {
2847  // opposite direction, driving towards each other
2848  return egoConflictLane;
2849  } else {
2850  // opposite direction, driving away from each other
2851  return nullptr;
2852  }
2853  }
2854 
2855  const MSLane* foeLane = foe->getLane();
2856  std::vector<MSLane*>::const_iterator laneIter = foe->getBestLanesContinuation().begin();
2857  std::vector<MSLane*>::const_iterator foeBestLanesEnd = foe->getBestLanesContinuation().end();
2858  assert(foeLane->isInternal() || *laneIter == foeLane);
2859  distToConflictLane = -foe->getPositionOnLane();
2860 
2861  // Potential conflict lies on junction if egoConflictLane is internal
2862  const MSJunction* conflictJunction = egoConflictLane->isInternal() ? egoConflictLane->getEdge().getToJunction() : nullptr;
2863 #ifdef DEBUG_SSM
2864  if (DEBUG_COND(myHolderMS))
2865  if (conflictJunction != 0) {
2866  std::cout << "Potential conflict on junction '" << conflictJunction->getID()
2867  << std::endl;
2868  }
2869 #endif
2870  if (foeLane->isInternal() && foeLane->getEdge().getToJunction() == conflictJunction) {
2871  // foe is already on the conflict junction
2872  if (egoConflictLane != nullptr && egoConflictLane->isInternal() && egoConflictLane->getLinkCont()[0]->getViaLane() == foeLane) {
2873  distToConflictLane += egoConflictLane->getLength();
2874  }
2875  return foeLane;
2876  }
2877 
2878  // Foe is not on the conflict junction
2879 
2880  // Leading internal lanes in bestlanes are resembled as a single NULL-pointer skip them
2881  if (*laneIter == nullptr) {
2882  while (foeLane != nullptr && foeLane->isInternal()) {
2883  distToConflictLane += foeLane->getLength();
2884  foeLane = foeLane->getLinkCont()[0]->getViaLane();
2885  }
2886  ++laneIter;
2887  assert(laneIter == foeBestLanesEnd || *laneIter != 0);
2888  }
2889 
2890  // Look for the junction downstream along foeBestLanes
2891  while (laneIter != foeBestLanesEnd && distToConflictLane <= myRange) {
2892  // Eventual internal lanes were skipped
2893  assert(*laneIter == foeLane || foeLane == 0);
2894  foeLane = *laneIter;
2895  assert(!foeLane->isInternal());
2896  if (&foeLane->getEdge() == &egoConflictLane->getEdge()) {
2897 #ifdef DEBUG_SSM
2898  if (DEBUG_COND(myHolderMS)) {
2899  std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
2900  }
2901 #endif
2902  // found the potential conflict edge along foeBestLanes
2903  return foeLane;
2904  }
2905  // No conflict on foeLane
2906  distToConflictLane += foeLane->getLength();
2907 
2908  // set laneIter to next non internal lane along foeBestLanes
2909  ++laneIter;
2910  if (laneIter == foeBestLanesEnd) {
2911  return nullptr;
2912  }
2913  MSLane* const nextNonInternalLane = *laneIter;
2914  const MSLink* const link = foeLane->getLinkTo(nextNonInternalLane);
2915  if (link == nullptr) {
2916  // encountered incomplete route
2917  return nullptr;
2918  }
2919  // Set foeLane to first internal lane on the next junction
2920  foeLane = link->getViaLane();
2921  assert(foeLane == 0 || foeLane->isInternal());
2922  if (foeLane == nullptr) {
2923  foeLane = nextNonInternalLane;
2924  continue;
2925  }
2926  if (foeLane->getEdge().getToJunction() == conflictJunction) {
2927  assert(foeLane != 0);
2928 #ifdef DEBUG_SSM
2929  if (DEBUG_COND(myHolderMS)) {
2930  std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
2931  }
2932 #endif
2933  // found egoConflictLane, resp. the conflict junction, along foeBestLanes
2934  return foeLane;
2935  }
2936  // No conflict on junction
2937  distToConflictLane += link->getInternalLengthsAfter();
2938  foeLane = nextNonInternalLane;
2939  }
2940  // Didn't find conflicting lane on foeBestLanes within range.
2941  return nullptr;
2942 }
2943 
2944 void
2946 #ifdef DEBUG_SSM
2947  if (DEBUG_COND(myHolderMS)) {
2948  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' flushConflicts past=" << myPastConflicts.size()
2950  << " topBegin=" << (myPastConflicts.size() > 0 ? myPastConflicts.top()->begin : -1)
2951  << "\n";
2952  }
2953 #endif
2954  while (!myPastConflicts.empty()) {
2955  Encounter* top = myPastConflicts.top();
2956  if (flushAll || top->begin <= myOldestActiveEncounterBegin) {
2957  bool write = true;
2958  if (myFilterConflictTypes) {
2959  std::vector<int> foundTypes;
2960  std::set<int> encounterTypes(top->typeSpan.begin(), top->typeSpan.end());
2961  std::set_intersection(
2963  encounterTypes.begin(), encounterTypes.end(),
2964  std::back_inserter(foundTypes));
2965  write = foundTypes.size() == 0;
2966  }
2967  if (write) {
2968  writeOutConflict(top);
2969  }
2970  myPastConflicts.pop();
2971  delete top;
2972  } else {
2973  break;
2974  }
2975  }
2976 }
2977 
2978 void
2980  std::string egoID = myHolderMS->getID();
2981 #ifdef DEBUG_SSM
2982  if (DEBUG_COND(myHolderMS))
2983  std::cout << SIMTIME << " flushGlobalMeasures() of vehicle '"
2984  << egoID << "'"
2985  << "'\ntoGeo=" << myUseGeoCoords << std::endl;
2986 #endif
2988  myOutputFile->openTag("globalMeasures");
2989  myOutputFile->writeAttr("ego", egoID);
2991  if (myWritePositions) {
2993  }
2994  if (myWriteLanesPositions) {
2997  }
2998  if (myComputeBR) {
2999  myOutputFile->openTag("BRSpan").writeAttr("values", myBRspan).closeTag();
3000 
3001  if (myMaxBR.second != 0.0) {
3002  if (myUseGeoCoords) {
3003  toGeo(myMaxBR.first.second);
3004  }
3005  myOutputFile->openTag("maxBR").writeAttr("time", myMaxBR.first.first).writeAttr("position", makeStringWithNAs(myMaxBR.first.second)).writeAttr("value", myMaxBR.second).closeTag();
3006  }
3007  }
3008 
3009  if (myComputeSGAP) {
3011  if (myMinSGAP.second != "") {
3012  if (myUseGeoCoords) {
3013  toGeo(myMinSGAP.first.first.second);
3014  }
3015  myOutputFile->openTag("minSGAP").writeAttr("time", myMinSGAP.first.first.first)
3016  .writeAttr("position", makeStringWithNAs(myMinSGAP.first.first.second))
3017  .writeAttr("value", myMinSGAP.first.second)
3018  .writeAttr("leader", myMinSGAP.second).closeTag();
3019  }
3020  }
3021 
3022  if (myComputeTGAP) {
3024  if (myMinTGAP.second != "") {
3025  if (myUseGeoCoords) {
3026  toGeo(myMinTGAP.first.first.second);
3027  }
3028  myOutputFile->openTag("minTGAP").writeAttr("time", myMinTGAP.first.first.first)
3029  .writeAttr("position", makeStringWithNAs(myMinTGAP.first.first.second))
3030  .writeAttr("value", myMinTGAP.first.second)
3031  .writeAttr("leader", myMinTGAP.second).closeTag();
3032  }
3033  }
3034  // close globalMeasures
3036  }
3037 }
3038 
3039 void
3042 }
3043 
3044 void
3046  for (Position& x : xv) {
3047  if (x != Position::INVALID) {
3048  toGeo(x);
3049  }
3050  }
3051 }
3052 
3053 void
3055 #ifdef DEBUG_SSM
3056  if (DEBUG_COND(myHolderMS))
3057  std::cout << SIMTIME << " writeOutConflict() of vehicles '"
3058  << e->egoID << "' and '" << e->foeID
3059  << "'\ntoGeo=" << myUseGeoCoords << std::endl;
3060 #endif
3061  myOutputFile->openTag("conflict");
3062  myOutputFile->writeAttr("begin", e->begin).writeAttr("end", e->end);
3063  myOutputFile->writeAttr("ego", e->egoID).writeAttr("foe", e->foeID);
3064 
3065  if (mySaveTrajectories) {
3066  myOutputFile->openTag("timeSpan").writeAttr("values", e->timeSpan).closeTag();
3067  myOutputFile->openTag("typeSpan").writeAttr("values", e->typeSpan).closeTag();
3068 
3069  // Some useful snippets for that (from MSFCDExport.cpp):
3070  if (myUseGeoCoords) {
3071  toGeo(e->egoTrajectory.x);
3072  toGeo(e->foeTrajectory.x);
3074  }
3075 
3077  if (myWriteLanesPositions) {
3078  myOutputFile->openTag("egoLane").writeAttr("values", ::toString(e->egoTrajectory.lane)).closeTag();
3079  myOutputFile->openTag("egoLanePosition").writeAttr("values", ::toString(e->egoTrajectory.lanePos)).closeTag();
3080  }
3081  myOutputFile->openTag("egoVelocity").writeAttr("values", ::toString(e->egoTrajectory.v)).closeTag();
3082 
3084  if (myWriteLanesPositions) {
3085  myOutputFile->openTag("foeLane").writeAttr("values", ::toString(e->foeTrajectory.lane)).closeTag();
3086  myOutputFile->openTag("foeLanePosition").writeAttr("values", ::toString(e->foeTrajectory.lanePos)).closeTag();
3087  }
3088  myOutputFile->openTag("foeVelocity").writeAttr("values", ::toString(e->foeTrajectory.v)).closeTag();
3089 
3090  myOutputFile->openTag("conflictPoint").writeAttr("values", makeStringWithNAs(e->conflictPointSpan)).closeTag();
3091  }
3092 
3093  if (myComputeTTC) {
3094  if (mySaveTrajectories) {
3096  }
3097  if (e->minTTC.time == INVALID_DOUBLE) {
3098  myOutputFile->openTag("minTTC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
3099  } else {
3100  std::string time = ::toString(e->minTTC.time);
3101  std::string type = ::toString(int(e->minTTC.type));
3102  std::string value = ::toString(e->minTTC.value);
3103  std::string speed = ::toString(e->minTTC.speed);
3104  if (myUseGeoCoords) {
3105  toGeo(e->minTTC.pos);
3106  }
3107  std::string position = makeStringWithNAs(e->minTTC.pos);
3108  myOutputFile->openTag("minTTC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
3109  }
3110  }
3111  if (myComputeDRAC) {
3112  if (mySaveTrajectories) {
3113  myOutputFile->openTag("DRACSpan").writeAttr("values", makeStringWithNAs(e->DRACspan, {0.0, INVALID_DOUBLE})).closeTag();
3114  }
3115  if (e->maxDRAC.time == INVALID_DOUBLE) {
3116  myOutputFile->openTag("maxDRAC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
3117  } else {
3118  std::string time = ::toString(e->maxDRAC.time);
3119  std::string type = ::toString(int(e->maxDRAC.type));
3120  std::string value = ::toString(e->maxDRAC.value);
3121  std::string speed = ::toString(e->maxDRAC.speed);
3122  if (myUseGeoCoords) {
3123  toGeo(e->maxDRAC.pos);
3124  }
3125  std::string position = makeStringWithNAs(e->maxDRAC.pos);
3126  myOutputFile->openTag("maxDRAC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
3127  }
3128  }
3129  if (myComputePET) {
3130  if (e->PET.time == INVALID_DOUBLE) {
3131  myOutputFile->openTag("PET").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
3132  } else {
3133  std::string time = ::toString(e->PET.time);
3134  std::string type = ::toString(int(e->PET.type));
3135  std::string value = ::toString(e->PET.value);
3136  std::string speed = ::toString(e->PET.speed);
3137  if (myUseGeoCoords) {
3138  toGeo(e->PET.pos);
3139  }
3140  std::string position = ::toString(e->PET.pos, myUseGeoCoords ? gPrecisionGeo : gPrecision);
3141  myOutputFile->openTag("PET").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
3142  }
3143  }
3144  if (myComputePPET) {
3145  if (mySaveTrajectories) {
3147  }
3148  if (e->minPPET.time == INVALID_DOUBLE) {
3149  myOutputFile->openTag("minPPET").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
3150  } else {
3151  std::string time = ::toString(e->minPPET.time);
3152  std::string type = ::toString(int(e->minPPET.type));
3153  std::string value = ::toString(e->minPPET.value);
3154  std::string speed = ::toString(e->minPPET.speed);
3155  if (myUseGeoCoords) {
3156  toGeo(e->minPPET.pos);
3157  }
3158  std::string position = makeStringWithNAs(e->minPPET.pos);
3159  myOutputFile->openTag("minPPET").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
3160  }
3161  }
3162  if (myComputeMDRAC) {
3163  if (mySaveTrajectories) {
3164  myOutputFile->openTag("MDRACSpan").writeAttr("values", makeStringWithNAs(e->MDRACspan, {0.0, INVALID_DOUBLE})).closeTag();
3165  }
3166  if (e->maxMDRAC.time == INVALID_DOUBLE) {
3167  myOutputFile->openTag("maxMDRAC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").writeAttr("speed", "NA").closeTag();
3168  } else {
3169  std::string time = ::toString(e->maxMDRAC.time);
3170  std::string type = ::toString(int(e->maxMDRAC.type));
3171  std::string value = ::toString(e->maxMDRAC.value);
3172  std::string speed = ::toString(e->maxMDRAC.speed);
3173  if (myUseGeoCoords) {
3174  toGeo(e->maxMDRAC.pos);
3175  }
3176  std::string position = makeStringWithNAs(e->maxMDRAC.pos);
3177  myOutputFile->openTag("maxMDRAC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).writeAttr("speed", speed).closeTag();
3178  }
3179  }
3181 }
3182 
3183 std::string
3184 MSDevice_SSM::makeStringWithNAs(const std::vector<double>& v, double NA) {
3185  std::string res = "";
3186  for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
3187  res += (i == v.begin() ? "" : " ") + (*i == NA ? "NA" : ::toString(*i));
3188  }
3189  return res;
3190 }
3191 
3192 std::string
3193 MSDevice_SSM::makeStringWithNAs(const std::vector<double>& v, const std::vector<double>& NAs) {
3194  std::string res = "";
3195  for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
3196  res += (i == v.begin() ? "" : " ") + (find(NAs.begin(), NAs.end(), *i) != NAs.end() ? "NA" : ::toString(*i));
3197  }
3198  return res;
3199 }
3200 
3201 std::string
3203  const int precision = myUseGeoCoords ? gPrecisionGeo : gPrecision;
3204  std::string res = "";
3205  for (PositionVector::const_iterator i = v.begin(); i != v.end(); ++i) {
3206  res += (i == v.begin() ? "" : " ") + (*i == Position::INVALID ? "NA" : ::toString(*i, precision));
3207  }
3208  return res;
3209 }
3210 
3211 std::string
3213  const int precision = myUseGeoCoords ? gPrecisionGeo : gPrecision;
3214  return p == Position::INVALID ? "NA" : toString(p, precision);
3215 }
3216 
3217 
3218 std::string
3219 MSDevice_SSM::writeNA(double v, double NA) {
3220  return v == NA ? "NA" : toString(v);
3221 }
3222 
3223 // ---------------------------------------------------------------------------
3224 // MSDevice_SSM-methods
3225 // ---------------------------------------------------------------------------
3226 MSDevice_SSM::MSDevice_SSM(SUMOVehicle& holder, const std::string& id, std::string outputFilename, std::map<std::string, double> thresholds,
3227  bool trajectories, double range, double extraTime, bool useGeoCoords, bool writePositions, bool writeLanesPositions,
3228  std::vector<int> conflictTypeFilter) :
3229  MSVehicleDevice(holder, id),
3230  myThresholds(thresholds),
3231  mySaveTrajectories(trajectories),
3232  myRange(range),
3233  myMDRACPRT(getMDRAC_PRT(holder)),
3234  myExtraTime(extraTime),
3239  myMaxBR(std::make_pair(-1, Position(0., 0.)), 0.0),
3240  myMinSGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), ""),
3241  myMinTGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), "") {
3242  // Take care! Holder is currently being constructed. Cast occurs before completion.
3243  myHolderMS = static_cast<MSVehicle*>(&holder);
3244 
3245  myComputeTTC = myThresholds.find("TTC") != myThresholds.end();
3246  myComputeDRAC = myThresholds.find("DRAC") != myThresholds.end();
3247  myComputeMDRAC = myThresholds.find("MDRAC") != myThresholds.end();
3248  myComputePET = myThresholds.find("PET") != myThresholds.end();
3249  myComputePPET = myThresholds.find("PPET") != myThresholds.end();
3250 
3251  myComputeBR = myThresholds.find("BR") != myThresholds.end();
3252  myComputeSGAP = myThresholds.find("SGAP") != myThresholds.end();
3253  myComputeTGAP = myThresholds.find("TGAP") != myThresholds.end();
3254 
3255  myDroppedConflictTypes = conflictTypeFilter;
3257 
3260 
3261  // XXX: Who deletes the OutputDevice?
3262  myOutputFile = &OutputDevice::getDevice(outputFilename);
3263 // TODO: make xsd, include header
3264 // myOutputFile.writeXMLHeader("SSMLog", "SSMLog.xsd");
3265  if (myCreatedOutputFiles.count(outputFilename) == 0) {
3266  myOutputFile->writeXMLHeader("SSMLog", "");
3267  myCreatedOutputFiles.insert(outputFilename);
3268  }
3269  // register at static instance container
3270  myInstances->insert(this);
3271 
3272 #ifdef DEBUG_SSM
3273  if (DEBUG_COND(myHolderMS)) {
3274  std::vector<std::string> measures;
3275  std::vector<double> threshVals;
3276  for (std::map<std::string, double>::const_iterator i = myThresholds.begin(); i != myThresholds.end(); ++i) {
3277  measures.push_back(i->first);
3278  threshVals.push_back(i->second);
3279  }
3280  std::cout << "Initialized ssm device '" << id << "' with "
3281  << "myMeasures=" << joinToString(measures, " ")
3282  << ", myThresholds=" << joinToString(threshVals, " ")
3283  << ", mySaveTrajectories=" << mySaveTrajectories
3284  << ", myRange=" << myRange << ", output file=" << outputFilename << ", extra time=" << myExtraTime << ", useGeo=" << myUseGeoCoords << "\n";
3285  }
3286 #endif
3287 }
3288 
3291  // Deleted in ~BaseVehicle()
3292  // unregister from static instance container
3293  myInstances->erase(this);
3294  resetEncounters();
3295  flushConflicts(true);
3297 }
3298 
3299 
3300 bool
3302  assert(veh.isVehicle());
3303 #ifdef DEBUG_SSM_NOTIFICATIONS
3304  MSBaseVehicle* v = (MSBaseVehicle*) &veh;
3305  if (DEBUG_COND(v)) {
3306  std::cout << SIMTIME << "device '" << getID() << "' notifyEnter: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
3307  }
3308 #else
3309  UNUSED_PARAMETER(veh);
3310  UNUSED_PARAMETER(reason);
3311 #endif
3312  return true; // keep the device
3313 }
3314 
3315 bool
3317  MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
3318  assert(veh.isVehicle());
3319 #ifdef DEBUG_SSM_NOTIFICATIONS
3320  MSBaseVehicle* v = (MSBaseVehicle*) &veh;
3321  if (DEBUG_COND(v)) {
3322  std::cout << SIMTIME << "device '" << getID() << "' notifyLeave: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
3323  }
3324 #else
3325  UNUSED_PARAMETER(veh);
3326  UNUSED_PARAMETER(reason);
3327 #endif
3328  return true; // keep the device
3329 }
3330 
3331 bool
3333  double /* newPos */, double newSpeed) {
3334 #ifdef DEBUG_SSM_NOTIFICATIONS
3335  MSBaseVehicle* v = (MSBaseVehicle*) &veh;
3336  if (DEBUG_COND(v)) {
3337  std::cout << SIMTIME << "device '" << getID() << "' notifyMove: newSpeed=" << newSpeed << "\n";
3338  }
3339 #else
3340  UNUSED_PARAMETER(veh);
3341  UNUSED_PARAMETER(newSpeed);
3342 #endif
3343  return true; // keep the device
3344 }
3345 
3346 
3347 void
3348 MSDevice_SSM::findSurroundingVehicles(const MSVehicle& veh, double range, FoeInfoMap& foeCollector) {
3349  if (!veh.isOnRoad()) {
3350  return;
3351  }
3352 #ifdef DEBUG_SSM_SURROUNDING
3353 
3354  gDebugFlag3 = DEBUG_COND_FIND(veh);
3355  if (gDebugFlag3) {
3356  std::cout << SIMTIME << " Looking for surrounding vehicles for ego vehicle '" << veh.getID()
3357  << "' on edge '" << veh.getLane()->getEdge().getID()
3358  << "'."
3359  << "\nVehicle's best lanes = " << ::toString(veh.getBestLanesContinuation())
3360  << std::endl;
3361  }
3362 #endif
3363 
3364 
3365  // The requesting vehicle's current route
3366  // XXX: Restriction to route scanning may have to be generalized to scanning of possible continuations when
3367  // considering situations involving sudden route changes. See also the definition of the EncounterTypes.
3368  // A second problem is that following situations on deviating routes may result in closing encounters
3369  // too early if a leading foe is not traced on its new lane. (see test 'foe_leader_deviating_routes')
3370 
3371  // If veh is on an internal edge, the edgeIter points towards the last edge before the junction
3372  //ConstMSEdgeVector::const_iterator edgeIter = veh.getCurrentRouteEdge();
3373  //assert(*edgeIter != 0);
3374 
3375  // Best continuation lanes for the ego vehicle
3376  std::vector<MSLane*> egoBestLanes = veh.getBestLanesContinuation();
3377 
3378  // current lane in loop below
3379  const MSLane* lane = veh.getLane();
3380  const MSEdge* egoEdge = &(lane->getEdge());
3381  const bool isOpposite = veh.getLaneChangeModel().isOpposite();
3382  std::vector<MSLane*>::const_iterator laneIter = egoBestLanes.begin();
3383  assert(lane->isInternal() || lane == *laneIter || isOpposite);
3384  assert(lane != 0);
3385  if (lane->isInternal() && egoBestLanes[0] != nullptr) { // outdated BestLanes, see #11336
3386  return;
3387  }
3388 
3389  if (isOpposite) {
3390  for (int i = 0; i < (int)egoBestLanes.size(); i++) {
3391  if (egoBestLanes[i] != nullptr && egoBestLanes[i]->getEdge().getOppositeEdge() != nullptr) {
3392  egoBestLanes[i] = egoBestLanes[i]->getEdge().getOppositeEdge()->getLanes().back();
3393  }
3394  }
3395  }
3396 
3397  // next non-internal lane on the route
3398  const MSLane* nextNonInternalLane = nullptr;
3399 
3400  const MSEdge* edge; // current edge in loop below
3401 
3402  // Init pos with vehicle's current position. Below pos is set to zero to denote
3403  // the beginning position of the currently considered edge
3404  double pos = veh.getPositionOnLane();
3405  // remainingDownstreamRange is the range minus the distance that is already scanned downstream along the vehicles route
3406  double remainingDownstreamRange = range;
3407  // distToConflictLane is the distance of the ego vehicle to the start of the currently considered potential conflict lane (can be negative for its current lane)
3408  double distToConflictLane = isOpposite ? pos - veh.getLane()->getLength() : -pos;
3409 
3410  // remember already visited lanes (no matter whether internal or not) and junctions downstream along the route
3411  std::set<const MSLane*> seenLanes;
3412  std::set<const MSJunction*> routeJunctions;
3413 
3414  // Starting points for upstream scans to be executed after downstream scan is complete.
3415  // Holds pairs (starting edge, starting position on edge)
3416  std::vector<UpstreamScanStartInfo> upstreamScanStartPositions;
3417 
3418 
3419  // if the current edge is internal, collect all vehicles from the junction and within upstream range (except on the vehicles own edge),
3420  // this is analogous to the code treating junctions in the loop below. Note that the distance on the junction itself is not included into
3421  // range, so vehicles farther away than range can be collected, too.
3422  if (lane->isInternal()) {
3423  edge = &(lane->getEdge());
3424 
3425 #ifdef DEBUG_SSM_SURROUNDING
3426  if (gDebugFlag3) {
3427  std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' is on internal edge " << edge->getID() << "'." << std::endl;
3428 // << "Previous edge of its route: '" << (*edgeIter)->getID() << "'" << std::endl;
3429  }
3430 #endif
3431 
3432  assert(edge->getToJunction() == edge->getFromJunction());
3433 
3434  const MSJunction* junction = edge->getToJunction();
3435  // Collect vehicles on the junction
3436  getVehiclesOnJunction(junction, lane, distToConflictLane, lane, foeCollector, seenLanes);
3437  routeJunctions.insert(junction);
3438 
3439  // Collect vehicles on incoming edges.
3440  // Note that this includes the previous edge on the ego vehicle's route.
3441  // (The distance on the current internal edge is ignored)
3442  const ConstMSEdgeVector& incoming = junction->getIncoming();
3443  for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
3444  if ((*ei)->isInternal()) {
3445  continue;
3446  }
3447  // Upstream range is taken from the vehicle's back
3448  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range + veh.getLength(), distToConflictLane, lane));
3449  }
3450 
3451 // // Take into account internal distance covered on the current lane
3452 // (commented out, because upstream scanning disregards internal lanes on the last scanned junction
3453 // -- this makes the scanning symmetric between leader and follower)
3454 // remainingDownstreamRange -= lane->getLength() - pos;
3455 
3456  // Take into account non-internal lengths until next non-internal lane
3457  MSLink* link = lane->getLinkCont()[0];
3458  remainingDownstreamRange -= link->getInternalLengthsAfter();
3459  distToConflictLane += lane->getLength() + link->getInternalLengthsAfter();
3460 
3461  // The next non-internal lane
3462  pos = 0.;
3463  lane = *(++laneIter);
3464  edge = &lane->getEdge();
3465  } else {
3466  // Collect all vehicles in range behind ego vehicle
3467  edge = &(lane->getEdge());
3468  double edgeLength = edge->getLength();
3469  double startScanPos = std::min(pos + remainingDownstreamRange, edgeLength);
3470  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, startScanPos, std::max(0., startScanPos - pos + range + veh.getLength()), distToConflictLane, lane));
3471  }
3472 
3473  assert(lane != 0);
3474  assert(!lane->isInternal());
3475 
3476  // Advance downstream the ego vehicle's route for distance 'range'.
3477  // Collect all vehicles on the traversed Edges and on incoming edges at junctions
3478  // and starting points for upstream vehicle collection strated below after downstream scan.
3479  while (remainingDownstreamRange > 0.) {
3480 
3481 #ifdef DEBUG_SSM_SURROUNDING
3482  if (gDebugFlag3) {
3483  std::cout << SIMTIME << " Scanning downstream for vehicle '" << veh.getID() << "' on lane '" << veh.getLane()->getID() << "', position=" << pos << ".\n"
3484  << "Considering edge '" << edge->getID() << "' Remaining downstream range = " << remainingDownstreamRange
3485  << "\nbestLanes=" << ::toString(egoBestLanes) << "\n"
3486  << std::endl;
3487  }
3488 #endif
3489  assert(!edge->isInternal());
3490  assert(!lane->isInternal());
3491  assert(pos == 0 || lane == veh.getLane());
3492  if (pos + remainingDownstreamRange < lane->getLength()) {
3493  // scan range ends on this lane
3494  if (edge->getID() != egoEdge->getID()) {
3495  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, pos + remainingDownstreamRange, remainingDownstreamRange, distToConflictLane, lane));
3496  }
3497  // scanned required downstream range
3498  break;
3499  } else {
3500  // Also need to scan area that reaches beyond the lane
3501  // Collecting vehicles on non-internal edge ahead
3502  if (edge->getID() != egoEdge->getID()) {
3503  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(edge, edge->getLength(), edge->getLength() - pos, distToConflictLane, lane));
3504  }
3505  // account for scanned distance on lane
3506  remainingDownstreamRange -= lane->getLength() - pos;
3507  distToConflictLane += lane->getLength();
3508  pos = 0.;
3509 
3510  // proceed to next non-internal lane
3511  ++laneIter;
3512  assert(laneIter == egoBestLanes.end() || *laneIter != 0);
3513 
3514  // If the vehicle's best lanes go on, collect vehicles on the upcoming junction
3515  if (laneIter != egoBestLanes.end()) {
3516  // Upcoming junction
3517  const MSJunction* junction;
3518  if (isOpposite) {
3519  junction = lane->getParallelOpposite()->getEdge().getToJunction();
3520  } else {
3521  junction = lane->getEdge().getToJunction();
3522  }
3523 
3524 
3525  // Find connection for ego on the junction
3526  nextNonInternalLane = *laneIter;
3527  const MSLink* link = lane->getLinkTo(nextNonInternalLane);
3528  if (isOpposite && link == nullptr) {
3529  link = nextNonInternalLane->getLinkTo(lane);
3530  if (link == nullptr) {
3531  link = lane->getParallelOpposite()->getLinkTo(nextNonInternalLane);
3532  }
3533  }
3534  if (link == nullptr) {
3535  // disconnected route
3536  break;
3537  }
3538 
3539  // First lane of the connection
3540  lane = link->getViaLane();
3541  if (lane == nullptr) {
3542  // link without internal lane
3543  lane = nextNonInternalLane;
3544  edge = &(lane->getEdge());
3545  if (seenLanes.count(lane) == 0) {
3546  seenLanes.insert(lane);
3547  continue;
3548  } else {
3549  break;
3550  }
3551  }
3552 
3553  if (seenLanes.count(lane) == 0) {
3554  // Collect vehicles on the junction, if it wasn't considered already
3555  getVehiclesOnJunction(junction, lane, distToConflictLane, lane, foeCollector, seenLanes);
3556  routeJunctions.insert(junction);
3557 
3558  // Collect vehicles on incoming edges (except the last edge, where we already collected). Use full range.
3559  if (isOpposite) {
3560  // look for vehicles that are also driving on the opposite side behind ego
3561  const ConstMSEdgeVector& outgoing = junction->getOutgoing();
3562  for (ConstMSEdgeVector::const_iterator ei = outgoing.begin(); ei != outgoing.end(); ++ei) {
3563  if (*ei == edge || (*ei)->isInternal()) {
3564  continue;
3565  }
3566  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range, distToConflictLane, lane));
3567  }
3568  } else {
3569  const ConstMSEdgeVector& incoming = junction->getIncoming();
3570  for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
3571  if (*ei == edge || (*ei)->isInternal()) {
3572  continue;
3573  }
3574  upstreamScanStartPositions.push_back(UpstreamScanStartInfo(*ei, (*ei)->getLength(), range, distToConflictLane, lane));
3575  }
3576  }
3577 
3578  // account for scanned distance on junction
3579  double linkLength = link->getInternalLengthsAfter();
3580  remainingDownstreamRange -= linkLength;
3581  distToConflictLane += linkLength;
3582 #ifdef DEBUG_SSM_SURROUNDING
3583  if (gDebugFlag3) {
3584  std::cout << " Downstream Scan for vehicle '" << veh.getID() << "' proceeded over junction '" << junction->getID()
3585  << "',\n linkLength=" << linkLength << ", remainingDownstreamRange=" << remainingDownstreamRange
3586  << std::endl;
3587  }
3588 #endif
3589 
3590  // update ego's lane to next non internal edge
3591  lane = nextNonInternalLane;
3592  edge = &(lane->getEdge());
3593  } else {
3594 #ifdef DEBUG_SSM_SURROUNDING
3595  if (gDebugFlag3) {
3596  std::cout << " Downstream Scan for vehicle '" << veh.getID() << "' stops at lane '" << lane->getID()
3597  << "', which has already been scanned."
3598  << std::endl;
3599  }
3600 #endif
3601  break;
3602  }
3603  } else {
3604  // Further vehicle path unknown, break search
3605  break;
3606  }
3607  }
3608  }
3609  // add junction from the end of the route
3610  routeJunctions.insert(lane->getEdge().getToJunction());
3611 
3612 
3613  // Scan upstream branches from collected starting points
3614  for (UpstreamScanStartInfo& i : upstreamScanStartPositions) {
3615  getUpstreamVehicles(i, foeCollector, seenLanes, routeJunctions);
3616  }
3617 
3618 #ifdef DEBUG_SSM_SURROUNDING
3619  if (gDebugFlag3) {
3620  for (std::pair<const MSVehicle*, FoeInfo*> foeInfo : foeCollector) {
3621  std::cout << " foe " << foeInfo.first->getID() << " conflict at " << foeInfo.second->egoConflictLane->getID() << " egoDist " << foeInfo.second->egoDistToConflictLane << std::endl;
3622  }
3623  }
3624 #endif
3625 
3626  // remove ego vehicle
3627  const auto& it = foeCollector.find(&veh);
3628  if (it != foeCollector.end()) {
3629  delete it->second;
3630  foeCollector.erase(it);
3631  }
3632  gDebugFlag3 = false;
3633 }
3634 
3635 
3636 void
3637 MSDevice_SSM::getUpstreamVehicles(const UpstreamScanStartInfo& scanStart, FoeInfoMap& foeCollector, std::set<const MSLane*>& seenLanes, const std::set<const MSJunction*>& routeJunctions) {
3638 #ifdef DEBUG_SSM_SURROUNDING
3639  if (gDebugFlag3) {
3640  std::cout << SIMTIME << " getUpstreamVehicles() for edge '" << scanStart.edge->getID() << "'"
3641  << " egoConflictLane=" << scanStart.egoConflictLane->getID()
3642  << " pos = " << scanStart.pos << " range = " << scanStart.range
3643  << std::endl;
3644  }
3645 #endif
3646  if (scanStart.range <= 0) {
3647  return;
3648  }
3649 
3650  // Collect vehicles on the given edge with position in [pos-range,pos]
3651  for (MSLane* lane : scanStart.edge->getLanes()) {
3652  if (seenLanes.find(lane) != seenLanes.end()) {
3653  return;
3654  }
3655 #ifdef DEBUG_SSM_SURROUNDING
3656  int foundCount = 0;
3657 #endif
3658  for (MSVehicle* const veh : lane->getVehiclesSecure()) {
3659  if (foeCollector.find(veh) != foeCollector.end()) {
3660  // vehicle already recognized, earlier recognized conflict has priority
3661  continue;
3662  }
3663  if (veh->getPositionOnLane() - veh->getLength() <= scanStart.pos && veh->getPositionOnLane() >= scanStart.pos - scanStart.range) {
3664 #ifdef DEBUG_SSM_SURROUNDING
3665  if (gDebugFlag3) {
3666  std::cout << "\t" << veh->getID() << "\n";
3667  }
3668  foundCount++;
3669 #endif
3670  FoeInfo* c = new FoeInfo(); // c is deleted in updateEncounter()
3672  c->egoConflictLane = scanStart.egoConflictLane;
3673  foeCollector[veh] = c;
3674  }
3675  }
3676  lane->releaseVehicles();
3677 
3678 #ifdef DEBUG_SSM_SURROUNDING
3679  if (gDebugFlag3 && foundCount > 0) {
3680  std::cout << "\t" << lane->getID() << ": Found " << foundCount << "\n";
3681  }
3682 #endif
3683  seenLanes.insert(lane);
3684  }
3685 
3686 #ifdef DEBUG_SSM_SURROUNDING
3687  if (gDebugFlag3) {
3688  std::cout << std::endl;
3689  }
3690 #endif
3691 
3692  // TODO: Gather vehicles from opposite direction. This should happen in any case, where opposite direction overtaking is possible.
3693  // If it isn't it might still be nicer to trace oncoming vehicles for the resulting trajectories in the encounters
3694  // if (edge->hasOpposite...)
3695 
3696  if (scanStart.range <= scanStart.pos) {
3697  return;
3698  }
3699 
3700  // Here we have: range > pos, i.e. we proceed collecting vehicles on preceding edges
3701  double remainingRange = scanStart.range - scanStart.pos;
3702 
3703  // Junction representing the origin of 'edge'
3704  const MSJunction* junction = scanStart.edge->getFromJunction();
3705 
3706  // stop if upstream search reaches the ego route
3707  if (routeJunctions.find(junction) != routeJunctions.end()) {
3708  return;
3709  }
3710 
3711  // Collect vehicles from incoming edges of the junction
3712  int incomingEdgeCount = 0;
3713  if (!scanStart.edge->isInternal()) {
3714  // collect vehicles on preceding junction (for internal edges this is already done in caller,
3715  // i.e. findSurroundingVehicles() or the recursive call from getUpstreamVehicles())
3716 
3717  // Collect vehicles on the junction, if it wasn't considered already
3718  // run vehicle collection for all incoming connections
3719  for (MSLane* const internalLane : junction->getInternalLanes()) {
3720  if (internalLane->getEdge().getSuccessors()[0]->getID() == scanStart.edge->getID()) {
3721  getVehiclesOnJunction(junction, internalLane, scanStart.egoDistToConflictLane, scanStart.egoConflictLane, foeCollector, seenLanes);
3722  incomingEdgeCount++;
3723  }
3724  }
3725  }
3726  // Collect vehicles from incoming edges from the junction representing the origin of 'edge'
3727  if (incomingEdgeCount > 0) {
3728  for (const MSEdge* inEdge : junction->getIncoming()) {
3729  if (inEdge->isInternal() || inEdge->isCrossing()) {
3730  continue;
3731  }
3732  bool skip = false;
3733  for (MSLane* const lane : inEdge->getLanes()) {
3734  if (seenLanes.find(lane) != seenLanes.end()) {
3735  skip = true;
3736  break;
3737  }
3738  }
3739  if (skip) {
3740 #ifdef DEBUG_SSM_SURROUNDING
3741  //if (gDebugFlag3) std::cout << "Scan skips already seen edge " << (*ei)->getID() << "\n";
3742 #endif
3743  continue;
3744  }
3745 
3746  // XXX the length may be wrong if there are parallel internal edges for different vClasses
3747  double distOnJunction = scanStart.edge->isInternal() ? 0. : inEdge->getInternalFollowingLengthTo(scanStart.edge, SVC_IGNORING);
3748  if (distOnJunction >= remainingRange) {
3749 #ifdef DEBUG_SSM_SURROUNDING
3750  //if (gDebugFlag3) std::cout << "Scan stops on junction (between " << inEdge->getID() << " and " << scanStart.edge->getID() << ") at rel. dist " << distOnJunction << "\n";
3751 #endif
3752  continue;
3753  }
3754  // account for vehicles on the predecessor edge
3755  UpstreamScanStartInfo nextInfo(inEdge, inEdge->getLength(), remainingRange - distOnJunction, scanStart.egoDistToConflictLane, scanStart.egoConflictLane);
3756  getUpstreamVehicles(nextInfo, foeCollector, seenLanes, routeJunctions);
3757  }
3758  }
3759 }
3760 
3761 
3762 void
3763 MSDevice_SSM::getVehiclesOnJunction(const MSJunction* junction, const MSLane* const egoJunctionLane, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector, std::set<const MSLane*>& seenLanes) {
3764 #ifdef DEBUG_SSM_SURROUNDING
3765  if (gDebugFlag3) {
3766  std::cout << SIMTIME << " getVehiclesOnJunction() for junction '" << junction->getID()
3767  << "' egoJunctionLane=" << Named::getIDSecure(egoJunctionLane)
3768  << "\nFound vehicles:"
3769  << std::endl;
3770  }
3771 #endif
3772  // FoeInfo creation
3773  auto collectFoeInfos = [&](const MSLane::VehCont & vehicles) {
3774  for (MSVehicle* const veh : vehicles) {
3775  if (foeCollector.find(veh) != foeCollector.end()) {
3776  delete foeCollector[veh];
3777  }
3778  FoeInfo* c = new FoeInfo();
3779  c->egoConflictLane = egoConflictLane;
3780  c->egoDistToConflictLane = egoDistToConflictLane;
3781  foeCollector[veh] = c;
3782 #ifdef DEBUG_SSM_SURROUNDING
3783  if (gDebugFlag3) {
3784  std::cout << "\t" << veh->getID() << " egoConflictLane=" << Named::getIDSecure(egoConflictLane) << "\n";
3785  }
3786 #endif
3787  }
3788  };
3789 
3790  // stop condition
3791  if (seenLanes.find(egoJunctionLane) != seenLanes.end() || egoJunctionLane->getEdge().isCrossing()) {
3792  return;
3793  }
3794 
3795  auto scanInternalLane = [&](const MSLane * lane) {
3796  const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
3797 
3798  // Add FoeInfos (XXX: for some situations, a vehicle may be collected twice. Then the later finding overwrites the earlier in foeCollector.
3799  // This could lead to neglecting a conflict when determining foeConflictLane later.) -> TODO: test with twice intersecting routes
3800  collectFoeInfos(vehicles);
3801 
3802  lane->releaseVehicles();
3803 
3804  // check additional internal link upstream in the same junction
3805  // TODO: getEntryLink returns nullptr
3806  if (lane->getCanonicalPredecessorLane()->isInternal()) {
3807  lane = lane->getCanonicalPredecessorLane();
3808 
3809  // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
3810  assert(!lane->getEntryLink()->fromInternalLane());
3811 
3812  // collect vehicles
3813  const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
3814  // Add FoeInfos for the first internal lane
3815  collectFoeInfos(vehicles2);
3816  lane->releaseVehicles();
3817  }
3818 
3819 
3820  // If there is an internal continuation lane, also collect vehicles on that lane
3821  if (lane->getLinkCont().size() > 1 && lane->getLinkCont()[0]->getViaLane() != nullptr) {
3822  // There's a second internal lane of the connection
3823  lane = lane->getLinkCont()[0]->getViaLane();
3824  // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
3825  assert(lane->getLinkCont().size() == 0 || lane->getLinkCont()[0]->getViaLane() == 0);
3826 
3827  // collect vehicles
3828  const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
3829  // Add FoeInfos for the first internal lane
3830  collectFoeInfos(vehicles2);
3831  lane->releaseVehicles();
3832  }
3833 
3834  };
3835 
3836  // Collect vehicles on conflicting lanes
3837  const MSLink* entryLink = egoJunctionLane->getEntryLink();
3838  if (entryLink->getFoeLanes().size() > 0) {
3839 
3840  const std::vector<MSLane*> foeLanes = junction->getFoeInternalLanes(entryLink);
3841  for (MSLane* lane : foeLanes) {
3842  if (seenLanes.find(lane) != seenLanes.end()) {
3843  continue;
3844  }
3845  scanInternalLane(lane);
3846  seenLanes.insert(lane);
3847  }
3848  }
3849  scanInternalLane(egoJunctionLane);
3850 
3851 #ifdef DEBUG_SSM_SURROUNDING
3852  if (gDebugFlag3) {
3853  std::cout << std::endl;
3854  }
3855 #endif
3856 }
3857 
3858 
3859 
3860 void
3862  // This is called once at vehicle removal.
3863  // Also: flush myOutputFile? Or is this done automatically?
3864  // myOutputFile->closeTag();
3865 }
3866 
3867 // ---------------------------------------------------------------------------
3868 // Static parameter load helpers
3869 // ---------------------------------------------------------------------------
3870 std::string
3871 MSDevice_SSM::getOutputFilename(const SUMOVehicle& v, std::string deviceID) {
3873  std::string file = deviceID + ".xml";
3874  if (v.getParameter().hasParameter("device.ssm.file")) {
3875  try {
3876  file = v.getParameter().getParameter("device.ssm.file", file);
3877  } catch (...) {
3878  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.measures'."), v.getParameter().getParameter("device.ssm.file", file));
3879  }
3880  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.file")) {
3881  try {
3882  file = v.getVehicleType().getParameter().getParameter("device.ssm.file", file);
3883  } catch (...) {
3884  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.measures'."), v.getVehicleType().getParameter().getParameter("device.ssm.file", file));
3885  }
3886  } else {
3887  file = oc.getString("device.ssm.file") == "" ? file : oc.getString("device.ssm.file");
3888  if (oc.isDefault("device.ssm.file") && (myIssuedParameterWarnFlags & SSM_WARN_FILE) == 0) {
3889  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.file'. Using default of '%'."), v.getID(), file);
3891  }
3892  }
3893  if (OptionsCont::getOptions().isSet("configuration-file")) {
3894  file = FileHelpers::checkForRelativity(file, OptionsCont::getOptions().getString("configuration-file"));
3895  try {
3896  file = StringUtils::urlDecode(file);
3897  } catch (NumberFormatException& e) {
3898  WRITE_WARNING(toString(e.what()) + " when trying to decode filename '" + file + "'.");
3899  }
3900  }
3901  return file;
3902 }
3903 
3904 bool
3907  bool useGeo = false;
3908  if (v.getParameter().hasParameter("device.ssm.geo")) {
3909  try {
3910  useGeo = StringUtils::toBool(v.getParameter().getParameter("device.ssm.geo", "no"));
3911  } catch (...) {
3912  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.geo'."), v.getParameter().getParameter("device.ssm.geo", "no"));
3913  }
3914  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.geo")) {
3915  try {
3916  useGeo = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no"));
3917  } catch (...) {
3918  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.geo'."), v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no"));
3919  }
3920  } else {
3921  useGeo = oc.getBool("device.ssm.geo");
3922  if (oc.isDefault("device.ssm.geo") && (myIssuedParameterWarnFlags & SSM_WARN_GEO) == 0) {
3923  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.geo'. Using default of '%'."), v.getID(), toString(useGeo));
3925  }
3926  }
3927  return useGeo;
3928 }
3929 
3930 
3931 bool
3934  bool writePos = false;
3935  if (v.getParameter().hasParameter("device.ssm.write-positions")) {
3936  try {
3937  writePos = StringUtils::toBool(v.getParameter().getParameter("device.ssm.write-positions", "no"));
3938  } catch (...) {
3939  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.write-positions'."), v.getParameter().getParameter("device.ssm.write-positions", "no"));
3940  }
3941  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.write-positions")) {
3942  try {
3943  writePos = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.write-positions", "no"));
3944  } catch (...) {
3945  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.write-positions'."), v.getVehicleType().getParameter().getParameter("device.ssm.write-positions", "no"));
3946  }
3947  } else {
3948  writePos = oc.getBool("device.ssm.write-positions");
3949  if (oc.isDefault("device.ssm.write-positions") && (myIssuedParameterWarnFlags & SSM_WARN_POS) == 0) {
3950  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.write-positions'. Using default of '%'."), v.getID(), toString(writePos));
3952  }
3953  }
3954  return writePos;
3955 }
3956 
3957 
3958 bool
3961  bool writeLanesPos = false;
3962  if (v.getParameter().hasParameter("device.ssm.write-lane-positions")) {
3963  try {
3964  writeLanesPos = StringUtils::toBool(v.getParameter().getParameter("device.ssm.write-lane-positions", "no"));
3965  } catch (...) {
3966  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.write-lane-positions'."), v.getParameter().getParameter("device.ssm.write-lane-positions", "no"));
3967  }
3968  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.write-lane-positions")) {
3969  try {
3970  writeLanesPos = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.write-lane-positions", "no"));
3971  } catch (...) {
3972  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.write-lane-positions'."), v.getVehicleType().getParameter().getParameter("device.ssm.write-lane-positions", "no"));
3973  }
3974  } else {
3975  writeLanesPos = oc.getBool("device.ssm.write-lane-positions");
3976  if (oc.isDefault("device.ssm.write-lane-positions") && (myIssuedParameterWarnFlags & SSM_WARN_LANEPOS) == 0) {
3977  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.write-positions'. Using default of '%'."), v.getID(), toString(writeLanesPos));
3979  }
3980  }
3981  return writeLanesPos;
3982 }
3983 
3984 
3985 bool
3986 MSDevice_SSM::filterByConflictType(const SUMOVehicle& v, std::string deviceID, std::vector<int>& conflictTypes) {
3988  std::string typeString = "";
3989  if (v.getParameter().hasParameter("device.ssm.exclude-conflict-types")) {
3990  try {
3991  typeString = v.getParameter().getParameter("device.ssm.exclude-conflict-types", "");
3992  } catch (...) {
3993  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.conflict-order'."), v.getParameter().getParameter("device.ssm.exclude-conflict-types", ""));
3994  }
3995  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.exclude-conflict-types")) {
3996  try {
3997  typeString = v.getVehicleType().getParameter().getParameter("device.ssm.exclude-conflict-types", "");
3998  } catch (...) {
3999  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.conflict-order'."), v.getVehicleType().getParameter().getParameter("device.ssm.exclude-conflict-types", ""));
4000  }
4001  } else {
4002  typeString = oc.getString("device.ssm.exclude-conflict-types");
4003  if (oc.isDefault("device.ssm.exclude-conflict-types") && (myIssuedParameterWarnFlags & SSM_WARN_CONFLICTFILTER) == 0) {
4004  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.exclude-conflict-types'. Using default of '%'."), v.getID(), typeString);
4006  }
4007  }
4008  // Check retrieved conflict keys
4010  st = (typeString.find(",") != std::string::npos) ? StringTokenizer(typeString, ",") : StringTokenizer(typeString);
4011  std::vector<std::string> found = st.getVector();
4012  std::set<int> confirmed;
4013  for (std::vector<std::string>::const_iterator i = found.begin(); i != found.end(); ++i) {
4014  if (*i == "foe") {
4015  confirmed.insert(FOE_ENCOUNTERTYPES.begin(), FOE_ENCOUNTERTYPES.end());
4016  } else if (*i == "ego") {
4017  confirmed.insert(EGO_ENCOUNTERTYPES.begin(), EGO_ENCOUNTERTYPES.end());
4018  } else if (encounterToString(static_cast<EncounterType>(std::stoi(*i))) != "UNKNOWN") {
4019  confirmed.insert(std::stoi(*i));
4020  } else {
4021  // Given identifier is unknown
4022  WRITE_ERRORF(TL("SSM order filter '%' is not supported. Aborting construction of SSM device '%'."), *i, deviceID);
4023  return false;
4024  }
4025  }
4026  conflictTypes.insert(conflictTypes.end(), confirmed.begin(), confirmed.end());
4027  return true;
4028 }
4029 
4030 
4031 double
4034  double range = -INVALID_DOUBLE;
4035  if (v.getParameter().hasParameter("device.ssm.range")) {
4036  try {
4037  range = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.range", ""));
4038  } catch (...) {
4039  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.range'."), v.getParameter().getParameter("device.ssm.range", ""));
4040  }
4041  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.range")) {
4042  try {
4043  range = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.range", ""));
4044  } catch (...) {
4045  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.range'."), v.getVehicleType().getParameter().getParameter("device.ssm.range", ""));
4046  }
4047  } else {
4048  range = oc.getFloat("device.ssm.range");
4049  if (oc.isDefault("device.ssm.range") && (myIssuedParameterWarnFlags & SSM_WARN_RANGE) == 0) {
4050  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.range'. Using default of '%'."), v.getID(), toString(range));
4052  }
4053  }
4054  return range;
4055 }
4056 
4057 
4058 double
4061  double prt = 1;
4062  if (v.getParameter().hasParameter("device.ssm.mdrac.prt")) {
4063  try {
4064  prt = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.mdrac.prt", ""));
4065  } catch (...) {
4066  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.mdrac.prt'."), v.getParameter().getParameter("device.ssm.mdrac.prt", ""));
4067  }
4068  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.mdrac.prt")) {
4069  try {
4070  prt = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.mdrac.prt", ""));
4071  } catch (...) {
4072  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.mdrac.prt'."), v.getVehicleType().getParameter().getParameter("device.ssm.mdrac.prt", ""));
4073  }
4074  } else {
4075  prt = oc.getFloat("device.ssm.mdrac.prt");
4076  if (oc.isDefault("device.ssm.mdrac.prt") && (myIssuedParameterWarnFlags & SSM_WARN_MDRAC_PRT) == 0) {
4077  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.mdrac.prt'. Using default of '%'."), v.getID(), toString(prt));
4079  }
4080  }
4081  return prt;
4082 }
4083 
4084 
4085 
4086 
4087 double
4090  double extraTime = INVALID_DOUBLE;
4091  if (v.getParameter().hasParameter("device.ssm.extratime")) {
4092  try {
4093  extraTime = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.extratime", ""));
4094  } catch (...) {
4095  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.extratime'."), v.getParameter().getParameter("device.ssm.extratime", ""));
4096  }
4097  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.extratime")) {
4098  try {
4099  extraTime = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.extratime", ""));
4100  } catch (...) {
4101  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.extratime'."), v.getVehicleType().getParameter().getParameter("device.ssm.extratime", ""));
4102  }
4103  } else {
4104  extraTime = oc.getFloat("device.ssm.extratime");
4105  if (oc.isDefault("device.ssm.extratime") && (myIssuedParameterWarnFlags & SSM_WARN_EXTRATIME) == 0) {
4106  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.extratime'. Using default of '%'."), v.getID(), toString(extraTime));
4108  }
4109  }
4110  if (extraTime < 0.) {
4111  extraTime = DEFAULT_EXTRA_TIME;
4112  WRITE_WARNINGF(TL("Negative (or no) value encountered for vehicle parameter 'device.ssm.extratime' in vehicle '%' using default value % instead."), v.getID(), ::toString(extraTime));
4113  }
4114  return extraTime;
4115 }
4116 
4117 
4118 bool
4121  bool trajectories = false;
4122  if (v.getParameter().hasParameter("device.ssm.trajectories")) {
4123  try {
4124  trajectories = StringUtils::toBool(v.getParameter().getParameter("device.ssm.trajectories", "no"));
4125  } catch (...) {
4126  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.trajectories'."), v.getParameter().getParameter("device.ssm.trajectories", "no"));
4127  }
4128  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.trajectories")) {
4129  try {
4130  trajectories = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no"));
4131  } catch (...) {
4132  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.trajectories'."), v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no"));
4133  }
4134  } else {
4135  trajectories = oc.getBool("device.ssm.trajectories");
4136  if (oc.isDefault("device.ssm.trajectories") && (myIssuedParameterWarnFlags & SSM_WARN_TRAJECTORIES) == 0) {
4137  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.trajectories'. Using default of '%'."), v.getID(), toString(trajectories));
4139  }
4140  }
4141  return trajectories;
4142 }
4143 
4144 
4145 bool
4146 MSDevice_SSM::getMeasuresAndThresholds(const SUMOVehicle& v, std::string deviceID, std::map<std::string, double>& thresholds) {
4148 
4149  // Measures
4150  std::string measures_str = "";
4151  if (v.getParameter().hasParameter("device.ssm.measures")) {
4152  try {
4153  measures_str = v.getParameter().getParameter("device.ssm.measures", "");
4154  } catch (...) {
4155  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.measures'."), v.getParameter().getParameter("device.ssm.measures", ""));
4156  }
4157  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.measures")) {
4158  try {
4159  measures_str = v.getVehicleType().getParameter().getParameter("device.ssm.measures", "");
4160  } catch (...) {
4161  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.measures'."), v.getVehicleType().getParameter().getParameter("device.ssm.measures", ""));
4162  }
4163  } else {
4164  measures_str = oc.getString("device.ssm.measures");
4165  if (oc.isDefault("device.ssm.measures") && (myIssuedParameterWarnFlags & SSM_WARN_MEASURES) == 0) {
4166  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.measures'. Using default of '%'."), v.getID(), measures_str);
4168  }
4169  }
4170 
4171  // Check retrieved measures
4172  if (measures_str == "") {
4173  WRITE_WARNINGF("No measures specified for ssm device of vehicle '%'. Registering all available SSMs.", v.getID());
4174  measures_str = AVAILABLE_SSMS;
4175  }
4177  std::vector<std::string> available = st.getVector();
4178  st = (measures_str.find(",") != std::string::npos) ? StringTokenizer(measures_str, ",") : StringTokenizer(measures_str);
4179  std::vector<std::string> measures = st.getVector();
4180  for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
4181  if (std::find(available.begin(), available.end(), *i) == available.end()) {
4182  // Given identifier is unknown
4183  WRITE_ERRORF(TL("SSM identifier '%' is not supported. Aborting construction of SSM device '%'."), *i, deviceID);
4184  return false;
4185  }
4186  }
4187 
4188  // Thresholds
4189  std::string thresholds_str = "";
4190  if (v.getParameter().hasParameter("device.ssm.thresholds")) {
4191  try {
4192  thresholds_str = v.getParameter().getParameter("device.ssm.thresholds", "");
4193  } catch (...) {
4194  WRITE_WARNINGF(TL("Invalid value '%' for vehicle parameter 'ssm.thresholds'."), v.getParameter().getParameter("device.ssm.thresholds", ""));
4195  }
4196  } else if (v.getVehicleType().getParameter().hasParameter("device.ssm.thresholds")) {
4197  try {
4198  thresholds_str = v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "");
4199  } catch (...) {
4200  WRITE_WARNINGF(TL("Invalid value '%' for vType parameter 'ssm.thresholds'."), v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", ""));
4201  }
4202  } else {
4203  thresholds_str = oc.getString("device.ssm.thresholds");
4204  if (oc.isDefault("device.ssm.thresholds") && (myIssuedParameterWarnFlags & SSM_WARN_THRESHOLDS) == 0) {
4205  WRITE_MESSAGEF(TL("Vehicle '%' does not supply vehicle parameter 'device.ssm.thresholds'. Using default of '%'."), v.getID(), thresholds_str);
4207  }
4208  }
4209 
4210  // Parse vector of doubles from threshold_str
4211  int count = 0;
4212  if (thresholds_str != "") {
4213  st = (thresholds_str.find(",") != std::string::npos) ? StringTokenizer(thresholds_str, ",") : StringTokenizer(thresholds_str);
4214  while (count < (int)measures.size() && st.hasNext()) {
4215  double thresh = StringUtils::toDouble(st.next());
4216  thresholds.insert(std::make_pair(measures[count], thresh));
4217  ++count;
4218  }
4219  if (thresholds.size() < measures.size() || st.hasNext()) {
4220  WRITE_ERRORF(TL("Given list of thresholds ('%') is not of the same size as the list of measures ('%').\nPlease specify exactly one threshold for each measure."), thresholds_str, measures_str);
4221  return false;
4222  }
4223  } else {
4224  // assume default thresholds if none are given
4225  for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
4226  if (*i == "TTC") {
4227  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TTC));
4228  } else if (*i == "DRAC") {
4229  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_DRAC));
4230  } else if (*i == "MDRAC") {
4231  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_MDRAC));
4232  } else if (*i == "PET") {
4233  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PET));
4234  } else if (*i == "PPET") {
4235  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PPET));
4236  } else if (*i == "BR") {
4237  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_BR));
4238  } else if (*i == "SGAP") {
4239  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_SGAP));
4240  } else if (*i == "TGAP") {
4241  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TGAP));
4242  } else {
4243  WRITE_ERROR("Unknown SSM identifier '" + (*i) + "'. Aborting construction of ssm device."); // should never occur
4244  return false;
4245  }
4246  }
4247  }
4248  return true;
4249 }
4250 
4251 
4252 std::string
4253 MSDevice_SSM::getParameter(const std::string& key) const {
4254  if (key == "minTTC" && !myComputeTTC) {
4255  throw InvalidArgument("Measure TTC is not tracked by ssm device");
4256  }
4257  if (key == "maxDRAC" && !myComputeDRAC) {
4258  throw InvalidArgument("Measure DRAC is not tracked by ssm device");
4259  }
4260  if (key == "maxMDRAC" && !myComputeMDRAC) {
4261  throw InvalidArgument("Measure MDRAC is not tracked by ssm device");
4262  }
4263  if (key == "minPET" && !myComputePET) {
4264  throw InvalidArgument("Measure PET is not tracked by ssm device");
4265  }
4266  if (key == "minPPET" && !myComputePPET) {
4267  throw InvalidArgument("Measure PPET is not tracked by ssm device");
4268  }
4269  if (key == "minTTC" ||
4270  key == "maxDRAC" ||
4271  key == "maxMDRAC" ||
4272  key == "minPET" ||
4273  key == "minPPET") {
4274  double value = INVALID_DOUBLE;
4275  double minTTC = INVALID_DOUBLE;
4276  double minPET = INVALID_DOUBLE;
4277  double maxDRAC = -INVALID_DOUBLE;
4278  double maxMDRAC = -INVALID_DOUBLE;
4279  double minPPET = INVALID_DOUBLE;
4280  for (Encounter* e : myActiveEncounters) {
4281  minTTC = MIN2(minTTC, e->minTTC.value);
4282  minPET = MIN2(minPET, e->PET.value);
4283  maxDRAC = MAX2(maxDRAC, e->maxDRAC.value);
4284  maxMDRAC = MAX2(maxMDRAC, e->maxMDRAC.value);
4285  minPPET = MIN2(minPPET, e->minPPET.value);
4286  }
4287  if (key == "minTTC") {
4288  value = minTTC;
4289  } else if (key == "maxDRAC") {
4290  value = maxDRAC;
4291  } else if (key == "maxMDRAC") {
4292  value = maxMDRAC;
4293  } else if (key == "minPET") {
4294  value = minPET;
4295  } else if (key == "minPPET") {
4296  value = minPPET;
4297  }
4298  if (fabs(value) == INVALID_DOUBLE) {
4299  return "";
4300  } else {
4301  return toString(value);
4302  }
4303  }
4304  throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'.");
4305 }
4306 
4307 
4308 void
4309 MSDevice_SSM::setParameter(const std::string& key, const std::string& value) {
4310  double doubleValue;
4311  try {
4312  doubleValue = StringUtils::toDouble(value);
4313  } catch (NumberFormatException&) {
4314  throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'.");
4315  }
4316  if (false || key == "foo") {
4317  UNUSED_PARAMETER(doubleValue);
4318  } else {
4319  throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'.");
4320  }
4321 }
4322 
4323 
4324 /****************************************************************************/
#define DEFAULT_THRESHOLD_SGAP
#define AVAILABLE_SSMS
#define DEFAULT_THRESHOLD_BR
#define DEFAULT_THRESHOLD_TGAP
#define DEFAULT_THRESHOLD_PPET
#define DEFAULT_THRESHOLD_DRAC
#define DEFAULT_THRESHOLD_TTC
#define DEFAULT_EXTRA_TIME
#define DEFAULT_THRESHOLD_PET
#define DEFAULT_THRESHOLD_MDRAC
std::ostream & operator<<(std::ostream &out, MSDevice_SSM::EncounterType type)
Nicer output for EncounterType enum.
std::vector< const MSEdge * > ConstMSEdgeVector
Definition: MSEdge.h:74
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:304
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
#define TLF(string,...)
Definition: MsgHandler.h:317
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition: SUMOTime.cpp:69
#define SIMSTEP
Definition: SUMOTime.h:61
#define TS
Definition: SUMOTime.h:42
#define SIMTIME
Definition: SUMOTime.h:62
@ SVC_IGNORING
vehicles ignoring classes
int gPrecision
the precision for floating point outputs
Definition: StdDefs.cpp:26
bool gDebugFlag3
Definition: StdDefs.cpp:39
int gPrecisionGeo
Definition: StdDefs.cpp:27
const double INVALID_DOUBLE
invalid double
Definition: StdDefs.h:64
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static std::string checkForRelativity(const std::string &filename, const std::string &basePath)
Returns the path from a configuration so that it is accessable from the current working directory.
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
The base class for microscopic and mesoscopic vehicles.
Definition: MSBaseVehicle.h:57
double getLength() const
Returns the vehicle's length.
const MSEdge * getEdge() const
Returns the edge the vehicle is currently at.
double getWidth() const
Returns the vehicle's width.
const MSRouteIterator & getCurrentRouteEdge() const
Returns an iterator pointing to the current edge in this vehicles route.
const MSVehicleType & getVehicleType() const
Returns the vehicle's type definition.
const MSRoute & getRoute() const
Returns the current route.
static double passingTime(const double lastPos, const double passedPos, const double currentPos, const double lastSpeed, const double currentSpeed)
Calculates the time at which the position passedPosition has been passed In case of a ballistic updat...
Definition: MSCFModel.cpp:658
static double estimateArrivalTime(double dist, double speed, double maxSpeed, double accel)
Computes the time needed to travel a distance dist given an initial speed and constant acceleration....
Definition: MSCFModel.cpp:450
An encounter is an episode involving two vehicles, which are closer to each other than some specified...
Definition: MSDevice_SSM.h:172
const MSVehicle * foe
Definition: MSDevice_SSM.h:245
ConflictPointInfo minPPET
Definition: MSDevice_SSM.h:296
EncounterType currentType
Definition: MSDevice_SSM.h:249
double foeConflictEntryTime
Times when the foe vehicle entered/left the conflict area. Currently only applies for crossing situat...
Definition: MSDevice_SSM.h:257
std::vector< double > foeDistsToConflict
Evolution of the foe vehicle's distance to the conflict point.
Definition: MSDevice_SSM.h:270
std::vector< double > timeSpan
time points corresponding to the trajectories
Definition: MSDevice_SSM.h:260
std::vector< int > typeSpan
Evolution of the encounter classification (.
Definition: MSDevice_SSM.h:262
bool closingRequested
this flag is set by updateEncounter() or directly in processEncounters(), where encounters are closed...
Definition: MSDevice_SSM.h:300
std::vector< double > TTCspan
All values for TTC.
Definition: MSDevice_SSM.h:278
std::size_t size() const
Returns the number of trajectory points stored.
Definition: MSDevice_SSM.h:218
std::vector< double > MDRACspan
All values for MDRAC.
Definition: MSDevice_SSM.h:282
void resetExtraTime(double value)
resets remainingExtraTime to the given value
ConflictPointInfo maxMDRAC
Definition: MSDevice_SSM.h:294
const MSVehicle * ego
Definition: MSDevice_SSM.h:244
PositionVector conflictPointSpan
Predicted location of the conflict: In case of MERGING and CROSSING: entry point to conflict area for...
Definition: MSDevice_SSM.h:275
ConflictPointInfo maxDRAC
Definition: MSDevice_SSM.h:293
const std::string foeID
Definition: MSDevice_SSM.h:247
ConflictPointInfo minTTC
Definition: MSDevice_SSM.h:292
void countDownExtraTime(double amount)
decreases myRemaingExtraTime by given amount in seconds
Trajectory foeTrajectory
Trajectory of the foe vehicle.
Definition: MSDevice_SSM.h:266
std::vector< double > egoDistsToConflict
Evolution of the ego vehicle's distance to the conflict point.
Definition: MSDevice_SSM.h:268
Trajectory egoTrajectory
Trajectory of the ego vehicle.
Definition: MSDevice_SSM.h:264
double egoConflictEntryTime
Times when the ego vehicle entered/left the conflict area. Currently only applies for crossing situat...
Definition: MSDevice_SSM.h:255
Encounter(const MSVehicle *_ego, const MSVehicle *const _foe, double _begin, double extraTime)
Constructor.
double getRemainingExtraTime() const
returns the remaining extra time
ConflictPointInfo PET
Definition: MSDevice_SSM.h:295
const std::string egoID
Definition: MSDevice_SSM.h:246
std::vector< double > PPETspan
All values for PPET.
Definition: MSDevice_SSM.h:284
void add(double time, EncounterType type, Position egoX, std::string egoLane, double egoLanePos, Position egoV, Position foeX, std::string foeLane, double foeLanePos, Position foeV, Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair< double, double > pet, double ppet, double mdrac)
add a new data point and update encounter type
std::vector< double > DRACspan
All values for DRAC.
Definition: MSDevice_SSM.h:280
A device which collects info on the vehicle trip (mainly on departure and arrival)
Definition: MSDevice_SSM.h:55
bool myComputeDRAC
Definition: MSDevice_SSM.h:759
std::map< const MSVehicle *, FoeInfo * > FoeInfoMap
Definition: MSDevice_SSM.h:367
double myExtraTime
Extra time in seconds to be logged after a conflict is over.
Definition: MSDevice_SSM.h:746
void generateOutput(OutputDevice *tripinfoOut) const
Finalizes output. Called on vehicle removal.
std::pair< std::pair< std::pair< double, Position >, double >, std::string > myMinTGAP
Definition: MSDevice_SSM.h:795
bool myComputeTTC
Flags for switching on / off comutation of different SSMs, derived from myMeasures.
Definition: MSDevice_SSM.h:759
PositionVector myGlobalMeasuresPositions
All values for positions (coordinates)
Definition: MSDevice_SSM.h:780
bool myComputeSGAP
Definition: MSDevice_SSM.h:759
static std::set< std::string > myCreatedOutputFiles
remember which files were created already (don't duplicate xml root-elements)
Definition: MSDevice_SSM.h:808
MSVehicle * myHolderMS
Definition: MSDevice_SSM.h:760
bool mySaveTrajectories
This determines whether the whole trajectories of the vehicles (position, speed, ssms) shall be saved...
Definition: MSDevice_SSM.h:740
bool updateEncounter(Encounter *e, FoeInfo *foeInfo)
Updates the encounter (adds a new trajectory point).
static bool requestsTrajectories(const SUMOVehicle &v)
static bool getMeasuresAndThresholds(const SUMOVehicle &v, std::string deviceID, std::map< std::string, double > &thresholds)
std::string getParameter(const std::string &key) const
try to retrieve the given parameter from this device. Throw exception for unsupported key
bool myComputeTGAP
Definition: MSDevice_SSM.h:759
EncounterType classifyEncounter(const FoeInfo *foeInfo, EncounterApproachInfo &eInfo) const
Classifies the current type of the encounter provided some information on the opponents.
void computeSSMs(EncounterApproachInfo &e) const
Compute current values of the logged SSMs (myMeasures) for the given encounter 'e' and update 'e' acc...
static void buildVehicleDevices(SUMOVehicle &v, std::vector< MSVehicleDevice * > &into)
Build devices for the given vehicle, if needed.
void writeOutConflict(Encounter *e)
EncounterType
Different types of encounters corresponding to relative positions of the vehicles....
Definition: MSDevice_SSM.h:64
@ ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:97
@ ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:103
@ ENCOUNTER_TYPE_MERGING
ENCOUNTER_TYPE_MERGING.
Definition: MSDevice_SSM.h:78
@ ENCOUNTER_TYPE_MERGING_FOLLOWER
ENCOUNTER_TYPE_MERGING_FOLLOWER.
Definition: MSDevice_SSM.h:84
@ ENCOUNTER_TYPE_FOLLOWING_FOLLOWER
ENCOUNTER_TYPE_FOLLOWING_FOLLOWER.
Definition: MSDevice_SSM.h:71
@ ENCOUNTER_TYPE_FOLLOWING
ENCOUNTER_TYPE_FOLLOWING.
Definition: MSDevice_SSM.h:69
@ ENCOUNTER_TYPE_MERGING_LEADER
ENCOUNTER_TYPE_MERGING_LEADER.
Definition: MSDevice_SSM.h:81
@ ENCOUNTER_TYPE_FOLLOWING_PASSED
ENCOUNTER_TYPE_FOLLOWING_PASSED.
Definition: MSDevice_SSM.h:110
@ ENCOUNTER_TYPE_FOLLOWING_LEADER
ENCOUNTER_TYPE_FOLLOWING_LEADER.
Definition: MSDevice_SSM.h:73
@ ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:107
@ ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:99
@ ENCOUNTER_TYPE_ONCOMING
Definition: MSDevice_SSM.h:114
@ ENCOUNTER_TYPE_MERGING_PASSED
ENCOUNTER_TYPE_FOLLOWING_PASSED.
Definition: MSDevice_SSM.h:112
@ ENCOUNTER_TYPE_ON_ADJACENT_LANES
ENCOUNTER_TYPE_ON_ADJACENT_LANES.
Definition: MSDevice_SSM.h:75
@ ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:101
@ ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:105
@ ENCOUNTER_TYPE_NOCONFLICT_AHEAD
ENCOUNTER_TYPE_NOCONFLICT_AHEAD.
Definition: MSDevice_SSM.h:66
@ ENCOUNTER_TYPE_COLLISION
ENCOUNTER_TYPE_COLLISION.
Definition: MSDevice_SSM.h:116
@ ENCOUNTER_TYPE_CROSSING
ENCOUNTER_TYPE_CROSSING.
Definition: MSDevice_SSM.h:89
@ ENCOUNTER_TYPE_CROSSING_FOLLOWER
ENCOUNTER_TYPE_CROSSING_FOLLOWER.
Definition: MSDevice_SSM.h:95
@ ENCOUNTER_TYPE_MERGING_ADJACENT
ENCOUNTER_TYPE_MERGING_ADJACENT.
Definition: MSDevice_SSM.h:86
@ ENCOUNTER_TYPE_CROSSING_LEADER
ENCOUNTER_TYPE_CROSSING_LEADER.
Definition: MSDevice_SSM.h:92
std::priority_queue< Encounter *, std::vector< Encounter * >, Encounter::compare > EncounterQueue
Definition: MSDevice_SSM.h:365
static std::string writeNA(double v, double NA=INVALID_DOUBLE)
static void initEdgeFilter()
initialize edge filter (once)
std::vector< double > myGlobalMeasuresLanesPositions
All values for positions on the lanes.
Definition: MSDevice_SSM.h:784
bool notifyMove(SUMOTrafficObject &veh, double oldPos, double newPos, double newSpeed)
Checks for waiting steps when the vehicle moves.
static void determineConflictPoint(EncounterApproachInfo &eInfo)
Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in ...
static double computeDRAC(double gap, double followerSpeed, double leaderSpeed)
Computes the DRAC (deceleration to avoid a collision) for a lead/follow situation as defined,...
EncounterQueue myPastConflicts
Past encounters that where qualified as conflicts and are not yet flushed to the output file.
Definition: MSDevice_SSM.h:771
static bool useGeoCoords(const SUMOVehicle &v)
void setParameter(const std::string &key, const std::string &value)
try to set the given parameter for this device. Throw exception for unsupported key
static double getMDRAC_PRT(const SUMOVehicle &v)
static bool myEdgeFilterInitialized
Definition: MSDevice_SSM.h:801
static const std::set< MSDevice_SSM *, ComparatorNumericalIdLess > & getInstances()
returns all currently existing SSM devices
void closeEncounter(Encounter *e)
Finalizes the encounter and calculates SSM values.
static std::string makeStringWithNAs(const std::vector< double > &v, const double NA)
make a string of a double vector and treat a special value as invalid ("NA")
static bool writePositions(const SUMOVehicle &v)
void determineTTCandDRACandPPETandMDRAC(EncounterApproachInfo &eInfo) const
Discriminates between different encounter types and correspondingly determines TTC and DRAC for those...
static double getDetectionRange(const SUMOVehicle &v)
static void cleanup()
Clean up remaining devices instances.
static void insertOptions(OptionsCont &oc)
Inserts MSDevice_SSM-options.
double myRange
Detection range. For vehicles closer than this distance from the ego vehicle, SSMs are traced.
Definition: MSDevice_SSM.h:742
const MSLane * findFoeConflictLane(const MSVehicle *foe, const MSLane *egoConflictLane, double &distToConflictLane) const
Computes the conflict lane for the foe.
std::vector< std::string > myGlobalMeasuresLaneIDs
All values for lanes.
Definition: MSDevice_SSM.h:782
std::vector< int > myDroppedConflictTypes
Which conflict types to exclude from the output.
Definition: MSDevice_SSM.h:756
static int myIssuedParameterWarnFlags
bitset storing info whether warning has already been issued about unset parameter (warn only once!...
Definition: MSDevice_SSM.h:812
bool notifyLeave(SUMOTrafficObject &veh, double lastPos, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
Called whenever the holder leaves a lane.
std::vector< Encounter * > EncounterVector
Definition: MSDevice_SSM.h:366
static void getUpstreamVehicles(const UpstreamScanStartInfo &scanStart, FoeInfoMap &foeCollector, std::set< const MSLane * > &seenLanes, const std::set< const MSJunction * > &routeJunctions)
Collects all vehicles within range 'range' upstream of the position 'pos' on the edge 'edge' into foe...
void createEncounters(FoeInfoMap &foes)
Makes new encounters for all given vehicles (these should be the ones entering the device's range in ...
static const std::set< int > FOE_ENCOUNTERTYPES
Definition: MSDevice_SSM.h:827
bool qualifiesAsConflict(Encounter *e)
Tests if the SSM values exceed the threshold for qualification as conflict.
std::map< std::string, double > myThresholds
Definition: MSDevice_SSM.h:737
static std::string getOutputFilename(const SUMOVehicle &v, std::string deviceID)
void updateAndWriteOutput()
This is called once per time step in MSNet::writeOutput() and collects the surrounding vehicles,...
static double computeMDRAC(double gap, double followerSpeed, double leaderSpeed, double prt)
Computes the MDRAC (deceleration to avoid a collision) for a lead/follow situation as defined conside...
static std::set< MSDevice_SSM *, ComparatorNumericalIdLess > * myInstances
All currently existing SSM devices.
Definition: MSDevice_SSM.h:59
std::pair< std::pair< std::pair< double, Position >, double >, std::string > myMinSGAP
Definition: MSDevice_SSM.h:794
OutputDevice * myOutputFile
Output device.
Definition: MSDevice_SSM.h:805
static double getExtraTime(const SUMOVehicle &v)
EncounterVector myActiveEncounters
Definition: MSDevice_SSM.h:767
std::vector< double > myGlobalMeasuresTimeSpan
Definition: MSDevice_SSM.h:778
void computeGlobalMeasures()
Stores measures, that are not associated to a specific encounter as headways and brake rates.
static std::string encounterToString(EncounterType type)
Definition: MSDevice_SSM.h:119
double myOldestActiveEncounterBegin
begin time of the oldest active encounter
Definition: MSDevice_SSM.h:769
static void checkConflictEntryAndExit(EncounterApproachInfo &eInfo)
Checks whether ego or foe have entered or left the conflict area in the last step and eventually writ...
double computeTTC(double gap, double followerSpeed, double leaderSpeed) const
Computes the time to collision (in seconds) for two vehicles with a given initial gap under the assum...
void flushConflicts(bool all=false)
Writes out all past conflicts that have begun earlier than the oldest active encounter.
void determinePET(EncounterApproachInfo &eInfo) const
Discriminates between different encounter types and correspondingly determines the PET for those case...
static std::set< const MSEdge * > myEdgeFilter
spatial filter for SSM device output
Definition: MSDevice_SSM.h:800
MSDevice_SSM(SUMOVehicle &holder, const std::string &id, std::string outputFilename, std::map< std::string, double > thresholds, bool trajectories, double range, double extraTime, bool useGeoCoords, bool writePositions, bool writeLanesPositions, std::vector< int > conflictOrder)
Constructor.
static const std::set< int > EGO_ENCOUNTERTYPES
Definition: MSDevice_SSM.h:828
static void toGeo(Position &x)
convert SUMO-positions to geo coordinates (in place)
static void findSurroundingVehicles(const MSVehicle &veh, double range, FoeInfoMap &foeCollector)
Returns all vehicles, which are within the given range of the given vehicle.
bool myWritePositions
Wether to print the positions for all timesteps.
Definition: MSDevice_SSM.h:750
void resetEncounters()
Closes all current Encounters and moves conflicts to myPastConflicts,.
std::pair< std::pair< double, Position >, double > myMaxBR
Extremal values for the global measures (as <<<time, Position>, value>, [leaderID]>-pairs)
Definition: MSDevice_SSM.h:793
std::vector< double > myBRspan
All values for brake rate.
Definition: MSDevice_SSM.h:786
bool myFilterConflictTypes
Whether to exclude certain conflicts containing certain conflict types from the output.
Definition: MSDevice_SSM.h:754
bool myUseGeoCoords
Whether to use the original coordinate system for output.
Definition: MSDevice_SSM.h:748
bool myWriteLanesPositions
Wether to print the lanes and positions for all timesteps and conflicts.
Definition: MSDevice_SSM.h:752
@ SSM_WARN_CONFLICTFILTER
Definition: MSDevice_SSM.h:824
static bool filterByConflictType(const SUMOVehicle &v, std::string deviceID, std::vector< int > &conflictTypes)
~MSDevice_SSM()
Destructor.
double myMDRACPRT
perception reaction time for MDRAC
Definition: MSDevice_SSM.h:744
const std::string deviceName() const
return the name for this type of device
Definition: MSDevice_SSM.h:495
bool myComputeMDRAC
Definition: MSDevice_SSM.h:759
static bool myEdgeFilterActive
Definition: MSDevice_SSM.h:802
void flushGlobalMeasures()
Write out all non-encounter specific measures as headways and braking rates.
std::vector< double > myTGAPspan
All values for time gap.
Definition: MSDevice_SSM.h:790
static void getVehiclesOnJunction(const MSJunction *, const MSLane *egoJunctionLane, double egoDistToConflictLane, const MSLane *const egoConflictLane, FoeInfoMap &foeCollector, std::set< const MSLane * > &seenLanes)
Collects all vehicles on the junction into foeCollector.
static void estimateConflictTimes(EncounterApproachInfo &eInfo)
Estimates the time until conflict for the vehicles based on the distance to the conflict entry points...
bool notifyEnter(SUMOTrafficObject &veh, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
Called whenever the holder enteres a lane.
bool myComputePPET
Definition: MSDevice_SSM.h:759
void updatePassedEncounter(Encounter *e, FoeInfo *foeInfo, EncounterApproachInfo &eInfo)
Updates an encounter, which was classified as ENCOUNTER_TYPE_NOCONFLICT_AHEAD this may be the case be...
void processEncounters(FoeInfoMap &foes, bool forceClose=false)
Finds encounters for which the foe vehicle has disappeared from range. remainingExtraTime is decrease...
static bool writeLanesPositions(const SUMOVehicle &v)
std::vector< double > mySGAPspan
All values for space gap.
Definition: MSDevice_SSM.h:788
static void insertDefaultAssignmentOptions(const std::string &deviceName, const std::string &optionsTopic, OptionsCont &oc, const bool isPerson=false)
Adds common command options that allow to assign devices to vehicles.
Definition: MSDevice.cpp:155
static bool equippedByDefaultAssignmentOptions(const OptionsCont &oc, const std::string &deviceName, DEVICEHOLDER &v, bool outputOptionSet, const bool isPerson=false)
Determines whether a vehicle should get a certain device.
Definition: MSDevice.h:195
A road/street connecting two junctions.
Definition: MSEdge.h:77
bool isCrossing() const
return whether this edge is a pedestrian crossing
Definition: MSEdge.h:273
const MSEdge * getOppositeEdge() const
Returns the opposite direction edge if on exists else a nullptr.
Definition: MSEdge.cpp:1288
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
const MSJunction * getFromJunction() const
Definition: MSEdge.h:414
double getLength() const
return the length of the edge
Definition: MSEdge.h:670
bool isInternal() const
return whether this edge is an internal edge
Definition: MSEdge.h:268
static bool dictionary(const std::string &id, MSEdge *edge)
Inserts edge into the static dictionary Returns true if the key id isn't already in the dictionary....
Definition: MSEdge.cpp:995
const MSJunction * getToJunction() const
Definition: MSEdge.h:418
static bool gUseMesoSim
Definition: MSGlobals.h:103
The base class for an intersection.
Definition: MSJunction.h:58
const ConstMSEdgeVector & getIncoming() const
Definition: MSJunction.h:108
const ConstMSEdgeVector & getOutgoing() const
Definition: MSJunction.h:114
virtual const std::vector< MSLane * > getInternalLanes() const
Returns all internal lanes on the junction.
Definition: MSJunction.h:120
virtual const std::vector< MSLane * > & getFoeInternalLanes(const MSLink *const) const
Definition: MSJunction.h:104
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.h:716
const MSLink * getEntryLink() const
Returns the entry link if this is an internal lane, else nullptr.
Definition: MSLane.cpp:2672
const MSLink * getLinkTo(const MSLane *const) const
returns the link to the given lane or nullptr, if it is not connected
Definition: MSLane.cpp:2649
std::vector< MSVehicle * > VehCont
Container for vehicles.
Definition: MSLane.h:119
double getLength() const
Returns the lane's length.
Definition: MSLane.h:598
const MSLane * getFirstInternalInConnection(double &offset) const
Returns 0 if the lane is not internal. Otherwise the first part of the connection (sequence of intern...
Definition: MSLane.cpp:2377
MSLane * getCanonicalSuccessorLane() const
Definition: MSLane.cpp:3196
const std::vector< IncomingLaneInfo > & getIncomingLanes() const
Definition: MSLane.h:942
bool isInternal() const
Definition: MSLane.cpp:2526
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:756
MSLane * getParallelOpposite() const
return the opposite direction lane of this lanes edge or nullptr
Definition: MSLane.cpp:4286
double getWidth() const
Returns the lane's width.
Definition: MSLane.h:627
virtual const PositionVector & getShape(bool) const
Definition: MSLane.h:294
Notification
Definition of a vehicle state.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:184
MSVehicleControl & getVehicleControl()
Returns the vehicle control.
Definition: MSNet.h:378
MSJunctionControl & getJunctionControl()
Returns the junctions control.
Definition: MSNet.h:461
MSRouteIterator end() const
Returns the end of the list of edges to pass.
Definition: MSRoute.cpp:79
SUMOVehicle * getVehicle(const std::string &id) const
Returns the vehicle with the given id.
Abstract in-vehicle device.
SUMOVehicle & myHolder
The vehicle that stores the device.
Representation of a vehicle in the micro simulation.
Definition: MSVehicle.h:77
bool isOnRoad() const
Returns the information whether the vehicle is on a road (is simulated)
Definition: MSVehicle.h:608
MSAbstractLaneChangeModel & getLaneChangeModel()
Definition: MSVehicle.cpp:5774
Position getPositionAlongBestLanes(double offset) const
Return the (x,y)-position, which the vehicle would reach if it continued along its best continuation ...
Definition: MSVehicle.cpp:1312
double getMaxSpeedOnLane() const
Returns the maximal speed for the vehicle on its current lane (including speed factor and deviation,...
Definition: MSVehicle.cpp:1378
double getAcceleration() const
Returns the vehicle's acceleration in m/s (this is computed as the last step's mean acceleration in c...
Definition: MSVehicle.h:517
Position getPosition(const double offset=0) const
Return current position (x/y, cartesian)
Definition: MSVehicle.cpp:1244
const std::vector< MSLane * > & getBestLanesContinuation() const
Returns the best sequence of lanes to continue the route starting at myLane.
Definition: MSVehicle.cpp:6290
double getBackPositionOnLane(const MSLane *lane) const
Get the vehicle's position relative to the given lane.
Definition: MSVehicle.h:401
std::pair< const MSVehicle *const, double > getLeader(double dist=0) const
Returns the leader of the vehicle looking for a fixed distance.
Definition: MSVehicle.cpp:6522
double getLastStepDist() const
Get the distance the vehicle covered in the previous timestep.
Definition: MSVehicle.h:384
double getLateralPositionOnLane() const
Get the vehicle's lateral position on the lane.
Definition: MSVehicle.h:416
double getSpeed() const
Returns the vehicle's current speed.
Definition: MSVehicle.h:493
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition: MSVehicle.h:377
const MSLane * getLane() const
Returns the lane the vehicle is on.
Definition: MSVehicle.h:584
const MSCFModel & getCarFollowModel() const
Returns the vehicle's car following model definition.
Definition: MSVehicle.h:977
double getPreviousSpeed() const
Returns the vehicle's speed before the previous time step.
Definition: MSVehicle.h:501
Position getVelocityVector() const
Returns the vehicle's direction in radians.
Definition: MSVehicle.h:742
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
double getMinGap() const
Get the free space in front of vehicles of this class.
double getLength() const
Get vehicle's length [m].
const SUMOVTypeParameter & getParameter() const
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition: Named.h:67
const std::string & getID() const
Returns the id.
Definition: Named.h:74
T get(const std::string &id) const
Retrieves an item.
A storage for options typed value containers)
Definition: OptionsCont.h:89
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
Definition: OptionsCont.cpp:76
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:254
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:322
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimensions
Definition: Position.h:266
A list of positions.
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
Representation of a vehicle, person, or container.
virtual bool isVehicle() const
Whether it is a vehicle.
virtual const MSVehicleType & getVehicleType() const =0
Returns the object's "vehicle" type.
virtual const SUMOVehicleParameter & getParameter() const =0
Returns the vehicle's parameter (including departure definition)
virtual const MSLane * getLane() const =0
Returns the lane the object is currently at.
virtual const MSEdge * getEdge() const =0
Returns the edge the object is currently at.
Representation of a vehicle.
Definition: SUMOVehicle.h:62
virtual const MSRoute & getRoute() const =0
Returns the current route.
virtual bool isOnRoad() const =0
Returns the information whether the vehicle is on a road (is simulated)
virtual const ConstMSEdgeVector::const_iterator & getCurrentRouteEdge() const =0
Returns an iterator pointing to the current edge in this vehicles route.
std::vector< std::string > getVector()
return vector of strings
bool hasNext()
returns the information whether further substrings exist
std::string next()
returns the next substring when it exists. Otherwise the behaviour is undefined
static std::string urlDecode(const std::string &encoded)
decode url (stem from http://bogomip.net/blog/cpp-url-encoding-and-decoding/)
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter
#define DEBUG_COND
Definition: json.hpp:4471
static double fn[10]
Definition: odrSpiral.cpp:87
#define M_PI
Definition: odrSpiral.cpp:45
EncounterType type
Type of the conflict.
Definition: MSDevice_SSM.h:196
double time
time point of the conflict
Definition: MSDevice_SSM.h:190
double speed
speed of the reporting vehicle at the given time/position
Definition: MSDevice_SSM.h:200
Position pos
Predicted location of the conflict: In case of MERGING and CROSSING: entry point to conflict area for...
Definition: MSDevice_SSM.h:194
double value
value of the corresponding SSM
Definition: MSDevice_SSM.h:198
std::vector< std::string > lane
Definition: MSDevice_SSM.h:180
Structure to collect some info on the encounter needed during ssm calculation by various functions.
Definition: MSDevice_SSM.h:312
std::pair< double, double > pet
Definition: MSDevice_SSM.h:330
const MSLane * egoConflictLane
Definition: MSDevice_SSM.h:341
Auxiliary structure used to handle upstream scanning start points Upstream scan has to be started aft...
Definition: MSDevice_SSM.h:355