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