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