LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_SSM.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1311 1556 84.3 %
Date: 2024-04-30 15:40:33 Functions: 66 69 95.7 %

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

Generated by: LCOV version 1.14