SUMO - Simulation of Urban MObility
MSDevice_SSM.cpp
Go to the documentation of this file.
1 /****************************************************************************/
10 // An SSM-device logs encounters / conflicts of the carrying vehicle with other surrounding vehicles
11 // XXX: Preliminary implementation. Use with care. Especially rerouting vehicles could be problematic.
12 /****************************************************************************/
13 // SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
14 // Copyright (C) 2013-2017 DLR (http://www.dlr.de/) and contributors
15 /****************************************************************************/
16 //
17 // This file is part of SUMO.
18 // SUMO is free software: you can redistribute it and/or modify
19 // it under the terms of the GNU General Public License as published by
20 // the Free Software Foundation, either version 3 of the License, or
21 // (at your option) any later version.
22 //
23 /****************************************************************************/
24 
25 // ===========================================================================
26 // included modules
27 // ===========================================================================
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 
34 #include <iostream>
36 #include <utils/geom/GeomHelper.h>
41 #include <microsim/MSNet.h>
42 #include <microsim/MSJunction.h>
43 #include <microsim/MSLane.h>
44 #include <microsim/MSEdge.h>
45 #include <microsim/MSVehicle.h>
47 #include <utils/geom/Position.h>
49 #include "MSDevice_SSM.h"
50 
51 // ===========================================================================
52 // Debug constants
53 // ===========================================================================
54 //#define DEBUG_SSM
55 //#define DEBUG_SSM_SURROUNDING
56 // #define DEBUG_SSM_NOTIFICATIONS
57 
58 // ===========================================================================
59 // Constants
60 // ===========================================================================
61 // value indicating an invalid double parameter
62 #define INVALID std::numeric_limits<double>::max()
63 
64 // default value for the detection range of potential opponents
65 #define DEFAULT_RANGE 50.0
66 
67 // list of implemented SSMs (NOTE: To add more SSMs, identifiers are added to AVAILABLE_SSMS
68 // and a default threshold must be defined. A corresponding
69 // case should be added to the switch in buildVehicleDevices,
70 // and in computeSSMs(), the SSM-value should be computed.)
71 #define AVAILABLE_SSMS "TTC DRAC PET"
72 #define DEFAULT_THRESHOLD_TTC 3. // in [s.], events get logged if below threshold
73 #define DEFAULT_THRESHOLD_DRAC 4. // in [m/s^2], events get logged if above threshold
74 #define DEFAULT_THRESHOLD_PET 2. // in seconds, events get logged if below threshold
75 #define DEFAULT_EXTRA_TIME 5. // in seconds, events get logged for extra time even if encounter is over
76 
77 // ===========================================================================
78 // method definitions
79 // ===========================================================================
80 
81 
82 
84 std::ostream& operator<<(std::ostream& out, MSDevice_SSM::EncounterType type) {
85  switch (type) {
87  out << "NOCONFLICT_AHEAD";
88  break;
90  out << "FOLLOWING";
91  break;
93  out << "FOLLOWING_FOLLOWER";
94  break;
96  out << "FOLLOWING_LEADER";
97  break;
99  out << "ON_ADJACENT_LANES";
100  break;
102  out << "MERGING";
103  break;
105  out << "MERGING_LEADER";
106  break;
108  out << "MERGING_FOLLOWER";
109  break;
111  out << "MERGING_ADJACENT";
112  break;
114  out << "CROSSING";
115  break;
117  out << "CROSSING_LEADER";
118  break;
120  out << "CROSSING_FOLLOWER";
121  break;
123  out << "EGO_ENTERED_CONFLICT_AREA";
124  break;
126  out << "FOE_ENTERED_CONFLICT_AREA";
127  break;
129  out << "BOTH_ENTERED_CONFLICT_AREA";
130  break;
132  out << "EGO_LEFT_CONFLICT_AREA";
133  break;
135  out << "FOE_LEFT_CONFLICT_AREA";
136  break;
138  out << "BOTH_LEFT_CONFLICT_AREA";
139  break;
141  out << "FOLLOWING_PASSED";
142  break;
144  out << "MERGING_PASSED";
145  break;
146  // Collision (currently unused, might be differentiated further)
148  out << "COLLISION";
149  break;
150  default:
151  out << "unknown type (" << int(type) << ")";
152  break;
153  }
154  return out;
155 }
156 
157 
158 // ---------------------------------------------------------------------------
159 // static initialisation methods
160 // ---------------------------------------------------------------------------
161 
162 std::set<MSDevice*>* MSDevice_SSM::instances = new std::set<MSDevice*>();
163 
164 const std::set<MSDevice*>&
166  return *instances;
167 }
168 
169 void
171  // Close current encounters and flush conflicts to file for all existing devices
172  if (instances != 0) {
173  for (std::set<MSDevice*>::iterator ii = instances->begin(); ii != instances->end(); ++ii) {
174  static_cast<MSDevice_SSM*>(*ii)->resetEncounters();
175  static_cast<MSDevice_SSM*>(*ii)->flushConflicts(true);
176  }
177  instances->clear();
178  }
179 }
180 
181 void
183  oc.addOptionSubTopic("SSM Device");
184  insertDefaultAssignmentOptions("ssm", "SSM Device", oc);
185 
186  // custom options
187  oc.doRegister("device.ssm.measures", new Option_String(""));
188  oc.addDescription("device.ssm.measures", "SSM Device", "Specifies which measures will be logged (as a space seperated sequence of IDs in ('TTC', 'DRAC', 'PET')).");
189  oc.doRegister("device.ssm.thresholds", new Option_String(""));
190  oc.addDescription("device.ssm.thresholds", "SSM Device", "Specifies thresholds corresponding to the specified measures (see documentation and watch the order!). Only events exceeding the thresholds will be logged.");
191  oc.doRegister("device.ssm.trajectories", new Option_Bool(false));
192  oc.addDescription("device.ssm.trajectories", "SSM Device", "Specifies whether trajectories will be logged (if false, only the extremal values and times are reported, this is the default).");
193  oc.doRegister("device.ssm.range", new Option_Float(DEFAULT_RANGE));
194  oc.addDescription("device.ssm.range", "SSM Device", "Specifies the detection range in meters (default is " + toString(DEFAULT_RANGE) + "m.). For vehicles below this distance from the equipped vehicle, SSM values are traced.");
195  oc.doRegister("device.ssm.extratime", new Option_Float(DEFAULT_EXTRA_TIME));
196  oc.addDescription("device.ssm.extratime", "SSM Device", "Specifies the time in seconds to be logged after a conflict is over (default is " + toString(DEFAULT_EXTRA_TIME) + "secs.). Required >0 if PET is to be calculated for crossing conflicts.");
197  oc.doRegister("device.ssm.geo", new Option_Bool(false));
198  oc.addDescription("device.ssm.geo", "SSM Device", "Whether to use coordinates of the original reference system in output (default is false).");
199 }
200 
201 void
202 MSDevice_SSM::buildVehicleDevices(SUMOVehicle& v, std::vector<MSDevice*>& into) {
205  WRITE_WARNING("SSM Device for vehicle '" + v.getID() + "' will not be built. (SSMs not supported in MESO)");
206  return;
207  }
208  // ID for the device
209  std::string deviceID = "ssm_" + v.getID();
210 
211  // Load parameters:
212 
213  // Measures and thresholds
214  std::map<std::string, double> thresholds;
215  bool success = getMeasuresAndThresholds(v, deviceID, thresholds);
216  if (!success) {
217  return;
218  }
219 
220  // TODO: modify trajectory option: "all", "conflictPoints", ("position" && "speed" == "vehState"), "SSMs"!
221  // Trajectories
222  bool trajectories = requestsTrajectories(v);
223 
224  // detection range
225  double range = getDetectionRange(v);
226 
227  // extra time
228  double extraTime = getExtraTime(v);
229 
230  // File
231  std::string file = getOutputFilename(v, deviceID);
232 
233  const bool useGeo = useGeoCoords(v, deviceID);
234 
235  // Build the device (XXX: who deletes it?)
236  MSDevice_SSM* device = new MSDevice_SSM(v, deviceID, file, thresholds, trajectories, range, extraTime, useGeo);
237  into.push_back(device);
238  }
239 }
240 
241 
242 MSDevice_SSM::Encounter::Encounter(const MSVehicle* _ego, const MSVehicle* const _foe, double _begin, double extraTime) :
243  currentType(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
244  ego(_ego),
245  foe(_foe),
246  egoID(_ego->getID()),
247  foeID(_foe->getID()),
248  begin(_begin),
249  end(-INVALID),
250  remainingExtraTime(extraTime),
251  egoConflictEntryTime(INVALID),
252  egoConflictExitTime(INVALID),
253  foeConflictEntryTime(INVALID),
254  foeConflictExitTime(INVALID),
255  minTTC(std::make_pair(std::make_pair(INVALID, INVALID), Position::invalidPosition())),
256  maxDRAC(std::make_pair(std::make_pair(INVALID, INVALID), Position::invalidPosition())),
257  PET(std::make_pair(std::make_pair(INVALID, INVALID), Position::invalidPosition())),
258  closingRequested(false) {
259 #ifdef DEBUG_SSM
260  std::cout << "\n" << SIMTIME << " Constructing encounter of '"
261  << ego->getID() << "' and '" << foe->getID() << "'" << std::endl;
262 #endif
263 }
264 
266 #ifdef DEBUG_SSM
267  std::cout << "\n" << SIMTIME << " Destroying encounter of '"
268  << egoID << "' and '" << foeID << "' (begin was " << begin << ")" << std::endl;
269 #endif
270 }
271 
272 
273 void
274 MSDevice_SSM::Encounter::add(double time, const EncounterType type, Position egoX, Position egoV, Position foeX, Position foeV,
275  Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair<double, double> pet) {
276 #ifdef DEBUG_SSM
277  std::cout << time << " Adding data point for encounter of '" << egoID << "' and '" << foeID << "':\n"
278  << "type=" << type << ", egoDistToConflict=" << (egoDistToConflict == INVALID ? "NA" : toString(egoDistToConflict))
279  << ", foeDistToConflict=" << (foeDistToConflict == INVALID ? "NA" : toString(foeDistToConflict))
280  << ",\nttc=" << (ttc == INVALID ? "NA" : toString(ttc))
281  << ", drac=" << (drac == INVALID ? "NA" : toString(drac))
282  << ", pet=" << (pet.second == INVALID ? "NA" : toString(pet.second))
283  << std::endl;
284 #endif
285  currentType = type;
286 
287  timeSpan.push_back(time);
288  typeSpan.push_back(type);
289  egoTrajectory.x.push_back(egoX);
290  egoTrajectory.v.push_back(egoV);
291  foeTrajectory.x.push_back(foeX);
292  foeTrajectory.v.push_back(foeV);
293  conflictPointSpan.push_back(conflictPoint);
294  egoDistsToConflict.push_back(egoDistToConflict);
295  foeDistsToConflict.push_back(foeDistToConflict);
296 
297  TTCspan.push_back(ttc);
298  if (ttc != INVALID && (ttc < minTTC.first.second || minTTC.first.second == INVALID)) {
299  minTTC = std::make_pair(std::make_pair(time, ttc), conflictPoint);
300  }
301 
302  DRACspan.push_back(drac);
303  if (drac != INVALID && (drac > maxDRAC.first.second || maxDRAC.first.second == INVALID)) {
304  maxDRAC = std::make_pair(std::make_pair(time, drac), conflictPoint);
305  }
306 
307  if (pet.first != INVALID && (PET.first.second >= pet.second || PET.first.second == INVALID)) {
308  PET = std::make_pair(pet, conflictPoint);
309  }
310 }
311 
312 
313 void
315  remainingExtraTime = value;
316 }
317 
318 
319 void
321  remainingExtraTime -= amount;
322 }
323 
324 
325 double
327  return remainingExtraTime;
328 }
329 
330 
332  encounter(e),
334  conflictPoint(Position::invalidPosition()),
335  egoConflictEntryDist(INVALID),
336  foeConflictEntryDist(INVALID),
337  egoConflictExitDist(INVALID),
338  foeConflictExitDist(INVALID),
339  egoEstimatedConflictEntryTime(INVALID),
340  foeEstimatedConflictEntryTime(INVALID),
341  egoEstimatedConflictExitTime(INVALID),
342  foeEstimatedConflictExitTime(INVALID),
343  egoConflictAreaLength(INVALID),
344  foeConflictAreaLength(INVALID),
345  egoLeftConflict(false),
346  foeLeftConflict(false),
347  ttc(INVALID),
348  drac(INVALID),
349  pet(std::make_pair(INVALID, INVALID)) {
350 }
351 
352 
353 void
355  if (myHolder.isOnRoad()) {
356  update();
357  // Write out past conflicts
358  flushConflicts();
359  } else {
360  resetEncounters();
361  // Write out past conflicts
362  flushConflicts(true);
363  }
364 }
365 
366 void
368 #ifdef DEBUG_SSM
369  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' update()\n"
370  << "Size of myActiveEncounters: " << myActiveEncounters.size()
371  << "\nSize of myPastConflicts: " << myPastConflicts.size()
372  << std::endl;
373 #endif
374  // Scan surroundings for other vehicles
375  FoeInfoMap foes;
377 
378 #ifdef DEBUG_SSM
379  if (foes.size() > 0) {
380  std::cout << "Scanned surroundings: Found potential foes:\n";
381  for (FoeInfoMap::const_iterator i = foes.begin(); i != foes.end(); ++i) {
382  std::cout << i->first->getID() << " ";
383  }
384  std::cout << std::endl;
385  } else {
386  std::cout << "Scanned surroundings: No potential conflict could be identified." << std::endl;
387  }
388 #endif
389 
390  // Update encounters and conflicts -> removes all foes (and deletes corresponding FoeInfos) for which already a corresponding encounter exists
391  processEncounters(foes);
392 
393  // Make new encounters for all foes, which were not removed by processEncounters (and deletes corresponding FoeInfos)
394  createEncounters(foes);
395  foes.clear();
396 }
397 
398 
399 void
401 #ifdef DEBUG_SSM
402  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' createEncounters()" << std::endl;
403  std::cout << "New foes:\n";
404  for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
405  std::cout << vi->first->getID() << "\n";
406  }
407  std::cout << std::endl;
408 #endif
409 
410  for (FoeInfoMap::const_iterator foe = foes.begin(); foe != foes.end(); ++foe) {
411  std::pair<MSLane*, MSLane*> conflictLanes;
412  Encounter* e = new Encounter(myHolderMS, foe->first, SIMTIME, myExtraTime);
413  updateEncounter(e, foe->second); // deletes foe->second
415  assert(myActiveEncounters.empty());
417  }
418  assert(myOldestActiveEncounterBegin <= e->begin);
419  myActiveEncounters.push_back(e);
420  }
421 }
422 
423 void
425  // Call processEncounters() with empty vehicle set
426  FoeInfoMap foes;
427  // processEncounters with empty argument closes all encounters
428  processEncounters(foes, true);
429 }
430 
431 void
433 #ifdef DEBUG_SSM
434  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' processEncounters()" << std::endl;
435  std::cout << "Currently present foes:\n";
436  for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
437  std::cout << vi->first->getID() << "\n";
438  }
439  std::cout << std::endl;
440 #endif
441 
442  // Run through active encounters. If corresponding foe is still present in foes update and
443  // remove foe from foes. If the foe has disappeared close the encounter (check if it qualifies
444  // as a conflict and in case transfer it to myPastConflicts).
445  // Afterwards run through remaining elements in foes and create new encounters for them.
446 
447  EncounterVector::iterator ei = myActiveEncounters.begin();
448  while (ei != myActiveEncounters.end()) {
449  Encounter* e = *ei;
450  // check whether foe is still on net
451  bool foeExists = !(MSNet::getInstance()->getVehicleControl().getVehicle(e->foeID) == 0);
452  if (!foeExists) {
453  e->foe = 0;
454  }
455  if (foes.find(e->foe) != foes.end()) {
456  FoeInfo* foeInfo = foes[e->foe];
457  // Update encounter
458  updateEncounter(e, foeInfo); // deletes foeInfo
459  // Erase foes which were already encountered
460  foes.erase(e->foe);
461  } else {
462  if (e->getRemainingExtraTime() <= 0. || forceClose || !foeExists) {
463  // Close encounter, extra time has expired (deletes e if it does not qualify as conflict)
464  e->closingRequested = true;
465  } else {
466  updateEncounter(e, 0); // counts down extra time
467  }
468  }
469 
470  if (e->closingRequested) {
471  double eBegin = e->begin;
472  closeEncounter(e);
473  ei = myActiveEncounters.erase(ei);
474  if (myActiveEncounters.empty()) {
476  } else if (eBegin == myOldestActiveEncounterBegin) {
477  // Erased the oldest encounter, update myOldestActiveEncounterBegin
478  auto i = myActiveEncounters.begin();
479  myOldestActiveEncounterBegin = (*i++)->begin;
480  while (i != myActiveEncounters.end()) {
482  }
483  }
484  } else {
485  ++ei;
486  }
487  }
488 }
489 
490 
491 bool
493  // Check if conflict measure thresholds are exceeded (to decide whether to keep the encounter for writing out)
494 #ifdef DEBUG_SSM
495  std::cout << SIMTIME << " qualifiesAsConflict() for encounter of vehicles '"
496  << e->egoID << "' and '" << e->foeID
497  << "'" << std::endl;
498 #endif
499 
500  if (myComputePET && e->PET.first.second != INVALID && e->PET.first.second <= myThresholds["PET"]) {
501  return true;
502  }
503  if (myComputeTTC && e->minTTC.first.second != INVALID && e->minTTC.first.second <= myThresholds["TTC"]) {
504  return true;
505  }
506  if (myComputeDRAC && e->maxDRAC.first.second != INVALID && e->maxDRAC.first.second >= myThresholds["DRAC"]) {
507  return true;
508  }
509  return false;
510 }
511 
512 
513 void
515  // erase pointers (encounter is stored before being destroyed and pointers could become invalid)
516  e->ego = 0;
517  e->foe = 0;
518  e->end = e->timeSpan.back();
519  bool wasConflict = qualifiesAsConflict(e);
520  if (wasConflict) {
521  myPastConflicts.push(e);
522  } else {
523  delete e;
524  }
525 #ifdef DEBUG_SSM
526  std::cout << SIMTIME << " closeEncounter() of vehicles '"
527  << e->egoID << "' and '" << e->foeID
528  << "' (was ranked as " << (wasConflict ? "conflict" : "non-conflict") << ")" << std::endl;
529 #endif
530 
531  return;
532 }
533 
534 
535 void
537 #ifdef DEBUG_SSM
538  std::cout << SIMTIME << " updateEncounter() of vehicles '"
539  << e->egoID << "' and '" << e->foeID
540  << "'" << std::endl;
541 #endif
542  assert(e->foe != 0);
543 
544  // Struct storing distances (determined in classifyEncounter()) and times to potential conflict entry / exit (in estimateConflictTimes())
545  EncounterApproachInfo eInfo(e);
546 
547  // Classify encounter type based on the present information
548  // More details on follower/lead relation are determined in a second step below, see estimateConflictTimes()
549  // If a crossing situation is ongoing (i.e. one of the vehicles entered the conflict area already in the last step,
550  // this is handled by passedEncounter by only tracing the vehicle's movements)
551  // The further developement of the encounter type is done in checkConflictEntryAndExit()
552  eInfo.type = classifyEncounter(foeInfo, eInfo);
553 
554  if (eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
555  // At this state, eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD implies that the foe
556  // is either out of the device's range or its route does not interfere with the ego's route.
557 #ifdef DEBUG_SSM
558  std::cout << SIMTIME << " Encounter of vehicles '"
559  << e->egoID << "' and '" << e->foeID
560  << "' does not imply any conflict." << std::endl;
561 #endif
562  updatePassedEncounter(e, foeInfo, eInfo);
563 // return;
569  // Ongoing encounter. Treat with update passed encounter (trace covered distances)
570  // eInfo.type only holds the previous type
571  updatePassedEncounter(e, foeInfo, eInfo);
572 
573  // Estimate times until a possible conflict / collision
574  estimateConflictTimes(eInfo);
575 
576  } else {
577  // Estimate times until a possible conflict / collision
578  // Not all are used for all types of encounters:
579  // Follow/lead situation doesn't need them at all, currently (might change if more SSMs are implemented).
580  // Crossing / Merging calculates entry times to determine leader/follower and calculates the exit time for the leader.
581  estimateConflictTimes(eInfo);
582 
583  // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
585  }
586 
587  // update entry/exit times for conflict area
589 
590  // update (x,y)-coords of conflict point
591  determineConflictPoint(eInfo);
592 
593  // Compute SSMs
594  computeSSMs(eInfo);
595 
596  // Add current states to trajectories and update type
597  e->add(SIMTIME, eInfo.type, e->ego->getPosition(), e->ego->getVelocityVector(), e->foe->getPosition(), e->foe->getVelocityVector(),
598  eInfo.conflictPoint, eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, eInfo.ttc, eInfo.drac, eInfo.pet);
599 
600  // free foeInfo
601  delete foeInfo;
602 
603 }
604 
605 
606 void
608  /* Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in
609  * eInfo.conflictPoint. In case of MERGING and CROSSING, this is the entry point to conflict area for follower
610  * In case of FOLLOWING it is the position of leader's back. */
611 
612 #ifdef DEBUG_SSM
613  std::cout << SIMTIME << " determineConflictPoint()" << std::endl;
614 #endif
615 
616  const EncounterType& type = eInfo.type;
617  const Encounter* e = eInfo.encounter;
622  || type == ENCOUNTER_TYPE_COLLISION) {
623  eInfo.conflictPoint = e->conflictPointSpan.back();
624  } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
628  } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
632  } else if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
633  eInfo.conflictPoint = e->foe->getPosition(-e->foe->getLength());
634  } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
635  eInfo.conflictPoint = e->ego->getPosition(-e->ego->getLength());
636  } else {
637 #ifdef DEBUG_SSM
638  std::cout << "No conflict point associated with encounter type " << type << std::endl;
639 #endif
640  return;
641  }
642 
643 #ifdef DEBUG_SSM
644  std::cout << " Conflict at " << eInfo.conflictPoint << std::endl;
645 #endif
646 }
647 
648 
649 void
651 
652  EncounterType& type = eInfo.type;
653  Encounter* e = eInfo.encounter;
654 
655  assert(type != ENCOUNTER_TYPE_NOCONFLICT_AHEAD); // arrival times not defined, if no conflict is ahead.
656 #ifdef DEBUG_SSM
657  std::cout << SIMTIME << " estimateConflictTimes() for ego '" << e->egoID << "' and foe '" << e->foeID << "'\n"
658  << " encounter type: " << eInfo.type << "\n"
659  << " egoConflictEntryDist=" << (eInfo.egoConflictEntryDist == INVALID ? "NA" : toString(eInfo.egoConflictEntryDist))
660  << ", foeConflictEntryDist=" << (eInfo.foeConflictEntryDist == INVALID ? "NA" : toString(eInfo.foeConflictEntryDist))
661  << "\n ego speed=" << e->ego->getSpeed()
662  << ", foe speed=" << e->foe->getSpeed()
663  << std::endl;
664 #endif
665 
667  // No need to know the times until ...ConflictDistEntry, currently. They would correspond to an estimated time headway or similar.
668  // TTC must take into account the movement of the leader, as would DRAC, PET doesn't need the time either, since it uses aposteriori
669  // values.
670 #ifdef DEBUG_SSM
671  std::cout << " encouter type " << type << " -> no entry/exit times to be calculated."
672  << std::endl;
673 #endif
674  return;
675  }
676 
677  assert(type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_CROSSING
684 
685  // Determine exit distances
686  if (type == ENCOUNTER_TYPE_MERGING) {
687  assert(type == ENCOUNTER_TYPE_MERGING);
690  } else {
693  }
694 
695  // Estimate entry times to stipulate a leader / follower relation for the encounter.
696  if (eInfo.egoConflictEntryDist > 0.) {
698  assert(eInfo.egoEstimatedConflictEntryTime > 0.);
699  } else {
700  // ego already entered conflict area
702  }
703  if (eInfo.foeConflictEntryDist > 0.) {
705  assert(eInfo.foeEstimatedConflictEntryTime > 0.);
706  } else {
707  // foe already entered conflict area
709  }
710 
711 #ifdef DEBUG_SSM
712  std::cout << " Potential conflict type: " << (type == ENCOUNTER_TYPE_CROSSING ? "CROSSING" : "MERGING") << "\n"
713  << " egoConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID ? "INVALID" : toString(eInfo.egoEstimatedConflictEntryTime))
714  << ", foeConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID ? "INVALID" : toString(eInfo.foeEstimatedConflictEntryTime))
715  << std::endl;
716 #endif
717 
718  // Estimate exit times from conflict area for leader / follower.
719  if (eInfo.egoConflictExitDist >= 0.) {
721  } else {
722  eInfo.egoEstimatedConflictExitTime = 0.;
723  }
724  if (eInfo.foeConflictExitDist >= 0.) {
726  } else {
727  eInfo.foeEstimatedConflictExitTime = 0.;
728  }
729 
730 
731  if (type != ENCOUNTER_TYPE_MERGING && type != ENCOUNTER_TYPE_CROSSING) {
732  // this call is issued in context of an ongoing conflict, therefore complete type is already known for the encounter
733  // (One of EGO_ENTERED_CONFLICT_AREA, FOE_ENTERED_CONFLICT_AREA, EGO_LEFT_CONFLICT_AREA, FOE_LEFT_CONFLICT_AREA, BOTH_ENTERED_CONFLICT_AREA)
734  // --> no need to specify incomplete encounter type
735  return;
736  }
737 
738  // For merging and crossing situation, the leader/follower relation not determined by classifyEncounter()
739  // This is done below based on the estimated conflict entry times
740  if (eInfo.egoEstimatedConflictEntryTime == 0. && eInfo.foeEstimatedConflictEntryTime == 0.) {
742  std::stringstream ss;
743  ss << "SSM device of vehicle '" << e->egoID << "' detected collision with vehicle '" << e->foeID << "'";
744  WRITE_WARNING(ss.str());
746  // ego is estimated first at conflict point
747 #ifdef DEBUG_SSM
748  std::cout << " -> ego is estimated leader at conflict entry."
749  << " egoConflictExitTime=" << (eInfo.egoEstimatedConflictExitTime == INVALID ? "NA" : toString(eInfo.egoEstimatedConflictExitTime))
750  << std::endl;
751 #endif
753  } else {
754  // ego is estimated second at conflict point
755 #ifdef DEBUG_SSM
756  std::cout << " -> foe is estimated leader at conflict entry."
757  << " foeConflictExitTime=" << (eInfo.foeEstimatedConflictExitTime == INVALID ? "NA" : toString(eInfo.foeEstimatedConflictExitTime))
758  << std::endl;
759 #endif
761  }
762 
763 }
764 
765 
766 
767 void
769 #ifdef DEBUG_SSM
770  Encounter* e = eInfo.encounter;
771  std::cout << SIMTIME << " computeSSMs() for vehicles '"
772  << e->ego->getID() << "' and '" << e->foe->getID()
773  << "'" << std::endl;
774 #endif
775 
776  const EncounterType& type = eInfo.type;
777 
782  determineTTCandDRAC(eInfo);
783  determinePET(eInfo);
784  } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
785  determinePET(eInfo);
786  } else if (type == ENCOUNTER_TYPE_COLLISION) {
787  // TODO: handle collision
790  // No conflict measures apply for these states, which correspond to intermediate times between
791  // one vehicle leaving the conflict area and the arrival time for the other (difference corresponds to the PET)
793  // No conflict measures apply for this state
794  } else if (type == ENCOUNTER_TYPE_MERGING_PASSED || type == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
795  // No conflict measures apply for this state
796  } else if (type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
797  // No conflict measures apply for this state
798  } else {
799  std::stringstream ss;
800  ss << "'" << type << "'";
801  WRITE_WARNING("Unknown or undetermined encounter type at computeSSMs(): " + ss.str());
802  }
803 
804 #ifdef DEBUG_SSM
805  std::cout << "computeSSMs() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "':\n"
806  << " ttc=" << (eInfo.ttc == INVALID ? "INVALID" : toString(eInfo.ttc))
807  << ", drac=" << (eInfo.drac == INVALID ? "INVALID" : toString(eInfo.drac))
808  << ", pet=" << (eInfo.pet.second == INVALID ? "INVALID" : toString(eInfo.pet.second))
809  << std::endl;
810 #endif
811 }
812 
813 
814 void
816  Encounter* e = eInfo.encounter;
817  if (e->size() == 0) {
818  return;
819  }
820  const EncounterType& type = eInfo.type;
821  std::pair<double, double>& pet = eInfo.pet;
822 
823 #ifdef DEBUG_SSM
824  std::cout << SIMTIME << " determinePET() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
825  << std::endl;
826 #endif
827 
829  // For a following situation, the corresponding PET-value is merely the time-headway.
830  // TODO: Determining these can be done by comparison of memorized gaps with memorized covered distances
831  // Implementation is postponed. Tracing the time gaps (in contrast to crossing PET) corresponds to
832  // a vector of values not a single value.
833 
834  // pass
835 
836  } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
837  // TODO: Also ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA would assure that PET can be calculated
838  EncounterType prevType = static_cast<EncounterType>(e->typeSpan.back());
839  if (prevType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
840 #ifdef DEBUG_SSM
841  std::cout << "PET for crossing encounter already calculated as " << e->PET.second
842  << std::endl;
843 #endif
844  // pet must have been calculated already
845  assert(e->PET.first.second != INVALID);
846  return;
847  }
848 
849  // this situation should have emerged from one of the following
850  assert(prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
851  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER
857 
858 
859 #ifdef DEBUG_SSM
860  std::cout << "e->egoDistsToConflict.back() = " << e->egoDistsToConflict.back()
861  << "\ne->egoConflictEntryTime = " << e->egoConflictEntryTime
862  << "\ne->egoConflictExitTime = " << e->egoConflictExitTime
863  << "\ne->foeDistsToConflict.back() = " << e->foeDistsToConflict.back()
864  << "\ne->foeConflictEntryTime = " << e->foeConflictEntryTime
865  << "\ne->foeConflictExitTime = " << e->foeConflictExitTime
866  << std::endl;
867 #endif
868 
869  assert(e->foeConflictEntryTime != INVALID);
870  assert(e->egoConflictEntryTime != INVALID);
871 
872  // Both have passed the conflict entry but no collision -> one must have left the conflict region already
873  // XXX: but being an encounter of type BOTH_LEFT_CONFLICT_AREA, we should have a '&&'
875  // both have left the conflict region already (can this really occur?)
877  pet.first = e->egoConflictEntryTime;
878  pet.second = e->egoConflictEntryTime - e->foeConflictExitTime;
879  } else if (e->foeConflictEntryTime > e->egoConflictExitTime) {
880  pet.first = e->foeConflictEntryTime;
881  pet.second = e->foeConflictEntryTime - e->egoConflictExitTime;
882  } else {
883 
884 #ifdef DEBUG_SSM
885  std::cout << "Unexpected branch in determinePET: Both passed conflict area in the same step."
886  << std::endl;
887 #endif
888  pet.first = INVALID;
889  pet.second = INVALID;
890  assert(prevType != ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
892  }
893 
894  // Reset entry and exit times two allow an eventual subsequent re-use
899 
900 #ifdef DEBUG_SSM
901  std::cout << "Calculated PET = " << pet.second << " (at t=" << pet.first << ")"
902  << std::endl;
903 #endif
904  } else {
905  // other cases (merging and pre-crossing situations) do not correspond to a PET calculation.
906 #ifdef DEBUG_SSM
907  std::cout << "PET unappropriate for merging and pre-crossing situations. No calculation performed."
908  << std::endl;
909 #endif
910  return;
911  }
912 }
913 
914 
915 void
917  // TODO: check for myComputeTTC, myComputeDRAC, before doing calculations
918 
919  Encounter* e = eInfo.encounter;
920  const EncounterType& type = eInfo.type;
921  double& ttc = eInfo.ttc;
922  double& drac = eInfo.drac;
923 
924 #ifdef DEBUG_SSM
925  std::cout << SIMTIME << " determineTTCandDRAC() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' (type = " << eInfo.type << ")"
926  << std::endl;
927 #endif
928 
929  // Dependent on the actual encounter situation (eInfo.type) calculate the TTC.
930  // For merging and crossing, different cases occur when a collision during the merging / crossing process is predicted.
931  if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
932  double gap = eInfo.egoConflictEntryDist;
933  ttc = computeTTC(gap, e->ego->getSpeed(), e->foe->getSpeed());
934  drac = computeDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed());
935  } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
936  double gap = eInfo.foeConflictEntryDist;
937  ttc = computeTTC(gap, e->foe->getSpeed(), e->ego->getSpeed());
938  drac = computeDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed());
939  } else if (type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER) {
940  // TODO: calculate more specifically whether a following situation in the merge conflict area
941  // is predicted when assuming constant speeds or whether a side collision is predicted.
942  // Currently, we ignore any conflict area before the actual merging point of the lanes.
943 
944 
945  // linearly extrapolated arrival times at the conflict
946  double egoEntryTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictEntryDist / e->ego->getSpeed() : INVALID;
947  double egoExitTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictExitDist / e->ego->getSpeed() : INVALID;
948  double foeEntryTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictEntryDist / e->foe->getSpeed() : INVALID;
949  double foeExitTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictExitDist / e->foe->getSpeed() : INVALID;
950 
951 #ifdef DEBUG_SSM
952  std::cout << " Conflict times with constant speed extrapolation for merging situation:\n "
953  << " egoEntryTime=" << (egoEntryTime == INVALID ? "NA" : toString(egoEntryTime))
954  << ", egoExitTime=" << (egoExitTime == INVALID ? "NA" : toString(egoExitTime))
955  << ", foeEntryTime=" << (foeEntryTime == INVALID ? "NA" : toString(foeEntryTime))
956  << ", foeExitTime=" << (foeExitTime == INVALID ? "NA" : toString(foeExitTime))
957  << std::endl;
958 #endif
959 
960  // based on that, we obtain
961  if (egoEntryTime == INVALID || foeEntryTime == INVALID) {
962  // at least one vehicle is stopped
963  ttc = INVALID;
964  drac = INVALID;
965 #ifdef DEBUG_SSM
966  std::cout << " No TTC and DRAC computed as one vehicle is stopped." << std::endl;
967 #endif
968  } else {
969  double leaderEntryTime = MIN2(egoEntryTime, foeEntryTime);
970  double followerEntryTime = MAX2(egoEntryTime, foeEntryTime);
971  double leaderExitTime = leaderEntryTime == egoEntryTime ? egoExitTime : foeExitTime;
972  //double followerExitTime = leaderEntryTime==egoEntryTime?foeExitTime:egoExitTime;
973  double leaderSpeed = leaderEntryTime == egoEntryTime ? e->ego->getSpeed() : e->foe->getSpeed();
974  double followerSpeed = leaderEntryTime == egoEntryTime ? e->foe->getSpeed() : e->ego->getSpeed();
975  double leaderConflictDist = leaderEntryTime == egoEntryTime ? eInfo.egoConflictEntryDist : eInfo.foeConflictEntryDist;
976  double followerConflictDist = leaderEntryTime == egoEntryTime ? eInfo.foeConflictEntryDist : eInfo.egoConflictEntryDist;
977  double leaderLength = leaderEntryTime == egoEntryTime ? e->ego->getLength() : e->foe->getLength();
978  if (leaderExitTime >= followerEntryTime) {
979  // collision would occur at merge area (XXX: currently only the crossection corresponding to the target lane's begin)
980  ttc = computeTTC(followerConflictDist, followerSpeed, 0.);
981  // TODO: calculate more specific drac (no need to completely stop before conflict area)
982  drac = computeDRAC(followerConflictDist, followerSpeed, 0.);
983 
984 #ifdef DEBUG_SSM
985  std::cout << " Extrapolation predicts collision *at* merge point with TTC=" << ttc
986  << ", drac=" << drac << std::endl;
987 #endif
988 
989  } else {
990  // -> No collision at the merge area
991  // Check if after merge a collision would occur if speeds are hold constant.
992  double gapAfterMerge = followerConflictDist - leaderExitTime * followerSpeed;
993  assert(gapAfterMerge >= 0);
994 
995  // ttc as for following situation (no collision until leader merged)
996  double ttcAfterMerge = computeTTC(gapAfterMerge, followerSpeed, leaderSpeed);
997  ttc = ttcAfterMerge == INVALID ? INVALID : leaderExitTime + ttcAfterMerge;
998 
999  // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
1000  double g0 = followerConflictDist - leaderConflictDist - leaderLength;
1001  // Speed difference. (Must be positive if g0<0)
1002  double dv = leaderSpeed - followerSpeed;
1003  if (g0 < 0) {
1004  assert(dv > 0);
1005  // no deceleration needed for dv>0 and gap after merge >= 0
1006  drac = INVALID;
1007  } else {
1008  // compute drac as for a following situation
1009  drac = computeDRAC(g0, followerSpeed, leaderSpeed);
1010  }
1011 
1012 #ifdef DEBUG_SSM
1013  if (ttc == INVALID) {
1014  assert(dv >= 0);
1015  assert(drac == INVALID);
1016  std::cout << " Extrapolation does not predict any collision." << std::endl;
1017  } else {
1018  std::cout << " Extrapolation predicts collision *after* merge point with TTC="
1019  << (ttc == INVALID ? "NA" : toString(ttc))
1020  << ", drac=" << (drac == INVALID ? "NA" : toString(drac)) << std::endl;
1021  }
1022 #endif
1023 
1024  }
1025  }
1026  } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1028  drac = computeDRAC(eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, e->ego->getSpeed(), e->foe->getSpeed(),
1031  // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1032  double gap = eInfo.egoConflictEntryDist;
1033  ttc = computeTTC(gap, e->ego->getSpeed(), 0.);
1034  } else {
1035  // encounter is expected to happen without collision
1036  ttc = INVALID;
1037  }
1038  } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
1040  drac = computeDRAC(eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, e->ego->getSpeed(), e->foe->getSpeed(),
1043  // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1044  double gap = eInfo.foeConflictEntryDist;
1045  ttc = computeTTC(gap, e->foe->getSpeed(), 0.);
1046  } else {
1047  // encounter is expected to happen without collision
1048  ttc = INVALID;
1049  }
1050  } else {
1051 #ifdef DEBUG_SSM
1052  std::stringstream ss;
1053  ss << "'" << type << "'";
1054  WRITE_WARNING("Underspecified or unknown encounter type in MSDevice_SSM::determineTTCandDRAC(): " + ss.str());
1055 #endif
1056  }
1057 
1058 #ifdef DEBUG_SSM
1059  std::cout << "ttc=" << (ttc == INVALID ? "INVALID" : toString(ttc)) << ", drac=" << (drac == INVALID ? "INVALID" : toString(drac))
1060  << std::endl;
1061 #endif
1062 }
1063 
1064 
1065 double
1066 MSDevice_SSM::computeTTC(double gap, double followerSpeed, double leaderSpeed) const {
1067  // TODO: in merging or crossing situations, the TTC may be lower than the one computed here for following situations
1068  // More specifically, the followers conflict time entry should be less than the leaders conflict exit time.
1069  // For merging conflicts, the minimum has to be taken from the two if a collision at merge was predicted.
1070 #ifdef DEBUG_SSM
1071  std::cout << "computeTTC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1072  << std::endl;
1073 #endif
1074  if (gap <= 0.) {
1075  return 0.; // collision already happend
1076  }
1077  double dv = followerSpeed - leaderSpeed;
1078  if (dv <= 0.) {
1079  return INVALID; // no collision
1080  }
1081 
1082  return gap / dv;
1083 }
1084 
1085 
1086 double
1087 MSDevice_SSM::computeDRAC(double gap, double followerSpeed, double leaderSpeed) {
1088 #ifdef DEBUG_SSM
1089  std::cout << "computeDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1090  << std::endl;
1091 #endif
1092  if (gap <= 0.) {
1093  return INVALID; // collision!
1094  }
1095  double dv = followerSpeed - leaderSpeed;
1096  if (dv <= 0.) {
1097  return INVALID; // no collision
1098  }
1099  assert(followerSpeed > 0.);
1100  double timeHeadway = gap / followerSpeed;
1101  return dv / timeHeadway;
1102 }
1103 
1104 double
1105 MSDevice_SSM::computeDRAC(double d1, double d2, double v1, double v2, double t1, double t2) {
1106 #ifdef DEBUG_SSM
1107  std::cout << "computeDRAC() with d1=" << d1 << ", d2=" << d2 << ", v1=" << v1 << ", v2=" << v2
1108  << ", t1=" << (t1 == INVALID ? "NA" : toString(t1)) << ", t2=" << (t2 == INVALID ? "NA" : toString(t2))
1109  << std::endl;
1110 #endif
1111  if (d1 <= 0. && d2 <= 0.) {
1112  return INVALID; // both already entered conflict area
1113  }
1114 
1115  double drac = 0;
1116  if (d1 > 0. && t1 != INVALID && d2 > 0. && t2 != INVALID) {
1117  drac = MIN2(2 * (v1 - d1 / t2) / t2, 2 * (v2 - d2 / t1) / t1);
1118  } else if (d1 > 0. && t2 != INVALID) {
1119  drac = 2 * (v1 - d1 / t2) / t2;
1120  } else if (d2 > 0. && t1 != INVALID) { // d2>0.
1121  drac = 2 * (v2 - d2 / t1) / t1;
1122  } else {
1123  // TODO: there might still be situations to cover,
1124  // since t1==INVALID indicates that the first vehicle is extrapolated
1125  // to stop before the conflict exit, which may still mean *on the conflict area*,
1126  // This is ignored so far.
1127  return INVALID;
1128  }
1129 
1130  return drac > 0 ? drac : INVALID;
1131 }
1132 
1133 void
1135  // determine exact entry and exit times
1136  Encounter* e = eInfo.encounter;
1137 
1138 #ifdef DEBUG_SSM
1139  std::cout << SIMTIME << " checkConflictEntryAndExit() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'" << std::endl;
1140 #endif
1141 
1142  // Distances to conflict area boundaries in previous step
1143  double prevEgoConflictEntryDist = eInfo.egoConflictEntryDist + e->ego->getLastStepDist();
1144  double prevFoeConflictEntryDist = eInfo.foeConflictEntryDist + e->foe->getLastStepDist();
1145  double prevEgoConflictExitDist = prevEgoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
1146  double prevFoeConflictExitDist = prevFoeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
1147  EncounterType prevType = e->currentType;
1148 
1149  if (e->timeSpan.size() == 0) {
1150  // Encounter has just been created, no data points yet.
1151  // If a vehicle was already beyond conflict area boundary in last step, we set the passing time to the previous time step
1152  prevEgoConflictEntryDist = MAX2(prevEgoConflictEntryDist, 0.);
1153  prevFoeConflictEntryDist = MAX2(prevFoeConflictEntryDist, 0.);
1154  prevEgoConflictExitDist = MAX2(prevEgoConflictExitDist, 0.);
1155  prevFoeConflictExitDist = MAX2(prevFoeConflictExitDist, 0.);
1156  }
1157 
1158 
1159  // Check if ego entered in last step
1160  if (e->egoConflictEntryTime == INVALID && eInfo.egoConflictEntryDist < 0 && prevEgoConflictEntryDist >= 0) {
1161  // ego must have entered the conflict in the last step. Determine exact entry time
1162  e->egoConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictEntryDist, 0., -eInfo.egoConflictEntryDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1163 #ifdef DEBUG_SSM
1164  std::cout << " ego entered conflict area at t=" << e->egoConflictEntryTime << std::endl;
1165 #endif
1166  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1167  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1168  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1170  }
1171  }
1172 
1173  // Check if foe entered in last step
1174  if (e->foeConflictEntryTime == INVALID && eInfo.foeConflictEntryDist < 0. && prevFoeConflictEntryDist >= 0) {
1175  // foe must have entered the conflict in the last step. Determine exact entry time
1176  e->foeConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictEntryDist, 0., -eInfo.foeConflictEntryDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1177 #ifdef DEBUG_SSM
1178  std::cout << " foe entered conflict area at t=" << e->foeConflictEntryTime << std::endl;
1179 #endif
1180  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1181  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1182  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1184  }
1185  }
1186 
1187  // Check if ego left conflict area
1188  if (e->egoConflictExitTime == INVALID && eInfo.egoConflictExitDist < 0 && prevEgoConflictExitDist >= 0) {
1189  // ego must have left the conflict area in the last step. Determine exact exit time
1190  e->egoConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictExitDist, 0., -eInfo.egoConflictExitDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1191  // Add cross section to calculate PET for foe
1192  e->foePETCrossSections.push_back(std::make_pair(eInfo.foeConflictEntryCrossSection, e->egoConflictExitTime));
1193 #ifdef DEBUG_SSM
1194  std::cout << " ego left conflict area at t=" << e->egoConflictExitTime << std::endl;
1195 #endif
1196  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1197  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1198  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1200  }
1201  }
1202 
1203  // Check if foe left conflict area
1204  if (e->foeConflictExitTime == INVALID && eInfo.foeConflictExitDist < 0 && prevFoeConflictExitDist >= 0) {
1205  // foe must have left the conflict area in the last step. Determine exact exit time
1206  e->foeConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictExitDist, 0., -eInfo.foeConflictExitDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1207  // Add cross section to calculate PET for ego
1208  e->egoPETCrossSections.push_back(std::make_pair(eInfo.egoConflictEntryCrossSection, e->foeConflictExitTime));
1209 #ifdef DEBUG_SSM
1210  std::cout << " foe left conflict area at t=" << e->foeConflictExitTime << std::endl;
1211 #endif
1212  // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1213  if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1214  || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1216  }
1217  }
1218 }
1219 
1220 
1221 void
1223 
1224 #ifdef DEBUG_SSM
1225  std::cout << SIMTIME << " updatePassedEncounter() for vehicles '"
1226  << e->egoID << "' and '" << e->foeID << "'"
1227  << std::endl;
1228 #endif
1229 
1230  if (foeInfo == 0) {
1231  // the foe is out of the device's range, proceed counting down the remaining extra time to trace
1232  e->countDownExtraTime(TS);
1233 #ifdef DEBUG_SSM
1234  std::cout << " Foe is out of range. Counting down extra time."
1235  << " Remaining seconds before closing encounter: " << e->getRemainingExtraTime()
1236  << std::endl;
1237 #endif
1238 
1239  } else {
1240  // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
1242  }
1243 
1244  // Check, whether this was really a potential conflict at some time:
1245  // Search through typeSpan for a type other than no conflict
1246  EncounterType lastPotentialConflictType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1247 
1248  if (lastPotentialConflictType == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
1249  // This encounter was no conflict in the last step -> remains so
1250 #ifdef DEBUG_SSM
1251  std::cout << " This encounter wasn't classified as a potential conflict lately." << std::endl;
1252 #endif
1253  if (foeInfo == 0) {
1254  // Encounter was either never a potential conflict and foe is out of range
1255  // or the foe has left the network
1256  // -> no use in further tracing this encounter
1257  e->closingRequested = true;
1258 #ifdef DEBUG_SSM
1259  std::cout << " Closing encounter." << std::endl;
1260 #endif
1262  }
1263  } else if (lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER
1264  || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_LEADER
1265  || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
1266  // if a following situation leads to a no-conflict situation this encounter switches no-conflict, since no further computations (PET) are needed.
1268 #ifdef DEBUG_SSM
1269  std::cout << " Encounter was previously classified as a follow/lead situation." << std::endl;
1270 #endif
1271  } else if (lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_FOLLOWER
1272  || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_LEADER
1273  || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_PASSED) {
1274  // if a merging situation leads to a no-conflict situation the leader was either removed from the net (we disregard special treatment)
1275  // or route- or lane-changes removed the conflict.
1277 #ifdef DEBUG_SSM
1278  std::cout << " Encounter was previously classified as a merging situation." << std::endl;
1279 #endif
1280  }
1281  if (lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1282  || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER
1283  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
1284  || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
1285  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
1286  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1287  || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1288  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
1289  || lastPotentialConflictType == ENCOUNTER_TYPE_COLLISION) {
1290  // Encounter has been a crossing situation.
1291 
1292 #ifdef DEBUG_SSM
1293  std::cout << " Encounter was previously classified as a crossing situation of type " << lastPotentialConflictType << "." << std::endl;
1294 #endif
1295  // For passed encounters, the xxxConflictAreaLength variables are not determined before -> we use the stored values.
1296 
1297  // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
1298  if (eInfo.egoConflictAreaLength == INVALID) {
1299  eInfo.egoConflictAreaLength = e->foe->getWidth();
1300  }
1301  if (eInfo.foeConflictAreaLength == INVALID) {
1302  eInfo.foeConflictAreaLength = e->ego->getWidth();
1303  }
1304 
1305  eInfo.egoConflictEntryDist = e->egoDistsToConflict.back() - e->ego->getLastStepDist();
1307  eInfo.foeConflictEntryDist = e->foeDistsToConflict.back() - e->foe->getLastStepDist();
1309 
1310 #ifdef DEBUG_SSM
1311  std::cout << " egoConflictEntryDist = " << eInfo.egoConflictEntryDist
1312  << ", egoConflictExitDist = " << eInfo.egoConflictExitDist
1313  << "\n foeConflictEntryDist = " << eInfo.foeConflictEntryDist
1314  << ", foeConflictExitDist = " << eInfo.foeConflictExitDist
1315  << std::endl;
1316 #endif
1317 
1318  // Determine actual encounter type
1319  bool egoEnteredConflict = eInfo.egoConflictEntryDist < 0.;
1320  bool foeEnteredConflict = eInfo.foeConflictEntryDist < 0.;
1321  bool egoLeftConflict = eInfo.egoConflictExitDist < 0.;
1322  bool foeLeftConflict = eInfo.foeConflictExitDist < 0.;
1323 
1324  if ((!egoEnteredConflict) && !foeEnteredConflict) {
1325  // XXX: do we need to recompute the follow/lead order, here?
1326  assert(lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1327  || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER);
1328  eInfo.type = lastPotentialConflictType;
1329  } else if (egoEnteredConflict && !foeEnteredConflict) {
1331  } else if ((!egoEnteredConflict) && foeEnteredConflict) {
1333  } else { // (egoEnteredConflict && foeEnteredConflict) {
1335  }
1336 
1337  if ((!egoLeftConflict) && !foeLeftConflict) {
1340  }
1341  } else if (egoLeftConflict && !foeLeftConflict) {
1344  }
1345  } else if ((!egoLeftConflict) && foeLeftConflict) {
1348  }
1349  } else {
1351  // It should not occur that both leave the conflict at the same step
1352  assert(lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1353  || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1354  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
1355  || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA);
1356  }
1357 
1358  // TODO: adjust the conflict distances according to lateral movement for single ENTERED-cases
1359 
1360 #ifdef DEBUG_SSM
1361  std::cout << " Updated classification: " << eInfo.type << std::endl;
1362 #endif
1363  }
1364 }
1365 
1366 
1369 #ifdef DEBUG_SSM
1370  std::cout << "classifyEncounter() called." << std::endl;
1371 #endif
1372  if (foeInfo == 0) {
1373  // foeInfo == 0 signalizes, that no corresponding foe info was returned by findSurroundingVehicles(),
1374  // i.e. the foe is actually out of range (This may also mean that it has left the network)
1376  }
1377  const Encounter* e = eInfo.encounter;
1378 
1379  // previous classification (if encounter was not just created)
1380  EncounterType prevType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1381  if (e->typeSpan.size() > 0
1387  // This is an ongoing crossing situation with at least one of the vehicles not
1388  // having passed the conflict area.
1389  // -> Merely trace the change of distances to the conflict entry / exit
1390  // -> Derefer this to updatePassedEncounter, where this is done anyhow.
1391 #ifdef DEBUG_SSM
1392  std::cout << " Ongoing crossing conflict will be traced by passedEncounter()." << std::endl;
1393 #endif
1394  return prevType;
1395  }
1396 
1397 
1398  // Ego's current Lane
1399  const MSLane* egoLane = e->ego->getLane();
1400  // Foe's current Lane
1401  const MSLane* foeLane = e->foe->getLane();
1402 
1403  // Ego's conflict lane is memorized in foeInfo
1404  const MSLane* egoConflictLane = foeInfo->egoConflictLane;
1405  double egoDistToConflictLane = foeInfo->egoDistToConflictLane;
1406  // Find conflicting lane and the distance to its entry link for the foe
1407  double foeDistToConflictLane;
1408  const MSLane* foeConflictLane = findFoeConflictLane(e->foe, foeInfo->egoConflictLane, foeDistToConflictLane);
1409 
1410 #ifdef DEBUG_SSM
1411  std::cout << "egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
1412  << "foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'"
1413  << "\nEgo's distance to conflict lane: " << egoDistToConflictLane
1414  << "\nFoe's distance to conflict lane: " << foeDistToConflictLane
1415  << std::endl;
1416 #endif
1417 
1418  // Treat different cases for foeConflictLane and egoConflictLane (internal or non-internal / equal to egoLane or to foeLane),
1419  // and thereby determine encounterType and the ego/foeEncounterDistance.
1420  // The encounter distance has a different meaning for different types of encounters:
1421  // 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.
1422  // 2) For merging encounters the encounter distance is the distance until the begin of the common target edge/lane.
1423  // (XXX: Perhaps this should be adjusted to include the entry point to the region where a simultaneous occupancy of
1424  // both merging lanes could imply a collision)
1425  // 3) For crossing encounters the encounter distances is the distance until the entry point to the conflicting lane.
1426 
1428 
1429  if (foeConflictLane == 0) {
1430  // foe vehicle is not on course towards the ego's route (see findFoeConflictLane)
1432 #ifdef DEBUG_SSM
1433  std::cout << "-> Encounter type: No conflict." << std::endl;
1434 #endif
1435  } else if (!egoConflictLane->isInternal()) {
1436  // The conflict lane is non-internal, therefore we either have no potential conflict or a lead/follow situation (i.e., no crossing or merging)
1437  if (egoConflictLane == egoLane) {
1438  // The conflict point is on the ego's current lane.
1439  if (foeLane == egoLane) {
1440  // Foe is on the same non-internal lane
1441  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
1444  } else {
1447  }
1448 #ifdef DEBUG_SSM
1449  std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'" << std::endl;
1450 #endif
1451  } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
1452  // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
1453  // XXX: this disregards conflicts for vehicles on adjacent lanes
1455 #ifdef DEBUG_SSM
1456  std::cout << "-> Encounter type: " << type << std::endl;
1457 #endif
1458  } else {
1459  assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
1460  assert(egoDistToConflictLane <= 0);
1461  // Foe must be on a route leading into the ego's edge
1462  if (foeConflictLane == egoLane) {
1464  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1465 
1466 #ifdef DEBUG_SSM
1467  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1468  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1469  << " (gap = " << eInfo.foeConflictEntryDist << ")"
1470  << std::endl;
1471 #endif
1472  } else {
1473  // Foe's route leads to an adjacent lane of the current lane of the ego
1475 #ifdef DEBUG_SSM
1476  std::cout << "-> Encounter type: " << type << std::endl;
1477 #endif
1478  }
1479  }
1480  } else {
1481  // 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
1482  // is located on the foe's current edge see findSurroundingVehicles()
1483  // (otherwise the foe would have had to enter the ego's route along a junction and the corresponding
1484  // conflict lane would be internal)
1485  assert(&(foeLane->getEdge()) == &(egoConflictLane->getEdge()));
1486  assert(foeDistToConflictLane <= 0);
1487  if (foeLane == egoConflictLane) {
1489  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1490 #ifdef DEBUG_SSM
1491  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1492  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1493  << " (gap = " << eInfo.egoConflictEntryDist << ")"
1494  << std::endl;
1495 #endif
1496  } else {
1497  // Ego's route leads to an adjacent lane of the current lane of the foe
1499 #ifdef DEBUG_SSM
1500  std::cout << "-> Encounter type: " << type << std::endl;
1501 #endif
1502  }
1503  }
1504  } else {
1505  // egoConflictLane is internal, i.e., lies on a junction. Besides the lead/follow situation (which may stretch over different lanes of a connection),
1506  // merging or crossing of the conflict lanes is possible.
1507  assert(foeConflictLane->isInternal());
1508  MSLink* egoEntryLink = egoConflictLane->getEntryLink();
1509  MSLink* foeEntryLink = foeConflictLane->getEntryLink();
1510  if (&(egoEntryLink->getViaLane()->getEdge()) == &(foeEntryLink->getViaLane()->getEdge())) {
1511  if (egoEntryLink != foeEntryLink) {
1512  // XXX: this disregards conflicts for vehicles on adjacent internal lanes
1514 #ifdef DEBUG_SSM
1515  std::cout << "-> Encounter type: " << type << std::endl;
1516 #endif
1517  } else {
1518  // Lead / follow situation on connection
1519  if (egoLane == egoConflictLane && foeLane != foeConflictLane) {
1520  // ego on junction, foe not yet
1522  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1523 #ifdef DEBUG_SSM
1524  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1525  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1526  << " (gap = " << eInfo.foeConflictEntryDist << ")"
1527  << std::endl;
1528 #endif
1529  } else if (egoLane != egoConflictLane && foeLane == foeConflictLane) {
1530  // foe on junction, ego not yet
1532  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1533 #ifdef DEBUG_SSM
1534  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1535  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1536  << " (gap = " << eInfo.egoConflictEntryDist << ")"
1537  << std::endl;
1538 #endif
1539  } else {
1540  // Both must be already on the junction in a lead / follow situation on a connection
1541  // (since they approach via the same link, findSurroundingVehicles() would have determined a
1542  // different conflictLane if both are not on the junction)
1543  assert(egoLane == egoConflictLane);
1544  assert(foeLane == foeConflictLane);
1545  if (egoLane == foeLane) {
1546  // both on the same internal lane
1547  if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
1549  eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1550 #ifdef DEBUG_SSM
1551  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1552  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1553  << " (gap = " << eInfo.foeConflictEntryDist << ")"
1554  << std::endl;
1555 #endif
1556  } else {
1558  eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1559 #ifdef DEBUG_SSM
1560  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1561  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1562  << " (gap = " << eInfo.egoConflictEntryDist << ")"
1563  << std::endl;
1564 #endif
1565  }
1566  } else {
1567  // ego and foe on distinct, consecutive internal lanes
1568 #ifdef DEBUG_SSM
1569  std::cout << " Lead/follow situation on consecutive internal lanes." << std::endl;
1570 #endif
1571  MSLane* lane = egoEntryLink->getViaLane();
1572 #ifdef _MSC_VER
1573 #pragma warning(push)
1574 #pragma warning(disable: 4127) // do not warn about constant conditional expression
1575 #endif
1576  while (true) {
1577 #ifdef _MSC_VER
1578 #pragma warning(pop)
1579 #endif
1580  // Find first of egoLane and foeLane while crossing the junction (this dertermines who's the follower)
1581  // Then set the conflict lane to the lane of the leader and adapt the follower's distance to conflict
1582  if (egoLane == lane) {
1583  // ego is follower
1585  // adapt conflict dist
1586  eInfo.egoConflictEntryDist = egoDistToConflictLane;
1587  while (lane != foeLane) {
1588  eInfo.egoConflictEntryDist += lane->getLength();
1589  lane = lane->getLinkCont()[0]->getViaLane();
1590  assert(lane != 0);
1591  }
1593  egoConflictLane = lane;
1594 #ifdef DEBUG_SSM
1595  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1596  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1597  << " (gap = " << eInfo.egoConflictEntryDist << ")"
1598  << std::endl;
1599 #endif
1600  break;
1601  } else if (foeLane == lane) {
1602  // ego is leader
1604  // adapt conflict dist
1605  eInfo.foeConflictEntryDist = foeDistToConflictLane;
1606  while (lane != egoLane) {
1607  eInfo.foeConflictEntryDist += lane->getLength();
1608  lane = lane->getLinkCont()[0]->getViaLane();
1609  assert(lane != 0);
1610  }
1612  foeConflictLane = lane;
1613 #ifdef DEBUG_SSM
1614  std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1615  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1616  << " (gap = " << eInfo.foeConflictEntryDist << ")"
1617  << std::endl;
1618 #endif
1619  break;
1620  }
1621  lane = lane->getLinkCont()[0]->getViaLane();
1622  assert(lane != 0);
1623  }
1624  }
1625 #ifdef DEBUG_SSM
1626  std::cout << "-> Encounter type: Lead/follow-situation on connection from '" << egoEntryLink->getLaneBefore()->getID()
1627  << "' to '" << egoEntryLink->getLane()->getID() << "'" << std::endl;
1628 #endif
1629  }
1630  }
1631  } else {
1632  // Entry links to junctions lead to different internal edges.
1633  // There are three possibilities, either the edges cross, merge or have no conflict
1634  const std::vector<MSLink*>& egoFoeLinks = egoEntryLink->getFoeLinks();
1635  const std::vector<MSLink*>& foeFoeLinks = foeEntryLink->getFoeLinks();
1636  // Determine whether ego and foe links are foes
1637  bool crossOrMerge = (find(egoFoeLinks.begin(), egoFoeLinks.end(), foeEntryLink) != egoFoeLinks.end()
1638  || find(foeFoeLinks.begin(), foeFoeLinks.end(), egoEntryLink) != foeFoeLinks.end());
1639  if (!crossOrMerge) {
1640 // if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
1641 // // XXX: the situation of merging into adjacent lanes is disregarded for now <- the alleged situation appears to imply crossOrMerge!!!
1642 // type = ENCOUNTER_TYPE_MERGING_ADJACENT;
1643 //#ifdef DEBUG_SSM
1644 // std::cout << "-> Encounter type: No conflict (adjacent lanes)." << std::endl;
1645 //#endif
1646 // } else {
1648 #ifdef DEBUG_SSM
1649  std::cout << "-> Encounter type: No conflict." << std::endl;
1650 #endif
1651 // }
1652  } else if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
1653  if (foeEntryLink->getLane() == egoEntryLink->getLane()) {
1654  type = ENCOUNTER_TYPE_MERGING;
1655  eInfo.egoConflictEntryDist = egoDistToConflictLane + egoEntryLink->getInternalLengthsAfter();
1656  eInfo.foeConflictEntryDist = foeDistToConflictLane + foeEntryLink->getInternalLengthsAfter();
1657 #ifdef DEBUG_SSM
1658  std::cout << "-> Encounter type: Merging situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
1659  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1660  << "\nDistances to merge-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
1661  << std::endl;
1662 #endif
1663  } else {
1664  // Links leading to the same edge but different lanes. XXX: Disregards conflicts on adjacent lanes
1666 #ifdef DEBUG_SSM
1667  std::cout << "-> Encounter type: No conflict: " << type << std::endl;
1668 #endif
1669  }
1670  } else {
1671  type = ENCOUNTER_TYPE_CROSSING;
1672 
1673  assert(egoConflictLane->isInternal());
1674  assert(foeConflictLane->getEdge().getToJunction() == egoConflictLane->getEdge().getToJunction());
1675 
1676  // If the conflict lanes are internal, they may not correspond to the
1677  // actually crossing parts of the corresponding connections.
1678  // Adjust the conflict lanes accordingly.
1679  // set back both to the first parts of the corresponding connections
1680  double offset = 0.;
1681  egoConflictLane = egoConflictLane->getFirstInternalInConnection(offset);
1682  egoDistToConflictLane -= offset;
1683  foeConflictLane = foeConflictLane->getFirstInternalInConnection(offset);
1684  foeDistToConflictLane -= offset;
1685  // find the distances to the conflict from the junction entry for both vehicles
1686  // Here we also determine the real crossing lanes (before the conflict lane is the first lane of the connection)
1687  // for the ego
1688  double egoDistToConflictFromJunctionEntry = INVALID;
1689  double foeInternalLaneLengthsBeforeCrossing = 0.;
1690  while (foeConflictLane != 0 && foeConflictLane->isInternal()) {
1691  egoDistToConflictFromJunctionEntry = egoEntryLink->getLengthsBeforeCrossing(foeConflictLane);
1692  if (egoDistToConflictFromJunctionEntry != INVALID) {
1693  // found correct foeConflictLane
1694  egoDistToConflictFromJunctionEntry += 0.5 * (foeConflictLane->getWidth() - e->foe->getVehicleType().getWidth());
1695  break;
1696  } else {
1697  foeInternalLaneLengthsBeforeCrossing += foeConflictLane->getLength();
1698  }
1699  foeConflictLane = foeConflictLane->getCanonicalSuccessorLane();
1700  assert(foeConflictLane != 0 && foeConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
1701  }
1702  assert(egoDistToConflictFromJunctionEntry != INVALID);
1703 
1704  // for the foe
1705  double foeDistToConflictFromJunctionEntry = INVALID;
1706  double egoInternalLaneLengthsBeforeCrossing = 0.;
1707  foeDistToConflictFromJunctionEntry = INVALID;
1708  while (egoConflictLane != 0 && egoConflictLane->isInternal()) {
1709  foeDistToConflictFromJunctionEntry = foeEntryLink->getLengthsBeforeCrossing(egoConflictLane);
1710  if (foeDistToConflictFromJunctionEntry != INVALID) {
1711  // found correct egoConflictLane
1712  foeDistToConflictFromJunctionEntry += 0.5 * (egoConflictLane->getWidth() - e->ego->getVehicleType().getWidth());
1713  break;
1714  } else {
1715  egoInternalLaneLengthsBeforeCrossing += egoConflictLane->getLength();
1716  }
1717  egoConflictLane = egoConflictLane->getCanonicalSuccessorLane();
1718  assert(egoConflictLane != 0 && egoConflictLane->isInternal()); // this loop should be ended by the break! Otherwise the lanes do not cross, which should be the case here.
1719  }
1720  assert(foeDistToConflictFromJunctionEntry != INVALID);
1721 
1722  // store conflict entry information in eInfo
1723  // TODO: equip these with exit times to store relevant PET sections in encounter
1724  eInfo.egoConflictEntryCrossSection = std::make_pair(egoConflictLane, egoDistToConflictFromJunctionEntry - egoInternalLaneLengthsBeforeCrossing);
1725  eInfo.foeConflictEntryCrossSection = std::make_pair(foeConflictLane, foeDistToConflictFromJunctionEntry - foeInternalLaneLengthsBeforeCrossing);
1726 
1727  // Take into account the lateral position for the exact determination of the conflict point
1728  // whether lateral position increases or decreases conflict distance depends on lane angles at conflict
1729  // -> conflictLaneOrientation in {-1,+1}
1730  // First, measure the angle between the two connection lines (straight lines from junction entry point to junction exit point)
1731  Position egoEntryPos = egoEntryLink->getViaLane()->getShape().front();
1732  Position egoExitPos = egoEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
1733  PositionVector egoConnectionLine(egoEntryPos, egoExitPos);
1734  Position foeEntryPos = foeEntryLink->getViaLane()->getShape().front();
1735  Position foeExitPos = foeEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
1736  PositionVector foeConnectionLine(foeEntryPos, foeExitPos);
1737  double angle = std::fmod(egoConnectionLine.rotationAtOffset(0.) - foeConnectionLine.rotationAtOffset(0.), (2 * M_PI));
1738  if (angle < 0) {
1739  angle += 2 * M_PI;
1740  }
1741  assert(angle >= 0);
1742  assert(angle <= 2 * M_PI);
1743  if (angle > M_PI) {
1744  angle -= 2 * M_PI;
1745  }
1746  assert(angle >= -M_PI);
1747  assert(angle <= M_PI);
1748  // Determine orientation of the connection lines. (Positive values mean that the ego vehicle approaches from the foe's left side.)
1749  double crossingOrientation = (angle < 0) - (angle > 0);
1750 
1751  // Adjust conflict dist to lateral positions
1752  // TODO: This could more precisely be calculated wrt the angle of the crossing *at the conflict point*
1753  egoDistToConflictFromJunctionEntry -= crossingOrientation * e->foe->getLateralPositionOnLane();
1754  foeDistToConflictFromJunctionEntry += crossingOrientation * e->ego->getLateralPositionOnLane();
1755 
1756  // Complete entry distances
1757  eInfo.egoConflictEntryDist = egoDistToConflictLane + egoDistToConflictFromJunctionEntry;
1758  eInfo.foeConflictEntryDist = foeDistToConflictLane + foeDistToConflictFromJunctionEntry;
1759 
1760 
1761 #ifdef DEBUG_SSM
1762  std::cout << " Determined exact conflict distances for crossing conflict."
1763  << "\n crossingOrientation=" << crossingOrientation
1764  << ", egoCrossingAngle=" << egoConnectionLine.rotationAtOffset(0.)
1765  << ", foeCrossingAngle=" << foeConnectionLine.rotationAtOffset(0.)
1766  << ", relativeAngle=" << angle
1767  << " (foe from " << (crossingOrientation > 0 ? "right)" : "left)")
1768  << "\n resulting offset for conflict entry distance:"
1769  << "\n ego=" << crossingOrientation* e->foe->getLateralPositionOnLane()
1770  << ", foe=" << crossingOrientation* e->ego->getLateralPositionOnLane()
1771  << "\n resulting entry distances:"
1772  << "\n ego=" << eInfo.egoConflictEntryDist
1773  << ", foe=" << eInfo.foeConflictEntryDist
1774  << std::endl;
1775 #endif
1776 
1777  // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
1778  eInfo.egoConflictAreaLength = e->foe->getWidth();
1779  eInfo.foeConflictAreaLength = e->ego->getWidth();
1780 
1781  // resulting exit distances
1784 
1785 #ifdef DEBUG_SSM
1786  std::cout << "real egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
1787  << "real foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
1788  << "-> Encounter type: Crossing situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
1789  << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1790  << "\nDistances to crossing-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
1791  << std::endl;
1792 #endif
1793  }
1794  }
1795  }
1796  return type;
1797 }
1798 
1799 
1800 const MSLane*
1801 MSDevice_SSM::findFoeConflictLane(const MSVehicle* foe, const MSLane* egoConflictLane, double& distToConflictLane) const {
1802 
1803 #ifdef DEBUG_SSM
1804  std::cout << SIMTIME << " findFoeConflictLane() for foe '"
1805  << foe->getID() << "' on lane '" << foe->getLane()->getID()
1806  << "' (with egoConflictLane=" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID())
1807  << ")\nfoeBestLanes: " << toString(foe->getBestLanesContinuation())
1808  << std::endl;
1809 #endif
1810  MSLane* foeLane = foe->getLane();
1811  std::vector<MSLane*>::const_iterator laneIter = foe->getBestLanesContinuation().begin();
1812  std::vector<MSLane*>::const_iterator foeBestLanesEnd = foe->getBestLanesContinuation().end();
1813  assert(foeLane->isInternal() || *laneIter == foeLane);
1814  distToConflictLane = -foe->getPositionOnLane();
1815 
1816  // Potential conflict lies on junction if egoConflictLane is internal
1817  const MSJunction* conflictJunction = egoConflictLane->isInternal() ? egoConflictLane->getEdge().getToJunction() : 0;
1818 #ifdef DEBUG_SSM
1819  if (conflictJunction != 0) {
1820  std::cout << "Potential conflict on junction '" << conflictJunction->getID()
1821  << std::endl;
1822  }
1823 #endif
1824  if (foeLane->isInternal() && foeLane->getEdge().getToJunction() == conflictJunction) {
1825  // foe is already on the conflict junction
1826  return foeLane;
1827  }
1828 
1829  // Foe is not on the conflict junction
1830 
1831  // Leading internal lanes in bestlanes are resembled as a single NULL-pointer skip them
1832  if (*laneIter == 0) {
1833  while (foeLane != 0 && foeLane->isInternal()) {
1834  distToConflictLane += foeLane->getLength();
1835  foeLane = foeLane->getLinkCont()[0]->getViaLane();
1836  }
1837  ++laneIter;
1838  assert(laneIter == foeBestLanesEnd || *laneIter != 0);
1839  }
1840 
1841  // Look for the junction downstream along foeBestLanes
1842  while (laneIter != foeBestLanesEnd && distToConflictLane <= myRange) {
1843  // Eventual internal lanes were skipped
1844  assert(*laneIter == foeLane || foeLane == 0);
1845  foeLane = *laneIter;
1846  assert(!foeLane->isInternal());
1847  if (&foeLane->getEdge() == &egoConflictLane->getEdge()) {
1848 #ifdef DEBUG_SSM
1849  std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
1850 #endif
1851  // found the potential conflict edge along foeBestLanes
1852  return foeLane;
1853  }
1854  // No conflict on foeLane
1855  distToConflictLane += foeLane->getLength();
1856 
1857  // set laneIter to next non internal lane along foeBestLanes
1858  ++laneIter;
1859  if (laneIter == foeBestLanesEnd) {
1860  return 0;
1861  }
1862  MSLane* nextNonInternalLane = *laneIter;
1863  MSLink* link = foeLane->getLinkTo(nextNonInternalLane);
1864  // Set foeLane to first internal lane on the next junction
1865  foeLane = link->getViaLane();
1866  assert(foeLane->isInternal());
1867  if (foeLane->getEdge().getToJunction() == conflictJunction) {
1868 #ifdef DEBUG_SSM
1869  std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
1870 #endif
1871  // found egoConflictLane, resp. the conflict junction, along foeBestLanes
1872  return foeLane;
1873  }
1874  // No conflict on junction
1875  distToConflictLane += link->getInternalLengthsAfter();
1876  foeLane = nextNonInternalLane;
1877  }
1878  // Didn't find conflicting lane on foeBestLanes within range.
1879  return 0;
1880 }
1881 
1882 void
1884 #ifdef DEBUG_SSM
1885  std::cout << "\n" << SIMTIME << " Device '" << getID() << "' flushConflicts()" << std::endl;
1886 #endif
1887  while (!myPastConflicts.empty()) {
1888  if (flushAll || myPastConflicts.top()->begin <= myOldestActiveEncounterBegin) {
1890  delete myPastConflicts.top();
1891  myPastConflicts.pop();
1892  } else {
1893  break;
1894  }
1895  }
1896 }
1897 
1898 void
1901 }
1902 
1903 void
1905  for (Position& x : xv) {
1906  toGeo(x);
1907  }
1908 }
1909 
1910 void
1912 #ifdef DEBUG_SSM
1913  std::cout << SIMTIME << " writeOutConflict() of vehicles '"
1914  << e->egoID << "' and '" << e->foeID
1915  << "'" << std::endl;
1916 #endif
1917  myOutputFile->openTag("conflict");
1918  myOutputFile->writeAttr("begin", e->begin).writeAttr("end", e->end);
1919  myOutputFile->writeAttr("ego", e->egoID).writeAttr("foe", e->foeID);
1920 
1921  if (mySaveTrajectories) {
1922  myOutputFile->openTag("timeSpan").writeAttr("values", e->timeSpan).closeTag();
1923  myOutputFile->openTag("typeSpan").writeAttr("values", e->typeSpan).closeTag();
1924 
1925  // Some useful snippets for that (from MSFCDExport.cpp):
1926  if (myUseGeoCoords) {
1927  toGeo(e->egoTrajectory.x);
1928  toGeo(e->foeTrajectory.x);
1930  }
1931 
1933  myOutputFile->openTag("egoVelocity").writeAttr("values", toString(e->egoTrajectory.v)).closeTag();
1934 
1935  myOutputFile->openTag("foePosition").writeAttr("values", toString(e->foeTrajectory.x, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
1936  myOutputFile->openTag("foeVelocity").writeAttr("values", toString(e->foeTrajectory.v)).closeTag();
1937 
1938  myOutputFile->openTag("conflictPoint").writeAttr("values", toString(e->conflictPointSpan, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
1939  }
1940 
1941  if (myComputeTTC) {
1942  if (mySaveTrajectories) {
1943  myOutputFile->openTag("TTCSpan").writeAttr("values", makeStringWithNAs(e->TTCspan, INVALID)).closeTag();
1944  }
1945  std::string time = (e->minTTC.first.first == INVALID) ? "NA" : toString(e->minTTC.first.first);
1946  std::string value = (e->minTTC.first.first == INVALID) ? "NA" : toString(e->minTTC.first.second);
1947  if (myUseGeoCoords) {
1948  toGeo(e->minTTC.second);
1949  }
1950  std::string position = (e->minTTC.first.first == INVALID) ? "NA" : toString(e->minTTC.second, myUseGeoCoords ? gPrecisionGeo : gPrecision);
1951  myOutputFile->openTag("minTTC").writeAttr("time", time).writeAttr("value", value).writeAttr("position", position).closeTag();
1952  }
1953  if (myComputeDRAC) {
1954  if (mySaveTrajectories) {
1955  myOutputFile->openTag("DRACSpan").writeAttr("values", makeStringWithNAs(e->DRACspan, INVALID)).closeTag();
1956  }
1957  std::string time = (e->maxDRAC.first.first == INVALID) ? "NA" : toString(e->maxDRAC.first.first);
1958  std::string value = (e->maxDRAC.first.first == INVALID) ? "NA" : toString(e->maxDRAC.first.second);
1959  if (myUseGeoCoords) {
1960  toGeo(e->maxDRAC.second);
1961  }
1962  std::string position = (e->maxDRAC.first.first == INVALID) ? "NA" : toString(e->maxDRAC.second, myUseGeoCoords ? gPrecisionGeo : gPrecision);
1963  myOutputFile->openTag("maxDRAC").writeAttr("time", time).writeAttr("value", value).writeAttr("position", position).closeTag();
1964  }
1965  if (myComputePET) {
1966  std::string time = (e->PET.first.first == INVALID) ? "NA" : toString(e->PET.first.first);
1967  std::string value = (e->PET.first.first == INVALID) ? "NA" : toString(e->PET.first.second);
1968  if (myUseGeoCoords) {
1969  toGeo(e->PET.second);
1970  }
1971  std::string position = (e->PET.first.first == INVALID) ? "NA" : toString(e->PET.second, myUseGeoCoords ? gPrecisionGeo : gPrecision);
1972  myOutputFile->openTag("minPET").writeAttr("time", time).writeAttr("value", value).writeAttr("position", position).closeTag();
1973  }
1975 }
1976 
1977 std::string
1978 MSDevice_SSM::makeStringWithNAs(std::vector<double> v, double NA, std::string sep) {
1979  std::string res = "";
1980  for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
1981  res += (i == v.begin() ? "" : sep) + (*i == NA ? "NA" : toString(*i));
1982  }
1983  return res;
1984 }
1985 
1986 
1987 // ---------------------------------------------------------------------------
1988 // MSDevice_SSM-methods
1989 // ---------------------------------------------------------------------------
1990 MSDevice_SSM::MSDevice_SSM(SUMOVehicle& holder, const std::string& id, std::string outputFilename, std::map<std::string, double> thresholds,
1991  bool trajectories, double range, double extraTime, bool useGeoCoords) :
1992  MSDevice(holder, id),
1993  myThresholds(thresholds),
1994  mySaveTrajectories(trajectories),
1995  myRange(range),
1996  myExtraTime(extraTime),
1997  myUseGeoCoords(useGeoCoords),
1999  // Take care! Holder is currently being constructed. Cast occurs before completion.
2000  myHolderMS = static_cast<MSVehicle*>(&holder);
2001 
2002  myComputeTTC = myThresholds.find("TTC") != myThresholds.end();
2003  myComputeDRAC = myThresholds.find("DRAC") != myThresholds.end();
2004  myComputePET = myThresholds.find("PET") != myThresholds.end();
2005 
2008 
2009  // XXX: Who deletes the OutputDevice?
2010  myOutputFile = &OutputDevice::getDevice(outputFilename);
2011 // TODO: make xsd, include header
2012 // myOutputFile.writeXMLHeader("SSMLog", "SSMLog.xsd");
2013  myOutputFile->openTag("SSMLog");
2014 
2015  // register at static instance container
2016  instances->insert(this);
2017 
2018 #ifdef DEBUG_SSM
2019  std::vector<std::string> measures;
2020  std::vector<double> threshVals;
2021  for (std::map<std::string, double>::const_iterator i = myThresholds.begin(); i != myThresholds.end(); ++i) {
2022  measures.push_back(i->first);
2023  threshVals.push_back(i->second);
2024  }
2025  std::cout << "Initialized ssm device '" << id << "' with "
2026  << "myMeasures=" << joinToString(measures, " ")
2027  << ", myThresholds=" << joinToString(threshVals, " ")
2028  << ", mySaveTrajectories=" << mySaveTrajectories
2029  << ", myRange=" << myRange << ", output file=" << outputFilename << ", extra time=" << myExtraTime << "\n";
2030 #endif
2031 }
2032 
2033 
2036  // XXX: Who deletes this device?
2037  // unregister from static instance container
2038  instances->erase((MSDevice*) this);
2039  resetEncounters();
2040  flushConflicts(true);
2042 }
2043 
2044 
2045 bool
2047 #ifdef DEBUG_SSM_NOTIFICATIONS
2048  std::cout << "device '" << getID() << "' notifyEnter: reason=" << reason << " currentEdge=" << veh.getLane()->getEdge().getID() << "\n";
2049 #else
2050  UNUSED_PARAMETER(veh);
2051  UNUSED_PARAMETER(reason);
2052 #endif
2053  return true; // keep the device
2054 }
2055 
2056 bool
2057 MSDevice_SSM::notifyLeave(SUMOVehicle& veh, double /*lastPos*/,
2058  MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
2059 #ifdef DEBUG_SSM_NOTIFICATIONS
2060  std::cout << "device '" << getID() << "' notifyLeave: reason=" << reason << " currentEdge=" << veh.getLane()->getEdge().getID() << "\n";
2061 #else
2062  UNUSED_PARAMETER(veh);
2063  UNUSED_PARAMETER(reason);
2064 #endif
2065  return true; // keep the device
2066 }
2067 
2068 bool
2069 MSDevice_SSM::notifyMove(SUMOVehicle& /* veh */, double /* oldPos */,
2070  double /* newPos */, double newSpeed) {
2071 #ifdef DEBUG_SSM_NOTIFICATIONS
2072  std::cout << "device '" << getID() << "' notifyMove: newSpeed=" << newSpeed << "\n";
2073 #else
2074  UNUSED_PARAMETER(newSpeed);
2075 #endif
2076  return true; // keep the device
2077 }
2078 
2079 
2080 void
2081 MSDevice_SSM::findSurroundingVehicles(const MSVehicle& veh, double range, FoeInfoMap& foeCollector) {
2082 #ifdef DEBUG_SSM_SURROUNDING
2083  std::cout << SIMTIME << " Looking for surrounding vehicles for ego vehicle '" << veh.getID()
2084  << "' on edge '" << veh.getLane()->getEdge().getID()
2085  << "'."
2086  << "\nVehicle's best lanes = " << toString(veh.getBestLanesContinuation())
2087  << std::endl;
2088 #endif
2089 
2090  if (!veh.isOnRoad()) {
2091  return;
2092  }
2093 
2094  // The requesting vehicle's current route
2095  // XXX: Restriction to route scanning may have to be generalized to scanning of possible continuations when
2096  // considering situations involving sudden route changes. See also the definition of the EncounterTypes.
2097  // A second problem is that following situations on deviating routes may result in closing encounters
2098  // too early if a leading foe is not traced on its new lane. (see test 'foe_leader_deviating_routes')
2099 
2100  // If veh is on an internal edge, the edgeIter points towards the last edge before the junction
2101  ConstMSEdgeVector::const_iterator edgeIter = veh.getCurrentRouteEdge();
2102  assert(*edgeIter != 0);
2103 
2104  // Best continuation lanes for the ego vehicle
2105  const std::vector<MSLane*> egoBestLanes = veh.getBestLanesContinuation();
2106  std::vector<MSLane*>::const_iterator laneIter = egoBestLanes.begin();
2107 
2108  // current lane in loop below
2109  const MSLane* lane = veh.getLane();
2110  assert(lane->isInternal() || lane == *laneIter);
2111  assert(lane != 0);
2112  // next non-internal lane on the route
2113  const MSLane* nextNonInternalLane = 0;
2114 
2115  const MSEdge* edge; // current edge in loop below
2116 
2117  // Init pos with vehicle's current position. Below pos is set to zero to denote
2118  // the beginning position of the currently considered edge
2119  double pos = veh.getPositionOnLane();
2120  // remainingRange is the range minus the distance that is already scanned downstream along the vehicles route
2121  double remainingDownstreamRange = range;
2122  // 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)
2123  double distToConflictLane = -pos;
2124  // junctions that were already scanned (break search in recurrent nets)
2125  std::set<const MSJunction*> seenJunctions;
2126 
2127  // if the current edge is internal, collect all vehicles from the junction and below range upstream (except on the vehicles own edge),
2128  // this is analogous to the code treating junctions in the loop below. Note that the distance on the junction itself is not included into
2129  // range, so vehicles farther away than range can be collected, too.
2130  if (lane->isInternal()) {
2131  edge = &(lane->getEdge());
2132 
2133 #ifdef DEBUG_SSM_SURROUNDING
2134  std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' is on internal edge " << edge->getID() << "'.\n"
2135  << "Previous edge of its route: '" << (*edgeIter)->getID() << "'" << std::endl;
2136 #endif
2137 
2138  assert(edge->getToJunction() == edge->getFromJunction());
2139 
2140  const MSJunction* junction = edge->getToJunction();
2141  // Collect vehicles on the junction
2142  getVehiclesOnJunction(junction, distToConflictLane, lane, foeCollector);
2143  seenJunctions.insert(junction);
2144 
2145  // Collect vehicles on incoming edges.
2146  // Note that this includes the previous edge on the ego vehicle's route.
2147  // (The distance on the current internal edge is ignored)
2148  const ConstMSEdgeVector& incoming = junction->getIncoming();
2149  for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2150  if ((*ei)->isInternal()) {
2151  continue;
2152  }
2153  // Upstream range is taken from the vehicle's back
2154  getUpstreamVehicles(*ei, (*ei)->getLength(), range + veh.getLength(), distToConflictLane, lane, foeCollector, seenJunctions);
2155  }
2156 
2157 // // Take into account internal distance covered on the current lane
2158 // (commented out, because upstream scanning disregards internal lanes on the last scanned junction
2159 // -- this makes the scanning symmetric between leader and follower)
2160 // remainingDownstreamRange -= lane->getLength() - pos;
2161 
2162  // Take into account non-internal lengths until next non-internal lane
2163  MSLink* link = lane->getLinkCont()[0];
2164  remainingDownstreamRange -= link->getInternalLengthsAfter();
2165  distToConflictLane += lane->getLength() + link->getInternalLengthsAfter();
2166 
2167  // The next non-internal lane
2168  pos = 0.;
2169  lane = *(++laneIter);
2170  edge = &lane->getEdge();
2171  } else {
2172  // Collect all vehicles in range behind ego vehicle
2173  edge = &(lane->getEdge());
2174  getUpstreamVehicles(edge, pos, range + veh.getLength(), distToConflictLane, lane, foeCollector, seenJunctions);
2175  }
2176 
2177  assert(lane != 0);
2178  assert(!lane->isInternal());
2179 
2180  // Advance downstream the ego vehicle's route for distance 'range'.
2181  // Collect all vehicles on the traversed Edges and on incoming edges at junctions.
2182  while (remainingDownstreamRange > 0.) {
2183 #ifdef DEBUG_SSM_SURROUNDING
2184  std::cout << SIMTIME << " Scanning downstream for vehicle '" << veh.getID() << "' on lane '" << veh.getLane()->getID() << "', position=" << pos << ".\n"
2185  << "Considering edge '" << edge->getID() << "' Remaining downstream range = " << remainingDownstreamRange
2186  << "\nbestLanes=" << toString(egoBestLanes) << "\n"
2187  << std::endl;
2188 #endif
2189  assert(!edge->isInternal());
2190  assert(!lane->isInternal());
2191  assert(pos == 0 || lane == veh.getLane());
2192  if (pos + remainingDownstreamRange < lane->getLength()) {
2193  // scan range ends on this lane
2194  getUpstreamVehicles(edge, pos + remainingDownstreamRange, remainingDownstreamRange, distToConflictLane, lane, foeCollector, seenJunctions);
2195  // scanned required downstream range
2196  break;
2197  } else {
2198  // Also need to scan area that reaches beyond the lane
2199  // Collecting vehicles on non-internal edge ahead
2200  getUpstreamVehicles(edge, edge->getLength(), edge->getLength() - pos, distToConflictLane, lane, foeCollector, seenJunctions);
2201  // account for scanned distance on lane
2202  remainingDownstreamRange -= lane->getLength() - pos;
2203  distToConflictLane += lane->getLength();
2204  pos = 0.;
2205 
2206  // proceed to next non-internal lane
2207  ++laneIter;
2208  assert(laneIter == egoBestLanes.end() || *laneIter != 0);
2209 
2210  // If the vehicle's best lanes go on, collect vehicles on the upcoming junction
2211  if (laneIter != egoBestLanes.end()) {
2212  // Upcoming junction
2213  const MSJunction* junction = lane->getEdge().getToJunction();
2214 
2215  // Find connection for ego on the junction
2216  nextNonInternalLane = *laneIter;
2217  MSLink* link = lane->getLinkTo(nextNonInternalLane);
2218  assert(link != 0);
2219  // First lane of the connection
2220  lane = link->getViaLane();
2221  assert(lane != 0);
2222 
2223  if (seenJunctions.count(junction) == 0) {
2224  // Collect vehicles on the junction, if it wasn't considered already
2225  getVehiclesOnJunction(junction, distToConflictLane, lane, foeCollector);
2226  seenJunctions.insert(junction);
2227  // Collect vehicles on incoming edges (except the last edge, where we already collected). Use full range.
2228  const ConstMSEdgeVector& incoming = junction->getIncoming();
2229  for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2230  if (*ei == edge || (*ei)->isInternal()) {
2231  continue;
2232  }
2233  getUpstreamVehicles(*ei, (*ei)->getLength(), range, distToConflictLane, lane, foeCollector, seenJunctions);
2234  }
2235  // account for scanned distance on junction
2236  double linkLength = link->getInternalLengthsAfter();
2237  remainingDownstreamRange -= linkLength;
2238  distToConflictLane += linkLength;
2239 #ifdef DEBUG_SSM_SURROUNDING
2240  std::cout << " Downstream Scan for vehicle '" << veh.getID() << "' proceeded over junction '" << junction->getID()
2241  << "',\n linkLength=" << linkLength << ", remainingDownstreamRange=" << remainingDownstreamRange
2242  << std::endl;
2243 #endif
2244 
2245  // update ego's lane to next non internal edge
2246  lane = nextNonInternalLane;
2247  edge = &(lane->getEdge());
2248  } else {
2249 #ifdef DEBUG_SSM_SURROUNDING
2250  std::cout << " Downstream Scan for vehicle '" << veh.getID() << "' stops at junction '" << junction->getID()
2251  << "', which has already been scanned."
2252  << std::endl;
2253 #endif
2254  break;
2255  }
2256  } else {
2257  // Further vehicle path unknown, break search
2258  break;
2259  }
2260  }
2261  }
2262  // remove ego vehicle
2263  foeCollector.erase(&veh);
2264 }
2265 
2266 void
2267 MSDevice_SSM::getUpstreamVehicles(const MSEdge* edge, double pos, double range, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector, std::set<const MSJunction*> seenJunctions) {
2268 #ifdef DEBUG_SSM_SURROUNDING
2269  std::cout << SIMTIME << " getUpstreamVehicles() for edge '" << edge->getID() << "'"
2270  << " pos = " << pos << " range = " << range
2271  << "\nFound vehicles:"
2272  << std::endl;
2273 #endif
2274  if (range <= 0) {
2275  return;
2276  }
2277 
2278  const std::vector<MSLane*>& lanes = edge->getLanes();
2279  // Collect vehicles on the given edge with position in [pos-range,pos]
2280  for (std::vector<MSLane*>::const_iterator li = lanes.begin(); li != lanes.end(); ++li) {
2281  MSLane* lane = *li;
2282  const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
2283  for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2284  MSVehicle* veh = *vi;
2285  if (veh->getPositionOnLane() - veh->getLength() <= pos && veh->getPositionOnLane() >= pos - range) {
2286 #ifdef DEBUG_SSM
2287  std::cout << veh->getID() << "\n";
2288 #endif
2289  FoeInfo* c = new FoeInfo(); // c is deleted in updateEncounter()
2290  c->egoDistToConflictLane = egoDistToConflictLane;
2291  c->egoConflictLane = egoConflictLane;
2292  foeCollector[veh] = c;
2293  }
2294  }
2295  lane->releaseVehicles();
2296  }
2297 
2298 #ifdef DEBUG_SSM
2299  std::cout << std::endl;
2300 #endif
2301 
2302  // TODO: Gather vehicles from opposite direction. This should happen in any case, where opposite direction overtaking is possible.
2303  // If it isn't it might still be nicer to trace oncoming vehicles for the resulting trajectories in the encounters
2304  // if (edge->hasOpposite...)
2305 
2306  if (range <= pos) {
2307  return;
2308  }
2309 
2310  // Here we have: range > pos, i.e. we proceed collecting vehicles on preceding edges
2311  range -= pos;
2312 
2313  // Junction representing the origin of 'edge'
2314  const MSJunction* junction = edge->getFromJunction();
2315  if (seenJunctions.count(junction) == 0) {
2316  // Collect vehicles from incoming edges of the junction
2317  if (!edge->isInternal()) {
2318  // collect vehicles on preceding junction (for internal edges this is already done in caller,
2319  // i.e. findSurroundingVehicles() or the recursive call from getUpstreamVehicles())
2320 
2321  // Collect vehicles on the junction, if it wasn't considered already
2322  getVehiclesOnJunction(junction, egoDistToConflictLane, egoConflictLane, foeCollector);
2323  seenJunctions.insert(junction);
2324  }
2325  // Collect vehicles from incoming edges from the junction representing the origin of 'edge'
2326  const ConstMSEdgeVector& incoming = junction->getIncoming();
2327  for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2328  if ((*ei)->isInternal()) {
2329  continue;
2330  }
2331  const MSEdge* inEdge = *ei;
2332  assert(inEdge != 0);
2333  double distOnJunction = edge->isInternal() ? 0. : inEdge->getInternalFollowingLengthTo(edge);
2334  if (distOnJunction >= range) {
2335  continue;
2336  }
2337  // account for vehicles on the predecessor edge
2338  getUpstreamVehicles(inEdge, inEdge->getLength(), range - distOnJunction, egoDistToConflictLane, egoConflictLane, foeCollector, seenJunctions);
2339  }
2340  } else {
2341 #ifdef DEBUG_SSM_SURROUNDING
2342  std::cout << " Downstream Scan for stops at junction '" << junction->getID()
2343  << "', which has already been scanned."
2344  << std::endl;
2345 #endif
2346  }
2347 }
2348 
2349 void
2350 MSDevice_SSM::getVehiclesOnJunction(const MSJunction* junction, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector) {
2351 #ifdef DEBUG_SSM_SURROUNDING
2352  std::cout << SIMTIME << " getVehiclesOnJunction() for junction '" << junction->getID() << "'"
2353  << "\nFound vehicles:"
2354  << std::endl;
2355 #endif
2356  // Collect vehicles on internal lanes
2357  const std::vector<MSLane*> lanes = junction->getInternalLanes();
2358  for (std::vector<MSLane*>::const_iterator li = lanes.begin(); li != lanes.end(); ++li) {
2359  MSLane* lane = *li;
2360  const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
2361 
2362  // Add FoeInfos (XXX: for some situations, a vehicle may be collected twice. Then the later finding overwrites the earlier in foeCollector.
2363  // This could lead to neglecting a conflict when determining foeConflictLane later.) -> TODO: test with twice intersecting routes
2364  for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2365  FoeInfo* c = new FoeInfo();
2366  c->egoConflictLane = egoConflictLane;
2367  c->egoDistToConflictLane = egoDistToConflictLane;
2368  foeCollector[*vi] = c;
2369 #ifdef DEBUG_SSM_SURROUNDING
2370  for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2371  std::cout << (*vi)->getID() << "\n";
2372  }
2373 #endif
2374  }
2375  lane->releaseVehicles();
2376 
2377  // If there is an internal continuation lane, also collect vehicles on that lane
2378  if (lane->getLinkCont().size() > 1 && lane->getLinkCont()[0]->getViaLane() != 0) {
2379  // There's a second internal lane of the connection
2380  lane = lane->getLinkCont()[0]->getViaLane();
2381  // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
2382  assert(lane->getLinkCont().size() == 0 || lane->getLinkCont()[0]->getViaLane() == 0);
2383 
2384  // collect vehicles
2385  const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
2386  // Add FoeInfos. This duplicates the loop for the first internal lane
2387  for (MSLane::VehCont::const_iterator vi = vehicles2.begin(); vi != vehicles2.end(); ++vi) {
2388  FoeInfo* c = new FoeInfo();
2389  c->egoConflictLane = egoConflictLane;
2390  c->egoDistToConflictLane = egoDistToConflictLane;
2391  foeCollector[*vi] = c;
2392 #ifdef DEBUG_SSM_SURROUNDING
2393  for (MSLane::VehCont::const_iterator vi = vehicles2.begin(); vi != vehicles2.end(); ++vi) {
2394  std::cout << (*vi)->getID() << "\n";
2395  }
2396 #endif
2397  }
2398  lane->releaseVehicles();
2399  }
2400  }
2401 
2402 #ifdef DEBUG_SSM_SURROUNDING
2403  std::cout << std::endl;
2404 #endif
2405 }
2406 
2407 
2408 
2409 void
2411  // This is called once at vehicle removal.
2412  // Also: flush myOutputFile? Or is this done automatically?
2413  // myOutputFile->closeTag();
2414 }
2415 
2416 // ---------------------------------------------------------------------------
2417 // Static parameter load helpers
2418 // ---------------------------------------------------------------------------
2419 std::string
2420 MSDevice_SSM::getOutputFilename(const SUMOVehicle& v, std::string deviceID) {
2421  std::string file = deviceID + ".xml";
2422  if (v.getParameter().knowsParameter("device.ssm.file")) {
2423  try {
2424  file = v.getParameter().getParameter("device.ssm.file", file);
2425  } catch (...) {
2426  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.file", file) + "'for vehicle parameter 'ssm.measures'");
2427  }
2428  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.file")) {
2429  try {
2430  file = v.getVehicleType().getParameter().getParameter("device.ssm.file", file);
2431  } catch (...) {
2432  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.file", file) + "'for vType parameter 'ssm.measures'");
2433  }
2434  } else {
2435  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.file'. Using default of '" << file << "'\n";
2436  }
2437  return file;
2438 }
2439 
2440 bool
2441 MSDevice_SSM::useGeoCoords(const SUMOVehicle& v, std::string deviceID) {
2443  bool useGeo = false;
2444  if (v.getParameter().knowsParameter("device.ssm.geo")) {
2445  try {
2446  useGeo = TplConvert::_2bool(v.getParameter().getParameter("device.ssm.geo", "no").c_str());
2447  } catch (...) {
2448  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.geo", "no") + "'for vehicle parameter 'ssm.geo'");
2449  }
2450  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.geo")) {
2451  try {
2452  useGeo = TplConvert::_2bool(v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no").c_str());
2453  } catch (...) {
2454  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no") + "'for vType parameter 'ssm.geo'");
2455  }
2456  } else {
2457  useGeo = oc.getBool("device.ssm.geo");
2458 #ifdef DEBUG_SSM
2459  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.geo'. Using default of '" << useGeo << "'\n";
2460 #endif
2461  }
2462  return useGeo;
2463 }
2464 
2465 
2466 double
2469  double range = -INVALID;
2470  if (v.getParameter().knowsParameter("device.ssm.range")) {
2471  try {
2472  range = TplConvert::_2double(v.getParameter().getParameter("device.ssm.range", "").c_str());
2473  } catch (...) {
2474  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.range", "") + "'for vehicle parameter 'ssm.range'");
2475  }
2476  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.range")) {
2477  try {
2478  range = TplConvert::_2double(v.getVehicleType().getParameter().getParameter("device.ssm.range", "").c_str());
2479  } catch (...) {
2480  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.range", "") + "'for vType parameter 'ssm.range'");
2481  }
2482  } else {
2483  range = oc.getFloat("device.ssm.range");
2484 #ifdef DEBUG_SSM
2485  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.range'. Using default of '" << range << "'\n";
2486 #endif
2487  }
2488  return range;
2489 }
2490 
2491 
2492 double
2495  double extraTime = INVALID;
2496  if (v.getParameter().knowsParameter("device.ssm.extratime")) {
2497  try {
2498  extraTime = TplConvert::_2double(v.getParameter().getParameter("device.ssm.extratime", "").c_str());
2499  } catch (...) {
2500  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.extratime", "") + "'for vehicle parameter 'ssm.extratime'");
2501  }
2502  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.extratime")) {
2503  try {
2504  extraTime = TplConvert::_2double(v.getVehicleType().getParameter().getParameter("device.ssm.extratime", "").c_str());
2505  } catch (...) {
2506  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.extratime", "") + "'for vType parameter 'ssm.extratime'");
2507  }
2508  } else {
2509  extraTime = oc.getFloat("device.ssm.extratime");
2510 #ifdef DEBUG_SSM
2511  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.extratime'. Using default of '" << extraTime << "'\n";
2512 #endif
2513  }
2514  if (extraTime < 0.) {
2515  extraTime = DEFAULT_EXTRA_TIME;
2516  WRITE_WARNING("Negative (or no) value encountered for vehicle parameter 'device.ssm.extratime' in vehicle '" + v.getID() + "' using default value " + toString(extraTime) + " instead");
2517  }
2518  return extraTime;
2519 }
2520 
2521 
2522 bool
2525  bool trajectories = false;
2526  if (v.getParameter().knowsParameter("device.ssm.trajectories")) {
2527  try {
2528  trajectories = TplConvert::_2bool(v.getParameter().getParameter("device.ssm.trajectories", "no").c_str());
2529  } catch (...) {
2530  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.trajectories", "no") + "'for vehicle parameter 'ssm.trajectories'");
2531  }
2532  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.trajectories")) {
2533  try {
2534  trajectories = TplConvert::_2bool(v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no").c_str());
2535  } catch (...) {
2536  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no") + "'for vType parameter 'ssm.trajectories'");
2537  }
2538  } else {
2539  trajectories = oc.getBool("device.ssm.trajectories");
2540 #ifdef DEBUG_SSM
2541  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.trajectories'. Using default of '" << trajectories << "'\n";
2542 #endif
2543  }
2544  return trajectories;
2545 }
2546 
2547 
2548 bool
2549 MSDevice_SSM::getMeasuresAndThresholds(const SUMOVehicle& v, std::string deviceID, std::map<std::string, double>& thresholds) {
2551 
2552  // Measures
2553  std::string measures_str = "";
2554  if (v.getParameter().knowsParameter("device.ssm.measures")) {
2555  try {
2556  measures_str = v.getParameter().getParameter("device.ssm.measures", "");
2557  } catch (...) {
2558  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.measures", "") + "'for vehicle parameter 'ssm.measures'");
2559  }
2560  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.measures")) {
2561  try {
2562  measures_str = v.getVehicleType().getParameter().getParameter("device.ssm.measures", "");
2563  } catch (...) {
2564  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.measures", "") + "'for vType parameter 'ssm.measures'");
2565  }
2566  } else {
2567  measures_str = oc.getString("device.ssm.measures");
2568  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.measures'. Using default of '" << measures_str << "'\n";
2569  }
2570 
2571  // Check retrieved measures
2572  if (measures_str == "") {
2573  WRITE_WARNING("No measures specified for ssm device of vehicle '" + v.getID() + "'. Registering all available SSMs.");
2574  measures_str = AVAILABLE_SSMS;
2575  }
2577  std::vector<std::string> available = st.getVector();
2578  st = StringTokenizer(measures_str);
2579  std::vector<std::string> measures = st.getVector();
2580  for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
2581  if (std::find(available.begin(), available.end(), *i) == available.end()) {
2582  // Given identifier is unknown
2583  WRITE_ERROR("SSM identifier '" + *i + "' is not supported. Aborting construction of SSM device '" + deviceID + "'.");
2584  return false;
2585  }
2586  }
2587 
2588  // Thresholds
2589  std::string thresholds_str = "";
2590  if (v.getParameter().knowsParameter("device.ssm.thresholds")) {
2591  try {
2592  thresholds_str = v.getParameter().getParameter("device.ssm.thresholds", "");
2593  } catch (...) {
2594  WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.thresholds", "") + "'for vehicle parameter 'ssm.thresholds'");
2595  }
2596  } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.thresholds")) {
2597  try {
2598  thresholds_str = v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "");
2599  } catch (...) {
2600  WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "") + "'for vType parameter 'ssm.thresholds'");
2601  }
2602  } else {
2603  thresholds_str = oc.getString("device.ssm.thresholds");
2604 #ifdef DEBUG_SSM
2605  std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.thresholds'. Using default of '" << thresholds_str << "'\n";
2606 #endif
2607  }
2608 
2609  // Parse vector of doubles from threshold_str
2610  int count = 0;
2611  if (thresholds_str != "") {
2612  st = StringTokenizer(thresholds_str);
2613  while (count < (int)measures.size() && st.hasNext()) {
2614  double thresh = TplConvert::_2double(st.next().c_str());
2615  thresholds.insert(std::make_pair(measures[count], thresh));
2616  ++count;
2617  }
2618  if (thresholds.size() < measures.size() || st.hasNext()) {
2619  WRITE_ERROR("Given list of thresholds ('" + thresholds_str + "') is not of the same size as the list of measures ('" + measures_str + "').\nPlease specify exactly one threshold for each measure.");
2620  return false;
2621  }
2622  } else {
2623  // assume default thresholds if none are given
2624  for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
2625  if (*i == "TTC") {
2626  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TTC));
2627  } else if (*i == "DRAC") {
2628  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_DRAC));
2629  } else if (*i == "PET") {
2630  thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PET));
2631  } else {
2632  WRITE_ERROR("Unknown SSM identifier '" + (*i) + "'. Aborting construction of ssm device."); // should never occur
2633  return false;
2634  }
2635  }
2636  }
2637  return true;
2638 }
2639 
2640 
2641 
2642 /****************************************************************************/
2643 
virtual MSLane * getLane() const =0
Returns the lane the vehicle is on.
double getInternalFollowingLengthTo(const MSEdge *followerAfterInternal) const
returns the length of all internal edges on the junction until reaching the non-internal edge followe...
Definition: MSEdge.cpp:677
bool myUseGeoCoords
Whether to use the original coordinate system for output.
Definition: MSDevice_SSM.h:605
double getLength() const
return the length of the edge
Definition: MSEdge.h:570
void doRegister(const std::string &name, Option *v)
Adds an option under the given name.
Definition: OptionsCont.cpp:82
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:261
static void determineConflictPoint(EncounterApproachInfo &eInfo)
Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in ...
#define AVAILABLE_SSMS
void determinePET(EncounterApproachInfo &eInfo) const
Discriminates between different encounter types and correspondingly determines the PET for those case...
std::pair< const MSLane *, double > egoConflictEntryCrossSection
Definition: MSDevice_SSM.h:256
ENCOUNTER_TYPE_MERGING_ADJACENT.
Definition: MSDevice_SSM.h:94
MSEdge & getEdge() const
Returns the lane&#39;s edge.
Definition: MSLane.h:582
const std::string egoID
Definition: MSDevice_SSM.h:176
static const std::set< MSDevice * > & getInstances()
returns all currently existing SSM devices
virtual bool isOnRoad() const =0
Returns the information whether the vehicle is on a road (is simulated)
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
static void getVehiclesOnJunction(const MSJunction *, double egoDistToConflictLane, const MSLane *const egoConflictLane, FoeInfoMap &foeCollector)
Collects all vehicles on the junction into foeCollector.
double getWidth() const
Returns the lane&#39;s width.
Definition: MSLane.h:500
Representation of a vehicle in the micro simulation.
Definition: MSVehicle.h:83
#define DEFAULT_RANGE
void createEncounters(FoeInfoMap &foes)
Makes new encounters for all given vehicles (these should be the ones entering the device&#39;s range in ...
std::string next()
std::pair< std::pair< double, double >, Position > maxDRAC
Definition: MSDevice_SSM.h:219
bool notifyLeave(SUMOVehicle &veh, double lastPos, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
Called whenever the holder leaves a lane.
std::pair< std::pair< double, double >, Position > minTTC
Definition: MSDevice_SSM.h:218
virtual void releaseVehicles() const
Allows to use the container for microsimulation again.
Definition: MSLane.h:406
const MSCFModel & getCarFollowModel() const
Returns the vehicle&#39;s car following model definition.
Definition: MSVehicle.h:796
MSLink * getLinkTo(const MSLane *) const
returns the link to the given lane or 0, if it is not connected
Definition: MSLane.cpp:1786
Position getVelocityVector() const
Returns the vehicle&#39;s direction in radians.
Definition: MSVehicle.h:613
static void cleanup()
Clean up remaining devices instances.
EncounterType
Different types of encounters corresponding to relative positions of the vehicles. The name describes the type from the ego perspective.
Definition: MSDevice_SSM.h:72
const std::vector< MSLane * > & getLanes() const
Returns this edge&#39;s lanes.
Definition: MSEdge.h:168
PositionVector conflictPointSpan
Predicted location of the conflict: In case of MERGING and CROSSING: entry point to conflict area for...
Definition: MSDevice_SSM.h:205
static bool _2bool(const E *const data)
converts a 0-terminated char-type array into the boolean value described by it
Definition: TplConvert.h:389
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...
const MSRouteIterator & getCurrentRouteEdge() const
Returns an iterator pointing to the current edge in this vehicles route.
static void insertOptions(OptionsCont &oc)
Inserts MSDevice_SSM-options.
MSVehicle * myHolderMS
Definition: MSDevice_SSM.h:608
SUMOVehicle & myHolder
The vehicle that stores the device.
Definition: MSDevice.h:187
int gPrecision
the precision for floating point outputs
Definition: StdDefs.cpp:30
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
double egoConflictEntryTime
Times when the ego vehicle entered/left the conflict area. Currently only applies for crossing situat...
Definition: MSDevice_SSM.h:185
A device which collects info on the vehicle trip (mainly on departure and arrival) ...
Definition: MSDevice_SSM.h:63
The base class for an intersection.
Definition: MSJunction.h:64
const MSVehicle * foe
Definition: MSDevice_SSM.h:175
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
EncounterVector myActiveEncounters
Definition: MSDevice_SSM.h:615
#define INVALID
Notification
Definition of a vehicle state.
std::vector< std::pair< std::pair< const MSLane *, double >, double > > foePETCrossSections
Definition: MSDevice_SSM.h:214
std::size_t size() const
Returns the number of trajectory points stored.
Definition: MSDevice_SSM.h:152
ENCOUNTER_TYPE_FOLLOWING_PASSED.
Definition: MSDevice_SSM.h:119
double getLength() const
Returns the lane&#39;s length.
Definition: MSLane.h:484
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:169
T MAX2(T a, T b)
Definition: StdDefs.h:74
double getAcceleration() const
Returns the vehicle&#39;s acceleration in m/s (this is computed as the last step&#39;s mean acceleration in c...
Definition: MSVehicle.h:460
Position getPosition(const double offset=0) const
Return current position (x/y, cartesian)
Definition: MSVehicle.cpp:806
void closeEncounter(Encounter *e)
Finalizes the encounter and calculates SSM values.
double getPositionOnLane() const
Get the vehicle&#39;s position along the lane.
Definition: MSVehicle.h:375
static double getDetectionRange(const SUMOVehicle &v)
ENCOUNTER_TYPE_COLLISION.
Definition: MSDevice_SSM.h:123
static void toGeo(Position &x)
convert SUMO-positions to geo coordinates (in place)
std::vector< double > TTCspan
All values for TTC.
Definition: MSDevice_SSM.h:208
ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:106
std::vector< const MSEdge * > ConstMSEdgeVector
Definition: MSEdge.h:79
bool isInternal() const
Definition: MSLane.cpp:1679
ENCOUNTER_TYPE_CROSSING.
Definition: MSDevice_SSM.h:97
const std::string foeID
Definition: MSDevice_SSM.h:177
#define TS
Definition: SUMOTime.h:52
#define DEFAULT_THRESHOLD_PET
const std::string & getParameter(const std::string &key, const std::string &defaultValue) const
Returns the value for a given key.
double getLength() const
Returns the vehicle&#39;s length.
void updateEncounter(Encounter *e, FoeInfo *foeInfo)
Updates the encounter (adds a new trajectory point) and deletes the foeInfo.
void resetExtraTime(double value)
resets remainingExtraTime to the given value
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)
Constructor.
ENCOUNTER_TYPE_CROSSING_FOLLOWER.
Definition: MSDevice_SSM.h:103
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:40
ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:114
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:200
std::pair< const MSLane *, double > foeConflictEntryCrossSection
Definition: MSDevice_SSM.h:257
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:65
#define SIMTIME
Definition: SUMOTime.h:70
static bool requestsTrajectories(const SUMOVehicle &v)
virtual const VehCont & getVehiclesSecure() const
Returns the vehicles container; locks it for microsimulation.
Definition: MSLane.h:379
Encounter(const MSVehicle *_ego, const MSVehicle *const _foe, double _begin, double extraTime)
Constructor.
ENCOUNTER_TYPE_CROSSING_LEADER.
Definition: MSDevice_SSM.h:100
EncounterType currentType
Definition: MSDevice_SSM.h:179
std::vector< double > DRACspan
All values for DRAC.
Definition: MSDevice_SSM.h:210
std::vector< std::pair< std::pair< const MSLane *, double >, double > > egoPETCrossSections
Cross sections at which a PET shall be calculated for the corresponding vehicle.
Definition: MSDevice_SSM.h:213
ENCOUNTER_TYPE_FOLLOWING_LEADER.
Definition: MSDevice_SSM.h:81
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
static void getUpstreamVehicles(const MSEdge *edge, double pos, double range, double egoDistToConflictLane, const MSLane *const egoConflictLane, FoeInfoMap &foeCollector, std::set< const MSJunction * > seenJunctions)
Collects all vehicles within range &#39;range&#39; upstream of the position &#39;pos&#39; on the edge &#39;edge&#39; into foe...
const std::string & getID() const
Returns the id.
Definition: Named.h:66
A road/street connecting two junctions.
Definition: MSEdge.h:81
ENCOUNTER_TYPE_MERGING_FOLLOWER.
Definition: MSDevice_SSM.h:92
MSLane * getCanonicalSuccessorLane() const
Definition: MSLane.cpp:2220
ENCOUNTER_TYPE_MERGING.
Definition: MSDevice_SSM.h:86
static bool getMeasuresAndThresholds(const SUMOVehicle &v, std::string deviceID, std::map< std::string, double > &thresholds)
ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:116
static bool useGeoCoords(const SUMOVehicle &v, std::string deviceID)
MSLink * getEntryLink() const
Returns the entry link if this is an internal lane, else 0.
Definition: MSLane.cpp:1807
virtual const std::vector< MSLane * > getInternalLanes() const
Returns all internal lanes on the junction.
Definition: MSJunction.h:115
const SUMOVTypeParameter & getParameter() const
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:56
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:838
Representation of a vehicle.
Definition: SUMOVehicle.h:67
double getRemainingExtraTime() const
returns the remaining extra time
static double computeDRAC(double gap, double followerSpeed, double leaderSpeed)
Computes the DRAC (deceleration to avoid a collision) for a lead/follow situation as defined...
void processEncounters(FoeInfoMap &foes, bool forceClose=false)
Finds encounters for which the foe vehicle has disappeared from range. remainingExtraTime is decrease...
#define DEFAULT_THRESHOLD_DRAC
const std::vector< MSLane * > & getBestLanesContinuation() const
Returns the best sequence of lanes to continue the route starting at myLane.
Definition: MSVehicle.cpp:3502
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:46
bool myComputeTTC
Flags for switching on / off comutation of different SSMs, derived from myMeasures.
Definition: MSDevice_SSM.h:607
int gPrecisionGeo
Definition: StdDefs.cpp:31
void determineTTCandDRAC(EncounterApproachInfo &eInfo) const
Discriminates between different encounter types and correspondingly determines TTC and DRAC for those...
A list of positions.
OutputDevice * myOutputFile
Output device.
Definition: MSDevice_SSM.h:623
ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:112
void resetEncounters()
Closes all current Encounters and moves conflicts to myPastConflicts,.
MSVehicleControl & getVehicleControl()
Returns the vehicle control.
Definition: MSNet.h:305
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:1535
double myRange
Detection range. For vehicles closer than this distance from the ego vehicle, SSMs are traced...
Definition: MSDevice_SSM.h:601
std::vector< double > egoDistsToConflict
Evolution of the ego vehicle&#39;s distance to the conflict point.
Definition: MSDevice_SSM.h:198
ENCOUNTER_TYPE_FOLLOWING_FOLLOWER.
Definition: MSDevice_SSM.h:79
void generateOutput() const
Finalizes output. Called on vehicle removal.
ENCOUNTER_TYPE_NOCONFLICT_AHEAD.
Definition: MSDevice_SSM.h:74
#define DEFAULT_EXTRA_TIME
bool notifyEnter(SUMOVehicle &veh, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
Called whenever the holder enteres a lane.
void computeSSMs(EncounterApproachInfo &e) const
Compute current values of the logged SSMs (myMeasures) for the given encounter &#39;e&#39; and update &#39;e&#39; acc...
bool qualifiesAsConflict(Encounter *e)
Tests if the SSM values exceed the threshold for qualification as conflict.
T MIN2(T a, T b)
Definition: StdDefs.h:68
static void checkConflictEntryAndExit(EncounterApproachInfo &eInfo)
Checks whether ego or foe have entered or left the conflict area in the last step and eventually writ...
ENCOUNTER_TYPE_MERGING_LEADER.
Definition: MSDevice_SSM.h:89
static void insertDefaultAssignmentOptions(const std::string &deviceName, const std::string &optionsTopic, OptionsCont &oc)
Adds common command options that allow to assign devices to vehicles.
Definition: MSDevice.cpp:97
std::ostream & operator<<(std::ostream &out, MSDevice_SSM::EncounterType type)
Nicer output for EncounterType enum.
ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA.
Definition: MSDevice_SSM.h:108
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
void updateAndWriteOutput()
This is called once per time step in MSNet::writeOutput() and collects the surrounding vehicles...
double getBackPositionOnLane(const MSLane *lane) const
Get the vehicle&#39;s position relative to the given lane.
Definition: MSVehicle.cpp:2617
virtual const MSVehicleType & getVehicleType() const =0
Returns the vehicle&#39;s type.
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
double myOldestActiveEncounterBegin
begin time of the oldest active encounter
Definition: MSDevice_SSM.h:617
static void buildVehicleDevices(SUMOVehicle &v, std::vector< MSDevice * > &into)
Build devices for the given vehicle, if needed.
ENCOUNTER_TYPE_FOLLOWING.
Definition: MSDevice_SSM.h:77
bool isInternal() const
return whether this edge is an internal edge
Definition: MSEdge.h:230
bool mySaveTrajectories
This determines whether the whole trajectories of the vehicles (position, speed, ssms) shall be saved...
Definition: MSDevice_SSM.h:599
static std::string getOutputFilename(const SUMOVehicle &v, std::string deviceID)
std::vector< MSVehicle * > VehCont
Container for vehicles.
Definition: MSLane.h:91
const MSVehicle * ego
Definition: MSDevice_SSM.h:174
static double getExtraTime(const SUMOVehicle &v)
void flushConflicts(bool all=false)
Writes out all past conflicts that have begun earlier than the oldest active encounter.
static std::string makeStringWithNAs(std::vector< double > v, double NA, std::string sep=" ")
make a string of a double vector and treat a special value as invalid ("NA")
std::vector< double > foeDistsToConflict
Evolution of the foe vehicle&#39;s distance to the conflict point.
Definition: MSDevice_SSM.h:200
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...
bool closingRequested
this flag is set by updateEncounter() or directly in processEncounters(), where encounters are closed...
Definition: MSDevice_SSM.h:224
std::vector< std::string > getVector()
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:206
Abstract in-vehicle device.
Definition: MSDevice.h:71
bool myComputeDRAC
Definition: MSDevice_SSM.h:607
ENCOUNTER_TYPE_FOLLOWING_PASSED.
Definition: MSDevice_SSM.h:121
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:494
double getMaxSpeedOnLane() const
Returns the maximal speed for the vehicle on its current lane (including speed factor and deviation...
Definition: MSVehicle.h:509
std::vector< Encounter * > EncounterVector
Definition: MSDevice_SSM.h:275
static bool equippedByDefaultAssignmentOptions(const OptionsCont &oc, const std::string &deviceName, SUMOVehicle &v)
Determines whether a vehicle should get a certain device.
Definition: MSDevice.cpp:111
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
double getPreviousSpeed() const
Returns the vehicle&#39;s speed before the previous time step.
Definition: MSVehicle.h:451
double getLength() const
Get vehicle&#39;s length [m].
void countDownExtraTime(double amount)
decreases myRemaingExtraTime by given amount in seconds
double foeConflictEntryTime
Times when the foe vehicle entered/left the conflict area. Currently only applies for crossing situat...
Definition: MSDevice_SSM.h:187
#define M_PI
Definition: odrSpiral.cpp:40
std::map< const MSVehicle *, FoeInfo * > FoeInfoMap
Definition: MSDevice_SSM.h:276
const MSLane * egoConflictLane
Definition: MSDevice_SSM.h:265
Trajectory egoTrajectory
Trajectory of the ego vehicle.
Definition: MSDevice_SSM.h:194
static OutputDevice & getDevice(const std::string &name)
Returns the described OutputDevice.
static std::set< MSDevice * > * instances
All currently existing SSM devices.
Definition: MSDevice_SSM.h:67
~MSDevice_SSM()
Destructor.
Structure to collect some info on the encounter needed during ssm calculation by various functions...
Definition: MSDevice_SSM.h:236
std::vector< double > timeSpan
time points corresponding to the trajectories
Definition: MSDevice_SSM.h:190
A storage for options typed value containers)
Definition: OptionsCont.h:99
const MSVehicleType & getVehicleType() const
Returns the vehicle&#39;s type definition.
static double _2double(const E *const data)
converts a char-type array into the double value described by it
Definition: TplConvert.h:312
const MSLane * findFoeConflictLane(const MSVehicle *foe, const MSLane *egoConflictLane, double &distToConflictLane) const
Computes the conflict lane for the foe.
virtual const std::string & getID() const =0
Get the vehicle&#39;s ID.
ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA.
Definition: MSDevice_SSM.h:110
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
std::pair< double, double > pet
Definition: MSDevice_SSM.h:255
double getLateralPositionOnLane() const
Get the vehicle&#39;s lateral position on the lane.
Definition: MSVehicle.h:407
std::map< std::string, double > myThresholds
Definition: MSDevice_SSM.h:596
SUMOVehicle * getVehicle(const std::string &id) const
Returns the vehicle with the given id.
virtual const SUMOVehicleParameter & getParameter() const =0
Returns the vehicle&#39;s parameter (including departure definition)
double getLastStepDist() const
Get the distance the vehicle covered in the previous timestep.
Definition: MSVehicle.h:382
const PositionVector & getShape() const
Returns this lane&#39;s shape.
Definition: MSLane.h:426
An encounter is an episode involving two vehicles, which are closer to each other than some specified...
Definition: MSDevice_SSM.h:129
bool closeTag()
Closes the most recently opened tag.
#define DEFAULT_THRESHOLD_TTC
const MSJunction * getFromJunction() const
Definition: MSEdge.h:349
double myExtraTime
Extra time in seconds to be logged after a conflict is over.
Definition: MSDevice_SSM.h:603
std::vector< int > typeSpan
Evolution of the encounter classification (.
Definition: MSDevice_SSM.h:192
double remainingExtraTime
Remaining extra time (decreases after an encounter ended)
Definition: MSDevice_SSM.h:182
ENCOUNTER_TYPE_ON_ADJACENT_LANES.
Definition: MSDevice_SSM.h:83
const MSLinkCont & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.cpp:1779
bool knowsParameter(const std::string &key) const
Returns whether the parameter is known.
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
const ConstMSEdgeVector & getIncoming() const
Definition: MSJunction.h:105
static void estimateConflictTimes(EncounterApproachInfo &eInfo)
Estimates the time until conflict for the vehicles based on the distance to the conflict entry points...
bool isOnRoad() const
Returns the information whether the vehicle is on a road (is simulated)
Definition: MSVehicle.h:521
void writeOutConflict(Encounter *e)
MSLane * getLane() const
Returns the lane the vehicle is on.
Definition: MSVehicle.h:499
bool notifyMove(SUMOVehicle &veh, double oldPos, double newPos, double newSpeed)
Checks for waiting steps when the vehicle moves.
static bool gUseMesoSim
Definition: MSGlobals.h:98
Representation of a lane in the micro simulation.
Definition: MSLane.h:79
EncounterType classifyEncounter(const FoeInfo *foeInfo, EncounterApproachInfo &eInfo) const
Classifies the current type of the encounter provided some information on the opponents.
double estimateArrivalTime(double dist, double speed, double maxSpeed, double accel) const
Computes the time needed to travel a distance dist given an initial speed and constant acceleration...
Definition: MSCFModel.cpp:333
std::pair< std::pair< double, double >, Position > PET
Definition: MSDevice_SSM.h:220
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:237
EncounterQueue myPastConflicts
Past encounters that where qualified as conflicts and are not yet flushed to the output file...
Definition: MSDevice_SSM.h:619
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
Trajectory foeTrajectory
Trajectory of the foe vehicle.
Definition: MSDevice_SSM.h:196
static void findSurroundingVehicles(const MSVehicle &veh, double range, FoeInfoMap &foeCollector)
Returns all vehicles, which are within the given range of the given vehicle.
const MSJunction * getToJunction() const
Definition: MSEdge.h:353
double getWidth() const
Returns the vehicle&#39;s width.
const std::string & getID() const
Returns the name of the vehicle.
double getSpeed() const
Returns the vehicle&#39;s current speed.
Definition: MSVehicle.h:443
std::priority_queue< Encounter *, std::vector< Encounter * >, Encounter::compare > EncounterQueue
Definition: MSDevice_SSM.h:274
void add(double time, EncounterType type, Position egoX, Position egoV, Position foeX, Position foeV, Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair< double, double > pet)
add a new data point and update encounter type