Eclipse SUMO - Simulation of Urban MObility
MSInductLoop.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2001-2022 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
23// An unextended detector measuring at a fixed position on a fixed lane.
24/****************************************************************************/
25#include <config.h>
26
27#include "MSInductLoop.h"
28#include <cassert>
29#include <numeric>
30#include <utility>
31#ifdef HAVE_FOX
33#endif
37#include <microsim/MSLane.h>
38#include <microsim/MSEdge.h>
39#include <microsim/MSVehicle.h>
40#include <microsim/MSNet.h>
47
48#define HAS_NOT_LEFT_DETECTOR -1
49
50//#define DEBUG_E1_NOTIFY_MOVE
51
52#define DEBUG_COND (true)
53//#define DEBUG_COND (isSelected())
54//#define DEBUG_COND (getID()=="")
55
56// ===========================================================================
57// method definitions
58// ===========================================================================
59MSInductLoop::MSInductLoop(const std::string& id, MSLane* const lane,
60 double positionInMeters,
61 double length, std::string name,
62 const std::string& vTypes,
63 const std::string& nextEdges,
64 int detectPersons,
65 const bool needLocking) :
66 MSMoveReminder(id, lane),
67 MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
68 myName(name),
69 myPosition(positionInMeters),
70 myEndPosition(myPosition + length),
71 myNeedLock(needLocking || MSGlobals::gNumSimThreads > 1),
72 myLastLeaveTime(SIMTIME),
73 myOverrideTime(-1),
74 myOverrideEntryTime(-1),
75 myVehicleDataCont(),
76 myVehiclesOnDet() {
77 assert(length >= 0);
78 assert(myPosition >= 0 && myEndPosition <= myLane->getLength());
79 reset();
80}
81
82
84}
85
86
87void
89#ifdef HAVE_FOX
90 ScopedLocker<> lock(myNotificationMutex, myNeedLock);
91#endif
94 myVehicleDataCont.clear();
95}
96
97
98bool
99MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
100 // vehicles must be kept if the "inductionloop" wants to detect passeengers
101 if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
102 return false;
103 }
104 if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
106 return false;
107 }
108 if (veh.getPositionOnLane() >= myPosition) {
109#ifdef HAVE_FOX
110 ScopedLocker<> lock(myNotificationMutex, myNeedLock);
111#endif
112 myVehiclesOnDet[&veh] = SIMTIME;
114 }
115 }
116 return true;
117}
118
119
120bool
122 double newPos, double newSpeed) {
123 if (newPos < myPosition) {
124 // detector not reached yet
125 return true;
126 }
127 if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
128 bool keep = false;
129 MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
130 for (MSTransportable* p : v.getPersons()) {
131 keep = notifyMove(*p, oldPos, newPos, newSpeed);
132 }
133 return keep;
134 }
135#ifdef HAVE_FOX
136 ScopedLocker<> lock(myNotificationMutex, myNeedLock);
137#endif
138 const double oldSpeed = veh.getPreviousSpeed();
139 if (newPos >= myPosition && oldPos < myPosition) {
140 // entered the detector by move
141 const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
142 myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
144#ifdef DEBUG_E1_NOTIFY_MOVE
145 if (DEBUG_COND) {
146 std::cout << SIMTIME << " det=" << getID() << " enteredVeh=" << veh.getID() << "\n";
147 }
148#endif
149 }
150 double oldBackPos = oldPos - veh.getVehicleType().getLength();
151 double newBackPos = newPos - veh.getVehicleType().getLength();
152 if (newBackPos > myEndPosition) {
153 // vehicle passed the detector (it may have changed onto this lane somewhere past the detector)
154 // assert(!MSGlobals::gSemiImplicitEulerUpdate || newSpeed > 0 || myVehiclesOnDet.find(&veh) == myVehiclesOnDet.end());
155 // assertion is invalid in case of teleportation
156 if (oldBackPos <= myEndPosition) {
157 const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
158 if (it != myVehiclesOnDet.end()) {
159 const double entryTime = it->second;
160 const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myEndPosition, newBackPos, oldSpeed, newSpeed);
161 myVehiclesOnDet.erase(it);
162 assert(entryTime <= leaveTime);
163 myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false));
164 myLastLeaveTime = leaveTime;
165#ifdef DEBUG_E1_NOTIFY_MOVE
166 if (DEBUG_COND) {
167 std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << "\n";
168 }
169#endif
170 } else {
171#ifdef DEBUG_E1_NOTIFY_MOVE
172 if (DEBUG_COND) {
173 std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (notFound)\n";
174 }
175#endif
176 }
177 } else {
178 // vehicle is already beyond the detector...
179 // This can happen even if it is still registered in myVehiclesOnDet, e.g., after teleport.
180 myVehiclesOnDet.erase(&veh);
181#ifdef DEBUG_E1_NOTIFY_MOVE
182 if (DEBUG_COND) {
183 std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (unusual)\n";
184 }
185#endif
186 }
187 return false;
188 }
189 // vehicle stays on the detector
190 return true;
191}
192
193
194bool
195MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
196 if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
197 const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
198 notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
199 }
201#ifdef HAVE_FOX
202 ScopedLocker<> lock(myNotificationMutex, myNeedLock);
203#endif
204 const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
205 if (it != myVehiclesOnDet.end()) {
206 const double entryTime = it->second;
207 const double leaveTime = SIMTIME + TS;
208 myVehiclesOnDet.erase(it);
209 myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
210 myLastLeaveTime = leaveTime;
211 }
212 return false;
213 }
214 return true;
215}
216
217
218double
219MSInductLoop::getSpeed(const int offset) const {
220 const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
221 return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
222}
223
224
225double
226MSInductLoop::getVehicleLength(const int offset) const {
227 const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
228 return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
229}
230
231
232double
234 if (myOverrideTime >= 0) {
235 return myOverrideTime < TS ? (TS - myOverrideTime) / TS * 100 : 0;
236 }
237 const SUMOTime tbeg = SIMSTEP - DELTA_T;
238 double occupancy = 0;
239 const double csecond = SIMTIME;
240 for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
241 const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
242 const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
243 occupancy += MIN2(leaveTime - entryTime, TS);
244 }
245 return occupancy / TS * 100.;
246}
247
248
249double
250MSInductLoop::getEnteredNumber(const int offset) const {
251 if (myOverrideTime >= 0) {
252 return myOverrideTime < TS ? 1 : 0;
253 }
254 return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
255}
256
257
258std::vector<std::string>
259MSInductLoop::getVehicleIDs(const int offset) const {
260 std::vector<std::string> ret;
261 for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
262 ret.push_back(i.idM);
263 }
264 return ret;
265}
266
267
268double
270 if (myOverrideTime >= 0) {
271 return myOverrideTime;
272 }
273 if (myVehiclesOnDet.size() != 0) {
274 // detector is occupied
275 return 0;
276 }
277 return SIMTIME - myLastLeaveTime;
278}
279
280
281double
283 if (myOverrideTime >= 0) {
285 }
286 if (myVehiclesOnDet.size() == 0) {
287 // detector is unoccupied
288 return 0;
289 } else {
290 double minEntry = std::numeric_limits<double>::max();
291 for (const auto& i : myVehiclesOnDet) {
292 minEntry = MIN2(i.second, minEntry);
293 }
294 return SIMTIME - minEntry;
295 }
296}
297
298
299
302 if (myOverrideTime >= 0) {
304 }
305 if (myVehiclesOnDet.size() != 0) {
307 }
309}
310
311
312void
314 myOverrideTime = time;
315 if (time < 0) {
317 } else {
318 const double entryTime = MAX2(0.0, SIMTIME - time);
319 if (myOverrideEntryTime >= 0) {
320 // maintain earlier entry time to achive continous detection
322 } else {
323 myOverrideEntryTime = entryTime;
324 }
325 }
326}
327
328void
330 dev.writeXMLHeader("detector", "det_e1_file.xsd");
331}
332
333
334void
336 if (dev.isNull()) {
337 reset();
338 return;
339 }
340 const double t(STEPS2TIME(stopTime - startTime));
341 double occupancy = 0.;
342 double speedSum = 0.;
343 double lengthSum = 0.;
344 int contrib = 0;
345 // to approximate the space mean speed
346 double inverseSpeedSum = 0.;
347 for (const VehicleData& vData : myVehicleDataCont) {
348 const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
349 occupancy += MIN2(timeOnDetDuringInterval, t);
350 if (!vData.leftEarlyM) {
351 speedSum += vData.speedM;
352 assert(vData.speedM > 0.);
353 inverseSpeedSum += 1. / vData.speedM;
354 lengthSum += vData.lengthM;
355 contrib++;
356 }
357 }
358 const double flow = (double)contrib / t * 3600.;
359 for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
360 occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
361 }
362 occupancy *= 100. / t;
363 const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
364 const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
365 const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
367 dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
368 dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
369 dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
370 reset();
371}
372
373
374void
376 if (myDetectPersons == (int)PersonMode::NONE) {
377 return;
378 }
379 if (myLane->hasPedestrians()) {
380 for (MSTransportable* p : myLane->getEdge().getPersons()) {
381 if (p->getLane() != myLane || !vehicleApplies(*p)) {
382 continue;
383 }
384 notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
385 }
386 }
387}
388
389
390void
392 if (personApplies(*p, dir)) {
393 const double newSpeed = p->getSpeed();
394 const double newPos = (dir == MSPModel::FORWARD
395 ? pos
396 // position relative to detector
397 : myPosition - (pos - myPosition));
398 const double oldPos = newPos - SPEED2DIST(newSpeed);
399 if (oldPos - p->getVehicleType().getLength() <= myPosition) {
400 notifyMove(*p, oldPos, newPos, newSpeed);
401 }
402 }
403}
404
405
406std::vector<MSInductLoop::VehicleData>
407MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy) const {
408#ifdef HAVE_FOX
409 ScopedLocker<> lock(myNotificationMutex, myNeedLock);
410#endif
411 const double t = STEPS2TIME(tMS);
412 std::vector<VehicleData> ret;
413 for (const VehicleData& i : myVehicleDataCont) {
414 if (includeEarly || !i.leftEarlyM) {
415 if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
416 ret.push_back(i);
417 }
418 }
419 }
420 for (const VehicleData& i : myLastVehicleDataCont) {
421 if (includeEarly || !i.leftEarlyM) {
422 if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
423 ret.push_back(i);
424 }
425 }
426 }
427 for (const auto& i : myVehiclesOnDet) {
428 if (i.second >= t || leaveTime || forOccupancy) { // no need to check leave time, they are still on the detector
429 SUMOTrafficObject* const v = i.first;
430 VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
431 d.speedM = v->getSpeed();
432 ret.push_back(d);
433 }
434 }
435 return ret;
436}
437
438
440 double leaveTimestep, const bool leftEarly)
441 : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
442 speedM(v.getVehicleType().getLength() / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
443 leftEarlyM(leftEarly) {}
444
445
446void
450 myLastVehicleDataCont.clear();
451 myVehicleDataCont.clear();
452 myVehiclesOnDet.clear();
453}
454
455/****************************************************************************/
long long int SUMOTime
Definition: GUI.h:36
#define HAS_NOT_LEFT_DETECTOR
#define DEBUG_COND
SUMOTime DELTA_T
Definition: SUMOTime.cpp:37
#define STEPS2TIME(x)
Definition: SUMOTime.h:54
#define SPEED2DIST(x)
Definition: SUMOTime.h:44
#define SIMSTEP
Definition: SUMOTime.h:60
#define TS
Definition: SUMOTime.h:41
#define SIMTIME
Definition: SUMOTime.h:61
#define TIME2STEPS(x)
Definition: SUMOTime.h:56
@ SUMO_TAG_INTERVAL
an aggreagated-output interval
@ SUMO_ATTR_BEGIN
weights: time range begin
@ SUMO_ATTR_END
weights: time range end
@ SUMO_ATTR_ID
T MIN2(T a, T b)
Definition: StdDefs.h:71
T MAX2(T a, T b)
Definition: StdDefs.h:77
The base class for microscopic and mesoscopic vehicles.
Definition: MSBaseVehicle.h:55
const std::vector< MSTransportable * > & getPersons() const
retrieve riding persons
static double passingTime(const double lastPos, const double passedPos, const double currentPos, const double lastSpeed, const double currentSpeed)
Calculates the time at which the position passedPosition has been passed In case of a ballistic updat...
Definition: MSCFModel.cpp:636
Base of value-generating classes (detectors)
bool vehicleApplies(const SUMOTrafficObject &veh) const
Checks whether the detector measures vehicles of the given type.
const int myDetectPersons
Whether pedestrians shall be detected instead of vehicles.
bool personApplies(const MSTransportable &p, int dir) const
const std::set< MSTransportable *, ComparatorNumericalIdLess > & getPersons() const
Returns this edge's persons set.
Definition: MSEdge.h:201
double getOccupancyTime() const
Returns the time of continous occupation by the same vehicle in seconds or 0 if there is no vehicle o...
static double lengthSum(double sumSoFar, const MSInductLoop::VehicleData &data)
Adds up VehicleData::lengthM.
Definition: MSInductLoop.h:337
double getOccupancy() const
Returns the current occupancy.
int myEnteredVehicleNumber
The number of entered vehicles.
Definition: MSInductLoop.h:368
double getEnteredNumber(const int offset) const
Returns the number of vehicles that have passed the detector.
void writeXMLOutput(OutputDevice &dev, SUMOTime startTime, SUMOTime stopTime)
Writes collected values into the given stream.
static double speedSum(double sumSoFar, const MSInductLoop::VehicleData &data)
Adds up VehicleData::speedM.
Definition: MSInductLoop.h:332
VehicleDataCont myVehicleDataCont
Data of vehicles that have completely passed the detector.
Definition: MSInductLoop.h:374
void overrideTimeSinceDetection(double time)
double myOverrideEntryTime
records the time at which overrideTimeSinceDetection was activated
Definition: MSInductLoop.h:365
double getSpeed(const int offset) const
Returns the speed of the vehicle on the detector.
std::vector< std::string > getVehicleIDs(const int offset) const
Returns the ids of vehicles that have passed the detector.
double getVehicleLength(const int offset) const
Returns the length of the vehicle on the detector.
virtual void reset()
Resets all generated values to allow computation of next interval.
const double myPosition
Detector's position on lane [m].
Definition: MSInductLoop.h:350
VehicleDataCont myLastVehicleDataCont
Data of vehicles that have completely passed the detector in the last time interval.
Definition: MSInductLoop.h:377
const double myEndPosition
Detector's end position (defaults to myPosition)
Definition: MSInductLoop.h:353
~MSInductLoop()
Destructor.
void writeXMLDetectorProlog(OutputDevice &dev) const
Opens the XML-output using "detector" as root element.
void detectorUpdate(const SUMOTime step)
Updates the detector (computes values) only used when detecting persons.
bool notifyMove(SUMOTrafficObject &veh, double oldPos, double newPos, double newSpeed)
Checks whether the vehicle shall be counted and/or shall still touch this MSMoveReminder.
void notifyMovePerson(MSTransportable *p, int dir, double pos)
helper function for mapping person movement
double myOverrideTime
overrides the time since last detection
Definition: MSInductLoop.h:362
MSInductLoop(const std::string &id, MSLane *const lane, double positionInMeters, double length, std::string name, const std::string &vTypes, const std::string &nextEdges, int detectPersons, const bool needLocking)
Constructor.
double getTimeSinceLastDetection() const
Returns the time since the last vehicle left the detector.
double myLastLeaveTime
Leave-time of the last vehicle detected [s].
Definition: MSInductLoop.h:359
const bool myNeedLock
whether internals need to be guarded against concurrent access (GUI or multi threading)
Definition: MSInductLoop.h:356
bool notifyEnter(SUMOTrafficObject &veh, Notification reason, const MSLane *enteredLane=0)
Checks whether the reminder is activated by a vehicle entering the lane.
virtual void clearState(SUMOTime time)
Remove all vehicles before quick-loading state.
std::vector< VehicleData > collectVehiclesOnDet(SUMOTime t, bool includeEarly=false, bool leaveTime=false, bool forOccupancy=false) const
Returns vehicle data for vehicles that have been on the detector starting at the given time.
std::map< SUMOTrafficObject *, double > myVehiclesOnDet
Data for vehicles that have entered the detector (vehicle -> enter time)
Definition: MSInductLoop.h:380
SUMOTime getLastDetectionTime() const
return last time a vehicle was on the detector
bool notifyLeave(SUMOTrafficObject &veh, double lastPos, MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
Dismisses the vehicle if it is on the detector due to a lane change.
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
bool hasPedestrians() const
whether the lane has pedestrians on it
Definition: MSLane.cpp:4171
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:713
Something on a lane to be noticed about vehicle movement.
MSLane *const myLane
Lane on which the reminder works.
Notification
Definition of a vehicle state.
@ NOTIFICATION_JUNCTION
The vehicle arrived at a junction.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:183
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:321
static const int BACKWARD
Definition: MSPModel.h:118
static const int FORWARD
Definition: MSPModel.h:117
virtual double getSpeed() const
the current speed of the transportable
const MSVehicleType & getVehicleType() const
Returns the object's "vehicle" type.
double getLength() const
Get vehicle's length [m].
const std::string & getID() const
Returns the id.
Definition: Named.h:74
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:251
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
virtual bool isNull()
returns the information whether the device will discard all output
Definition: OutputDevice.h:152
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
Representation of a vehicle, person, or container.
virtual const MSVehicleType & getVehicleType() const =0
Returns the object's "vehicle" type.
virtual double getPreviousSpeed() const =0
Returns the object's previous speed.
virtual double getSpeed() const =0
Returns the object's current speed.
virtual bool isPerson() const
Whether it is a person.
virtual double getBackPositionOnLane(const MSLane *lane) const =0
Get the object's back position along the given lane.
virtual double getPositionOnLane() const =0
Get the object's position along the lane.
A scoped lock which only triggers on condition.
Definition: ScopedLocker.h:40
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
Struct to store the data of the counted vehicle internally.
Definition: MSInductLoop.h:281
VehicleData(const SUMOTrafficObject &v, double entryTimestep, double leaveTimestep, const bool leftEarly)
Constructor.
double speedM
Speed of the vehicle in [m/s].
Definition: MSInductLoop.h:302