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
35 #include <utils/common/ToString.h>
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 // ===========================================================================
51 // method definitions
52 // ===========================================================================
53 MSInductLoop::MSInductLoop(const std::string& id, MSLane* const lane,
54  double positionInMeters,
55  const std::string& vTypes,
56  int detectPersons,
57  const bool needLocking) :
58  MSMoveReminder(id, lane),
59  MSDetectorFileOutput(id, vTypes, detectPersons),
60  myPosition(positionInMeters),
61  myNeedLock(needLocking || MSGlobals::gNumSimThreads > 1),
62  myLastLeaveTime(SIMTIME),
63  myVehicleDataCont(),
64  myVehiclesOnDet() {
65  assert(myPosition >= 0 && myPosition <= myLane->getLength());
66  reset();
67 }
68 
69 
71 }
72 
73 
74 void
76 #ifdef HAVE_FOX
77  ScopedLocker<> lock(myNotificationMutex, myNeedLock);
78 #endif
81  myVehicleDataCont.clear();
82 }
83 
84 
85 bool
86 MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
87  // vehicles must be kept if the "inductionloop" wants to detect passeengers
88  if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
89  return false;
90  }
91  if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
93  return false;
94  }
95  if (veh.getPositionOnLane() >= myPosition) {
96 #ifdef HAVE_FOX
97  ScopedLocker<> lock(myNotificationMutex, myNeedLock);
98 #endif
99  myVehiclesOnDet[&veh] = SIMTIME;
101  }
102  }
103  return true;
104 }
105 
106 
107 bool
109  double newPos, double newSpeed) {
110  if (newPos < myPosition) {
111  // detector not reached yet
112  return true;
113  }
114  if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
115  bool keep = false;
116  MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
117  for (MSTransportable* p : v.getPersons()) {
118  keep = notifyMove(*p, oldPos, newPos, newSpeed);
119  }
120  return keep;
121  }
122 #ifdef HAVE_FOX
123  ScopedLocker<> lock(myNotificationMutex, myNeedLock);
124 #endif
125  const double oldSpeed = veh.getPreviousSpeed();
126  if (newPos >= myPosition && oldPos < myPosition) {
127  // entered the detector by move
128  const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
129  myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
131  }
132  double oldBackPos = oldPos - veh.getVehicleType().getLength();
133  double newBackPos = newPos - veh.getVehicleType().getLength();
134  if (newBackPos > myPosition) {
135  // vehicle passed the detector (it may have changed onto this lane somewhere past the detector)
136  // assert(!MSGlobals::gSemiImplicitEulerUpdate || newSpeed > 0 || myVehiclesOnDet.find(&veh) == myVehiclesOnDet.end());
137  // assertion is invalid in case of teleportation
138  if (oldBackPos <= myPosition) {
139  const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
140  if (it != myVehiclesOnDet.end()) {
141  const double entryTime = it->second;
142  const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myPosition, newBackPos, oldSpeed, newSpeed);
143  myVehiclesOnDet.erase(it);
144  assert(entryTime <= leaveTime);
145  myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false));
146  myLastLeaveTime = leaveTime;
147  }
148  } else {
149  // vehicle is already beyond the detector...
150  // This can happen even if it is still registered in myVehiclesOnDet, e.g., after teleport.
151  myVehiclesOnDet.erase(&veh);
152  }
153  return false;
154  }
155  // vehicle stays on the detector
156  return true;
157 }
158 
159 
160 bool
161 MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
162  if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
163  const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
164  notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
165  }
167 #ifdef HAVE_FOX
168  ScopedLocker<> lock(myNotificationMutex, myNeedLock);
169 #endif
170  const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
171  if (it != myVehiclesOnDet.end()) {
172  const double entryTime = it->second;
173  const double leaveTime = SIMTIME + TS;
174  myVehiclesOnDet.erase(it);
175  myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
176  myLastLeaveTime = leaveTime;
177  }
178  return false;
179  }
180  return true;
181 }
182 
183 
184 double
185 MSInductLoop::getSpeed(const int offset) const {
186  const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
187  return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
188 }
189 
190 
191 double
192 MSInductLoop::getVehicleLength(const int offset) const {
193  const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
194  return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
195 }
196 
197 
198 double
200  const SUMOTime tbeg = SIMSTEP - DELTA_T;
201  double occupancy = 0;
202  const double csecond = SIMTIME;
203  for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
204  const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
205  const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
206  occupancy += MIN2(leaveTime - entryTime, TS);
207  }
208  return occupancy / TS * 100.;
209 }
210 
211 
212 double
213 MSInductLoop::getEnteredNumber(const int offset) const {
214  return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
215 }
216 
217 
218 std::vector<std::string>
219 MSInductLoop::getVehicleIDs(const int offset) const {
220  std::vector<std::string> ret;
221  for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
222  ret.push_back(i.idM);
223  }
224  return ret;
225 }
226 
227 
228 double
230  if (myVehiclesOnDet.size() != 0) {
231  // detector is occupied
232  return 0;
233  }
234  return SIMTIME - myLastLeaveTime;
235 }
236 
237 
238 SUMOTime
240  if (myVehiclesOnDet.size() != 0) {
242  }
243  return TIME2STEPS(myLastLeaveTime);
244 }
245 
246 
247 void
249  dev.writeXMLHeader("detector", "det_e1_file.xsd");
250 }
251 
252 
253 void
255  if (dev.isNull()) {
256  reset();
257  return;
258  }
259  const double t(STEPS2TIME(stopTime - startTime));
260  double occupancy = 0.;
261  double speedSum = 0.;
262  double lengthSum = 0.;
263  int contrib = 0;
264  // to approximate the space mean speed
265  double inverseSpeedSum = 0.;
266  for (const VehicleData& vData : myVehicleDataCont) {
267  const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
268  occupancy += MIN2(timeOnDetDuringInterval, t);
269  if (!vData.leftEarlyM) {
270  speedSum += vData.speedM;
271  assert(vData.speedM > 0.);
272  inverseSpeedSum += 1. / vData.speedM;
273  lengthSum += vData.lengthM;
274  contrib++;
275  }
276  }
277  const double flow = (double)contrib / t * 3600.;
278  for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
279  occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
280  }
281  occupancy *= 100. / t;
282  const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
283  const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
284  const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
285  dev.openTag(SUMO_TAG_INTERVAL).writeAttr(SUMO_ATTR_BEGIN, STEPS2TIME(startTime)).writeAttr(SUMO_ATTR_END, STEPS2TIME(stopTime));
286  dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
287  dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
288  dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
289  reset();
290 }
291 
292 
293 void
295  if (myDetectPersons == (int)PersonMode::NONE) {
296  return;
297  }
298  if (myLane->hasPedestrians()) {
299  for (MSTransportable* p : myLane->getEdge().getPersons()) {
300  if (p->getLane() != myLane || !vehicleApplies(*p)) {
301  continue;
302  }
303  notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
304  }
305  }
306 }
307 
308 
309 void
311  if (personApplies(*p, dir)) {
312  const double newSpeed = p->getSpeed();
313  const double newPos = (dir == MSPModel::FORWARD
314  ? pos
315  // position relative to detector
316  : myPosition - (pos - myPosition));
317  const double oldPos = newPos - SPEED2DIST(newSpeed);
318  if (oldPos - p->getVehicleType().getLength() <= myPosition) {
319  notifyMove(*p, oldPos, newPos, newSpeed);
320  }
321  }
322 }
323 
324 
325 std::vector<MSInductLoop::VehicleData>
326 MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy) const {
327 #ifdef HAVE_FOX
328  ScopedLocker<> lock(myNotificationMutex, myNeedLock);
329 #endif
330  const double t = STEPS2TIME(tMS);
331  std::vector<VehicleData> ret;
332  for (const VehicleData& i : myVehicleDataCont) {
333  if (includeEarly || !i.leftEarlyM) {
334  if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
335  ret.push_back(i);
336  }
337  }
338  }
339  for (const VehicleData& i : myLastVehicleDataCont) {
340  if (includeEarly || !i.leftEarlyM) {
341  if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
342  ret.push_back(i);
343  }
344  }
345  }
346  for (const auto& i : myVehiclesOnDet) {
347  if (i.second >= t || leaveTime || forOccupancy) { // no need to check leave time, they are still on the detector
348  SUMOTrafficObject* const v = i.first;
349  VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
350  d.speedM = v->getSpeed();
351  ret.push_back(d);
352  }
353  }
354  return ret;
355 }
356 
357 
359  double leaveTimestep, const bool leftEarly)
360  : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
361  speedM(v.getVehicleType().getLength() / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
362  leftEarlyM(leftEarly) {}
363 
364 
365 void
368  myLastVehicleDataCont.clear();
369  myVehicleDataCont.clear();
370  myVehiclesOnDet.clear();
371 }
372 
373 /****************************************************************************/
#define HAS_NOT_LEFT_DETECTOR
SUMOTime DELTA_T
Definition: SUMOTime.cpp:37
#define STEPS2TIME(x)
Definition: SUMOTime.h:53
#define SPEED2DIST(x)
Definition: SUMOTime.h:43
#define SIMSTEP
Definition: SUMOTime.h:59
#define TS
Definition: SUMOTime.h:40
#define SIMTIME
Definition: SUMOTime.h:60
#define TIME2STEPS(x)
Definition: SUMOTime.h:55
long long int SUMOTime
Definition: SUMOTime.h:32
@ 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:74
T MAX2(T a, T b)
Definition: StdDefs.h:80
The base class for microscopic and mesoscopic vehicles.
Definition: MSBaseVehicle.h:51
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:600
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 * > & getPersons() const
Returns this edge's persons set.
Definition: MSEdge.h:198
static double lengthSum(double sumSoFar, const MSInductLoop::VehicleData &data)
Adds up VehicleData::lengthM.
Definition: MSInductLoop.h:312
double getOccupancy() const
Returns the current occupancy.
int myEnteredVehicleNumber
The number of entered vehicles.
Definition: MSInductLoop.h:331
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:307
VehicleDataCont myVehicleDataCont
Data of vehicles that have completely passed the detector.
Definition: MSInductLoop.h:337
MSInductLoop(const std::string &id, MSLane *const lane, double positionInMeters, const std::string &vTypes, int detectPersons, const bool needLocking)
Constructor.
double getSpeed(const int offset) const
Returns the speed of the vehicle on the detector.
virtual void clearState()
Remove all vehicles before quick-loading state.
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:322
VehicleDataCont myLastVehicleDataCont
Data of vehicles that have completely passed the detector in the last time interval.
Definition: MSInductLoop.h:340
~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 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:328
const bool myNeedLock
whether internals need to be guarded against concurrent access (GUI or multi threading)
Definition: MSInductLoop.h:325
bool notifyEnter(SUMOTrafficObject &veh, Notification reason, const MSLane *enteredLane=0)
Checks whether the reminder is activated by a vehicle entering the lane.
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:343
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:82
bool hasPedestrians() const
whether the lane has pedestrians on it
Definition: MSLane.cpp:3971
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:674
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:174
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:318
static const int BACKWARD
Definition: MSPModel.h:109
static const int FORWARD
Definition: MSPModel.h:108
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 & 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
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:248
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 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 const MSVehicleType & getVehicleType() const =0
Returns the object's "vehicle" type.
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:256
VehicleData(const SUMOTrafficObject &v, double entryTimestep, double leaveTimestep, const bool leftEarly)
Constructor.
double speedM
Speed of the vehicle in [m/s].
Definition: MSInductLoop.h:277