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