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