A Discrete-Event Network Simulator
API
random-walk-2d-outdoor-mobility-model.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2006,2007 INRIA
3  * Copyright (c) 2019 University of Padova
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
19  * Author: Michele Polese <michele.polese@gmail.com>
20  */
22 
23 #include "ns3/building-list.h"
24 #include "ns3/building.h"
25 #include "ns3/double.h"
26 #include "ns3/enum.h"
27 #include "ns3/log.h"
28 #include "ns3/pointer.h"
29 #include "ns3/simulator.h"
30 #include "ns3/string.h"
31 #include "ns3/uinteger.h"
32 
33 #include <cmath>
34 
35 namespace ns3
36 {
37 
38 NS_LOG_COMPONENT_DEFINE("RandomWalk2dOutdoor");
39 
40 NS_OBJECT_ENSURE_REGISTERED(RandomWalk2dOutdoorMobilityModel);
41 
42 TypeId
44 {
45  static TypeId tid =
46  TypeId("ns3::RandomWalk2dOutdoorMobilityModel")
48  .SetGroupName("Mobility")
49  .AddConstructor<RandomWalk2dOutdoorMobilityModel>()
50  .AddAttribute("Bounds",
51  "Bounds of the area to cruise.",
52  RectangleValue(Rectangle(0.0, 100.0, 0.0, 100.0)),
53  MakeRectangleAccessor(&RandomWalk2dOutdoorMobilityModel::m_bounds),
54  MakeRectangleChecker())
55  .AddAttribute("Time",
56  "Change current direction and speed after moving for this delay.",
57  TimeValue(Seconds(20.0)),
60  .AddAttribute("Distance",
61  "Change current direction and speed after moving for this distance.",
62  DoubleValue(30.0),
64  MakeDoubleChecker<double>())
65  .AddAttribute("Mode",
66  "The mode indicates the condition used to "
67  "change the current speed and direction",
71  "Distance",
73  "Time"))
74  .AddAttribute("Direction",
75  "A random variable used to pick the direction (radians).",
76  StringValue("ns3::UniformRandomVariable[Min=0.0|Max=6.283184]"),
78  MakePointerChecker<RandomVariableStream>())
79  .AddAttribute(
80  "Speed",
81  "A random variable used to pick the speed (m/s)."
82  "The default value is taken from Figure 1 of the paper"
83  "Henderson, L.F., 1971. The statistics of crowd fluids. nature, 229(5284), p.381.",
84  StringValue("ns3::NormalRandomVariable[Mean=1.53|Variance=0.040401]"),
86  MakePointerChecker<RandomVariableStream>())
87  .AddAttribute("Tolerance",
88  "Tolerance for the intersection point with buildings (m)."
89  "It represents a small distance from where the building limit"
90  "is actually placed, for example to represent a sidewalk.",
91  DoubleValue(1e-6),
93  MakeDoubleChecker<double>())
94  .AddAttribute("MaxIterations",
95  "Maximum number of attempts to find an alternative next position"
96  "if the original one is inside a building.",
97  UintegerValue(100),
99  MakeUintegerChecker<uint32_t>());
100  return tid;
101 }
102 
103 void
105 {
108 }
109 
110 void
112 {
113  m_helper.Update();
114  double speed = m_speed->GetValue();
115  double direction = m_direction->GetValue();
116  Vector vector(std::cos(direction) * speed, std::sin(direction) * speed, 0.0);
117  m_helper.SetVelocity(vector);
118  m_helper.Unpause();
119 
120  Time delayLeft;
122  {
123  delayLeft = m_modeTime;
124  }
125  else
126  {
127  delayLeft = Seconds(m_modeDistance / speed);
128  }
129  DoWalk(delayLeft);
130 }
131 
132 void
134 {
135  NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
136 
137  Vector position = m_helper.GetCurrentPosition();
138  Vector speed = m_helper.GetVelocity();
139  Vector nextPosition = position;
140  nextPosition.x += speed.x * delayLeft.GetSeconds();
141  nextPosition.y += speed.y * delayLeft.GetSeconds();
142  m_event.Cancel();
143 
144  // check if the nextPosition is inside a building, or if the line
145  // from position to the next position intersects a building
146  auto outdoorBuilding = IsLineClearOfBuildings(position, nextPosition);
147  bool outdoor = std::get<0>(outdoorBuilding);
148  Ptr<Building> building = std::get<1>(outdoorBuilding);
149 
150  if (m_bounds.IsInside(nextPosition))
151  {
152  if (outdoor)
153  {
154  m_event = Simulator::Schedule(delayLeft,
156  this);
157  }
158  else
159  {
160  NS_LOG_LOGIC("NextPosition would lead into a building");
161  nextPosition =
162  CalculateIntersectionFromOutside(position, nextPosition, building->GetBoundaries());
163  Time delay = Seconds((nextPosition.x - position.x) / speed.x);
166  this,
167  delayLeft - delay,
168  nextPosition);
169  }
170  }
171  else
172  {
173  NS_LOG_LOGIC("Out of bounding box");
174  nextPosition = m_bounds.CalculateIntersection(position, speed);
175  // check that this nextPosition is outdoor
176  auto outdoorBuilding = IsLineClearOfBuildings(position, nextPosition);
177  bool outdoor = std::get<0>(outdoorBuilding);
178  Ptr<Building> building = std::get<1>(outdoorBuilding);
179 
180  if (outdoor)
181  {
182  Time delay = Seconds((nextPosition.x - position.x) / speed.x);
185  this,
186  delayLeft - delay);
187  }
188  else
189  {
190  NS_LOG_LOGIC("NextPosition would lead into a building");
191  nextPosition =
192  CalculateIntersectionFromOutside(position, nextPosition, building->GetBoundaries());
193  Time delay = Seconds((nextPosition.x - position.x) / speed.x);
196  this,
197  delayLeft - delay,
198  nextPosition);
199  }
200  }
201  NS_LOG_LOGIC("Position " << position << " NextPosition " << nextPosition);
202 
203  // store the previous position
204  m_prevPosition = position;
206 }
207 
208 std::pair<bool, Ptr<Building>>
210  Vector nextPosition) const
211 {
212  NS_LOG_FUNCTION(this << currentPosition << nextPosition);
213 
214  bool intersectBuilding = false;
215  double minIntersectionDistance = std::numeric_limits<double>::max();
216  Ptr<Building> minIntersectionDistanceBuilding;
217 
218  for (BuildingList::Iterator bit = BuildingList::Begin(); bit != BuildingList::End(); ++bit)
219  {
220  // check if this building intersects the line between the current and next positions
221  // this checks also if the next position is inside the building
222  if ((*bit)->IsIntersect(currentPosition, nextPosition))
223  {
224  NS_LOG_LOGIC("Building " << (*bit)->GetBoundaries() << " intersects the line between "
225  << currentPosition << " and " << nextPosition);
226  auto intersection = CalculateIntersectionFromOutside(currentPosition,
227  nextPosition,
228  (*bit)->GetBoundaries());
229  double distance = CalculateDistance(intersection, currentPosition);
230  intersectBuilding = true;
231  if (distance < minIntersectionDistance)
232  {
233  minIntersectionDistance = distance;
234  minIntersectionDistanceBuilding = (*bit);
235  }
236  }
237  }
238 
239  return std::make_pair(!intersectBuilding, minIntersectionDistanceBuilding);
240 }
241 
242 Vector
244  const Vector& next,
245  Box boundaries) const
246 {
247  NS_LOG_FUNCTION(this << " current " << current << " next " << next);
248  bool inside = boundaries.IsInside(current);
249  NS_ASSERT(!inside);
250 
251  // get the closest side
252  Rectangle rect = Rectangle(boundaries.xMin, boundaries.xMax, boundaries.yMin, boundaries.yMax);
253  NS_LOG_INFO("rect " << rect);
254  Rectangle::Side closestSide = rect.GetClosestSide(current);
255 
256  double xIntersect = 0;
257  double yIntersect = 0;
258 
259  switch (closestSide)
260  {
261  case Rectangle::RIGHT:
262  NS_LOG_INFO("The closest side is RIGHT");
263  NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
264  xIntersect = boundaries.xMax + m_epsilon;
265  yIntersect =
266  (next.y - current.y) / (next.x - current.x) * (xIntersect - current.x) + current.y;
267  break;
268  case Rectangle::LEFT:
269  NS_LOG_INFO("The closest side is LEFT");
270  xIntersect = boundaries.xMin - m_epsilon;
271  NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
272  yIntersect =
273  (next.y - current.y) / (next.x - current.x) * (xIntersect - current.x) + current.y;
274  break;
275  case Rectangle::TOP:
276  NS_LOG_INFO("The closest side is TOP");
277  yIntersect = boundaries.yMax + m_epsilon;
278  NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
279  xIntersect =
280  (next.x - current.x) / (next.y - current.y) * (yIntersect - current.y) + current.x;
281  break;
282  case Rectangle::BOTTOM:
283  NS_LOG_INFO("The closest side is BOTTOM");
284  yIntersect = boundaries.yMin - m_epsilon;
285  NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
286  xIntersect =
287  (next.x - current.x) / (next.y - current.y) * (yIntersect - current.y) + current.x;
288  break;
289  }
290  NS_LOG_INFO("xIntersect " << xIntersect << " yIntersect " << yIntersect);
291  return Vector(xIntersect, yIntersect, 0);
292 }
293 
294 void
296 {
297  NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
299  Vector position = m_helper.GetCurrentPosition();
300  Vector speed = m_helper.GetVelocity();
301  switch (m_bounds.GetClosestSide(position))
302  {
303  case Rectangle::RIGHT:
304  NS_LOG_INFO("The closest side is RIGHT");
305  case Rectangle::LEFT:
306  NS_LOG_INFO("The closest side is LEFT");
307  speed.x = -speed.x;
308  break;
309  case Rectangle::TOP:
310  NS_LOG_INFO("The closest side is TOP");
311  case Rectangle::BOTTOM:
312  NS_LOG_INFO("The closest side is BOTTOM");
313  speed.y = -speed.y;
314  break;
315  }
316  m_helper.SetVelocity(speed);
317  m_helper.Unpause();
318  DoWalk(delayLeft);
319 }
320 
321 void
322 RandomWalk2dOutdoorMobilityModel::AvoidBuilding(Time delayLeft, Vector intersectPosition)
323 {
324  NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
325  m_helper.Update();
326 
327  bool nextWouldBeInside = true;
328  uint32_t iter = 0;
329 
330  while (nextWouldBeInside && iter < m_maxIter)
331  {
332  NS_LOG_INFO("The next position would be inside a building, compute an alternative");
333  iter++;
334  double speed = m_speed->GetValue();
335  double direction = m_direction->GetValue();
336  Vector velocityVector(std::cos(direction) * speed, std::sin(direction) * speed, 0.0);
337  m_helper.SetVelocity(velocityVector);
338 
339  Vector nextPosition = intersectPosition;
340  nextPosition.x += velocityVector.x * delayLeft.GetSeconds();
341  nextPosition.y += velocityVector.y * delayLeft.GetSeconds();
342 
343  // check if this is inside the current buildingBox
344  auto outdoorBuilding = IsLineClearOfBuildings(intersectPosition, nextPosition);
345  bool outdoor = std::get<0>(outdoorBuilding);
346 
347  if (!outdoor)
348  {
349  NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
350  << nextPosition << " " << outdoor << " building "
351  << std::get<1>(outdoorBuilding)->GetBoundaries());
352  }
353  else
354  {
355  NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
356  << nextPosition << " " << outdoor);
357  }
358 
359  if (outdoor && m_bounds.IsInside(nextPosition))
360  {
361  nextWouldBeInside = false;
362  }
363  }
364 
365  // after m_maxIter iterations, the positions tested are all inside
366  // to avoid increasing m_maxIter too much, it is possible to perform a step back
367  // to the previous position and continue from there
368  if (iter >= m_maxIter)
369  {
370  NS_LOG_INFO("Move back to the previous position");
371 
372  // compute the difference between the previous position and the intersection
373  Vector posDiff = m_prevPosition - intersectPosition;
374  // compute the distance
375  double distance = CalculateDistance(m_prevPosition, intersectPosition);
376  double speed = distance / delayLeft.GetSeconds(); // compute the speed
377 
378  NS_LOG_LOGIC("prev " << m_prevPosition << " intersectPosition " << intersectPosition
379  << " diff " << posDiff << " dist " << distance);
380 
381  Vector velocityVector(posDiff.x / distance * speed, posDiff.y / distance * speed, 0.0);
382  m_helper.SetVelocity(velocityVector);
383 
384  Vector nextPosition = intersectPosition;
385  nextPosition.x += velocityVector.x * delayLeft.GetSeconds();
386  nextPosition.y += velocityVector.y * delayLeft.GetSeconds();
387 
388  // check if the path is clear
389  auto outdoorBuilding = IsLineClearOfBuildings(intersectPosition, nextPosition);
390  bool outdoor = std::get<0>(outdoorBuilding);
391  if (!outdoor)
392  {
393  NS_LOG_LOGIC("The position is still inside after "
394  << m_maxIter + 1 << " iterations, loop intersect " << intersectPosition
395  << " nextPosition " << nextPosition << " " << outdoor << " building "
396  << std::get<1>(outdoorBuilding)->GetBoundaries());
397  // This error may be due to buildings being attached to one another, or to the boundary
398  // of the scenario.
400  "Not able to find an outdoor position. Try to increase the attribute MaxIterations "
401  "and check the position of the buildings in the scenario.");
402  }
403  else
404  {
405  NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
406  << nextPosition << " " << outdoor);
407  }
408  }
409 
410  m_helper.Unpause();
411 
412  DoWalk(delayLeft);
413 }
414 
415 void
417 {
418  // chain up
420 }
421 
422 Vector
424 {
426  return m_helper.GetCurrentPosition();
427 }
428 
429 void
431 {
432  NS_ASSERT(m_bounds.IsInside(position));
433  m_helper.SetPosition(position);
436 }
437 
438 Vector
440 {
441  return m_helper.GetVelocity();
442 }
443 
444 int64_t
446 {
447  m_speed->SetStream(stream);
448  m_direction->SetStream(stream + 1);
449  return 2;
450 }
451 
452 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:43
a 3d box
Definition: box.h:35
double yMax
The y coordinate of the top bound of the box.
Definition: box.h:116
bool IsInside(const Vector &position) const
Definition: box.cc:54
double xMin
The x coordinate of the left bound of the box.
Definition: box.h:110
double yMin
The y coordinate of the bottom bound of the box.
Definition: box.h:114
double xMax
The x coordinate of the right bound of the box.
Definition: box.h:112
std::vector< Ptr< Building > >::const_iterator Iterator
Const Iterator.
Definition: building-list.h:41
static Iterator End()
static Iterator Begin()
Vector GetCurrentPosition() const
Get current position vector.
Vector GetVelocity() const
Get velocity; if paused, will return a zero vector.
void Update() const
Update position, if not paused, from last position and time of last update.
void UpdateWithBounds(const Rectangle &rectangle) const
Update position, if not paused, from last position and time of last update.
void Unpause()
Resume mobility from current position at current velocity.
void SetPosition(const Vector &position)
Set position vector.
void SetVelocity(const Vector &vel)
Set new velocity vector.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Hold variables of type enum.
Definition: enum.h:56
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:55
Keep track of the current position and velocity of an object.
void NotifyCourseChange() const
Must be invoked by subclasses when the course of the position changes to notify course change listene...
virtual void DoInitialize()
Initialize() implementation.
Definition: object.cc:360
virtual void DoDispose()
Destructor implementation.
Definition: object.cc:353
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
virtual double GetValue()=0
Get the next random value drawn from the distribution.
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
2D random walk mobility model which avoids buildings.
void AvoidBuilding(Time delayLeft, Vector intersectPosition)
Avoid a building.
void DoInitializePrivate()
Perform initialization of the object before MobilityModel::DoInitialize ()
int64_t DoAssignStreams(int64_t) override
The default implementation does nothing but return the passed-in parameter.
double m_modeDistance
Change direction and speed after this distance.
ConstantVelocityHelper m_helper
helper for this object
Vector m_prevPosition
Store the previous position in case a step back is needed.
void Rebound(Time timeLeft)
Performs the rebound of the node if it reaches a boundary.
uint32_t m_maxIter
Maximum number of tries to find the next position.
Vector CalculateIntersectionFromOutside(const Vector &current, const Vector &next, const Box boundaries) const
Compute the intersecting point of the box represented by boundaries and the line between current and ...
Ptr< RandomVariableStream > m_direction
rv for picking direction
void DoDispose() override
Destructor implementation.
double m_epsilon
Tolerance for the intersection point with buildings.
void DoWalk(Time delayLeft)
Walk according to position and velocity, until distance is reached, time is reached,...
std::pair< bool, Ptr< Building > > IsLineClearOfBuildings(Vector currentPosition, Vector nextPosition) const
Check if there is a building between two positions (or if the nextPosition is inside a building).
static TypeId GetTypeId()
Register this type with the TypeId system.
void DoInitialize() override
Initialize() implementation.
Time m_modeTime
Change current direction and speed after this delay.
Ptr< RandomVariableStream > m_speed
rv for picking speed
a 2d rectangle
Definition: rectangle.h:35
Side GetClosestSide(const Vector &position) const
Definition: rectangle.cc:56
bool IsInside(const Vector &position) const
Definition: rectangle.cc:49
Vector CalculateIntersection(const Vector &current, const Vector &speed) const
Definition: rectangle.cc:178
Side
enum for naming sides
Definition: rectangle.h:41
AttributeValue implementation for Rectangle.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition: simulator.h:606
static void Remove(const EventId &id)
Remove an event from the event list.
Definition: simulator.cc:266
Hold variables of type string.
Definition: string.h:56
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:402
AttributeValue implementation for Time.
Definition: nstime.h:1423
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
Hold an unsigned integer type.
Definition: uinteger.h:45
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: double.h:43
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: enum.h:205
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: pointer.h:231
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: nstime.h:1424
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: uinteger.h:46
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeEnumChecker(int v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition: enum.h:163
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535
double CalculateDistance(const Vector3D &a, const Vector3D &b)
Definition: vector.cc:109