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
34namespace ns3 {
35
36NS_LOG_COMPONENT_DEFINE ("RandomWalk2dOutdoor");
37
38NS_OBJECT_ENSURE_REGISTERED (RandomWalk2dOutdoorMobilityModel);
39
40TypeId
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)),
50 MakeRectangleAccessor (&RandomWalk2dOutdoorMobilityModel::m_bounds),
51 MakeRectangleChecker ())
52 .AddAttribute ("Time",
53 "Change current direction and speed after moving for this delay.",
54 TimeValue (Seconds (20.0)),
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
98void
100{
103}
104
105void
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
129void
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
197std::pair<bool, Ptr<Building> >
198RandomWalk2dOutdoorMobilityModel::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
230Vector
231RandomWalk2dOutdoorMobilityModel::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;
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
280void
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");
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
307void
308RandomWalk2dOutdoorMobilityModel::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
404void
406{
407 // chain up
409}
410Vector
412{
415}
416void
418{
419 NS_ASSERT (m_bounds.IsInside (position));
420 m_helper.SetPosition (position);
423}
424Vector
426{
427 return m_helper.GetVelocity ();
428}
429int64_t
431{
432 m_speed->SetStream (stream);
433 m_direction->SetStream (stream + 1);
434 return 2;
435}
436
437
438} // 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:40
static Iterator End(void)
static Iterator Begin(void)
void Unpause(void)
Resume mobility from current position at current velocity.
Vector GetVelocity(void) const
Get velocity; if paused, will return a zero vector.
void UpdateWithBounds(const Rectangle &rectangle) const
Update position, if not paused, from last position and time of last update.
void SetPosition(const Vector &position)
Set position vector.
void SetVelocity(const Vector &vel)
Set new velocity vector.
void Update(void) const
Update position, if not paused, from last position and time of last update.
Vector GetCurrentPosition(void) const
Get current position vector.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:41
Hold variables of type enum.
Definition: enum.h:55
void Cancel(void)
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:53
Keep track of the current position and velocity of an object.
void NotifyCourseChange(void) const
Must be invoked by subclasses when the course of the position changes to notify course change listene...
virtual void DoDispose(void)
Destructor implementation.
Definition: object.cc:346
virtual void DoInitialize(void)
Initialize() implementation.
Definition: object.cc:353
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:74
virtual double GetValue(void)=0
Get the next random value as a double 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.
double m_modeDistance
Change direction and speed after this distance.
ConstantVelocityHelper m_helper
helper for this object
void DoInitializePrivate(void)
Perform initialization of the object before MobilityModel::DoInitialize ()
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
virtual void DoInitialize(void)
Initialize() implementation.
virtual void DoDispose(void)
Destructor implementation.
double m_epsilon
Tolerance for the intersection point with buildings.
virtual int64_t DoAssignStreams(int64_t)
The default implementation does nothing but return the passed-in parameter.
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).
enum Mode m_mode
whether in time or distance mode
Time m_modeTime
Change current direction and speed after this delay.
static TypeId GetTypeId(void)
Register this type with the TypeId system.
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:48
Vector CalculateIntersection(const Vector &current, const Vector &speed) const
Definition: rectangle.cc:177
Side
enum for naming sides
Definition: rectangle.h:40
AttributeValue implementation for Rectangle.
static EventId Schedule(Time const &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:555
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition: simulator.h:586
static void Remove(const EventId &id)
Remove an event from the event list.
Definition: simulator.cc:258
Hold variables of type string.
Definition: string.h:41
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
double GetSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:379
AttributeValue implementation for Time.
Definition: nstime.h:1308
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:922
Hold an unsigned integer type.
Definition: uinteger.h:44
#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
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:42
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Definition: enum.h:205
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Definition: pointer.h:227
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1309
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:45
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
#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:205
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
#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:281
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1244
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:522
double CalculateDistance(const Vector3D &a, const Vector3D &b)
Definition: vector.cc:105
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:162