1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2013-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    MSDevice_SSM.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Michael Behrisch
13 /// @author  Jakob Erdmann
14 /// @author  Leonhard Luecken
15 /// @date    11.06.2013
16 /// @version $Id$
17 ///
18 // An SSM-device logs encounters / conflicts of the carrying vehicle with other surrounding vehicles
19 // XXX: Preliminary implementation. Use with care. Especially rerouting vehicles could be problematic.
20 // TODO: implement SSM time-gap (estimated conflict entry and exit times are already calculated for PET calculation)
21 /****************************************************************************/
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27 
28 #include <iostream>
29 #include <utils/common/StringTokenizer.h>
30 #include <utils/geom/GeomHelper.h>
31 #include <utils/common/StringUtils.h>
32 #include <utils/options/OptionsCont.h>
33 #include <utils/iodevices/OutputDevice.h>
34 #include <utils/vehicle/SUMOVehicle.h>
35 #include <microsim/MSNet.h>
36 #include <microsim/MSJunction.h>
37 #include <microsim/MSLane.h>
38 #include <microsim/MSEdge.h>
39 #include <microsim/MSVehicle.h>
40 #include <microsim/MSVehicleControl.h>
41 #include <utils/geom/Position.h>
42 #include <utils/geom/GeoConvHelper.h>
43 #include "MSDevice_SSM.h"
44 
45 // ===========================================================================
46 // Debug constants
47 // ===========================================================================
48 //#define DEBUG_SSM
49 //#define DEBUG_SSM_DRAC
50 //#define DEBUG_SSM_SURROUNDING
51 //#define DEBUG_SSM_NOTIFICATIONS
52 //#define DEBUG_COND MSNet::getInstance()->getCurrentTimeStep() > 308000
53 //#define DEBUG_COND1(ego) MSNet::getInstance()->getCurrentTimeStep() > 308000
54 #define DEBUG_COND1(ego) ego!=nullptr && ego->isSelected()
55 //#define DEBUG_COND1(ego) ego!=nullptr && ego->getID() == "flow1.23"
56 #define DEBUG_COND false
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 BR SGAP TGAP"
72 
73 #define DEFAULT_THRESHOLD_TTC 3. // in [s.], events get logged if time to collision is below threshold (1.5s. is an appropriate criticality threshold according to Van der Horst, A. R. A. (1991). Time-to-collision as a Cue for Decision-making in Braking [also see Guido et al. 2011])
74 #define DEFAULT_THRESHOLD_DRAC 3. // in [m/s^2], events get logged if "deceleration to avoid a crash" is above threshold (3.4s. is an appropriate criticality threshold according to American Association of State Highway and Transportation Officials (2004). A Policy on Geometric Design of Highways and Streets [also see Guido et al. 2011])
75 #define DEFAULT_THRESHOLD_PET 2. // in seconds, events get logged if post encroachment time is below threshold
76 
77 #define DEFAULT_THRESHOLD_BR 0.0 // in [m/s^2], events get logged if brake rate is above threshold
78 #define DEFAULT_THRESHOLD_SGAP 0.2 // in [m.], events get logged if the space headway is below threshold.
79 #define DEFAULT_THRESHOLD_TGAP 0.5 // in [m.], events get logged if the time headway is below threshold.
80 
81 #define DEFAULT_EXTRA_TIME 5.      // in seconds, events get logged for extra time even if encounter is over
82 
83 // ===========================================================================
84 // method definitions
85 // ===========================================================================
86 
87 
88 
89 /// Nicer output for EncounterType enum
operator <<(std::ostream & out,MSDevice_SSM::EncounterType type)90 std::ostream& operator<<(std::ostream& out, MSDevice_SSM::EncounterType type) {
91     switch (type) {
92         case MSDevice_SSM::ENCOUNTER_TYPE_NOCONFLICT_AHEAD:
93             out << "NOCONFLICT_AHEAD";
94             break;
95         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING:
96             out << "FOLLOWING";
97             break;
98         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_FOLLOWER:
99             out << "FOLLOWING_FOLLOWER";
100             break;
101         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_LEADER:
102             out << "FOLLOWING_LEADER";
103             break;
104         case MSDevice_SSM::ENCOUNTER_TYPE_ON_ADJACENT_LANES:
105             out << "ON_ADJACENT_LANES";
106             break;
107         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING:
108             out << "MERGING";
109             break;
110         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_LEADER:
111             out << "MERGING_LEADER";
112             break;
113         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_FOLLOWER:
114             out << "MERGING_FOLLOWER";
115             break;
116         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_ADJACENT:
117             out << "MERGING_ADJACENT";
118             break;
119         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING:
120             out << "CROSSING";
121             break;
122         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING_LEADER:
123             out << "CROSSING_LEADER";
124             break;
125         case MSDevice_SSM::ENCOUNTER_TYPE_CROSSING_FOLLOWER:
126             out << "CROSSING_FOLLOWER";
127             break;
128         case MSDevice_SSM::ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA:
129             out << "EGO_ENTERED_CONFLICT_AREA";
130             break;
131         case MSDevice_SSM::ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA:
132             out << "FOE_ENTERED_CONFLICT_AREA";
133             break;
134         case MSDevice_SSM::ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA:
135             out << "BOTH_ENTERED_CONFLICT_AREA";
136             break;
137         case MSDevice_SSM::ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA:
138             out << "EGO_LEFT_CONFLICT_AREA";
139             break;
140         case MSDevice_SSM::ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA:
141             out << "FOE_LEFT_CONFLICT_AREA";
142             break;
143         case MSDevice_SSM::ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA:
144             out << "BOTH_LEFT_CONFLICT_AREA";
145             break;
146         case MSDevice_SSM::ENCOUNTER_TYPE_FOLLOWING_PASSED:
147             out << "FOLLOWING_PASSED";
148             break;
149         case MSDevice_SSM::ENCOUNTER_TYPE_MERGING_PASSED:
150             out << "MERGING_PASSED";
151             break;
152         // Collision (currently unused, might be differentiated further)
153         case MSDevice_SSM::ENCOUNTER_TYPE_COLLISION:
154             out << "COLLISION";
155             break;
156         default:
157             out << "unknown type (" << int(type) << ")";
158             break;
159     }
160     return out;
161 }
162 
163 
164 // ---------------------------------------------------------------------------
165 // static initialisation methods
166 // ---------------------------------------------------------------------------
167 
168 std::set<MSDevice_SSM*>* MSDevice_SSM::instances = new std::set<MSDevice_SSM*>();
169 
170 std::set<std::string> MSDevice_SSM::createdOutputFiles;
171 
172 int MSDevice_SSM::issuedParameterWarnFlags = 0;
173 
174 const std::set<MSDevice_SSM*>&
getInstances()175 MSDevice_SSM::getInstances() {
176     return *instances;
177 }
178 
179 void
cleanup()180 MSDevice_SSM::cleanup() {
181     // Close current encounters and flush conflicts to file for all existing devices
182     if (instances != nullptr) {
183         for (std::set<MSDevice_SSM*>::iterator ii = instances->begin(); ii != instances->end(); ++ii) {
184             (*ii)->resetEncounters();
185             (*ii)->flushConflicts(true);
186             (*ii)->flushGlobalMeasures();
187         }
188         instances->clear();
189     }
190     for (auto& fn : createdOutputFiles) {
191         OutputDevice* file = &OutputDevice::getDevice(fn);
192         file->closeTag();
193     }
194 }
195 
196 void
insertOptions(OptionsCont & oc)197 MSDevice_SSM::insertOptions(OptionsCont& oc) {
198     oc.addOptionSubTopic("SSM Device");
199     insertDefaultAssignmentOptions("ssm", "SSM Device", oc);
200 
201     // custom options
202     oc.doRegister("device.ssm.measures", Option::makeUnsetWithDefault<Option_String, std::string>(""));
203     oc.addDescription("device.ssm.measures", "SSM Device", "Specifies which measures will be logged (as a space separated sequence of IDs in ('TTC', 'DRAC', 'PET')).");
204     oc.doRegister("device.ssm.thresholds", Option::makeUnsetWithDefault<Option_String, std::string>(""));
205     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.");
206     oc.doRegister("device.ssm.trajectories",  Option::makeUnsetWithDefault<Option_Bool, bool>(false));
207     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).");
208     oc.doRegister("device.ssm.range", Option::makeUnsetWithDefault<Option_Float, double>(DEFAULT_RANGE));
209     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.");
210     oc.doRegister("device.ssm.extratime", Option::makeUnsetWithDefault<Option_Float, double>(DEFAULT_EXTRA_TIME));
211     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.");
212     oc.doRegister("device.ssm.file", Option::makeUnsetWithDefault<Option_String, std::string>(""));
213     oc.addDescription("device.ssm.file", "SSM Device", "Give a global default filename for the SSM output.");
214     oc.doRegister("device.ssm.geo", Option::makeUnsetWithDefault<Option_Bool, bool>(false));
215     oc.addDescription("device.ssm.geo", "SSM Device", "Whether to use coordinates of the original reference system in output (default is false).");
216 }
217 
218 void
buildVehicleDevices(SUMOVehicle & v,std::vector<MSVehicleDevice * > & into)219 MSDevice_SSM::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
220     if (equippedByDefaultAssignmentOptions(OptionsCont::getOptions(), "ssm", v, false)) {
221         if (MSGlobals::gUseMesoSim) {
222             WRITE_WARNING("SSM Device for vehicle '" + v.getID() + "' will not be built. (SSMs not supported in MESO)");
223             return;
224         }
225         // ID for the device
226         std::string deviceID = "ssm_" + v.getID();
227 
228         // Load parameters:
229 
230         // Measures and thresholds
231         std::map<std::string, double> thresholds;
232         bool success = getMeasuresAndThresholds(v, deviceID, thresholds);
233         if (!success) {
234             return;
235         }
236 
237         // TODO: modify trajectory option: "all", "conflictPoints", ("position" && "speed" == "vehState"), "SSMs"!
238         // Trajectories
239         bool trajectories = requestsTrajectories(v);
240 
241         // detection range
242         double range = getDetectionRange(v);
243 
244         // extra time
245         double extraTime = getExtraTime(v);
246 
247         // File
248         std::string file = getOutputFilename(v, deviceID);
249 
250         const bool useGeo = useGeoCoords(v);
251 
252         // Build the device (XXX: who deletes it?)
253         MSDevice_SSM* device = new MSDevice_SSM(v, deviceID, file, thresholds, trajectories, range, extraTime, useGeo);
254         into.push_back(device);
255     }
256 }
257 
258 
Encounter(const MSVehicle * _ego,const MSVehicle * const _foe,double _begin,double extraTime)259 MSDevice_SSM::Encounter::Encounter(const MSVehicle* _ego, const MSVehicle* const _foe, double _begin, double extraTime) :
260     ego(_ego),
261     foe(_foe),
262     egoID(_ego->getID()),
263     foeID(_foe->getID()),
264     begin(_begin),
265     end(-INVALID),
266     currentType(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
267     remainingExtraTime(extraTime),
268     egoConflictEntryTime(INVALID),
269     egoConflictExitTime(INVALID),
270     foeConflictEntryTime(INVALID),
271     foeConflictExitTime(INVALID),
272     minTTC(INVALID, Position::invalidPosition(), ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID),
273     maxDRAC(INVALID, Position::invalidPosition(), ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID),
274     PET(INVALID, Position::invalidPosition(), ENCOUNTER_TYPE_NOCONFLICT_AHEAD, INVALID),
275     closingRequested(false) {
276 #ifdef DEBUG_SSM
277     if (DEBUG_COND1(ego))
278         std::cout << "\n" << SIMTIME << " Constructing encounter of '"
279                   << ego->getID() << "' and '" << foe->getID() << "'" << std::endl;
280 #endif
281 }
282 
~Encounter()283 MSDevice_SSM::Encounter::~Encounter() {
284 #ifdef DEBUG_SSM
285     if (DEBUG_COND1(ego))
286         std::cout << "\n" << SIMTIME << " Destroying encounter of '"
287                   << egoID << "' and '" << foeID << "' (begin was " << begin << ")" << std::endl;
288 #endif
289 }
290 
291 
292 void
add(double time,const 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)293 MSDevice_SSM::Encounter::add(double time, const EncounterType type, Position egoX, Position egoV, Position foeX, Position foeV,
294                              Position conflictPoint, double egoDistToConflict, double foeDistToConflict, double ttc, double drac, std::pair<double, double> pet) {
295 #ifdef DEBUG_SSM
296     if (DEBUG_COND1(ego))
297         std::cout << time << " Adding data point for encounter of '" << egoID << "' and '" << foeID << "':\n"
298                   << "type=" << type << ", egoDistToConflict=" << (egoDistToConflict == INVALID ? "NA" : ::toString(egoDistToConflict))
299                   << ", foeDistToConflict=" << (foeDistToConflict == INVALID ? "NA" : ::toString(foeDistToConflict))
300                   << ",\nttc=" << (ttc == INVALID ? "NA" : ::toString(ttc))
301                   << ", drac=" << (drac == INVALID ? "NA" : ::toString(drac))
302                   << ", pet=" << (pet.second == INVALID ? "NA" : ::toString(pet.second))
303                   << std::endl;
304 #endif
305     currentType = type;
306 
307     timeSpan.push_back(time);
308     typeSpan.push_back(type);
309     egoTrajectory.x.push_back(egoX);
310     egoTrajectory.v.push_back(egoV);
311     foeTrajectory.x.push_back(foeX);
312     foeTrajectory.v.push_back(foeV);
313     conflictPointSpan.push_back(conflictPoint);
314     egoDistsToConflict.push_back(egoDistToConflict);
315     foeDistsToConflict.push_back(foeDistToConflict);
316 
317     TTCspan.push_back(ttc);
318     if (ttc != INVALID && (ttc < minTTC.value || minTTC.value == INVALID)) {
319         minTTC.value = ttc;
320         minTTC.time = time;
321         minTTC.pos = conflictPoint;
322         minTTC.type = type;
323     }
324 
325     DRACspan.push_back(drac);
326     if (drac != INVALID && (drac > maxDRAC.value || maxDRAC.value == INVALID)) {
327         maxDRAC.value = drac;
328         maxDRAC.time = time;
329         maxDRAC.pos = conflictPoint;
330         maxDRAC.type = type;
331     }
332 
333     if (pet.first != INVALID && (PET.value >= pet.second || PET.value == INVALID)) {
334         PET.value = pet.second;
335         PET.time = pet.first;
336         PET.pos = conflictPoint;
337         PET.type = type;
338     }
339 }
340 
341 
342 void
resetExtraTime(double value)343 MSDevice_SSM::Encounter::resetExtraTime(double value) {
344     remainingExtraTime = value;
345 }
346 
347 
348 void
countDownExtraTime(double amount)349 MSDevice_SSM::Encounter::countDownExtraTime(double amount) {
350     remainingExtraTime -= amount;
351 }
352 
353 
354 double
getRemainingExtraTime() const355 MSDevice_SSM::Encounter::getRemainingExtraTime() const {
356     return remainingExtraTime;
357 }
358 
359 
EncounterApproachInfo(Encounter * e)360 MSDevice_SSM::EncounterApproachInfo::EncounterApproachInfo(Encounter* e) :
361     encounter(e),
362     type(ENCOUNTER_TYPE_NOCONFLICT_AHEAD),
363     conflictPoint(Position::invalidPosition()),
364     egoConflictEntryDist(INVALID),
365     foeConflictEntryDist(INVALID),
366     egoConflictExitDist(INVALID),
367     foeConflictExitDist(INVALID),
368     egoEstimatedConflictEntryTime(INVALID),
369     foeEstimatedConflictEntryTime(INVALID),
370     egoEstimatedConflictExitTime(INVALID),
371     foeEstimatedConflictExitTime(INVALID),
372     egoConflictAreaLength(INVALID),
373     foeConflictAreaLength(INVALID),
374     egoLeftConflict(false),
375     foeLeftConflict(false),
376     ttc(INVALID),
377     drac(INVALID),
378     pet(std::make_pair(INVALID, INVALID)) {
379 }
380 
381 
382 void
updateAndWriteOutput()383 MSDevice_SSM::updateAndWriteOutput() {
384     if (myHolder.isOnRoad()) {
385         update();
386         // Write out past conflicts
387         flushConflicts();
388     } else {
389         resetEncounters();
390         // Write out past conflicts
391         flushConflicts(true);
392     }
393 }
394 
395 void
update()396 MSDevice_SSM::update() {
397 #ifdef DEBUG_SSM
398     if (DEBUG_COND1(myHolderMS))
399         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' update()\n"
400                   << "Size of myActiveEncounters: " << myActiveEncounters.size()
401                   << "\nSize of myPastConflicts: " << myPastConflicts.size()
402                   << std::endl;
403 #endif
404     // Scan surroundings for other vehicles
405     FoeInfoMap foes;
406     findSurroundingVehicles(*myHolderMS, myRange, foes);
407 
408 #ifdef DEBUG_SSM
409     if (DEBUG_COND1(myHolderMS)) {
410         if (foes.size() > 0) {
411             std::cout << "Scanned surroundings: Found potential foes:\n";
412             for (FoeInfoMap::const_iterator i = foes.begin(); i != foes.end(); ++i) {
413                 std::cout << i->first->getID() << " ";
414             }
415             std::cout << std::endl;
416         } else {
417             std::cout << "Scanned surroundings: No potential conflict could be identified." << std::endl;
418         }
419     }
420 #endif
421 
422     // Update encounters and conflicts -> removes all foes (and deletes corresponding FoeInfos) for which already a corresponding encounter exists
423     processEncounters(foes);
424 
425     // Make new encounters for all foes, which were not removed by processEncounters (and deletes corresponding FoeInfos)
426     createEncounters(foes);
427     foes.clear();
428 
429     // Compute "global SSMs" (only computed once per time-step)
430     computeGlobalMeasures();
431 
432 
433 }
434 
435 
436 void
computeGlobalMeasures()437 MSDevice_SSM::computeGlobalMeasures() {
438     if (myComputeBR || myComputeSGAP || myComputeTGAP) {
439         myGlobalMeasuresTimeSpan.push_back(SIMTIME);
440         if (myComputeBR) {
441             double br = MAX2(-myHolderMS->getAcceleration(), 0.0);
442             if (br > myMaxBR.second) {
443                 myMaxBR = std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), br);
444             }
445             myBRspan.push_back(br);
446         }
447 
448         double leaderSearchDist = 0;
449         std::pair<const MSVehicle*, double> leader(nullptr, 0.);
450         if (myComputeSGAP) {
451             leaderSearchDist = myThresholds["SGAP"];
452         }
453         if (myComputeTGAP) {
454             leaderSearchDist = MAX2(leaderSearchDist, myThresholds["TGAP"] * myHolderMS->getSpeed());
455         }
456 
457         if (leaderSearchDist > 0.) {
458             leader = myHolderMS->getLeader(leaderSearchDist);
459         }
460 
461         if (myComputeSGAP) {
462             if (leader.first == nullptr) {
463                 mySGAPspan.push_back(INVALID);
464             } else {
465                 double sgap = leader.second + leader.first->getVehicleType().getMinGap();
466                 mySGAPspan.push_back(sgap);
467                 if (sgap < myMinSGAP.first.second) {
468                     myMinSGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), sgap), leader.first->getID());
469                 }
470             }
471         }
472 
473         if (myComputeTGAP) {
474             if (leader.first == nullptr || myHolderMS->getSpeed() == 0.) {
475                 myTGAPspan.push_back(INVALID);
476             } else {
477                 const double tgap = (leader.second + leader.first->getVehicleType().getMinGap()) / myHolderMS->getSpeed();
478                 myTGAPspan.push_back(tgap);
479                 if (tgap < myMinTGAP.first.second) {
480                     myMinTGAP = std::make_pair(std::make_pair(std::make_pair(SIMTIME, myHolderMS->getPosition()), tgap), leader.first->getID());
481                 }
482             }
483         }
484 
485     }
486 }
487 
488 
489 void
createEncounters(FoeInfoMap & foes)490 MSDevice_SSM::createEncounters(FoeInfoMap& foes) {
491 #ifdef DEBUG_SSM
492     if (DEBUG_COND1(myHolderMS)) {
493         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' createEncounters()" << std::endl;
494         std::cout << "New foes:\n";
495         for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
496             std::cout << vi->first->getID() << "\n";
497         }
498         std::cout << std::endl;
499     }
500 #endif
501 
502     for (FoeInfoMap::const_iterator foe = foes.begin(); foe != foes.end(); ++foe) {
503         Encounter* e = new Encounter(myHolderMS, foe->first, SIMTIME, myExtraTime);
504         if (updateEncounter(e, foe->second)) { // deletes foe->second
505             if (myOldestActiveEncounterBegin == INVALID) {
506                 assert(myActiveEncounters.empty());
507                 myOldestActiveEncounterBegin = e->begin;
508             }
509             assert(myOldestActiveEncounterBegin <= e->begin);
510             myActiveEncounters.push_back(e);
511         } else {
512             // Discard encounters, where one vehicle already left the conflict area
513         }
514     }
515 }
516 
517 void
resetEncounters()518 MSDevice_SSM::resetEncounters() {
519     // Call processEncounters() with empty vehicle set
520     FoeInfoMap foes;
521     // processEncounters with empty argument closes all encounters
522     processEncounters(foes, true);
523 }
524 
525 void
processEncounters(FoeInfoMap & foes,bool forceClose)526 MSDevice_SSM::processEncounters(FoeInfoMap& foes, bool forceClose) {
527 #ifdef DEBUG_SSM
528     if (DEBUG_COND1(myHolderMS)) {
529         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' processEncounters()" << std::endl;
530         std::cout << "Currently present foes:\n";
531         for (FoeInfoMap::const_iterator vi = foes.begin(); vi != foes.end(); ++vi) {
532             std::cout << vi->first->getID() << "\n";
533         }
534         std::cout << std::endl;
535     }
536 #endif
537 
538     // Run through active encounters. If corresponding foe is still present in foes update and
539     // remove foe from foes. If the foe has disappeared close the encounter (check if it qualifies
540     // as a conflict and in case transfer it to myPastConflicts).
541     // Afterwards run through remaining elements in foes and create new encounters for them.
542 
543     EncounterVector::iterator ei = myActiveEncounters.begin();
544     while (ei != myActiveEncounters.end()) {
545         Encounter* e = *ei;
546         // check whether foe is still on net
547         bool foeExists = !(MSNet::getInstance()->getVehicleControl().getVehicle(e->foeID) == nullptr);
548         if (!foeExists) {
549             e->foe = nullptr;
550         }
551         if (foes.find(e->foe) != foes.end()) {
552             FoeInfo* foeInfo = foes[e->foe];
553             // Update encounter
554             updateEncounter(e, foeInfo); // deletes foeInfo
555             // Erase foes which were already encountered
556             foes.erase(e->foe);
557         } else {
558             if (e->getRemainingExtraTime() <= 0. || forceClose || !foeExists) {
559                 // Close encounter, extra time has expired (deletes e if it does not qualify as conflict)
560                 e->closingRequested = true;
561             } else {
562                 updateEncounter(e, nullptr); // counts down extra time
563             }
564         }
565 
566         if (e->closingRequested) {
567             double eBegin = e->begin;
568             closeEncounter(e);
569             ei = myActiveEncounters.erase(ei);
570             if (myActiveEncounters.empty()) {
571                 myOldestActiveEncounterBegin = INVALID;
572             } else if (eBegin == myOldestActiveEncounterBegin) {
573                 // Erased the oldest encounter, update myOldestActiveEncounterBegin
574                 auto i = myActiveEncounters.begin();
575                 myOldestActiveEncounterBegin = (*i++)->begin;
576                 while (i != myActiveEncounters.end()) {
577                     myOldestActiveEncounterBegin = MIN2(myOldestActiveEncounterBegin, (*i++)->begin);
578                 }
579             }
580         } else {
581             ++ei;
582         }
583     }
584 }
585 
586 
587 bool
qualifiesAsConflict(Encounter * e)588 MSDevice_SSM::qualifiesAsConflict(Encounter* e) {
589     // Check if conflict measure thresholds are exceeded (to decide whether to keep the encounter for writing out)
590 #ifdef DEBUG_SSM
591     if (DEBUG_COND1(myHolderMS))
592         std::cout << SIMTIME << " qualifiesAsConflict() for encounter of vehicles '"
593                   << e->egoID << "' and '" << e->foeID
594                   << "'" << std::endl;
595 #endif
596 
597     if (myComputePET && e->PET.value != INVALID && e->PET.value <= myThresholds["PET"]) {
598         return true;
599     }
600     if (myComputeTTC && e->minTTC.value != INVALID && e->minTTC.value <= myThresholds["TTC"]) {
601         return true;
602     }
603     if (myComputeDRAC && e->maxDRAC.value != INVALID && e->maxDRAC.value >= myThresholds["DRAC"]) {
604         return true;
605     }
606     return false;
607 }
608 
609 
610 void
closeEncounter(Encounter * e)611 MSDevice_SSM::closeEncounter(Encounter* e) {
612     // erase pointers (encounter is stored before being destroyed and pointers could become invalid)
613     e->ego = nullptr;
614     e->foe = nullptr;
615     e->end = e->timeSpan.back();
616     bool wasConflict = qualifiesAsConflict(e);
617     if (wasConflict) {
618         myPastConflicts.push(e);
619 
620 #ifdef DEBUG_SSM
621         if (!myPastConflicts.empty()) {
622             if (DEBUG_COND1(myHolderMS)) {
623                 std::cout << "pastConflictsQueue of veh '" << myHolderMS->getID() << "':\n";
624             }
625             auto myPastConflicts_bak = myPastConflicts;
626             double lastBegin = myPastConflicts.top()->begin;
627             while (!myPastConflicts.empty()) {
628                 auto c = myPastConflicts.top();
629                 myPastConflicts.pop();
630                 if (DEBUG_COND1(myHolderMS)) {
631                     std::cout << "  Conflict with foe '" << c->foe << "' (time " << c->begin << "-" << c->end << ")\n";
632                 }
633                 if (c->begin < lastBegin) {
634                     std::cout << "  Queue corrupt...\n";
635                     assert(false);
636                 }
637                 lastBegin = c->begin;
638             }
639             std::cout << std::endl;
640             myPastConflicts = myPastConflicts_bak;
641         }
642 #endif
643     } else {
644         delete e;
645     }
646 #ifdef DEBUG_SSM
647     if (DEBUG_COND1(myHolderMS))
648         std::cout << SIMTIME << " closeEncounter() of vehicles '"
649                   << e->egoID << "' and '" << e->foeID
650                   << "' (was ranked as " << (wasConflict ? "conflict" : "non-conflict") << ")" << std::endl;
651 #endif
652 
653     return;
654 }
655 
656 
657 bool
updateEncounter(Encounter * e,FoeInfo * foeInfo)658 MSDevice_SSM::updateEncounter(Encounter* e, FoeInfo* foeInfo) {
659 #ifdef DEBUG_SSM
660     if (DEBUG_COND1(myHolderMS))
661         std::cout << SIMTIME << " updateEncounter() of vehicles '"
662                   << e->egoID << "' and '" << e->foeID
663                   << "'" << std::endl;
664 #endif
665     assert(e->foe != 0);
666 
667     // Struct storing distances (determined in classifyEncounter()) and times to potential conflict entry / exit (in estimateConflictTimes())
668     EncounterApproachInfo eInfo(e);
669 
670     // Classify encounter type based on the present information
671     // More details on follower/lead relation are determined in a second step below, see estimateConflictTimes()
672     // If a crossing situation is ongoing (i.e. one of the vehicles entered the conflict area already in the last step,
673     // this is handled by passedEncounter by only tracing the vehicle's movements)
674     // The further development of the encounter type is done in checkConflictEntryAndExit()
675     eInfo.type = classifyEncounter(foeInfo, eInfo);
676 
677     // Discard new encounters, where one vehicle has already left the conflict area
678     if (eInfo.encounter->size() == 0) {
679         if (eInfo.type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
680                 || eInfo.type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA) {
681             delete foeInfo;
682             // Signalize to discard
683             return false;
684         }
685     }
686 
687     if (eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
688         // At this state, eInfo.type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD implies that the foe
689         // is either out of the device's range or its route does not interfere with the ego's route.
690 #ifdef DEBUG_SSM
691         if (DEBUG_COND1(myHolderMS))
692             std::cout << SIMTIME << " Encounter of vehicles '"
693                       << e->egoID << "' and '" << e->foeID
694                       << "' does not imply any conflict." << std::endl;
695 #endif
696         updatePassedEncounter(e, foeInfo, eInfo);
697 //        return;
698     } else if (eInfo.type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
699                || eInfo.type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
700                || eInfo.type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
701                || eInfo.type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
702                || eInfo.type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
703         // Ongoing encounter. Treat with update passed encounter (trace covered distances)
704         // eInfo.type only holds the previous type
705         updatePassedEncounter(e, foeInfo, eInfo);
706 
707         // Estimate times until a possible conflict / collision
708         estimateConflictTimes(eInfo);
709 
710     } else {
711         // Estimate times until a possible conflict / collision
712         // Not all are used for all types of encounters:
713         // Follow/lead situation doesn't need them at all, currently (might change if more SSMs are implemented).
714         // Crossing / Merging calculates entry times to determine leader/follower and calculates the exit time for the leader.
715         estimateConflictTimes(eInfo);
716 
717         // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
718         e->resetExtraTime(myExtraTime);
719     }
720 
721     // update entry/exit times for conflict area
722     checkConflictEntryAndExit(eInfo);
723 
724     // update (x,y)-coords of conflict point
725     determineConflictPoint(eInfo);
726 
727     // Compute SSMs
728     computeSSMs(eInfo);
729 
730     // Add current states to trajectories and update type
731     e->add(SIMTIME, eInfo.type, e->ego->getPosition(), e->ego->getVelocityVector(), e->foe->getPosition(), e->foe->getVelocityVector(),
732            eInfo.conflictPoint, eInfo.egoConflictEntryDist, eInfo.foeConflictEntryDist, eInfo.ttc, eInfo.drac, eInfo.pet);
733 
734     // free foeInfo
735     delete foeInfo;
736     // Keep encounter
737     return true;
738 }
739 
740 
741 void
determineConflictPoint(EncounterApproachInfo & eInfo)742 MSDevice_SSM::determineConflictPoint(EncounterApproachInfo& eInfo) {
743     /*  Calculates the (x,y)-coordinate for the eventually predicted conflict point and stores the result in
744      *         eInfo.conflictPoint. In case of MERGING and CROSSING, this is the entry point to conflict area for follower
745      *         In case of FOLLOWING it is the position of leader's back. */
746 
747 #ifdef DEBUG_SSM
748     if (DEBUG_COND1(eInfo.encounter->ego)) {
749         std::cout << SIMTIME << " determineConflictPoint()" << std::endl;
750     }
751 #endif
752 
753     const EncounterType& type = eInfo.type;
754     const Encounter* e = eInfo.encounter;
755     if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
756             || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
757             || type == ENCOUNTER_TYPE_COLLISION) {
758         // Both vehicles have already past the conflict entry.
759         assert(e->size() > 0); // A new encounter should not be created if both vehicles already entered the conflict area
760         eInfo.conflictPoint = e->conflictPointSpan.back();
761     } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
762                || type == ENCOUNTER_TYPE_MERGING_FOLLOWER
763                || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
764                || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA) {
765         eInfo.conflictPoint = e->ego->getPositionAlongBestLanes(eInfo.egoConflictEntryDist);
766     } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
767                || type == ENCOUNTER_TYPE_MERGING_LEADER
768                || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
769                || type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA) {
770         eInfo.conflictPoint = e->foe->getPositionAlongBestLanes(eInfo.foeConflictEntryDist);
771     } else if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
772         eInfo.conflictPoint = e->foe->getPosition(-e->foe->getLength());
773     } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
774         eInfo.conflictPoint = e->ego->getPosition(-e->ego->getLength());
775     } else {
776 #ifdef DEBUG_SSM
777         if (DEBUG_COND1(eInfo.encounter->ego)) {
778             std::cout << "No conflict point associated with encounter type " << type << std::endl;
779         }
780 #endif
781         return;
782     }
783 
784 #ifdef DEBUG_SSM
785     if (DEBUG_COND1(eInfo.encounter->ego)) {
786         std::cout << "    Conflict at " << eInfo.conflictPoint << std::endl;
787     }
788 #endif
789 }
790 
791 
792 void
estimateConflictTimes(EncounterApproachInfo & eInfo)793 MSDevice_SSM::estimateConflictTimes(EncounterApproachInfo& eInfo) {
794 
795     EncounterType& type = eInfo.type;
796     Encounter* e = eInfo.encounter;
797 
798     assert(type != ENCOUNTER_TYPE_NOCONFLICT_AHEAD); // arrival times not defined, if no conflict is ahead.
799 #ifdef DEBUG_SSM
800     if (DEBUG_COND1(e->ego))
801         std::cout << SIMTIME << " estimateConflictTimes() for ego '" << e->egoID << "' and foe '" << e->foeID << "'\n"
802                   << "    encounter type: " << eInfo.type << "\n"
803                   << "    egoConflictEntryDist=" << (eInfo.egoConflictEntryDist == INVALID ? "NA" : ::toString(eInfo.egoConflictEntryDist))
804                   << ", foeConflictEntryDist=" << (eInfo.foeConflictEntryDist == INVALID ? "NA" : ::toString(eInfo.foeConflictEntryDist))
805                   << "\n    ego speed=" << e->ego->getSpeed()
806                   << ", foe speed=" << e->foe->getSpeed()
807                   << std::endl;
808 #endif
809 
810     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER || type == ENCOUNTER_TYPE_MERGING_ADJACENT || type == ENCOUNTER_TYPE_ON_ADJACENT_LANES) {
811         // No need to know the times until ...ConflictDistEntry, currently. They would correspond to an estimated time headway or similar.
812         // TTC must take into account the movement of the leader, as would DRAC, PET doesn't need the time either, since it uses aposteriori
813         // values.
814 #ifdef DEBUG_SSM
815         if (DEBUG_COND1(e->ego))
816             std::cout << "    encouter type " << type << " -> no entry/exit times to be calculated."
817                       << std::endl;
818 #endif
819         return;
820     }
821 
822     assert(type == ENCOUNTER_TYPE_MERGING || type == ENCOUNTER_TYPE_CROSSING
823            || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
824            || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
825            || type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
826            || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
827            || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
828            || type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA);
829 
830     // Determine exit distances
831     if (type == ENCOUNTER_TYPE_MERGING)  {
832         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + e->ego->getVehicleType().getLength();
833         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + e->foe->getVehicleType().getLength();
834     } else {
835         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getVehicleType().getLength();
836         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getVehicleType().getLength();
837     }
838 
839     // Estimate entry times to stipulate a leader / follower relation for the encounter.
840     if (eInfo.egoConflictEntryDist > 0.) {
841         eInfo.egoEstimatedConflictEntryTime = e->ego->getCarFollowModel().estimateArrivalTime(eInfo.egoConflictEntryDist, e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(), MIN2(0., e->ego->getAcceleration()));
842         assert(eInfo.egoEstimatedConflictEntryTime > 0.);
843     } else {
844         // ego already entered conflict area
845         eInfo.egoEstimatedConflictEntryTime = 0.;
846     }
847     if (eInfo.foeConflictEntryDist > 0.) {
848         eInfo.foeEstimatedConflictEntryTime = e->foe->getCarFollowModel().estimateArrivalTime(eInfo.foeConflictEntryDist, e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(), MIN2(0., e->foe->getAcceleration()));
849         assert(eInfo.foeEstimatedConflictEntryTime > 0.);
850     } else {
851         // foe already entered conflict area
852         eInfo.foeEstimatedConflictEntryTime = 0.;
853     }
854 
855 #ifdef DEBUG_SSM
856     if (DEBUG_COND1(e->ego))
857         std::cout << "    Conflict type: " << toString(type) << "\n"
858                   << "    egoConflictEntryTime=" << (eInfo.egoEstimatedConflictEntryTime == INVALID ? "INVALID" : ::toString(eInfo.egoEstimatedConflictEntryTime))
859                   << ", foeConflictEntryTime=" << (eInfo.foeEstimatedConflictEntryTime == INVALID ? "INVALID" : ::toString(eInfo.foeEstimatedConflictEntryTime))
860                   << std::endl;
861 #endif
862 
863     // Estimate exit times from conflict area for leader / follower.
864     if (eInfo.egoConflictExitDist >= 0.) {
865         eInfo.egoEstimatedConflictExitTime = e->ego->getCarFollowModel().estimateArrivalTime(eInfo.egoConflictExitDist, e->ego->getSpeed(), e->ego->getMaxSpeedOnLane(), MIN2(0., e->ego->getAcceleration()));
866     } else {
867         eInfo.egoEstimatedConflictExitTime = 0.;
868     }
869     if (eInfo.foeConflictExitDist >= 0.) {
870         eInfo.foeEstimatedConflictExitTime = e->foe->getCarFollowModel().estimateArrivalTime(eInfo.foeConflictExitDist, e->foe->getSpeed(), e->foe->getMaxSpeedOnLane(), MIN2(0., e->foe->getAcceleration()));
871     } else {
872         eInfo.foeEstimatedConflictExitTime = 0.;
873     }
874 
875 
876     if (type != ENCOUNTER_TYPE_MERGING && type != ENCOUNTER_TYPE_CROSSING) {
877         // this call is issued in context of an ongoing conflict, therefore complete type is already known for the encounter
878         // (One of EGO_ENTERED_CONFLICT_AREA, FOE_ENTERED_CONFLICT_AREA, EGO_LEFT_CONFLICT_AREA, FOE_LEFT_CONFLICT_AREA, BOTH_ENTERED_CONFLICT_AREA)
879         // --> no need to specify incomplete encounter type
880         return;
881     }
882 
883     // For merging and crossing situation, the leader/follower relation not determined by classifyEncounter()
884     // This is done below based on the estimated conflict entry times
885     if (eInfo.egoEstimatedConflictEntryTime == 0. && eInfo.foeEstimatedConflictEntryTime == 0.) {
886         type = ENCOUNTER_TYPE_COLLISION;
887         std::stringstream ss;
888         ss << "SSM device of vehicle '" << e->egoID << "' detected collision with vehicle '" << e->foeID << "'";
889         WRITE_WARNING(ss.str());
890     } else if (eInfo.egoEstimatedConflictEntryTime < eInfo.foeEstimatedConflictEntryTime) {
891         // ego is estimated first at conflict point
892 #ifdef DEBUG_SSM
893         if (DEBUG_COND1(e->ego))
894             std::cout << "    -> ego is estimated leader at conflict entry."
895                       << " egoConflictExitTime=" << (eInfo.egoEstimatedConflictExitTime == INVALID ? "NA" : ::toString(eInfo.egoEstimatedConflictExitTime))
896                       << std::endl;
897 #endif
898         type = type == ENCOUNTER_TYPE_CROSSING ? ENCOUNTER_TYPE_CROSSING_LEADER : ENCOUNTER_TYPE_MERGING_LEADER;
899     } else {
900         // ego is estimated second at conflict point
901 #ifdef DEBUG_SSM
902         if (DEBUG_COND1(e->ego))
903             std::cout << "    -> foe is estimated leader at conflict entry."
904                       << " foeConflictExitTime=" << (eInfo.foeEstimatedConflictExitTime == INVALID ? "NA" : ::toString(eInfo.foeEstimatedConflictExitTime))
905                       << std::endl;
906 #endif
907         type = type == ENCOUNTER_TYPE_CROSSING ? ENCOUNTER_TYPE_CROSSING_FOLLOWER : ENCOUNTER_TYPE_MERGING_FOLLOWER;
908     }
909 
910 }
911 
912 
913 
914 void
computeSSMs(EncounterApproachInfo & eInfo) const915 MSDevice_SSM::computeSSMs(EncounterApproachInfo& eInfo) const {
916 #ifdef DEBUG_SSM
917     if (DEBUG_COND1(myHolderMS)) {
918         Encounter* e = eInfo.encounter;
919         std::cout << SIMTIME << " computeSSMs() for vehicles '"
920                   << e->ego->getID() << "' and '" << e->foe->getID()
921                   << "'" << std::endl;
922     }
923 #endif
924 
925     const EncounterType& type = eInfo.type;
926 
927     if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER || type == ENCOUNTER_TYPE_CROSSING_LEADER
928             || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
929             || type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER
930             || type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
931         if (myComputeTTC || myComputeDRAC) {
932             determineTTCandDRAC(eInfo);
933         }
934         determinePET(eInfo);
935     } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
936         determinePET(eInfo);
937     } else if (type == ENCOUNTER_TYPE_COLLISION) {
938         // TODO: handle collision
939     } else if (type == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA || type == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
940                || type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA || type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
941         // No conflict measures apply for these states, which correspond to intermediate times between
942         // one vehicle leaving the conflict area and the arrival time for the other (difference corresponds to the PET)
943     } else if (type == ENCOUNTER_TYPE_ON_ADJACENT_LANES || type == ENCOUNTER_TYPE_MERGING_ADJACENT) {
944         // No conflict measures apply for this state
945     } else if (type == ENCOUNTER_TYPE_MERGING_PASSED || type == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
946         // No conflict measures apply for this state
947     } else if (type == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
948         // No conflict measures apply for this state
949     } else {
950         std::stringstream ss;
951         ss << "'" << type << "'";
952         WRITE_WARNING("Unknown or undetermined encounter type at computeSSMs(): " + ss.str());
953     }
954 
955 #ifdef DEBUG_SSM
956     if (DEBUG_COND1(myHolderMS)) {
957         Encounter* e = eInfo.encounter;
958         std::cout << "computeSSMs() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "':\n"
959                   << "  ttc=" << (eInfo.ttc == INVALID ? "INVALID" : ::toString(eInfo.ttc))
960                   << ", drac=" << (eInfo.drac == INVALID ? "INVALID" : ::toString(eInfo.drac))
961                   << ", pet=" << (eInfo.pet.second == INVALID ? "INVALID" : ::toString(eInfo.pet.second))
962                   << std::endl;
963     }
964 #endif
965 }
966 
967 
968 void
determinePET(EncounterApproachInfo & eInfo) const969 MSDevice_SSM::determinePET(EncounterApproachInfo& eInfo) const {
970     Encounter* e = eInfo.encounter;
971     if (e->size() == 0) {
972         return;
973     }
974     const EncounterType& type = eInfo.type;
975     std::pair<double, double>& pet = eInfo.pet;
976 
977 #ifdef DEBUG_SSM
978     if (DEBUG_COND1(myHolderMS))
979         std::cout << SIMTIME << " determinePET() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'"
980                   << "(type: " << toString(static_cast<EncounterType>(e->typeSpan.back())) << ")" << std::endl;
981 #endif
982 
983     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER || type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
984         // For a following situation, the corresponding PET-value is merely the time-headway.
985         //       Determining these could be done by comparison of memorized gaps with memorized covered distances
986         //       Implementation is postponed. Tracing the time gaps (in contrast to crossing PET) corresponds to
987         //       a vector of values not a single value.
988         // pass
989     } else if (type == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
990         EncounterType prevType = static_cast<EncounterType>(e->typeSpan.back());
991         if (prevType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA) {
992 #ifdef DEBUG_SSM
993             if (DEBUG_COND1(myHolderMS))
994                 std::cout << "PET for crossing encounter already calculated as " << e->PET.value
995                           << std::endl;
996 #endif
997             // pet must have been calculated already
998             assert(e->PET.value != INVALID);
999             return;
1000         }
1001 
1002         // this situation should have emerged from one of the following
1003         assert(prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1004                || prevType == ENCOUNTER_TYPE_CROSSING_LEADER
1005                || prevType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1006                || prevType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1007                || prevType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
1008                || prevType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
1009                || prevType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA);
1010 
1011 
1012 #ifdef DEBUG_SSM
1013         if (DEBUG_COND1(myHolderMS))
1014             std::cout << "e->egoDistsToConflict.back() = " << e->egoDistsToConflict.back()
1015                       << "\ne->egoConflictEntryTime = " << e->egoConflictEntryTime
1016                       << "\ne->egoConflictExitTime = " << e->egoConflictExitTime
1017                       << "\ne->foeDistsToConflict.back() = " << e->foeDistsToConflict.back()
1018                       << "\ne->foeConflictEntryTime = " << e->foeConflictEntryTime
1019                       << "\ne->foeConflictExitTime = " << e->foeConflictExitTime
1020                       << std::endl;
1021 #endif
1022 
1023         // But both have passed the conflict area
1024         assert(e->egoConflictEntryTime != INVALID || e->foeConflictEntryTime != INVALID);
1025 
1026         // Both have left the conflict region
1027         // (Conflict may have started as one was already within the conflict area - thus the check for INVALID entry times)
1028         if (e->foeConflictEntryTime == INVALID || (e->egoConflictEntryTime != INVALID && e->egoConflictEntryTime > e->foeConflictExitTime)) {
1029             pet.first = e->egoConflictEntryTime;
1030             pet.second = e->egoConflictEntryTime - e->foeConflictExitTime;
1031         } else if (e->egoConflictEntryTime == INVALID || (e->egoConflictEntryTime != INVALID && e->foeConflictEntryTime > e->egoConflictExitTime)) {
1032             pet.first = e->foeConflictEntryTime;
1033             pet.second = e->foeConflictEntryTime - e->egoConflictExitTime;
1034         } else {
1035 #ifdef DEBUG_SSM
1036             if (DEBUG_COND1(myHolderMS))
1037                 std::cout << "Unexpected branch in determinePET: Both passed conflict area in the same step."
1038                           << std::endl;
1039 #endif
1040             pet.first = INVALID;
1041             pet.second = INVALID;
1042             assert(prevType != ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1043                    && prevType != ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA);
1044             assert(false); // let this fail for debug build
1045         }
1046 
1047         // Reset entry and exit times two allow an eventual subsequent re-use
1048         e->egoConflictEntryTime = INVALID;
1049         e->egoConflictExitTime = INVALID;
1050         e->foeConflictEntryTime = INVALID;
1051         e->foeConflictExitTime = INVALID;
1052 
1053 #ifdef DEBUG_SSM
1054         if (DEBUG_COND1(myHolderMS))
1055             std::cout << "Calculated PET = " << pet.second << " (at t=" << pet.first << ")"
1056                       << std::endl;
1057 #endif
1058     } else {
1059         // other cases (merging and pre-crossing situations) do not correspond to a PET calculation.
1060 #ifdef DEBUG_SSM
1061         if (DEBUG_COND1(myHolderMS))
1062             std::cout << "PET unappropriate for merging and pre-crossing situations. No calculation performed."
1063                       << std::endl;
1064 #endif
1065         return;
1066     }
1067 }
1068 
1069 
1070 void
determineTTCandDRAC(EncounterApproachInfo & eInfo) const1071 MSDevice_SSM::determineTTCandDRAC(EncounterApproachInfo& eInfo) const {
1072     Encounter* e = eInfo.encounter;
1073     const EncounterType& type = eInfo.type;
1074     double& ttc = eInfo.ttc;
1075     double& drac = eInfo.drac;
1076 
1077 #ifdef DEBUG_SSM
1078     if (DEBUG_COND1(myHolderMS))
1079         std::cout << SIMTIME << " determineTTCandDRAC() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "' (type = " << eInfo.type << ")"
1080                   << std::endl;
1081 #endif
1082 
1083     // Dependent on the actual encounter situation (eInfo.type) calculate the TTC.
1084     // For merging and crossing, different cases occur when a collision during the merging / crossing process is predicted.
1085     if (type == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER) {
1086         double gap = eInfo.egoConflictEntryDist;
1087         if (myComputeTTC) {
1088             ttc = computeTTC(gap, e->ego->getSpeed(), e->foe->getSpeed());
1089         }
1090         if (myComputeDRAC) {
1091             drac = computeDRAC(gap, e->ego->getSpeed(), e->foe->getSpeed());
1092         }
1093     } else if (type == ENCOUNTER_TYPE_FOLLOWING_LEADER) {
1094         double gap = eInfo.foeConflictEntryDist;
1095         if (myComputeTTC) {
1096             ttc = computeTTC(gap, e->foe->getSpeed(), e->ego->getSpeed());
1097         }
1098         if (myComputeDRAC) {
1099             drac = computeDRAC(gap, e->foe->getSpeed(), e->ego->getSpeed());
1100         }
1101     } else if (type == ENCOUNTER_TYPE_MERGING_FOLLOWER || type == ENCOUNTER_TYPE_MERGING_LEADER) {
1102         // TODO: calculate more specifically whether a following situation in the merge conflict area
1103         //       is predicted when assuming constant speeds or whether a side collision is predicted.
1104         //       Currently, we ignore any conflict area before the actual merging point of the lanes.
1105 
1106         // linearly extrapolated arrival times at the conflict
1107         // NOTE: These differ from the estimated times stored in eInfo
1108         double egoEntryTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictEntryDist / e->ego->getSpeed() : INVALID;
1109         double egoExitTime = e->ego->getSpeed() > 0 ? eInfo.egoConflictExitDist / e->ego->getSpeed() : INVALID;
1110         double foeEntryTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictEntryDist / e->foe->getSpeed() : INVALID;
1111         double foeExitTime = e->foe->getSpeed() > 0 ? eInfo.foeConflictExitDist / e->foe->getSpeed() : INVALID;
1112 
1113 #ifdef DEBUG_SSM
1114         if (DEBUG_COND1(myHolderMS))
1115             std::cout << "   Conflict times with constant speed extrapolation for merging situation:\n   "
1116                       << " egoEntryTime=" << (egoEntryTime == INVALID ? "NA" : ::toString(egoEntryTime))
1117                       << ", egoExitTime=" << (egoExitTime == INVALID ? "NA" : ::toString(egoExitTime))
1118                       << ", foeEntryTime=" << (foeEntryTime == INVALID ? "NA" : ::toString(foeEntryTime))
1119                       << ", foeExitTime=" << (foeExitTime == INVALID ? "NA" : ::toString(foeExitTime))
1120                       << std::endl;
1121 #endif
1122 
1123         // based on that, we obtain
1124         if (egoEntryTime == INVALID || foeEntryTime == INVALID) {
1125             // at least one vehicle is stopped
1126             ttc = INVALID;
1127             drac = INVALID;
1128 #ifdef DEBUG_SSM
1129             if (DEBUG_COND1(myHolderMS)) {
1130                 std::cout << "    No TTC and DRAC computed as one vehicle is stopped." << std::endl;
1131             }
1132 #endif
1133             return;
1134         }
1135         double leaderEntryTime = MIN2(egoEntryTime, foeEntryTime);
1136         double followerEntryTime = MAX2(egoEntryTime, foeEntryTime);
1137         double leaderExitTime = leaderEntryTime == egoEntryTime ? egoExitTime : foeExitTime;
1138         //double followerExitTime = leaderEntryTime==egoEntryTime?foeExitTime:egoExitTime;
1139         double leaderSpeed = leaderEntryTime == egoEntryTime ? e->ego->getSpeed() : e->foe->getSpeed();
1140         double followerSpeed = leaderEntryTime == egoEntryTime ? e->foe->getSpeed() : e->ego->getSpeed();
1141         double leaderConflictDist = leaderEntryTime == egoEntryTime ? eInfo.egoConflictEntryDist : eInfo.foeConflictEntryDist;
1142         double followerConflictDist = leaderEntryTime == egoEntryTime ? eInfo.foeConflictEntryDist : eInfo.egoConflictEntryDist;
1143         double leaderLength = leaderEntryTime == egoEntryTime ? e->ego->getLength() : e->foe->getLength();
1144         if (leaderExitTime >= followerEntryTime) {
1145             // collision would occur at merge area
1146             if (myComputeTTC) {
1147                 ttc = computeTTC(followerConflictDist, followerSpeed, 0.);
1148             }
1149             // TODO: Calculate more specific drac for merging case here (complete stop is not always necessary -> see calculation for crossing case)
1150             //       Rather the
1151             if (myComputeDRAC) {
1152                 drac = computeDRAC(followerConflictDist, followerSpeed, 0.);
1153             }
1154 //            if (myComputeDRAC) drac = computeDRAC(eInfo);
1155 
1156 #ifdef DEBUG_SSM
1157             if (DEBUG_COND1(myHolderMS))
1158                 std::cout << "    Extrapolation predicts collision *at* merge point with TTC=" << ttc
1159                           << ", drac=" << drac << std::endl;
1160 #endif
1161 
1162         } else {
1163             // -> No collision at the merge area
1164             if (myComputeTTC) {
1165                 // Check if after merge a collision would occur if speeds are hold constant.
1166                 double gapAfterMerge = followerConflictDist - leaderExitTime * followerSpeed;
1167                 assert(gapAfterMerge >= 0);
1168 
1169                 // ttc as for following situation (assumes no collision until leader merged)
1170                 double ttcAfterMerge = computeTTC(gapAfterMerge, followerSpeed, leaderSpeed);
1171                 ttc = ttcAfterMerge == INVALID ? INVALID : leaderExitTime + ttcAfterMerge;
1172             }
1173             if (myComputeDRAC) {
1174                 // Intitial gap. (May be negative only if the leader speed is higher than the follower speed, i.e., dv < 0)
1175                 double g0 = followerConflictDist - leaderConflictDist - leaderLength;
1176                 if (g0 < 0) {
1177                     // Speed difference must be positive if g0<0.
1178                     assert(leaderSpeed - followerSpeed > 0);
1179                     // no deceleration needed for dv>0 and gap after merge >= 0
1180                     drac = INVALID;
1181                 } else {
1182                     // compute drac as for a following situation
1183                     drac = computeDRAC(g0, followerSpeed, leaderSpeed);
1184                 }
1185             }
1186 #ifdef DEBUG_SSM
1187             if (DEBUG_COND1(myHolderMS)) {
1188                 if (ttc == INVALID) {
1189                     // assert(dv >= 0);
1190                     assert(drac == INVALID || drac == 0.0);
1191                     std::cout << "    Extrapolation does not predict any collision." << std::endl;
1192                 } else {
1193                     std::cout << "    Extrapolation predicts collision *after* merge point with TTC="
1194                               << (ttc == INVALID ? "NA" : ::toString(ttc))
1195                               << ", drac=" << (drac == INVALID ? "NA" : ::toString(drac)) << std::endl;
1196                 }
1197             }
1198 #endif
1199 
1200         }
1201 
1202     } else if (type == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1203                || type == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA) {
1204         if (myComputeDRAC) {
1205             drac = computeDRAC(eInfo);
1206         }
1207         if (eInfo.egoEstimatedConflictEntryTime <= eInfo.foeEstimatedConflictExitTime) {
1208             // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1209             double gap = eInfo.egoConflictEntryDist;
1210             if (myComputeTTC) {
1211                 ttc = computeTTC(gap, e->ego->getSpeed(), 0.);
1212             }
1213         } else {
1214             // encounter is expected to happen without collision
1215             ttc = INVALID;
1216         }
1217     } else if (type == ENCOUNTER_TYPE_CROSSING_LEADER
1218                || type == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA) {
1219         if (myComputeDRAC) {
1220             drac = computeDRAC(eInfo);
1221         }
1222         if (eInfo.foeEstimatedConflictEntryTime <= eInfo.egoEstimatedConflictExitTime) {
1223             // follower's predicted arrival at the crossing area is earlier than the leader's predicted exit -> collision predicted
1224             double gap = eInfo.foeConflictEntryDist;
1225             if (myComputeTTC) {
1226                 ttc = computeTTC(gap, e->foe->getSpeed(), 0.);
1227             }
1228         } else {
1229             // encounter is expected to happen without collision
1230             ttc = INVALID;
1231         }
1232     } else {
1233 #ifdef DEBUG_SSM
1234         if (DEBUG_COND1(myHolderMS)) {
1235             std::stringstream ss;
1236             ss << "'" << type << "'";
1237             WRITE_WARNING("Underspecified or unknown encounter type in MSDevice_SSM::determineTTCandDRAC(): " + ss.str());
1238         }
1239 #endif
1240     }
1241 
1242 #ifdef DEBUG_SSM
1243     if (DEBUG_COND1(myHolderMS))
1244         std::cout << "ttc=" << (ttc == INVALID ? "INVALID" : ::toString(ttc)) << ", drac=" << (drac == INVALID ? "INVALID" : ::toString(drac))
1245                   << std::endl;
1246 #endif
1247 }
1248 
1249 
1250 double
computeTTC(double gap,double followerSpeed,double leaderSpeed) const1251 MSDevice_SSM::computeTTC(double gap, double followerSpeed, double leaderSpeed) const {
1252     // TODO: in merging situations, the TTC may be lower than the one computed here for following situations
1253     //  (currently only the cross section corresponding to the target lane's begin is considered)
1254     // More specifically, the minimum has to be taken from the two if a collision at merge was predicted.
1255 #ifdef DEBUG_SSM
1256     if (DEBUG_COND1(myHolderMS))
1257         std::cout << "computeTTC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1258                   << std::endl;
1259 #endif
1260     if (gap <= 0.) {
1261         return 0.;    // collision already happend
1262     }
1263     double dv = followerSpeed - leaderSpeed;
1264     if (dv <= 0.) {
1265         return INVALID;    // no collision
1266     }
1267 
1268     return gap / dv;
1269 }
1270 
1271 
1272 double
computeDRAC(double gap,double followerSpeed,double leaderSpeed)1273 MSDevice_SSM::computeDRAC(double gap, double followerSpeed, double leaderSpeed) {
1274 //#ifdef DEBUG_SSM_DRAC
1275 //    if (DEBUG_COND)
1276 //    std::cout << "computeDRAC() with gap=" << gap << ", followerSpeed=" << followerSpeed << ", leaderSpeed=" << leaderSpeed
1277 //              << std::endl;
1278 //#endif
1279     if (gap <= 0.) {
1280         return INVALID;    // collision!
1281     }
1282     double dv = followerSpeed - leaderSpeed;
1283     if (dv <= 0.) {
1284         return 0.0;    // no need to break
1285     }
1286     assert(followerSpeed > 0.);
1287     return 0.5 * dv * dv / gap; // following Guido et al. (2011)
1288 }
1289 
1290 double
computeDRAC(const EncounterApproachInfo & eInfo)1291 MSDevice_SSM::computeDRAC(const EncounterApproachInfo& eInfo) {
1292     // Introduce concise variable names
1293     double dEntry1 = eInfo.egoConflictEntryDist;
1294     double dEntry2 = eInfo.foeConflictEntryDist;
1295     double dExit1 = eInfo.egoConflictExitDist;
1296     double dExit2 = eInfo.foeConflictExitDist;
1297     double v1 = eInfo.encounter->ego->getSpeed();
1298     double v2 = eInfo.encounter->foe->getSpeed();
1299     double tEntry1 = eInfo.egoEstimatedConflictEntryTime;
1300     double tEntry2 = eInfo.foeEstimatedConflictEntryTime;
1301     double tExit1 = eInfo.egoEstimatedConflictExitTime;
1302     double tExit2 = eInfo.foeEstimatedConflictExitTime;
1303 #ifdef DEBUG_SSM_DRAC
1304     if (DEBUG_COND1(eInfo.encounter->ego))
1305         std::cout << SIMTIME << "computeDRAC() with"
1306                   << "\ndEntry1=" << dEntry1 << ", dEntry2=" << dEntry2
1307                   << ", dExit1=" << dExit1 << ", dExit2=" << dExit2
1308                   << ",\nv1=" << v1 << ", v2=" << v2
1309                   << "\ntEntry1=" << (tEntry1 == INVALID ? "NA" : ::toString(tEntry1)) << ", tEntry2=" << (tEntry2 == INVALID ? "NA" : ::toString(tEntry2))
1310                   << ", tExit1=" << (tExit1 == INVALID ? "NA" : ::toString(tExit1)) << ", tExit2=" << (tExit2 == INVALID ? "NA" : ::toString(tExit2))
1311                   << std::endl;
1312 #endif
1313     if (dExit1 <= 0. || dExit2 <= 0.) {
1314         // At least one vehicle already left or is not about to enter conflict area at all => no breaking needed.
1315 #ifdef DEBUG_SSM_DRAC
1316         if (DEBUG_COND1(eInfo.encounter->ego)) {
1317             std::cout << "One already left conflict area -> drac == 0." << std::endl;
1318         }
1319 #endif
1320         return 0.;
1321     }
1322     if (dEntry1 <= 0. && dEntry2 <= 0.) {
1323         // collision... (both already entered conflict area but none left)
1324 #ifdef DEBUG_SSM_DRAC
1325         if (DEBUG_COND1(eInfo.encounter->ego)) {
1326             std::cout << "Both entered conflict area but neither left. -> collision!" << std::endl;
1327         }
1328 #endif
1329         return INVALID;
1330     }
1331 
1332     double drac = std::numeric_limits<double>::max();
1333     if (dEntry1 > 0.) {
1334         // vehicle 1 could break
1335 #ifdef DEBUG_SSM_DRAC
1336         if (DEBUG_COND1(eInfo.encounter->ego)) {
1337             std::cout << "Ego could break..." << std::endl;
1338         }
1339 #endif
1340         if (tExit2 != INVALID) {
1341             // Vehicle 2 is expected to leave conflict area at t2
1342             drac = MIN2(drac, 2 * (v1 - dEntry1 / tExit2) / tExit2);
1343 #ifdef DEBUG_SSM_DRAC
1344             if (DEBUG_COND1(eInfo.encounter->ego)) {
1345                 std::cout << "  Foe expected to leave in " << tExit2 << "-> Ego needs drac=" << drac << std::endl;
1346             }
1347 #endif
1348         } else {
1349             // Vehicle 2 is expected to stop on conflict area or earlier
1350             if (tEntry2 != INVALID) {
1351                 // ... on conflict area => veh1 has to stop before entry
1352                 drac = MIN2(drac, computeDRAC(dEntry1, v1, 0));
1353 #ifdef DEBUG_SSM_DRAC
1354                 if (DEBUG_COND1(eInfo.encounter->ego)) {
1355                     std::cout << "  Foe is expected stop on conflict area -> Ego needs drac=" << drac << std::endl;
1356                 }
1357 #endif
1358             } else {
1359                 // ... before conflict area
1360 #ifdef DEBUG_SSM_DRAC
1361                 if (DEBUG_COND1(eInfo.encounter->ego)) {
1362                     std::cout << "  Foe is expected stop before conflict area -> no drac computation for ego (will be done for foe if applicable)" << std::endl;
1363                 }
1364 #endif
1365             }
1366         }
1367     }
1368 
1369     if (dEntry2 > 0.) {
1370         // vehicle 2 could break
1371 #ifdef DEBUG_SSM_DRAC
1372         if (DEBUG_COND1(eInfo.encounter->ego)) {
1373             std::cout << "Foe could break..." << std::endl;
1374         }
1375 #endif
1376         if (tExit1 != INVALID) {
1377             // Vehicle 1 is expected to leave conflict area at t1
1378 #ifdef DEBUG_SSM_DRAC
1379             if (DEBUG_COND1(eInfo.encounter->ego)) {
1380                 std::cout << "  Ego expected to leave in " << tExit1 << "-> Foe needs drac=" << (2 * (v2 - dEntry2 / tExit1) / tExit1) << std::endl;
1381             }
1382 #endif
1383             drac = MIN2(drac, 2 * (v2 - dEntry2 / tExit1) / tExit1);
1384         } else {
1385             // Vehicle 1 is expected to stop on conflict area or earlier
1386             if (tEntry1 != INVALID) {
1387                 // ... on conflict area => veh2 has to stop before entry
1388 #ifdef DEBUG_SSM_DRAC
1389                 if (DEBUG_COND1(eInfo.encounter->ego)) {
1390                     std::cout << "  Ego is expected stop on conflict area -> Foe needs drac=" << computeDRAC(dEntry2, v2, 0) << std::endl;
1391                 }
1392 #endif
1393                 drac = MIN2(drac, computeDRAC(dEntry2, v2, 0));
1394             } else {
1395                 // ... before conflict area
1396 #ifdef DEBUG_SSM_DRAC
1397                 if (DEBUG_COND1(eInfo.encounter->ego)) {
1398                     std::cout << "  Ego is expected stop before conflict area -> no drac computation for foe (done for ego if applicable)" << std::endl;
1399                 }
1400 #endif
1401             }
1402         }
1403     }
1404 
1405     return drac > 0 ? drac : INVALID;
1406 }
1407 
1408 void
checkConflictEntryAndExit(EncounterApproachInfo & eInfo)1409 MSDevice_SSM::checkConflictEntryAndExit(EncounterApproachInfo& eInfo) {
1410     // determine exact entry and exit times
1411     Encounter* e = eInfo.encounter;
1412 
1413 #ifdef DEBUG_SSM
1414     if (DEBUG_COND1(eInfo.encounter->ego)) {
1415         std::cout << SIMTIME << " checkConflictEntryAndExit() for encounter of vehicles '" << e->egoID << "' and '" << e->foeID << "'" << std::endl;
1416     }
1417 #endif
1418 
1419     const bool foePastConflictEntry = eInfo.foeConflictEntryDist < 0.0;
1420     const bool egoPastConflictEntry = eInfo.egoConflictEntryDist < 0.0;
1421     const bool foePastConflictExit = eInfo.foeConflictEntryDist < 0.0;
1422     const bool egoPastConflictExit = eInfo.egoConflictEntryDist < 0.0;
1423 
1424 
1425     if (e->size() == 0) {
1426         // This is a new conflict
1427 
1428         // An encounter should be created earlier than when both already entered the conflict area.
1429         // TODO: Check! If this does not fail, the case distinctions can be simplified to a large degree!
1430         assert(!foePastConflictEntry || !egoPastConflictEntry);
1431 
1432         if (egoPastConflictExit) {
1433             if (foePastConflictExit) {
1434                 eInfo.type = ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA;
1435             } else if (foePastConflictEntry) {
1436                 eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
1437             } else {
1438                 eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
1439             }
1440         } else if (foePastConflictExit) {
1441             if (egoPastConflictEntry) {
1442                 eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
1443             } else {
1444                 eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
1445             }
1446         } else {
1447             // No one left conflict area
1448             if (egoPastConflictEntry) {
1449                 if (foePastConflictEntry) {
1450                     eInfo.type = ENCOUNTER_TYPE_COLLISION;
1451                 } else {
1452                     eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
1453                 }
1454             } else if (foePastConflictEntry) {
1455                 eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
1456             }
1457         }
1458         return;
1459     }
1460 
1461     // Distances to conflict area boundaries in previous step
1462     double prevEgoConflictEntryDist = eInfo.egoConflictEntryDist + e->ego->getLastStepDist();
1463     double prevFoeConflictEntryDist = eInfo.foeConflictEntryDist + e->foe->getLastStepDist();
1464     double prevEgoConflictExitDist = prevEgoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
1465     double prevFoeConflictExitDist = prevFoeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
1466     EncounterType prevType = e->currentType;
1467 
1468     // Check if ego entered in last step
1469     if (e->egoConflictEntryTime == INVALID && egoPastConflictEntry && prevEgoConflictEntryDist >= 0) {
1470         // ego must have entered the conflict in the last step. Determine exact entry time
1471         e->egoConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictEntryDist, 0., -eInfo.egoConflictEntryDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1472 #ifdef DEBUG_SSM
1473         if (DEBUG_COND1(eInfo.encounter->ego)) {
1474             std::cout << "    ego entered conflict area at t=" << e->egoConflictEntryTime << std::endl;
1475         }
1476 #endif
1477         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1478         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1479                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1480             eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
1481         }
1482     }
1483 
1484     // Check if foe entered in last step
1485     if (e->foeConflictEntryTime == INVALID && foePastConflictEntry && prevFoeConflictEntryDist >= 0) {
1486         // foe must have entered the conflict in the last step. Determine exact entry time
1487         e->foeConflictEntryTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictEntryDist, 0., -eInfo.foeConflictEntryDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1488 #ifdef DEBUG_SSM
1489         if (DEBUG_COND1(eInfo.encounter->ego)) {
1490             std::cout << "    foe entered conflict area at t=" << e->foeConflictEntryTime << std::endl;
1491         }
1492 #endif
1493         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1494         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1495                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1496             eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
1497         }
1498     }
1499 
1500     // Check if ego left conflict area
1501     if (e->egoConflictExitTime == INVALID && eInfo.egoConflictExitDist < 0 && prevEgoConflictExitDist >= 0) {
1502         // ego must have left the conflict area in the last step. Determine exact exit time
1503         e->egoConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevEgoConflictExitDist, 0., -eInfo.egoConflictExitDist, e->ego->getPreviousSpeed(), e->ego->getSpeed());
1504         // Add cross section to calculate PET for foe
1505 //        e->foePETCrossSections.push_back(std::make_pair(eInfo.foeConflictEntryCrossSection, e->egoConflictExitTime));
1506 #ifdef DEBUG_SSM
1507         if (DEBUG_COND1(eInfo.encounter->ego)) {
1508             std::cout << "    ego left conflict area at t=" << e->egoConflictExitTime << std::endl;
1509         }
1510 #endif
1511         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1512         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1513                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1514             eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
1515         }
1516     }
1517 
1518     // Check if foe left conflict area
1519     if (e->foeConflictExitTime == INVALID && eInfo.foeConflictExitDist < 0 && prevFoeConflictExitDist >= 0) {
1520         // foe must have left the conflict area in the last step. Determine exact exit time
1521         e->foeConflictExitTime = SIMTIME - TS + MSCFModel::passingTime(-prevFoeConflictExitDist, 0., -eInfo.foeConflictExitDist, e->foe->getPreviousSpeed(), e->foe->getSpeed());
1522         // Add cross section to calculate PET for ego
1523 //        e->egoPETCrossSections.push_back(std::make_pair(eInfo.egoConflictEntryCrossSection, e->foeConflictExitTime));
1524 #ifdef DEBUG_SSM
1525         if (DEBUG_COND1(eInfo.encounter->ego)) {
1526             std::cout << "    foe left conflict area at t=" << e->foeConflictExitTime << std::endl;
1527         }
1528 #endif
1529         // Update encounter type (only done here for entering, the other transitions are done in updatePassedEncounter)
1530         if (prevType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1531                 || prevType == ENCOUNTER_TYPE_CROSSING_LEADER) {
1532             eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
1533         }
1534     }
1535 }
1536 
1537 
1538 void
updatePassedEncounter(Encounter * e,FoeInfo * foeInfo,EncounterApproachInfo & eInfo)1539 MSDevice_SSM::updatePassedEncounter(Encounter* e, FoeInfo* foeInfo, EncounterApproachInfo& eInfo) {
1540 
1541 #ifdef DEBUG_SSM
1542     if (DEBUG_COND1(eInfo.encounter->ego))
1543         std::cout << SIMTIME << " updatePassedEncounter() for vehicles '"
1544                   << e->egoID << "' and '" << e->foeID << "'"
1545                   << std::endl;
1546 #endif
1547 
1548     if (foeInfo == nullptr) {
1549         // the foe is out of the device's range, proceed counting down the remaining extra time to trace
1550         e->countDownExtraTime(TS);
1551 #ifdef DEBUG_SSM
1552         if (DEBUG_COND1(eInfo.encounter->ego))
1553             std::cout << "    Foe is out of range. Counting down extra time."
1554                       << " Remaining seconds before closing encounter: " << e->getRemainingExtraTime()
1555                       << std::endl;
1556 #endif
1557 
1558     } else {
1559         // reset the remaining extra time (foe could have re-entered the device range after beginning extra time countdown already)
1560         e->resetExtraTime(myExtraTime);
1561     }
1562 
1563     // Check, whether this was really a potential conflict at some time:
1564     // Search through typeSpan for a type other than no conflict
1565     EncounterType lastPotentialConflictType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1566 
1567     if (lastPotentialConflictType == ENCOUNTER_TYPE_NOCONFLICT_AHEAD) {
1568         // This encounter was no conflict in the last step -> remains so
1569 #ifdef DEBUG_SSM
1570         if (DEBUG_COND1(eInfo.encounter->ego)) {
1571             std::cout << "    This encounter wasn't classified as a potential conflict lately." << std::endl;
1572         }
1573 #endif
1574         if (foeInfo == nullptr) {
1575             // Encounter was either never a potential conflict and foe is out of range
1576             // or the foe has left the network
1577             // -> no use in further tracing this encounter
1578             e->closingRequested = true;
1579 #ifdef DEBUG_SSM
1580             if (DEBUG_COND1(eInfo.encounter->ego)) {
1581                 std::cout << "    Closing encounter." << std::endl;
1582             }
1583 #endif
1584             eInfo.type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1585         }
1586     } else if (lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_FOLLOWER
1587                || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_LEADER
1588                || lastPotentialConflictType == ENCOUNTER_TYPE_FOLLOWING_PASSED) {
1589         // if a following situation leads to a no-conflict situation this encounter switches no-conflict, since no further computations (PET) are needed.
1590         eInfo.type = ENCOUNTER_TYPE_FOLLOWING_PASSED;
1591 #ifdef DEBUG_SSM
1592         if (DEBUG_COND1(eInfo.encounter->ego)) {
1593             std::cout << "    Encounter was previously classified as a follow/lead situation." << std::endl;
1594         }
1595 #endif
1596     } else if (lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_FOLLOWER
1597                || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_LEADER
1598                || lastPotentialConflictType == ENCOUNTER_TYPE_MERGING_PASSED) {
1599         // if a merging situation leads to a no-conflict situation the leader was either removed from the net (we disregard special treatment)
1600         // or route- or lane-changes removed the conflict.
1601         eInfo.type = ENCOUNTER_TYPE_MERGING_PASSED;
1602 #ifdef DEBUG_SSM
1603         if (DEBUG_COND1(eInfo.encounter->ego)) {
1604             std::cout << "    Encounter was previously classified as a merging situation." << std::endl;
1605         }
1606 #endif
1607     }
1608     if (lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1609             || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER
1610             || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
1611             || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
1612             || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
1613             || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1614             || lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1615             || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA
1616             || lastPotentialConflictType == ENCOUNTER_TYPE_COLLISION) {
1617         // Encounter has been a crossing situation.
1618 
1619 #ifdef DEBUG_SSM
1620         if (DEBUG_COND1(eInfo.encounter->ego)) {
1621             std::cout << "    Encounter was previously classified as a crossing situation of type " << lastPotentialConflictType << "." << std::endl;
1622         }
1623 #endif
1624         // For passed encounters, the xxxConflictAreaLength variables are not determined before -> we use the stored values.
1625 
1626         // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
1627         if (eInfo.egoConflictAreaLength == INVALID) {
1628             eInfo.egoConflictAreaLength = e->foe->getWidth();
1629         }
1630         if (eInfo.foeConflictAreaLength == INVALID) {
1631             eInfo.foeConflictAreaLength = e->ego->getWidth();
1632         }
1633 
1634         eInfo.egoConflictEntryDist = e->egoDistsToConflict.back() - e->ego->getLastStepDist();
1635         eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
1636         eInfo.foeConflictEntryDist = e->foeDistsToConflict.back() - e->foe->getLastStepDist();
1637         eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
1638 
1639 #ifdef DEBUG_SSM
1640         if (DEBUG_COND1(eInfo.encounter->ego))
1641             std::cout << "    egoConflictEntryDist = " << eInfo.egoConflictEntryDist
1642                       << ", egoConflictExitDist = " << eInfo.egoConflictExitDist
1643                       << "\n    foeConflictEntryDist = " << eInfo.foeConflictEntryDist
1644                       << ", foeConflictExitDist = " << eInfo.foeConflictExitDist
1645                       << std::endl;
1646 #endif
1647 
1648         // Determine actual encounter type
1649         bool egoEnteredConflict =  eInfo.egoConflictEntryDist < 0.;
1650         bool foeEnteredConflict =  eInfo.foeConflictEntryDist < 0.;
1651         bool egoLeftConflict =  eInfo.egoConflictExitDist < 0.;
1652         bool foeLeftConflict =  eInfo.foeConflictExitDist < 0.;
1653 
1654         if ((!egoEnteredConflict) && !foeEnteredConflict) {
1655             // XXX: do we need to recompute the follow/lead order, here?
1656             assert(lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_FOLLOWER
1657                    || lastPotentialConflictType == ENCOUNTER_TYPE_CROSSING_LEADER);
1658             eInfo.type = lastPotentialConflictType;
1659         } else if (egoEnteredConflict && !foeEnteredConflict) {
1660             eInfo.type = ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA;
1661         } else if ((!egoEnteredConflict) && foeEnteredConflict) {
1662             eInfo.type = ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA;
1663         } else { // (egoEnteredConflict && foeEnteredConflict) {
1664             eInfo.type = ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA;
1665         }
1666 
1667         if ((!egoLeftConflict) && !foeLeftConflict) {
1668             if (eInfo.type == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
1669                 eInfo.type = ENCOUNTER_TYPE_COLLISION;
1670             }
1671         } else if (egoLeftConflict && !foeLeftConflict) {
1672             if (eInfo.type != ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
1673                 eInfo.type = ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA;
1674             }
1675         } else if ((!egoLeftConflict) && foeLeftConflict) {
1676             if (eInfo.type != ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA) {
1677                 eInfo.type = ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA;
1678             }
1679         } else {
1680             eInfo.type = ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA;
1681             // It should not occur that both leave the conflict at the same step
1682             assert(lastPotentialConflictType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1683                    || lastPotentialConflictType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1684                    || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA
1685                    || lastPotentialConflictType == ENCOUNTER_TYPE_BOTH_LEFT_CONFLICT_AREA);
1686         }
1687 
1688         // TODO: adjust the conflict distances according to lateral movement for single ENTERED-cases
1689 
1690 #ifdef DEBUG_SSM
1691         if (DEBUG_COND1(eInfo.encounter->ego)) {
1692             std::cout << "    Updated classification: " << eInfo.type << std::endl;
1693         }
1694 #endif
1695     }
1696 }
1697 
1698 
1699 MSDevice_SSM::EncounterType
classifyEncounter(const FoeInfo * foeInfo,EncounterApproachInfo & eInfo) const1700 MSDevice_SSM::classifyEncounter(const FoeInfo* foeInfo, EncounterApproachInfo& eInfo)  const {
1701 #ifdef DEBUG_SSM
1702     if (DEBUG_COND1(myHolderMS)) {
1703         std::cout << "classifyEncounter() called." << std::endl;
1704     }
1705 #endif
1706     if (foeInfo == nullptr) {
1707         // foeInfo == 0 signalizes, that no corresponding foe info was returned by findSurroundingVehicles(),
1708         // i.e. the foe is actually out of range (This may also mean that it has left the network)
1709         return ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1710     }
1711     const Encounter* e = eInfo.encounter;
1712 
1713     // previous classification (if encounter was not just created)
1714     EncounterType prevType = e->typeSpan.size() > 0 ? static_cast<EncounterType>(e->typeSpan.back()) : ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1715     if (e->typeSpan.size() > 0
1716             && (prevType == ENCOUNTER_TYPE_EGO_ENTERED_CONFLICT_AREA
1717                 ||  prevType == ENCOUNTER_TYPE_FOE_ENTERED_CONFLICT_AREA
1718                 ||  prevType == ENCOUNTER_TYPE_EGO_LEFT_CONFLICT_AREA
1719                 ||  prevType == ENCOUNTER_TYPE_FOE_LEFT_CONFLICT_AREA
1720                 ||  prevType == ENCOUNTER_TYPE_BOTH_ENTERED_CONFLICT_AREA)) {
1721         // This is an ongoing crossing situation with at least one of the vehicles not
1722         // having passed the conflict area.
1723         // -> Merely trace the change of distances to the conflict entry / exit
1724         // -> Derefer this to updatePassedEncounter, where this is done anyhow.
1725 #ifdef DEBUG_SSM
1726         if (DEBUG_COND1(myHolderMS)) {
1727             std::cout << "    Ongoing crossing conflict will be traced by passedEncounter()." << std::endl;
1728         }
1729 #endif
1730         return prevType;
1731     }
1732 
1733 
1734     // Ego's current Lane
1735     const MSLane* egoLane = e->ego->getLane();
1736     // Foe's current Lane
1737     const MSLane* foeLane = e->foe->getLane();
1738 
1739     // Ego's conflict lane is memorized in foeInfo
1740     const MSLane* egoConflictLane = foeInfo->egoConflictLane;
1741     double egoDistToConflictLane = foeInfo->egoDistToConflictLane;
1742     // Find conflicting lane and the distance to its entry link for the foe
1743     double foeDistToConflictLane;
1744     const MSLane* foeConflictLane = findFoeConflictLane(e->foe, foeInfo->egoConflictLane, foeDistToConflictLane);
1745 
1746 #ifdef DEBUG_SSM
1747     if (DEBUG_COND1(myHolderMS))
1748         std::cout << "egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
1749                   << "foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'"
1750                   << "\nEgo's distance to conflict lane: " << egoDistToConflictLane
1751                   << "\nFoe's distance to conflict lane: " << foeDistToConflictLane
1752                   << std::endl;
1753 #endif
1754 
1755     // Treat different cases for foeConflictLane and egoConflictLane (internal or non-internal / equal to egoLane or to foeLane),
1756     // and thereby determine encounterType and the ego/foeEncounterDistance.
1757     // The encounter distance has a different meaning for different types of encounters:
1758     // 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.
1759     // 2) For merging encounters the encounter distance is the distance until the begin of the common target edge/lane.
1760     //    (XXX: Perhaps this should be adjusted to include the entry point to the region where a simultaneous occupancy of
1761     //          both merging lanes could imply a collision)
1762     // 3) For crossing encounters the encounter distances is the distance until the entry point to the conflicting lane.
1763 
1764     EncounterType type;
1765 
1766     if (foeConflictLane == nullptr) {
1767         // foe vehicle is not on course towards the ego's route (see findFoeConflictLane)
1768         type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
1769 #ifdef DEBUG_SSM
1770         if (DEBUG_COND1(myHolderMS)) {
1771             std::cout << "-> Encounter type: No conflict." << std::endl;
1772         }
1773 #endif
1774     } else if (!egoConflictLane->isInternal()) {
1775         // The conflict lane is non-internal, therefore we either have no potential conflict or a lead/follow situation (i.e., no crossing or merging)
1776         if (egoConflictLane == egoLane) {
1777             // The conflict point is on the ego's current lane.
1778             if (foeLane == egoLane) {
1779                 // Foe is on the same non-internal lane
1780                 if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
1781                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
1782                     eInfo.foeConflictEntryDist = e->ego->getBackPositionOnLane() - e->foe->getPositionOnLane();
1783                 } else {
1784                     type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
1785                     eInfo.egoConflictEntryDist = e->foe->getBackPositionOnLane() - e->ego->getPositionOnLane();
1786                 }
1787 #ifdef DEBUG_SSM
1788                 if (DEBUG_COND1(myHolderMS)) {
1789                     std::cout << "-> Encounter type: Lead/follow-situation on non-internal lane '" << egoLane->getID() << "'" << std::endl;
1790                 }
1791 #endif
1792             } else if (&(foeLane->getEdge()) == &(egoLane->getEdge())) {
1793                 // Foe is on the same non-internal edge but not on the same lane. Treat this as no conflict for now
1794                 // XXX: this disregards conflicts for vehicles on adjacent lanes
1795                 type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
1796 #ifdef DEBUG_SSM
1797                 if (DEBUG_COND1(myHolderMS)) {
1798                     std::cout << "-> Encounter type: " << type << std::endl;
1799                 }
1800 #endif
1801             } else {
1802                 assert(&(egoLane->getEdge()) == &(foeConflictLane->getEdge()));
1803                 assert(egoDistToConflictLane <= 0);
1804                 // Foe must be on a route leading into the ego's edge
1805                 if (foeConflictLane == egoLane) {
1806                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
1807                     eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1808 
1809 #ifdef DEBUG_SSM
1810                     if (DEBUG_COND1(myHolderMS))
1811                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1812                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1813                                   << " (gap = " << eInfo.foeConflictEntryDist << ")"
1814                                   << std::endl;
1815 #endif
1816                 } else {
1817                     // Foe's route leads to an adjacent lane of the current lane of the ego
1818                     type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
1819 #ifdef DEBUG_SSM
1820                     if (DEBUG_COND1(myHolderMS)) {
1821                         std::cout << "-> Encounter type: " << type << std::endl;
1822                     }
1823 #endif
1824                 }
1825             }
1826         } else {
1827             // 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
1828             // is located on the foe's current edge see findSurroundingVehicles()
1829             // (otherwise the foe would have had to enter the ego's route along a junction and the corresponding
1830             // conflict lane would be internal)
1831             assert(&(foeLane->getEdge()) == &(egoConflictLane->getEdge()));
1832             assert(foeDistToConflictLane <= 0);
1833             if (foeLane == egoConflictLane) {
1834                 type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
1835                 eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1836 #ifdef DEBUG_SSM
1837                 if (DEBUG_COND1(myHolderMS))
1838                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1839                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1840                               << " (gap = " << eInfo.egoConflictEntryDist << ")"
1841                               << std::endl;
1842 #endif
1843             } else {
1844                 // Ego's route leads to an adjacent lane of the current lane of the foe
1845                 type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
1846 #ifdef DEBUG_SSM
1847                 if (DEBUG_COND1(myHolderMS)) {
1848                     std::cout << "-> Encounter type: " << type << std::endl;
1849                 }
1850 #endif
1851             }
1852         }
1853     } else {
1854         // egoConflictLane is internal, i.e., lies on a junction. Besides the lead/follow situation (which may stretch over different lanes of a connection),
1855         // merging or crossing of the conflict lanes is possible.
1856         assert(foeConflictLane->isInternal());
1857         MSLink* egoEntryLink = egoConflictLane->getEntryLink();
1858         MSLink* foeEntryLink = foeConflictLane->getEntryLink();
1859         if (&(egoEntryLink->getViaLane()->getEdge()) == &(foeEntryLink->getViaLane()->getEdge())) {
1860             if (egoEntryLink != foeEntryLink) {
1861                 // XXX: this disregards conflicts for vehicles on adjacent internal lanes
1862                 type = ENCOUNTER_TYPE_ON_ADJACENT_LANES;
1863 #ifdef DEBUG_SSM
1864                 if (DEBUG_COND1(myHolderMS)) {
1865                     std::cout << "-> Encounter type: " << type << std::endl;
1866                 }
1867 #endif
1868             } else {
1869                 // Lead / follow situation on connection
1870                 if (egoLane == egoConflictLane && foeLane != foeConflictLane) {
1871                     // ego on junction, foe not yet
1872                     type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
1873                     eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1874 #ifdef DEBUG_SSM
1875                     if (DEBUG_COND1(myHolderMS))
1876                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1877                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1878                                   << " (gap = " << eInfo.foeConflictEntryDist << ")"
1879                                   << std::endl;
1880 #endif
1881                 } else if (egoLane != egoConflictLane && foeLane == foeConflictLane) {
1882                     // foe on junction, ego not yet
1883                     type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
1884                     eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1885 #ifdef DEBUG_SSM
1886                     if (DEBUG_COND1(myHolderMS))
1887                         std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1888                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1889                                   << " (gap = " << eInfo.egoConflictEntryDist << ")"
1890                                   << std::endl;
1891 #endif
1892                 } else {
1893                     // Both must be already on the junction in a lead / follow situation on a connection
1894                     // (since they approach via the same link, findSurroundingVehicles() would have determined a
1895                     // different conflictLane if both are not on the junction)
1896                     assert(egoLane == egoConflictLane);
1897                     assert(foeLane == foeConflictLane);
1898                     if (egoLane == foeLane) {
1899                         // both on the same internal lane
1900                         if (e->ego->getPositionOnLane() > e->foe->getPositionOnLane()) {
1901                             type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
1902                             eInfo.foeConflictEntryDist = foeDistToConflictLane + e->ego->getBackPositionOnLane();
1903 #ifdef DEBUG_SSM
1904                             if (DEBUG_COND1(myHolderMS))
1905                                 std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1906                                           << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1907                                           << " (gap = " << eInfo.foeConflictEntryDist << ")"
1908                                           << std::endl;
1909 #endif
1910                         } else {
1911                             type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
1912                             eInfo.egoConflictEntryDist = egoDistToConflictLane + e->foe->getBackPositionOnLane();
1913 #ifdef DEBUG_SSM
1914                             if (DEBUG_COND1(myHolderMS))
1915                                 std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1916                                           << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1917                                           << " (gap = " << eInfo.egoConflictEntryDist << ")"
1918                                           << std::endl;
1919 #endif
1920                         }
1921                     } else {
1922                         // ego and foe on distinct, consecutive internal lanes
1923 #ifdef DEBUG_SSM
1924                         if (DEBUG_COND1(myHolderMS)) {
1925                             std::cout << "    Lead/follow situation on consecutive internal lanes." << std::endl;
1926                         }
1927 #endif
1928                         MSLane* lane = egoEntryLink->getViaLane();
1929 #ifdef _MSC_VER
1930 #pragma warning(push)
1931 #pragma warning(disable: 4127) // do not warn about constant conditional expression
1932 #endif
1933                         while (true) {
1934 #ifdef _MSC_VER
1935 #pragma warning(pop)
1936 #endif
1937                             // Find first of egoLane and foeLane while crossing the junction (this dertermines who's the follower)
1938                             // Then set the conflict lane to the lane of the leader and adapt the follower's distance to conflict
1939                             if (egoLane == lane) {
1940                                 // ego is follower
1941                                 type = ENCOUNTER_TYPE_FOLLOWING_FOLLOWER;
1942                                 // adapt conflict dist
1943                                 eInfo.egoConflictEntryDist = egoDistToConflictLane;
1944                                 while (lane != foeLane) {
1945                                     eInfo.egoConflictEntryDist += lane->getLength();
1946                                     lane = lane->getLinkCont()[0]->getViaLane();
1947                                     assert(lane != 0);
1948                                 }
1949                                 eInfo.egoConflictEntryDist += e->foe->getBackPositionOnLane();
1950                                 egoConflictLane = lane;
1951 #ifdef DEBUG_SSM
1952                                 if (DEBUG_COND1(myHolderMS))
1953                                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' follows foe '"
1954                                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1955                                               << " (gap = " << eInfo.egoConflictEntryDist << ")"
1956                                               << std::endl;
1957 #endif
1958                                 break;
1959                             } else if (foeLane == lane) {
1960                                 // ego is leader
1961                                 type = ENCOUNTER_TYPE_FOLLOWING_LEADER;
1962                                 // adapt conflict dist
1963                                 eInfo.foeConflictEntryDist = foeDistToConflictLane;
1964                                 while (lane != egoLane) {
1965                                     eInfo.foeConflictEntryDist += lane->getLength();
1966                                     lane = lane->getLinkCont()[0]->getViaLane();
1967                                     assert(lane != 0);
1968                                 }
1969                                 eInfo.foeConflictEntryDist += e->ego->getBackPositionOnLane();
1970                                 foeConflictLane = lane;
1971 #ifdef DEBUG_SSM
1972                                 if (DEBUG_COND1(myHolderMS))
1973                                     std::cout << "-> Encounter type: Ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' leads foe '"
1974                                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
1975                                               << " (gap = " << eInfo.foeConflictEntryDist << ")"
1976                                               << std::endl;
1977 #endif
1978                                 break;
1979                             }
1980                             lane = lane->getLinkCont()[0]->getViaLane();
1981                             assert(lane != 0);
1982                         }
1983                     }
1984 #ifdef DEBUG_SSM
1985                     if (DEBUG_COND1(myHolderMS))
1986                         std::cout << "-> Encounter type: Lead/follow-situation on connection from '" << egoEntryLink->getLaneBefore()->getID()
1987                                   << "' to '" << egoEntryLink->getLane()->getID() << "'" << std::endl;
1988 #endif
1989                 }
1990             }
1991         } else {
1992             // Entry links to junctions lead to different internal edges.
1993             // There are three possibilities, either the edges cross, merge or have no conflict
1994             const std::vector<MSLink*>& egoFoeLinks = egoEntryLink->getFoeLinks();
1995             const std::vector<MSLink*>& foeFoeLinks = foeEntryLink->getFoeLinks();
1996             // Determine whether ego and foe links are foes
1997             bool crossOrMerge = (find(egoFoeLinks.begin(), egoFoeLinks.end(), foeEntryLink) != egoFoeLinks.end()
1998                                  || std::find(foeFoeLinks.begin(), foeFoeLinks.end(), egoEntryLink) != foeFoeLinks.end());
1999             if (!crossOrMerge) {
2000 //                if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
2001 //                    // XXX: the situation of merging into adjacent lanes is disregarded for now <- the alleged situation appears to imply crossOrMerge!!!
2002 //                    type = ENCOUNTER_TYPE_MERGING_ADJACENT;
2003 //#ifdef DEBUG_SSM
2004 //                    std::cout << "-> Encounter type: No conflict (adjacent lanes)." << std::endl;
2005 //#endif
2006 //                } else {
2007                 type = ENCOUNTER_TYPE_NOCONFLICT_AHEAD;
2008 #ifdef DEBUG_SSM
2009                 if (DEBUG_COND1(myHolderMS)) {
2010                     std::cout << "-> Encounter type: No conflict." << std::endl;
2011                 }
2012 #endif
2013 //                }
2014             } else if (&(foeEntryLink->getLane()->getEdge()) == &(egoEntryLink->getLane()->getEdge())) {
2015                 if (foeEntryLink->getLane() == egoEntryLink->getLane()) {
2016                     type = ENCOUNTER_TYPE_MERGING;
2017                     eInfo.egoConflictEntryDist = egoDistToConflictLane + egoEntryLink->getInternalLengthsAfter();
2018                     eInfo.foeConflictEntryDist = foeDistToConflictLane + foeEntryLink->getInternalLengthsAfter();
2019 #ifdef DEBUG_SSM
2020                     if (DEBUG_COND1(myHolderMS))
2021                         std::cout << "-> Encounter type: Merging situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
2022                                   << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2023                                   << "\nDistances to merge-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
2024                                   << std::endl;
2025 #endif
2026                 } else {
2027                     // Links leading to the same edge but different lanes. XXX: Disregards conflicts on adjacent lanes
2028                     type = ENCOUNTER_TYPE_MERGING_ADJACENT;
2029 #ifdef DEBUG_SSM
2030                     if (DEBUG_COND1(myHolderMS)) {
2031                         std::cout << "-> Encounter type: No conflict: " << type << std::endl;
2032                     }
2033 #endif
2034                 }
2035             } else {
2036                 type = ENCOUNTER_TYPE_CROSSING;
2037 
2038                 assert(egoConflictLane->isInternal());
2039                 assert(foeConflictLane->getEdge().getToJunction() == egoConflictLane->getEdge().getToJunction());
2040 
2041                 // If the conflict lanes are internal, they may not correspond to the
2042                 // actually crossing parts of the corresponding connections.
2043                 // Adjust the conflict lanes accordingly.
2044                 // set back both to the first parts of the corresponding connections
2045                 double offset = 0.;
2046                 egoConflictLane = egoConflictLane->getFirstInternalInConnection(offset);
2047                 egoDistToConflictLane -= offset;
2048                 foeConflictLane = foeConflictLane->getFirstInternalInConnection(offset);
2049                 foeDistToConflictLane -= offset;
2050                 // find the distances to the conflict from the junction entry for both vehicles
2051                 // Here we also determine the real crossing lanes (before the conflict lane is the first lane of the connection)
2052                 // for the ego
2053                 double egoDistToConflictFromJunctionEntry = INVALID;
2054                 double foeInternalLaneLengthsBeforeCrossing = 0.;
2055                 while (foeConflictLane != nullptr && foeConflictLane->isInternal()) {
2056                     egoDistToConflictFromJunctionEntry = egoEntryLink->getLengthsBeforeCrossing(foeConflictLane);
2057                     if (egoDistToConflictFromJunctionEntry != INVALID) {
2058                         // found correct foeConflictLane
2059                         egoDistToConflictFromJunctionEntry += 0.5 * (foeConflictLane->getWidth() - e->foe->getVehicleType().getWidth());
2060                         break;
2061                     } else {
2062                         foeInternalLaneLengthsBeforeCrossing += foeConflictLane->getLength();
2063                     }
2064                     foeConflictLane = foeConflictLane->getCanonicalSuccessorLane();
2065                     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.
2066                 }
2067                 assert(egoDistToConflictFromJunctionEntry != INVALID);
2068 
2069                 // for the foe
2070                 double foeDistToConflictFromJunctionEntry = INVALID;
2071                 double egoInternalLaneLengthsBeforeCrossing = 0.;
2072                 foeDistToConflictFromJunctionEntry = INVALID;
2073                 while (egoConflictLane != nullptr && egoConflictLane->isInternal()) {
2074                     foeDistToConflictFromJunctionEntry = foeEntryLink->getLengthsBeforeCrossing(egoConflictLane);
2075                     if (foeDistToConflictFromJunctionEntry != INVALID) {
2076                         // found correct egoConflictLane
2077                         foeDistToConflictFromJunctionEntry += 0.5 * (egoConflictLane->getWidth() - e->ego->getVehicleType().getWidth());
2078                         break;
2079                     } else {
2080                         egoInternalLaneLengthsBeforeCrossing += egoConflictLane->getLength();
2081                     }
2082                     egoConflictLane = egoConflictLane->getCanonicalSuccessorLane();
2083                     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.
2084                 }
2085                 assert(foeDistToConflictFromJunctionEntry != INVALID);
2086 
2087                 // store conflict entry information in eInfo
2088 
2089 //                // TO-DO: equip these with exit times to store relevant PET sections in encounter
2090 //                eInfo.egoConflictEntryCrossSection = std::make_pair(egoConflictLane, egoDistToConflictFromJunctionEntry - egoInternalLaneLengthsBeforeCrossing);
2091 //                eInfo.foeConflictEntryCrossSection = std::make_pair(foeConflictLane, foeDistToConflictFromJunctionEntry - foeInternalLaneLengthsBeforeCrossing);
2092 
2093                 // Take into account the lateral position for the exact determination of the conflict point
2094                 // whether lateral position increases or decreases conflict distance depends on lane angles at conflict
2095                 // -> conflictLaneOrientation in {-1,+1}
2096                 // First, measure the angle between the two connection lines (straight lines from junction entry point to junction exit point)
2097                 Position egoEntryPos = egoEntryLink->getViaLane()->getShape().front();
2098                 Position egoExitPos = egoEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
2099                 PositionVector egoConnectionLine(egoEntryPos, egoExitPos);
2100                 Position foeEntryPos = foeEntryLink->getViaLane()->getShape().front();
2101                 Position foeExitPos = foeEntryLink->getCorrespondingExitLink()->getInternalLaneBefore()->getShape().back();
2102                 PositionVector foeConnectionLine(foeEntryPos, foeExitPos);
2103                 double angle = std::fmod(egoConnectionLine.rotationAtOffset(0.) - foeConnectionLine.rotationAtOffset(0.), (2 * M_PI));
2104                 if (angle < 0) {
2105                     angle += 2 * M_PI;
2106                 }
2107                 assert(angle >= 0);
2108                 assert(angle <= 2 * M_PI);
2109                 if (angle > M_PI) {
2110                     angle -= 2 * M_PI;
2111                 }
2112                 assert(angle >= -M_PI);
2113                 assert(angle <= M_PI);
2114                 // Determine orientation of the connection lines. (Positive values mean that the ego vehicle approaches from the foe's left side.)
2115                 double crossingOrientation = (angle < 0) - (angle > 0);
2116 
2117                 // Adjust conflict dist to lateral positions
2118                 // TODO: This could more precisely be calculated wrt the angle of the crossing *at the conflict point*
2119                 egoDistToConflictFromJunctionEntry -= crossingOrientation * e->foe->getLateralPositionOnLane();
2120                 foeDistToConflictFromJunctionEntry += crossingOrientation * e->ego->getLateralPositionOnLane();
2121 
2122                 // Complete entry distances
2123                 eInfo.egoConflictEntryDist = egoDistToConflictLane + egoDistToConflictFromJunctionEntry;
2124                 eInfo.foeConflictEntryDist = foeDistToConflictLane + foeDistToConflictFromJunctionEntry;
2125 
2126 
2127 #ifdef DEBUG_SSM
2128                 if (DEBUG_COND1(myHolderMS))
2129                     std::cout << "    Determined exact conflict distances for crossing conflict."
2130                               << "\n    crossingOrientation=" << crossingOrientation
2131                               << ", egoCrossingAngle=" << egoConnectionLine.rotationAtOffset(0.)
2132                               << ", foeCrossingAngle=" << foeConnectionLine.rotationAtOffset(0.)
2133                               << ", relativeAngle=" << angle
2134                               << " (foe from " << (crossingOrientation > 0 ? "right)" : "left)")
2135                               << "\n    resulting offset for conflict entry distance:"
2136                               << "\n     ego=" << crossingOrientation* e->foe->getLateralPositionOnLane()
2137                               << ", foe=" << crossingOrientation* e->ego->getLateralPositionOnLane()
2138                               << "\n    resulting entry distances:"
2139                               << "\n     ego=" << eInfo.egoConflictEntryDist
2140                               << ", foe=" << eInfo.foeConflictEntryDist
2141                               << std::endl;
2142 #endif
2143 
2144                 // TODO: This could also more precisely be calculated wrt the angle of the crossing *at the conflict point*
2145                 eInfo.egoConflictAreaLength = e->foe->getWidth();
2146                 eInfo.foeConflictAreaLength = e->ego->getWidth();
2147 
2148                 // resulting exit distances
2149                 eInfo.egoConflictExitDist = eInfo.egoConflictEntryDist + eInfo.egoConflictAreaLength + e->ego->getLength();
2150                 eInfo.foeConflictExitDist = eInfo.foeConflictEntryDist + eInfo.foeConflictAreaLength + e->foe->getLength();
2151 
2152 #ifdef DEBUG_SSM
2153                 if (DEBUG_COND1(myHolderMS))
2154                     std::cout << "real egoConflictLane: '" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID()) << "'\n"
2155                               << "real foeConflictLane: '" << (foeConflictLane == 0 ? "NULL" : foeConflictLane->getID()) << "'\n"
2156                               << "-> Encounter type: Crossing situation of ego '" << e->ego->getID() << "' on lane '" << egoLane->getID() << "' and foe '"
2157                               << e->foe->getID() << "' on lane '" << foeLane->getID() << "'"
2158                               << "\nDistances to crossing-point: ego: " << eInfo.egoConflictEntryDist << ", foe: " << eInfo.foeConflictEntryDist
2159                               << std::endl;
2160 #endif
2161             }
2162         }
2163     }
2164     return type;
2165 }
2166 
2167 
2168 const MSLane*
findFoeConflictLane(const MSVehicle * foe,const MSLane * egoConflictLane,double & distToConflictLane) const2169 MSDevice_SSM::findFoeConflictLane(const MSVehicle* foe, const MSLane* egoConflictLane, double& distToConflictLane) const {
2170 
2171 #ifdef DEBUG_SSM
2172     if (DEBUG_COND1(myHolderMS))
2173         std::cout << SIMTIME << " findFoeConflictLane() for foe '"
2174                   << foe->getID() << "' on lane '" << foe->getLane()->getID()
2175                   << "' (with egoConflictLane=" << (egoConflictLane == 0 ? "NULL" : egoConflictLane->getID())
2176                   << ")\nfoeBestLanes: " << ::toString(foe->getBestLanesContinuation())
2177                   << std::endl;
2178 #endif
2179     MSLane* foeLane = foe->getLane();
2180     std::vector<MSLane*>::const_iterator laneIter = foe->getBestLanesContinuation().begin();
2181     std::vector<MSLane*>::const_iterator foeBestLanesEnd = foe->getBestLanesContinuation().end();
2182     assert(foeLane->isInternal() || *laneIter == foeLane);
2183     distToConflictLane = -foe->getPositionOnLane();
2184 
2185     // Potential conflict lies on junction if egoConflictLane is internal
2186     const MSJunction* conflictJunction = egoConflictLane->isInternal() ? egoConflictLane->getEdge().getToJunction() : nullptr;
2187 #ifdef DEBUG_SSM
2188     if (DEBUG_COND1(myHolderMS))
2189         if (conflictJunction != 0) {
2190             std::cout << "Potential conflict on junction '" << conflictJunction->getID()
2191                       << std::endl;
2192         }
2193 #endif
2194     if (foeLane->isInternal() && foeLane->getEdge().getToJunction() == conflictJunction) {
2195         // foe is already on the conflict junction
2196         return foeLane;
2197     }
2198 
2199     // Foe is not on the conflict junction
2200 
2201     // Leading internal lanes in bestlanes are resembled as a single NULL-pointer skip them
2202     if (*laneIter == nullptr) {
2203         while (foeLane != nullptr && foeLane->isInternal()) {
2204             distToConflictLane += foeLane->getLength();
2205             foeLane = foeLane->getLinkCont()[0]->getViaLane();
2206         }
2207         ++laneIter;
2208         assert(laneIter == foeBestLanesEnd || *laneIter != 0);
2209     }
2210 
2211     // Look for the junction downstream along foeBestLanes
2212     while (laneIter != foeBestLanesEnd && distToConflictLane <= myRange) {
2213         // Eventual internal lanes were skipped
2214         assert(*laneIter == foeLane || foeLane == 0);
2215         foeLane = *laneIter;
2216         assert(!foeLane->isInternal());
2217         if (&foeLane->getEdge() == &egoConflictLane->getEdge()) {
2218 #ifdef DEBUG_SSM
2219             if (DEBUG_COND1(myHolderMS)) {
2220                 std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
2221             }
2222 #endif
2223             // found the potential conflict edge along foeBestLanes
2224             return foeLane;
2225         }
2226         // No conflict on foeLane
2227         distToConflictLane += foeLane->getLength();
2228 
2229         // set laneIter to next non internal lane along foeBestLanes
2230         ++laneIter;
2231         if (laneIter == foeBestLanesEnd) {
2232             return nullptr;
2233         }
2234         MSLane* nextNonInternalLane = *laneIter;
2235         MSLink* link = foeLane->getLinkTo(nextNonInternalLane);
2236         // Set foeLane to first internal lane on the next junction
2237         foeLane = link->getViaLane();
2238         assert(foeLane == 0 || foeLane->isInternal());
2239         if (foeLane == nullptr) {
2240             foeLane = nextNonInternalLane;
2241             continue;
2242         }
2243         if (foeLane->getEdge().getToJunction() == conflictJunction) {
2244             assert(foeLane != 0);
2245 #ifdef DEBUG_SSM
2246             if (DEBUG_COND1(myHolderMS)) {
2247                 std::cout << "Found conflict lane for foe: '" << foeLane->getID() << "'" << std::endl;
2248             }
2249 #endif
2250             // found egoConflictLane, resp. the conflict junction, along foeBestLanes
2251             return foeLane;
2252         }
2253         // No conflict on junction
2254         distToConflictLane += link->getInternalLengthsAfter();
2255         foeLane = nextNonInternalLane;
2256     }
2257     // Didn't find conflicting lane on foeBestLanes within range.
2258     return nullptr;
2259 }
2260 
2261 void
flushConflicts(bool flushAll)2262 MSDevice_SSM::flushConflicts(bool flushAll) {
2263 #ifdef DEBUG_SSM
2264     if (DEBUG_COND) {
2265         std::cout << "\n" << SIMTIME << " Device '" << getID() << "' flushConflicts()" << std::endl;
2266     }
2267 #endif
2268     while (!myPastConflicts.empty()) {
2269         Encounter* top = myPastConflicts.top();
2270         if (flushAll || top->begin <= myOldestActiveEncounterBegin) {
2271             writeOutConflict(top);
2272             myPastConflicts.pop();
2273             delete top;
2274         } else {
2275             break;
2276         }
2277     }
2278 }
2279 
2280 void
flushGlobalMeasures()2281 MSDevice_SSM::flushGlobalMeasures() {
2282     std::string egoID = myHolderMS->getID();
2283 #ifdef DEBUG_SSM
2284     if (DEBUG_COND1(myHolderMS))
2285         std::cout << SIMTIME << " flushGlobalMeasures() of vehicle '"
2286                   << egoID << "'"
2287                   << "'\ntoGeo=" << myUseGeoCoords << std::endl;
2288 #endif
2289     if (myComputeBR || myComputeSGAP || myComputeTGAP) {
2290         myOutputFile->openTag("globalMeasures");
2291         myOutputFile->writeAttr("ego", egoID);
2292         myOutputFile->openTag("timeSpan").writeAttr("values", myGlobalMeasuresTimeSpan).closeTag();
2293         if (myComputeBR) {
2294             myOutputFile->openTag("BRSpan").writeAttr("values", myBRspan).closeTag();
2295 
2296             if (myMaxBR.second != 0.0) {
2297                 if (myUseGeoCoords) {
2298                     toGeo(myMaxBR.first.second);
2299                 }
2300                 myOutputFile->openTag("maxBR").writeAttr("time", myMaxBR.first.first).writeAttr("position", ::toString(myMaxBR.first.second)).writeAttr("value", myMaxBR.second).closeTag();
2301             }
2302         }
2303 
2304         if (myComputeSGAP) {
2305             myOutputFile->openTag("SGAPSpan").writeAttr("values", makeStringWithNAs(mySGAPspan, INVALID)).closeTag();
2306             if (myMinSGAP.second != "") {
2307                 if (myUseGeoCoords) {
2308                     toGeo(myMinSGAP.first.first.second);
2309                 }
2310                 myOutputFile->openTag("minSGAP").writeAttr("time", myMinSGAP.first.first.first)
2311                 .writeAttr("position", ::toString(myMinSGAP.first.first.second))
2312                 .writeAttr("value", myMinSGAP.first.second)
2313                 .writeAttr("leader", myMinSGAP.second).closeTag();
2314             }
2315         }
2316 
2317         if (myComputeTGAP) {
2318             myOutputFile->openTag("TGAPSpan").writeAttr("values", makeStringWithNAs(myTGAPspan, INVALID)).closeTag();
2319             if (myMinTGAP.second != "") {
2320                 if (myUseGeoCoords) {
2321                     toGeo(myMinTGAP.first.first.second);
2322                 }
2323                 myOutputFile->openTag("minTGAP").writeAttr("time", myMinTGAP.first.first.first)
2324                 .writeAttr("position", ::toString(myMinTGAP.first.first.second))
2325                 .writeAttr("value", myMinTGAP.first.second)
2326                 .writeAttr("leader", myMinTGAP.second).closeTag();
2327             }
2328         }
2329         // close globalMeasures
2330         myOutputFile->closeTag();
2331     }
2332 }
2333 
2334 void
toGeo(Position & x)2335 MSDevice_SSM::toGeo(Position& x) {
2336     GeoConvHelper::getFinal().cartesian2geo(x);
2337 }
2338 
2339 void
toGeo(PositionVector & xv)2340 MSDevice_SSM::toGeo(PositionVector& xv) {
2341     for (Position& x : xv) {
2342         toGeo(x);
2343     }
2344 }
2345 
2346 void
writeOutConflict(Encounter * e)2347 MSDevice_SSM::writeOutConflict(Encounter* e) {
2348 #ifdef DEBUG_SSM
2349     if (DEBUG_COND1(myHolderMS))
2350         std::cout << SIMTIME << " writeOutConflict() of vehicles '"
2351                   << e->egoID << "' and '" << e->foeID
2352                   << "'\ntoGeo=" << myUseGeoCoords << std::endl;
2353 #endif
2354     myOutputFile->openTag("conflict");
2355     myOutputFile->writeAttr("begin", e->begin).writeAttr("end", e->end);
2356     myOutputFile->writeAttr("ego", e->egoID).writeAttr("foe", e->foeID);
2357 
2358     if (mySaveTrajectories) {
2359         myOutputFile->openTag("timeSpan").writeAttr("values", e->timeSpan).closeTag();
2360         myOutputFile->openTag("typeSpan").writeAttr("values", e->typeSpan).closeTag();
2361 
2362         // Some useful snippets for that (from MSFCDExport.cpp):
2363         if (myUseGeoCoords) {
2364             toGeo(e->egoTrajectory.x);
2365             toGeo(e->foeTrajectory.x);
2366             toGeo(e->conflictPointSpan);
2367         }
2368 
2369         myOutputFile->openTag("egoPosition").writeAttr("values", ::toString(e->egoTrajectory.x, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
2370         myOutputFile->openTag("egoVelocity").writeAttr("values", ::toString(e->egoTrajectory.v)).closeTag();
2371 
2372         myOutputFile->openTag("foePosition").writeAttr("values", ::toString(e->foeTrajectory.x, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
2373         myOutputFile->openTag("foeVelocity").writeAttr("values", ::toString(e->foeTrajectory.v)).closeTag();
2374 
2375         myOutputFile->openTag("conflictPoint").writeAttr("values", ::toString(e->conflictPointSpan, myUseGeoCoords ? gPrecisionGeo : gPrecision)).closeTag();
2376     }
2377 
2378     if (myComputeTTC) {
2379         if (mySaveTrajectories) {
2380             myOutputFile->openTag("TTCSpan").writeAttr("values", makeStringWithNAs(e->TTCspan, INVALID)).closeTag();
2381         }
2382         if (e->minTTC.time == INVALID) {
2383             myOutputFile->openTag("minTTC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").closeTag();
2384         } else {
2385             std::string time = ::toString(e->minTTC.time);
2386             std::string type = ::toString(int(e->minTTC.type));
2387             std::string value = ::toString(e->minTTC.value);
2388             if (myUseGeoCoords) {
2389                 toGeo(e->minTTC.pos);
2390             }
2391             std::string position = ::toString(e->minTTC.pos, myUseGeoCoords ? gPrecisionGeo : gPrecision);
2392             myOutputFile->openTag("minTTC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).closeTag();
2393         }
2394     }
2395     if (myComputeDRAC) {
2396         if (mySaveTrajectories) {
2397             myOutputFile->openTag("DRACSpan").writeAttr("values", makeStringWithNAs(e->DRACspan, {0.0, INVALID})).closeTag();
2398         }
2399         if (e->maxDRAC.time == INVALID) {
2400             myOutputFile->openTag("maxDRAC").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").closeTag();
2401         } else {
2402             std::string time = ::toString(e->maxDRAC.time);
2403             std::string type = ::toString(int(e->maxDRAC.type));
2404             std::string value = ::toString(e->maxDRAC.value);
2405             if (myUseGeoCoords) {
2406                 toGeo(e->maxDRAC.pos);
2407             }
2408             std::string position = ::toString(e->maxDRAC.pos, myUseGeoCoords ? gPrecisionGeo : gPrecision);
2409             myOutputFile->openTag("maxDRAC").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).closeTag();
2410         }
2411     }
2412     if (myComputePET) {
2413         if (e->PET.time == INVALID) {
2414             myOutputFile->openTag("PET").writeAttr("time", "NA").writeAttr("position", "NA").writeAttr("type", "NA").writeAttr("value", "NA").closeTag();
2415         } else {
2416             std::string time = ::toString(e->PET.time);
2417             std::string type = ::toString(int(e->PET.type));
2418             std::string value = ::toString(e->PET.value);
2419             if (myUseGeoCoords) {
2420                 toGeo(e->PET.pos);
2421             }
2422             std::string position = ::toString(e->PET.pos, myUseGeoCoords ? gPrecisionGeo : gPrecision);
2423             myOutputFile->openTag("PET").writeAttr("time", time).writeAttr("position", position).writeAttr("type", type).writeAttr("value", value).closeTag();
2424         }
2425     }
2426     myOutputFile->closeTag();
2427 }
2428 
2429 std::string
makeStringWithNAs(std::vector<double> v,double NA,std::string sep)2430 MSDevice_SSM::makeStringWithNAs(std::vector<double> v, double NA, std::string sep) {
2431     std::string res = "";
2432     for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
2433         res += (i == v.begin() ? "" : sep) + (*i == NA ? "NA" : ::toString(*i));
2434     }
2435     return res;
2436 }
2437 
2438 std::string
makeStringWithNAs(std::vector<double> v,std::vector<double> NAs,std::string sep)2439 MSDevice_SSM::makeStringWithNAs(std::vector<double> v, std::vector<double> NAs, std::string sep) {
2440     std::string res = "";
2441     for (std::vector<double>::const_iterator i = v.begin(); i != v.end(); ++i) {
2442         res += (i == v.begin() ? "" : sep) + (find(NAs.begin(), NAs.end(), *i) != NAs.end() ? "NA" : ::toString(*i));
2443     }
2444     return res;
2445 }
2446 
2447 
2448 // ---------------------------------------------------------------------------
2449 // MSDevice_SSM-methods
2450 // ---------------------------------------------------------------------------
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)2451 MSDevice_SSM::MSDevice_SSM(SUMOVehicle& holder, const std::string& id, std::string outputFilename, std::map<std::string, double> thresholds,
2452                            bool trajectories, double range, double extraTime, bool useGeoCoords) :
2453     MSVehicleDevice(holder, id),
2454     myThresholds(thresholds),
2455     mySaveTrajectories(trajectories),
2456     myRange(range),
2457     myExtraTime(extraTime),
2458     myUseGeoCoords(useGeoCoords),
2459     myOldestActiveEncounterBegin(INVALID),
2460     myMaxBR(std::make_pair(-1, Position(0., 0.)), 0.0),
2461     myMinSGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), ""),
2462     myMinTGAP(std::make_pair(std::make_pair(-1, Position(0., 0.)), std::numeric_limits<double>::max()), "") {
2463     // Take care! Holder is currently being constructed. Cast occurs before completion.
2464     myHolderMS = static_cast<MSVehicle*>(&holder);
2465 
2466     myComputeTTC = myThresholds.find("TTC") != myThresholds.end();
2467     myComputeDRAC = myThresholds.find("DRAC") != myThresholds.end();
2468     myComputePET = myThresholds.find("PET") != myThresholds.end();
2469 
2470     myComputeBR = myThresholds.find("BR") != myThresholds.end();
2471     myComputeSGAP = myThresholds.find("SGAP") != myThresholds.end();
2472     myComputeTGAP = myThresholds.find("TGAP") != myThresholds.end();
2473 
2474     myActiveEncounters = EncounterVector();
2475     myPastConflicts = EncounterQueue();
2476 
2477     // XXX: Who deletes the OutputDevice?
2478     myOutputFile = &OutputDevice::getDevice(outputFilename);
2479 //    TODO: make xsd, include header
2480 //    myOutputFile.writeXMLHeader("SSMLog", "SSMLog.xsd");
2481     if (createdOutputFiles.count(outputFilename) == 0) {
2482         myOutputFile->openTag("SSMLog");
2483         createdOutputFiles.insert(outputFilename);
2484     }
2485     // register at static instance container
2486     instances->insert(this);
2487 
2488 #ifdef DEBUG_SSM
2489     if (DEBUG_COND1(myHolderMS)) {
2490         std::vector<std::string> measures;
2491         std::vector<double> threshVals;
2492         for (std::map<std::string, double>::const_iterator i = myThresholds.begin(); i != myThresholds.end(); ++i) {
2493             measures.push_back(i->first);
2494             threshVals.push_back(i->second);
2495         }
2496         std::cout << "Initialized ssm device '" << id << "' with "
2497                   << "myMeasures=" << joinToString(measures, " ")
2498                   << ", myThresholds=" << joinToString(threshVals, " ")
2499                   << ", mySaveTrajectories=" << mySaveTrajectories
2500                   << ", myRange=" << myRange << ", output file=" << outputFilename << ", extra time=" << myExtraTime << ", useGeo=" << myUseGeoCoords << "\n";
2501     }
2502 #endif
2503 }
2504 
2505 /// @brief Destructor.
~MSDevice_SSM()2506 MSDevice_SSM::~MSDevice_SSM() {
2507     // Deleted in ~BaseVehicle()
2508     // unregister from static instance container
2509     instances->erase(this);
2510     resetEncounters();
2511     flushConflicts(true);
2512     flushGlobalMeasures();
2513 }
2514 
2515 
2516 bool
notifyEnter(SUMOTrafficObject & veh,MSMoveReminder::Notification reason,const MSLane *)2517 MSDevice_SSM::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
2518     assert(veh.isVehicle());
2519 #ifdef DEBUG_SSM_NOTIFICATIONS
2520     MSBaseVehicle* v = (MSBaseVehicle*) &veh;
2521     std::cout << "device '" << getID() << "' notifyEnter: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
2522 #else
2523     UNUSED_PARAMETER(veh);
2524     UNUSED_PARAMETER(reason);
2525 #endif
2526     return true; // keep the device
2527 }
2528 
2529 bool
notifyLeave(SUMOTrafficObject & veh,double,MSMoveReminder::Notification reason,const MSLane *)2530 MSDevice_SSM::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
2531                           MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
2532     assert(veh.isVehicle());
2533 #ifdef DEBUG_SSM_NOTIFICATIONS
2534     MSBaseVehicle* v = (MSBaseVehicle*) &veh;
2535     std::cout << "device '" << getID() << "' notifyLeave: reason=" << reason << " currentEdge=" << v->getLane()->getEdge().getID() << "\n";
2536 #else
2537     UNUSED_PARAMETER(veh);
2538     UNUSED_PARAMETER(reason);
2539 #endif
2540     return true; // keep the device
2541 }
2542 
2543 bool
notifyMove(SUMOTrafficObject &,double,double,double newSpeed)2544 MSDevice_SSM::notifyMove(SUMOTrafficObject& /* veh */, double /* oldPos */,
2545                          double /* newPos */, double newSpeed) {
2546 #ifdef DEBUG_SSM_NOTIFICATIONS
2547     std::cout << "device '" << getID() << "' notifyMove: newSpeed=" << newSpeed << "\n";
2548 #else
2549     UNUSED_PARAMETER(newSpeed);
2550 #endif
2551     return true; // keep the device
2552 }
2553 
2554 
2555 void
findSurroundingVehicles(const MSVehicle & veh,double range,FoeInfoMap & foeCollector)2556 MSDevice_SSM::findSurroundingVehicles(const MSVehicle& veh, double range, FoeInfoMap& foeCollector) {
2557 #ifdef DEBUG_SSM_SURROUNDING
2558     std::cout << SIMTIME << " Looking for surrounding vehicles for ego vehicle '" << veh.getID()
2559               << "' on edge '" << veh.getLane()->getEdge().getID()
2560               << "'."
2561               << "\nVehicle's best lanes = " << ::toString(veh.getBestLanesContinuation())
2562               << std::endl;
2563 #endif
2564 
2565     if (!veh.isOnRoad()) {
2566         return;
2567     }
2568 
2569     // The requesting vehicle's current route
2570     // XXX: Restriction to route scanning may have to be generalized to scanning of possible continuations when
2571     //      considering situations involving sudden route changes. See also the definition of the EncounterTypes.
2572     //      A second problem is that following situations on deviating routes may result in closing encounters
2573     //      too early if a leading foe is not traced on its new lane. (see test 'foe_leader_deviating_routes')
2574 
2575     // If veh is on an internal edge, the edgeIter points towards the last edge before the junction
2576     //ConstMSEdgeVector::const_iterator edgeIter = veh.getCurrentRouteEdge();
2577     //assert(*edgeIter != 0);
2578 
2579     // Best continuation lanes for the ego vehicle
2580     const std::vector<MSLane*> egoBestLanes = veh.getBestLanesContinuation();
2581     std::vector<MSLane*>::const_iterator laneIter = egoBestLanes.begin();
2582 
2583     // current lane in loop below
2584     const MSLane* lane = veh.getLane();
2585     assert(lane->isInternal() || lane == *laneIter);
2586     assert(lane != 0);
2587     // next non-internal lane on the route
2588     const MSLane* nextNonInternalLane = nullptr;
2589 
2590     const MSEdge* edge; // current edge in loop below
2591 
2592     // Init pos with vehicle's current position. Below pos is set to zero to denote
2593     // the beginning position of the currently considered edge
2594     double pos = veh.getPositionOnLane();
2595     // remainingDownstreamRange is the range minus the distance that is already scanned downstream along the vehicles route
2596     double remainingDownstreamRange = range;
2597     // 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)
2598     double distToConflictLane = -pos;
2599     // junctions that were already scanned (break search in recurrent nets)
2600     std::set<const MSJunction*> seenJunctions;
2601 
2602     // if the current edge is internal, collect all vehicles from the junction and within upstream range (except on the vehicles own edge),
2603     // this is analogous to the code treating junctions in the loop below. Note that the distance on the junction itself is not included into
2604     // range, so vehicles farther away than range can be collected, too.
2605     if (lane->isInternal()) {
2606         edge = &(lane->getEdge());
2607 
2608 #ifdef DEBUG_SSM_SURROUNDING
2609         std::cout << SIMTIME << " Vehicle '" << veh.getID() << "' is on internal edge " << edge->getID() << "'." << std::endl;
2610 //                  << "Previous edge of its route: '" << (*edgeIter)->getID() << "'" << std::endl;
2611 #endif
2612 
2613         assert(edge->getToJunction() == edge->getFromJunction());
2614 
2615         const MSJunction* junction = edge->getToJunction();
2616         // Collect vehicles on the junction
2617         getVehiclesOnJunction(junction, distToConflictLane, lane, foeCollector);
2618         seenJunctions.insert(junction);
2619 
2620         // Collect vehicles on incoming edges.
2621         // Note that this includes the previous edge on the ego vehicle's route.
2622         // (The distance on the current internal edge is ignored)
2623         const ConstMSEdgeVector& incoming = junction->getIncoming();
2624         for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2625             if ((*ei)->isInternal()) {
2626                 continue;
2627             }
2628             // Upstream range is taken from the vehicle's back
2629             getUpstreamVehicles(*ei, (*ei)->getLength(), range + veh.getLength(), distToConflictLane, lane, foeCollector, seenJunctions);
2630         }
2631 
2632 //        // Take into account internal distance covered on the current lane
2633 //        (commented out, because upstream scanning disregards internal lanes on the last scanned junction
2634 //        -- this makes the scanning symmetric between leader and follower)
2635 //        remainingDownstreamRange -= lane->getLength() - pos;
2636 
2637         // Take into account non-internal lengths until next non-internal lane
2638         MSLink* link = lane->getLinkCont()[0];
2639         remainingDownstreamRange -= link->getInternalLengthsAfter();
2640         distToConflictLane += lane->getLength() + link->getInternalLengthsAfter();
2641 
2642         // The next non-internal lane
2643         pos = 0.;
2644         lane = *(++laneIter);
2645         edge = &lane->getEdge();
2646     } else {
2647         // Collect all vehicles in range behind ego vehicle
2648         edge = &(lane->getEdge());
2649         getUpstreamVehicles(edge, pos, range + veh.getLength(), distToConflictLane, lane, foeCollector, seenJunctions);
2650     }
2651 
2652     assert(lane != 0);
2653     assert(!lane->isInternal());
2654 
2655     // Advance downstream the ego vehicle's route for distance 'range'.
2656     // Collect all vehicles on the traversed Edges and on incoming edges at junctions.
2657     while (remainingDownstreamRange > 0.) {
2658 #ifdef DEBUG_SSM_SURROUNDING
2659         std::cout << SIMTIME << " Scanning downstream for vehicle '" << veh.getID() << "' on lane '" << veh.getLane()->getID() << "', position=" << pos << ".\n"
2660                   << "Considering edge '" << edge->getID() << "' Remaining downstream range = " << remainingDownstreamRange
2661                   << "\nbestLanes=" << ::toString(egoBestLanes) << "\n"
2662                   << std::endl;
2663 #endif
2664         assert(!edge->isInternal());
2665         assert(!lane->isInternal());
2666         assert(pos == 0 || lane == veh.getLane());
2667         if (pos + remainingDownstreamRange < lane->getLength()) {
2668             // scan range ends on this lane
2669             getUpstreamVehicles(edge, pos + remainingDownstreamRange, remainingDownstreamRange, distToConflictLane, lane, foeCollector, seenJunctions);
2670             // scanned required downstream range
2671             break;
2672         } else {
2673             // Also need to scan area that reaches beyond the lane
2674             // Collecting vehicles on non-internal edge ahead
2675             getUpstreamVehicles(edge, edge->getLength(), edge->getLength() - pos, distToConflictLane, lane, foeCollector, seenJunctions);
2676             // account for scanned distance on lane
2677             remainingDownstreamRange -= lane->getLength() - pos;
2678             distToConflictLane += lane->getLength();
2679             pos = 0.;
2680 
2681             // proceed to next non-internal lane
2682             ++laneIter;
2683             assert(laneIter == egoBestLanes.end() || *laneIter != 0);
2684 
2685             // If the vehicle's best lanes go on, collect vehicles on the upcoming junction
2686             if (laneIter != egoBestLanes.end()) {
2687                 // Upcoming junction
2688                 const MSJunction* junction = lane->getEdge().getToJunction();
2689 
2690                 // Find connection for ego on the junction
2691                 nextNonInternalLane = *laneIter;
2692                 MSLink* link = lane->getLinkTo(nextNonInternalLane);
2693                 assert(link != 0 || link->getLength() == 0.);
2694 
2695                 // First lane of the connection
2696                 lane = link->getViaLane();
2697                 if (lane == nullptr) {
2698                     // link without internal lane
2699                     lane = nextNonInternalLane;
2700                     edge = &(lane->getEdge());
2701                     if (seenJunctions.count(junction) == 0) {
2702                         seenJunctions.insert(junction);
2703                         continue;
2704                     } else {
2705                         break;
2706                     }
2707                 }
2708 
2709                 if (seenJunctions.count(junction) == 0) {
2710                     // Collect vehicles on the junction, if it wasn't considered already
2711                     getVehiclesOnJunction(junction, distToConflictLane, lane, foeCollector);
2712                     seenJunctions.insert(junction);
2713                     // Collect vehicles on incoming edges (except the last edge, where we already collected). Use full range.
2714                     const ConstMSEdgeVector& incoming = junction->getIncoming();
2715                     for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2716                         if (*ei == edge || (*ei)->isInternal()) {
2717                             continue;
2718                         }
2719                         getUpstreamVehicles(*ei, (*ei)->getLength(), range, distToConflictLane, lane, foeCollector, seenJunctions);
2720                     }
2721                     // account for scanned distance on junction
2722                     double linkLength = link->getInternalLengthsAfter();
2723                     remainingDownstreamRange -= linkLength;
2724                     distToConflictLane += linkLength;
2725 #ifdef DEBUG_SSM_SURROUNDING
2726                     std::cout << "    Downstream Scan for vehicle '" << veh.getID() << "' proceeded over junction '" << junction->getID()
2727                               << "',\n    linkLength=" << linkLength << ", remainingDownstreamRange=" << remainingDownstreamRange
2728                               << std::endl;
2729 #endif
2730 
2731                     // update ego's lane to next non internal edge
2732                     lane = nextNonInternalLane;
2733                     edge = &(lane->getEdge());
2734                 } else {
2735 #ifdef DEBUG_SSM_SURROUNDING
2736                     std::cout << "    Downstream Scan for vehicle '" << veh.getID() << "' stops at junction '" << junction->getID()
2737                               << "', which has already been scanned."
2738                               << std::endl;
2739 #endif
2740                     break;
2741                 }
2742             } else {
2743                 // Further vehicle path unknown, break search
2744                 break;
2745             }
2746         }
2747     }
2748     // remove ego vehicle
2749     foeCollector.erase(&veh);
2750 }
2751 
2752 void
getUpstreamVehicles(const MSEdge * edge,double pos,double range,double egoDistToConflictLane,const MSLane * const egoConflictLane,FoeInfoMap & foeCollector,std::set<const MSJunction * > & seenJunctions)2753 MSDevice_SSM::getUpstreamVehicles(const MSEdge* edge, double pos, double range, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector, std::set<const MSJunction*>& seenJunctions) {
2754 #ifdef DEBUG_SSM_SURROUNDING
2755     std::cout << SIMTIME << " getUpstreamVehicles() for edge '" << edge->getID() << "'"
2756               << " pos = " << pos << " range = " << range
2757               << "\nFound vehicles:"
2758               << std::endl;
2759 #endif
2760     if (range <= 0) {
2761         return;
2762     }
2763 
2764     const std::vector<MSLane*>& lanes = edge->getLanes();
2765     // Collect vehicles on the given edge with position in [pos-range,pos]
2766     for (std::vector<MSLane*>::const_iterator li = lanes.begin(); li != lanes.end(); ++li) {
2767         MSLane* lane = *li;
2768         const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
2769         for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2770             MSVehicle* veh = *vi;
2771             if (foeCollector.find(veh) != foeCollector.end()) {
2772                 // vehicle already recognized, earlier recognized conflict has priority
2773                 continue;
2774             }
2775             if (veh->getPositionOnLane() - veh->getLength() <= pos && veh->getPositionOnLane() >= pos - range) {
2776 #ifdef DEBUG_SSM_SURROUNDING
2777                 std::cout << veh->getID()  << "\n";
2778 #endif
2779                 FoeInfo* c = new FoeInfo(); // c is deleted in updateEncounter()
2780                 c->egoDistToConflictLane = egoDistToConflictLane;
2781                 c->egoConflictLane = egoConflictLane;
2782                 foeCollector[veh] = c;
2783             }
2784         }
2785         lane->releaseVehicles();
2786     }
2787 
2788 #ifdef DEBUG_SSM_SURROUNDING
2789     std::cout << std::endl;
2790 #endif
2791 
2792     // TODO: Gather vehicles from opposite direction. This should happen in any case, where opposite direction overtaking is possible.
2793     //       If it isn't it might still be nicer to trace oncoming vehicles for the resulting trajectories in the encounters
2794     //    if (edge->hasOpposite...)
2795 
2796     if (range <= pos) {
2797         return;
2798     }
2799 
2800     // Here we have: range > pos, i.e. we proceed collecting vehicles on preceding edges
2801     range -= pos;
2802 
2803     // Junction representing the origin of 'edge'
2804     const MSJunction* junction = edge->getFromJunction();
2805     if (seenJunctions.count(junction) == 0) {
2806         // Collect vehicles from incoming edges of the junction
2807         if (!edge->isInternal()) {
2808             // collect vehicles on preceding junction (for internal edges this is already done in caller,
2809             // i.e. findSurroundingVehicles() or the recursive call from getUpstreamVehicles())
2810 
2811             // Collect vehicles on the junction, if it wasn't considered already
2812             getVehiclesOnJunction(junction, egoDistToConflictLane, egoConflictLane, foeCollector);
2813             seenJunctions.insert(junction);
2814         }
2815         // Collect vehicles from incoming edges from the junction representing the origin of 'edge'
2816         const ConstMSEdgeVector& incoming = junction->getIncoming();
2817         for (ConstMSEdgeVector::const_iterator ei = incoming.begin(); ei != incoming.end(); ++ei) {
2818             if ((*ei)->isInternal()) {
2819                 continue;
2820             }
2821             const MSEdge* inEdge = *ei;
2822             assert(inEdge != 0);
2823             double distOnJunction = edge->isInternal() ? 0. : inEdge->getInternalFollowingLengthTo(edge);
2824             if (distOnJunction >= range) {
2825                 continue;
2826             }
2827             // account for vehicles on the predecessor edge
2828             getUpstreamVehicles(inEdge, inEdge->getLength(), range - distOnJunction, egoDistToConflictLane, egoConflictLane, foeCollector, seenJunctions);
2829         }
2830     } else {
2831 #ifdef DEBUG_SSM_SURROUNDING
2832         std::cout << "    Downstream Scan for stops at junction '" << junction->getID()
2833                   << "', which has already been scanned."
2834                   << std::endl;
2835 #endif
2836     }
2837 }
2838 
2839 void
getVehiclesOnJunction(const MSJunction * junction,double egoDistToConflictLane,const MSLane * const egoConflictLane,FoeInfoMap & foeCollector)2840 MSDevice_SSM::getVehiclesOnJunction(const MSJunction* junction, double egoDistToConflictLane, const MSLane* const egoConflictLane, FoeInfoMap& foeCollector) {
2841 #ifdef DEBUG_SSM_SURROUNDING
2842     std::cout << SIMTIME << " getVehiclesOnJunction() for junction '" << junction->getID() << "'"
2843               << "\nFound vehicles:"
2844               << std::endl;
2845 #endif
2846     // Collect vehicles on internal lanes
2847     const std::vector<MSLane*> lanes = junction->getInternalLanes();
2848     for (std::vector<MSLane*>::const_iterator li = lanes.begin(); li != lanes.end(); ++li) {
2849         MSLane* lane = *li;
2850         const MSLane::VehCont& vehicles = lane->getVehiclesSecure();
2851 
2852         // Add FoeInfos (XXX: for some situations, a vehicle may be collected twice. Then the later finding overwrites the earlier in foeCollector.
2853         // This could lead to neglecting a conflict when determining foeConflictLane later.) -> TODO: test with twice intersecting routes
2854         for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2855             FoeInfo* c = new FoeInfo();
2856             c->egoConflictLane = egoConflictLane;
2857             c->egoDistToConflictLane = egoDistToConflictLane;
2858             foeCollector[*vi] = c;
2859 #ifdef DEBUG_SSM_SURROUNDING
2860             for (MSLane::VehCont::const_iterator vi = vehicles.begin(); vi != vehicles.end(); ++vi) {
2861                 std::cout << (*vi)->getID() << "\n";
2862             }
2863 #endif
2864         }
2865         lane->releaseVehicles();
2866 
2867         // If there is an internal continuation lane, also collect vehicles on that lane
2868         if (lane->getLinkCont().size() > 1 && lane->getLinkCont()[0]->getViaLane() != nullptr) {
2869             // There's a second internal lane of the connection
2870             lane = lane->getLinkCont()[0]->getViaLane();
2871             // This code must be modified, if more than two-piece internal lanes are allowed. Thus, assert:
2872             assert(lane->getLinkCont().size() == 0 || lane->getLinkCont()[0]->getViaLane() == 0);
2873 
2874             // collect vehicles
2875             const MSLane::VehCont& vehicles2 = lane->getVehiclesSecure();
2876             // Add FoeInfos. This duplicates the loop for the first internal lane
2877             for (MSLane::VehCont::const_iterator vi = vehicles2.begin(); vi != vehicles2.end(); ++vi) {
2878                 FoeInfo* c = new FoeInfo();
2879                 c->egoConflictLane = egoConflictLane;
2880                 c->egoDistToConflictLane = egoDistToConflictLane;
2881                 foeCollector[*vi] = c;
2882 #ifdef DEBUG_SSM_SURROUNDING
2883                 for (MSLane::VehCont::const_iterator vi = vehicles2.begin(); vi != vehicles2.end(); ++vi) {
2884                     std::cout << (*vi)->getID() << "\n";
2885                 }
2886 #endif
2887             }
2888             lane->releaseVehicles();
2889         }
2890     }
2891 
2892 #ifdef DEBUG_SSM_SURROUNDING
2893     std::cout << std::endl;
2894 #endif
2895 }
2896 
2897 
2898 
2899 void
generateOutput() const2900 MSDevice_SSM::generateOutput() const {
2901     // This is called once at vehicle removal.
2902     //       Also: flush myOutputFile? Or is this done automatically?
2903     // myOutputFile->closeTag();
2904 }
2905 
2906 // ---------------------------------------------------------------------------
2907 // Static parameter load helpers
2908 // ---------------------------------------------------------------------------
2909 std::string
getOutputFilename(const SUMOVehicle & v,std::string deviceID)2910 MSDevice_SSM::getOutputFilename(const SUMOVehicle& v, std::string deviceID) {
2911     OptionsCont& oc = OptionsCont::getOptions();
2912     std::string file = deviceID + ".xml";
2913     if (v.getParameter().knowsParameter("device.ssm.file")) {
2914         try {
2915             file = v.getParameter().getParameter("device.ssm.file", file);
2916         } catch (...) {
2917             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.file", file) + "'for vehicle parameter 'ssm.measures'");
2918         }
2919     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.file")) {
2920         try {
2921             file = v.getVehicleType().getParameter().getParameter("device.ssm.file", file);
2922         } catch (...) {
2923             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.file", file) + "'for vType parameter 'ssm.measures'");
2924         }
2925     } else {
2926         file = oc.getString("device.ssm.file") == "" ? file : oc.getString("device.ssm.file");
2927         if (!oc.isSet("device.ssm.file") && (issuedParameterWarnFlags & SSM_WARN_FILE) == 0) {
2928             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.file'. Using default of '" << file << "'\n";
2929             issuedParameterWarnFlags |= SSM_WARN_FILE;
2930         }
2931     }
2932     return file;
2933 }
2934 
2935 bool
useGeoCoords(const SUMOVehicle & v)2936 MSDevice_SSM::useGeoCoords(const SUMOVehicle& v) {
2937     OptionsCont& oc = OptionsCont::getOptions();
2938     bool useGeo = false;
2939     if (v.getParameter().knowsParameter("device.ssm.geo")) {
2940         try {
2941             useGeo = StringUtils::toBool(v.getParameter().getParameter("device.ssm.geo", "no"));
2942         } catch (...) {
2943             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.geo", "no") + "'for vehicle parameter 'ssm.geo'");
2944         }
2945     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.geo")) {
2946         try {
2947             useGeo = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no"));
2948         } catch (...) {
2949             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.geo", "no") + "'for vType parameter 'ssm.geo'");
2950         }
2951     } else {
2952         useGeo = oc.getBool("device.ssm.geo");
2953         if (!oc.isSet("device.ssm.geo") && (issuedParameterWarnFlags & SSM_WARN_GEO) == 0) {
2954             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.geo'. Using default of '" << ::toString(useGeo) << "'\n";
2955             issuedParameterWarnFlags |= SSM_WARN_GEO;
2956         }
2957     }
2958     return useGeo;
2959 }
2960 
2961 
2962 double
getDetectionRange(const SUMOVehicle & v)2963 MSDevice_SSM::getDetectionRange(const SUMOVehicle& v) {
2964     OptionsCont& oc = OptionsCont::getOptions();
2965     double range = -INVALID;
2966     if (v.getParameter().knowsParameter("device.ssm.range")) {
2967         try {
2968             range = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.range", ""));
2969         } catch (...) {
2970             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.range", "") + "'for vehicle parameter 'ssm.range'");
2971         }
2972     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.range")) {
2973         try {
2974             range = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.range", ""));
2975         } catch (...) {
2976             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.range", "") + "'for vType parameter 'ssm.range'");
2977         }
2978     } else {
2979         range = oc.getFloat("device.ssm.range");
2980         if (!oc.isSet("device.ssm.range") && (issuedParameterWarnFlags & SSM_WARN_RANGE) == 0) {
2981             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.range'. Using default of '" << range << "'\n";
2982             issuedParameterWarnFlags |= SSM_WARN_RANGE;
2983         }
2984     }
2985     return range;
2986 }
2987 
2988 
2989 double
getExtraTime(const SUMOVehicle & v)2990 MSDevice_SSM::getExtraTime(const SUMOVehicle& v) {
2991     OptionsCont& oc = OptionsCont::getOptions();
2992     double extraTime = INVALID;
2993     if (v.getParameter().knowsParameter("device.ssm.extratime")) {
2994         try {
2995             extraTime = StringUtils::toDouble(v.getParameter().getParameter("device.ssm.extratime", ""));
2996         } catch (...) {
2997             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.extratime", "") + "'for vehicle parameter 'ssm.extratime'");
2998         }
2999     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.extratime")) {
3000         try {
3001             extraTime = StringUtils::toDouble(v.getVehicleType().getParameter().getParameter("device.ssm.extratime", ""));
3002         } catch (...) {
3003             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.extratime", "") + "'for vType parameter 'ssm.extratime'");
3004         }
3005     } else {
3006         extraTime = oc.getFloat("device.ssm.extratime");
3007         if (!oc.isSet("device.ssm.extratime") && (issuedParameterWarnFlags & SSM_WARN_EXTRATIME) == 0) {
3008             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.extratime'. Using default of '" << extraTime << "'\n";
3009             issuedParameterWarnFlags |= SSM_WARN_EXTRATIME;
3010         }
3011     }
3012     if (extraTime < 0.) {
3013         extraTime = DEFAULT_EXTRA_TIME;
3014         WRITE_WARNING("Negative (or no) value encountered for vehicle parameter 'device.ssm.extratime' in vehicle '" + v.getID() + "' using default value " + ::toString(extraTime) + " instead");
3015     }
3016     return extraTime;
3017 }
3018 
3019 
3020 bool
requestsTrajectories(const SUMOVehicle & v)3021 MSDevice_SSM::requestsTrajectories(const SUMOVehicle& v) {
3022     OptionsCont& oc = OptionsCont::getOptions();
3023     bool trajectories = false;
3024     if (v.getParameter().knowsParameter("device.ssm.trajectories")) {
3025         try {
3026             trajectories = StringUtils::toBool(v.getParameter().getParameter("device.ssm.trajectories", "no"));
3027         } catch (...) {
3028             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.trajectories", "no") + "'for vehicle parameter 'ssm.trajectories'");
3029         }
3030     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.trajectories")) {
3031         try {
3032             trajectories = StringUtils::toBool(v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no"));
3033         } catch (...) {
3034             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.trajectories", "no") + "'for vType parameter 'ssm.trajectories'");
3035         }
3036     } else {
3037         trajectories = oc.getBool("device.ssm.trajectories");
3038         if (!oc.isSet("device.ssm.trajectories") && (issuedParameterWarnFlags & SSM_WARN_TRAJECTORIES) == 0) {
3039             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.trajectories'. Using default of '" << ::toString(trajectories) << "'\n";
3040             issuedParameterWarnFlags |= SSM_WARN_TRAJECTORIES;
3041         }
3042     }
3043     return trajectories;
3044 }
3045 
3046 
3047 bool
getMeasuresAndThresholds(const SUMOVehicle & v,std::string deviceID,std::map<std::string,double> & thresholds)3048 MSDevice_SSM::getMeasuresAndThresholds(const SUMOVehicle& v, std::string deviceID, std::map<std::string, double>& thresholds) {
3049     OptionsCont& oc = OptionsCont::getOptions();
3050 
3051     // Measures
3052     std::string measures_str = "";
3053     if (v.getParameter().knowsParameter("device.ssm.measures")) {
3054         try {
3055             measures_str = v.getParameter().getParameter("device.ssm.measures", "");
3056         } catch (...) {
3057             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.measures", "") + "'for vehicle parameter 'ssm.measures'");
3058         }
3059     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.measures")) {
3060         try {
3061             measures_str = v.getVehicleType().getParameter().getParameter("device.ssm.measures", "");
3062         } catch (...) {
3063             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.measures", "") + "'for vType parameter 'ssm.measures'");
3064         }
3065     } else {
3066         measures_str = oc.getString("device.ssm.measures");
3067         if (!oc.isSet("device.ssm.measures") && (issuedParameterWarnFlags & SSM_WARN_MEASURES) == 0) {
3068             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.measures'. Using default of '" << measures_str << "'\n";
3069             issuedParameterWarnFlags |= SSM_WARN_THRESHOLDS;
3070         }
3071     }
3072 
3073     // Check retrieved measures
3074     if (measures_str == "") {
3075         WRITE_WARNING("No measures specified for ssm device of vehicle '" + v.getID() + "'. Registering all available SSMs.");
3076         measures_str = AVAILABLE_SSMS;
3077     }
3078     StringTokenizer st = StringTokenizer(AVAILABLE_SSMS);
3079     std::vector<std::string> available = st.getVector();
3080     st = StringTokenizer(measures_str);
3081     std::vector<std::string> measures = st.getVector();
3082     for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
3083         if (std::find(available.begin(), available.end(), *i) == available.end()) {
3084             // Given identifier is unknown
3085             WRITE_ERROR("SSM identifier '" + *i + "' is not supported. Aborting construction of SSM device '" + deviceID + "'.");
3086             return false;
3087         }
3088     }
3089 
3090     // Thresholds
3091     std::string thresholds_str = "";
3092     if (v.getParameter().knowsParameter("device.ssm.thresholds")) {
3093         try {
3094             thresholds_str = v.getParameter().getParameter("device.ssm.thresholds", "");
3095         } catch (...) {
3096             WRITE_WARNING("Invalid value '" + v.getParameter().getParameter("device.ssm.thresholds", "") + "'for vehicle parameter 'ssm.thresholds'");
3097         }
3098     } else if (v.getVehicleType().getParameter().knowsParameter("device.ssm.thresholds")) {
3099         try {
3100             thresholds_str = v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "");
3101         } catch (...) {
3102             WRITE_WARNING("Invalid value '" + v.getVehicleType().getParameter().getParameter("device.ssm.thresholds", "") + "'for vType parameter 'ssm.thresholds'");
3103         }
3104     } else {
3105         thresholds_str = oc.getString("device.ssm.thresholds");
3106         if (!oc.isSet("device.ssm.thresholds") && (issuedParameterWarnFlags & SSM_WARN_THRESHOLDS) == 0) {
3107             std::cout << "vehicle '" << v.getID() << "' does not supply vehicle parameter 'device.ssm.thresholds'. Using default of '" << thresholds_str << "'\n";
3108             issuedParameterWarnFlags |= SSM_WARN_THRESHOLDS;
3109         }
3110     }
3111 
3112     // Parse vector of doubles from threshold_str
3113     int count = 0;
3114     if (thresholds_str != "") {
3115         st = StringTokenizer(thresholds_str);
3116         while (count < (int)measures.size() && st.hasNext()) {
3117             double thresh = StringUtils::toDouble(st.next());
3118             thresholds.insert(std::make_pair(measures[count], thresh));
3119             ++count;
3120         }
3121         if (thresholds.size() < measures.size() || st.hasNext()) {
3122             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.");
3123             return false;
3124         }
3125     } else {
3126         // assume default thresholds if none are given
3127         for (std::vector<std::string>::const_iterator i = measures.begin(); i != measures.end(); ++i) {
3128             if (*i == "TTC") {
3129                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TTC));
3130             } else if (*i == "DRAC") {
3131                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_DRAC));
3132             } else if (*i == "PET") {
3133                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_PET));
3134             } else if (*i == "BR") {
3135                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_BR));
3136             } else if (*i == "SGAP") {
3137                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_SGAP));
3138             } else if (*i == "TGAP") {
3139                 thresholds.insert(std::make_pair(*i, DEFAULT_THRESHOLD_TGAP));
3140             } else {
3141                 WRITE_ERROR("Unknown SSM identifier '" + (*i) + "'. Aborting construction of ssm device."); // should never occur
3142                 return false;
3143             }
3144         }
3145     }
3146     return true;
3147 }
3148 
3149 
3150 
3151 /****************************************************************************/
3152 
3153