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