A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
8 * Author: Michele Polese <michele.polese@gmail.com>
9 */
11
12#include "building-list.h"
13#include "building.h"
14
15#include "ns3/double.h"
16#include "ns3/enum.h"
17#include "ns3/log.h"
18#include "ns3/pointer.h"
19#include "ns3/simulator.h"
20#include "ns3/string.h"
21#include "ns3/uinteger.h"
22
23#include <cmath>
24#include <limits>
25
26namespace ns3
27{
28
29NS_LOG_COMPONENT_DEFINE("RandomWalk2dOutdoor");
30
31NS_OBJECT_ENSURE_REGISTERED(RandomWalk2dOutdoorMobilityModel);
32
33TypeId
35{
36 static TypeId tid =
37 TypeId("ns3::RandomWalk2dOutdoorMobilityModel")
39 .SetGroupName("Mobility")
40 .AddConstructor<RandomWalk2dOutdoorMobilityModel>()
41 .AddAttribute("Bounds",
42 "Bounds of the area to cruise.",
43 RectangleValue(Rectangle(0.0, 100.0, 0.0, 100.0)),
46 .AddAttribute("Time",
47 "Change current direction and speed after moving for this delay.",
48 TimeValue(Seconds(20.0)),
51 .AddAttribute("Distance",
52 "Change current direction and speed after moving for this distance.",
53 DoubleValue(30.0),
56 .AddAttribute("Mode",
57 "The mode indicates the condition used to "
58 "change the current speed and direction",
62 "Distance",
64 "Time"))
65 .AddAttribute("Direction",
66 "A random variable used to pick the direction (radians).",
67 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=6.283184]"),
70 .AddAttribute(
71 "Speed",
72 "A random variable used to pick the speed (m/s)."
73 "The default value is taken from Figure 1 of the paper"
74 "Henderson, L.F., 1971. The statistics of crowd fluids. nature, 229(5284), p.381.",
75 StringValue("ns3::NormalRandomVariable[Mean=1.53|Variance=0.040401]"),
78 .AddAttribute("Tolerance",
79 "Tolerance for the intersection point with buildings (m)."
80 "It represents a small distance from where the building limit"
81 "is actually placed, for example to represent a sidewalk.",
82 DoubleValue(1e-6),
85 .AddAttribute("MaxIterations",
86 "Maximum number of attempts to find an alternative next position"
87 "if the original one is inside a building.",
88 UintegerValue(100),
91 return tid;
92}
93
94void
100
101void
103{
105 double speed = m_speed->GetValue();
106 double direction = m_direction->GetValue();
107 Vector velocity(std::cos(direction) * speed, std::sin(direction) * speed, 0.0);
108 m_helper.SetVelocity(velocity);
110
111 Time delayLeft;
113 {
114 delayLeft = m_modeTime;
115 }
116 else
117 {
118 delayLeft = Seconds(m_modeDistance / speed);
119 }
120 DoWalk(delayLeft);
121}
122
123void
125{
126 if (delayLeft.IsNegative())
127 {
128 NS_LOG_INFO(this << " Ran out of time");
129 return;
130 }
131 NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
132
133 Vector position = m_helper.GetCurrentPosition();
134 Vector velocity = m_helper.GetVelocity();
135 Vector nextPosition = position;
136 nextPosition.x += velocity.x * delayLeft.GetSeconds();
137 nextPosition.y += velocity.y * delayLeft.GetSeconds();
138 m_event.Cancel();
139
140 // check if the nextPosition is inside a building, or if the line
141 // from position to the next position intersects a building
142 auto outdoorBuilding = IsLineClearOfBuildings(position, nextPosition);
143 bool outdoor = std::get<0>(outdoorBuilding);
144 Ptr<Building> building = std::get<1>(outdoorBuilding);
145
146 if (m_bounds.IsInside(nextPosition))
147 {
148 if (outdoor)
149 {
150 m_event = Simulator::Schedule(delayLeft,
152 this);
153 }
154 else
155 {
156 NS_LOG_LOGIC("NextPosition would lead into a building");
157 nextPosition =
158 CalculateIntersectionFromOutside(position, nextPosition, building->GetBoundaries());
159
160 double delaySecondsX = std::numeric_limits<double>::max();
161 double delaySecondsY = std::numeric_limits<double>::max();
162 if (velocity.x != 0)
163 {
164 delaySecondsX = std::abs((nextPosition.x - position.x) / velocity.x);
165 }
166 if (velocity.y != 0)
167 {
168 delaySecondsY = std::abs((nextPosition.y - position.y) / velocity.y);
169 }
170 Time delay = Seconds(std::min(delaySecondsX, delaySecondsY));
173 this,
174 delayLeft - delay,
175 nextPosition);
176 }
177 }
178 else
179 {
180 NS_LOG_LOGIC("Out of bounding box");
181 nextPosition = m_bounds.CalculateIntersection(position, velocity);
182 // check that this nextPosition is outdoor
183 auto outdoorBuilding = IsLineClearOfBuildings(position, nextPosition);
184 bool outdoor = std::get<0>(outdoorBuilding);
185 Ptr<Building> building = std::get<1>(outdoorBuilding);
186
187 if (outdoor)
188 {
189 double delaySeconds = std::numeric_limits<double>::max();
190 if (velocity.x != 0)
191 {
192 delaySeconds =
193 std::min(delaySeconds, std::abs((nextPosition.x - position.x) / velocity.x));
194 }
195 else if (velocity.y != 0)
196 {
197 delaySeconds =
198 std::min(delaySeconds, std::abs((nextPosition.y - position.y) / velocity.y));
199 }
200 else
201 {
202 NS_ABORT_MSG("RandomWalk2dOutdoorMobilityModel::DoWalk: unable to calculate the "
203 "rebound time "
204 "(the node is stationary).");
205 }
206 Time delay = Seconds(delaySeconds);
209 this,
210 delayLeft - delay);
211 }
212 else
213 {
214 NS_LOG_LOGIC("NextPosition would lead into a building");
215 nextPosition =
216 CalculateIntersectionFromOutside(position, nextPosition, building->GetBoundaries());
217
218 double delaySecondsX = std::numeric_limits<double>::max();
219 double delaySecondsY = std::numeric_limits<double>::max();
220 if (velocity.x != 0)
221 {
222 delaySecondsX =
223 std::min(delaySecondsX, std::abs((nextPosition.x - position.x) / velocity.x));
224 }
225 if (velocity.y != 0)
226 {
227 delaySecondsY =
228 std::min(delaySecondsY, std::abs((nextPosition.y - position.y) / velocity.y));
229 }
230 if (delaySecondsX == std::numeric_limits<double>::max() &&
231 delaySecondsY == std::numeric_limits<double>::max())
232 {
233 NS_ABORT_MSG("RandomWalk2dOutdoorMobilityModel::DoWalk: unable to calculate the "
234 "rebound time "
235 "(the node is stationary).");
236 }
237
238 Time delay = Seconds(std::min(delaySecondsX, delaySecondsY));
241 this,
242 delayLeft - delay,
243 nextPosition);
244 }
245 }
246 NS_LOG_LOGIC("Position " << position << " NextPosition " << nextPosition);
247
248 // store the previous position
249 m_prevPosition = position;
251}
252
253std::pair<bool, Ptr<Building>>
255 Vector nextPosition) const
256{
257 NS_LOG_FUNCTION(this << currentPosition << nextPosition);
258
259 bool intersectBuilding = false;
260 double minIntersectionDistance = std::numeric_limits<double>::max();
261 Ptr<Building> minIntersectionDistanceBuilding;
262
263 for (auto bit = BuildingList::Begin(); bit != BuildingList::End(); ++bit)
264 {
265 // check if this building intersects the line between the current and next positions
266 // this checks also if the next position is inside the building
267 if ((*bit)->IsIntersect(currentPosition, nextPosition))
268 {
269 NS_LOG_LOGIC("Building " << (*bit)->GetBoundaries() << " intersects the line between "
270 << currentPosition << " and " << nextPosition);
271 auto intersection = CalculateIntersectionFromOutside(currentPosition,
272 nextPosition,
273 (*bit)->GetBoundaries());
274 double distance = CalculateDistance(intersection, currentPosition);
275 intersectBuilding = true;
276 if (distance < minIntersectionDistance)
277 {
278 minIntersectionDistance = distance;
279 minIntersectionDistanceBuilding = (*bit);
280 }
281 }
282 }
283
284 return std::make_pair(!intersectBuilding, minIntersectionDistanceBuilding);
285}
286
287Vector
289 const Vector& next,
290 Box boundaries) const
291{
292 NS_LOG_FUNCTION(this << " current " << current << " next " << next);
293 bool inside = boundaries.IsInside(current);
294 NS_ASSERT(!inside);
295
296 // get the closest side
297 Rectangle rect = Rectangle(boundaries.xMin, boundaries.xMax, boundaries.yMin, boundaries.yMax);
298 NS_LOG_DEBUG("rect " << rect);
299 Rectangle::Side closestSide = rect.GetClosestSideOrCorner(current);
300
301 double xIntersect = 0;
302 double yIntersect = 0;
303
304 switch (closestSide)
305 {
307 NS_LOG_DEBUG("The closest side is RIGHT");
308 xIntersect = boundaries.xMax + m_epsilon;
309 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
310 yIntersect =
311 (next.y - current.y) / (next.x - current.x) * (xIntersect - current.x) + current.y;
312 break;
314 NS_LOG_DEBUG("The closest side is LEFT");
315 xIntersect = boundaries.xMin - m_epsilon;
316 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
317 yIntersect =
318 (next.y - current.y) / (next.x - current.x) * (xIntersect - current.x) + current.y;
319 break;
321 NS_LOG_DEBUG("The closest side is TOP");
322 yIntersect = boundaries.yMax + m_epsilon;
323 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
324 xIntersect =
325 (next.x - current.x) / (next.y - current.y) * (yIntersect - current.y) + current.x;
326 break;
328 NS_LOG_DEBUG("The closest side is BOTTOM");
329 yIntersect = boundaries.yMin - m_epsilon;
330 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
331 xIntersect =
332 (next.x - current.x) / (next.y - current.y) * (yIntersect - current.y) + current.x;
333 break;
335 NS_LOG_DEBUG("The closest side is TOPRIGHT");
336 xIntersect = boundaries.xMax + m_epsilon;
337 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
338 yIntersect = boundaries.yMax + m_epsilon;
339 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
340 break;
342 NS_LOG_DEBUG("The closest side is TOPLEFT");
343 xIntersect = boundaries.xMin - m_epsilon;
344 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
345 yIntersect = boundaries.yMax + m_epsilon;
346 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
347 break;
349 NS_LOG_DEBUG("The closest side is BOTTOMRIGHT");
350 xIntersect = boundaries.xMax + m_epsilon;
351 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
352 yIntersect = boundaries.yMin - m_epsilon;
353 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
354 break;
356 NS_LOG_DEBUG("The closest side is BOTTOMLEFT");
357 xIntersect = boundaries.xMin - m_epsilon;
358 NS_ABORT_MSG_IF(next.x - current.x == 0, "x position not updated");
359 yIntersect = boundaries.yMin - m_epsilon;
360 NS_ABORT_MSG_IF(next.y - current.y == 0, "y position not updated");
361 break;
362 }
363 NS_LOG_DEBUG("xIntersect " << xIntersect << " yIntersect " << yIntersect);
364 return Vector(xIntersect, yIntersect, 0);
365}
366
367void
369{
370 NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
372 Vector position = m_helper.GetCurrentPosition();
373 Vector velocity = m_helper.GetVelocity();
374 switch (m_bounds.GetClosestSideOrCorner(position))
375 {
378 NS_LOG_DEBUG("The closest side is RIGHT or LEFT");
379 velocity.x = -velocity.x;
380 break;
383 NS_LOG_DEBUG("The closest side is TOP or BOTTOM");
384 velocity.y = -velocity.y;
385 break;
390 NS_LOG_DEBUG("The closest side is a corner");
391 velocity.x = -velocity.x;
392 velocity.y = -velocity.y;
393 break;
394 }
395 m_helper.SetVelocity(velocity);
397 DoWalk(delayLeft);
398}
399
400void
401RandomWalk2dOutdoorMobilityModel::AvoidBuilding(Time delayLeft, Vector intersectPosition)
402{
403 NS_LOG_FUNCTION(this << delayLeft.GetSeconds());
405
406 bool nextWouldBeInside = true;
407 uint32_t iter = 0;
408
409 while (nextWouldBeInside && iter < m_maxIter)
410 {
411 NS_LOG_INFO("The next position would be inside a building, compute an alternative");
412 iter++;
413 double speed = m_speed->GetValue();
414 double direction = m_direction->GetValue();
415 Vector velocityVector(std::cos(direction) * speed, std::sin(direction) * speed, 0.0);
416 m_helper.SetVelocity(velocityVector);
417
418 Vector nextPosition = intersectPosition;
419 nextPosition.x += velocityVector.x * delayLeft.GetSeconds();
420 nextPosition.y += velocityVector.y * delayLeft.GetSeconds();
421
422 // check if this is inside the current buildingBox
423 auto outdoorBuilding = IsLineClearOfBuildings(intersectPosition, nextPosition);
424 bool outdoor = std::get<0>(outdoorBuilding);
425
426 if (!outdoor)
427 {
428 NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
429 << nextPosition << " " << outdoor << " building "
430 << std::get<1>(outdoorBuilding)->GetBoundaries());
431 }
432 else
433 {
434 NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
435 << nextPosition << " " << outdoor);
436 }
437
438 if (outdoor && m_bounds.IsInside(nextPosition))
439 {
440 nextWouldBeInside = false;
441 }
442 }
443
444 // after m_maxIter iterations, the positions tested are all inside
445 // to avoid increasing m_maxIter too much, it is possible to perform a step back
446 // to the previous position and continue from there
447 if (iter >= m_maxIter)
448 {
449 NS_LOG_INFO("Move back to the previous position");
450
451 // compute the difference between the previous position and the intersection
452 Vector posDiff = m_prevPosition - intersectPosition;
453 // compute the distance
454 double distance = CalculateDistance(m_prevPosition, intersectPosition);
455 double speed = distance / delayLeft.GetSeconds(); // compute the speed
456
457 NS_LOG_LOGIC("prev " << m_prevPosition << " intersectPosition " << intersectPosition
458 << " diff " << posDiff << " dist " << distance);
459
460 Vector velocityVector(posDiff.x / distance * speed, posDiff.y / distance * speed, 0.0);
461 m_helper.SetVelocity(velocityVector);
462
463 Vector nextPosition = intersectPosition;
464 nextPosition.x += velocityVector.x * delayLeft.GetSeconds();
465 nextPosition.y += velocityVector.y * delayLeft.GetSeconds();
466
467 // check if the path is clear
468 auto outdoorBuilding = IsLineClearOfBuildings(intersectPosition, nextPosition);
469 bool outdoor = std::get<0>(outdoorBuilding);
470 if (!outdoor)
471 {
472 NS_LOG_LOGIC("The position is still inside after "
473 << m_maxIter + 1 << " iterations, loop intersect " << intersectPosition
474 << " nextPosition " << nextPosition << " " << outdoor << " building "
475 << std::get<1>(outdoorBuilding)->GetBoundaries());
476 // This error may be due to buildings being attached to one another, or to the boundary
477 // of the scenario.
479 "Not able to find an outdoor position. Try to increase the attribute MaxIterations "
480 "and check the position of the buildings in the scenario.");
481 }
482 else
483 {
484 NS_LOG_LOGIC("inside loop intersect " << intersectPosition << " nextPosition "
485 << nextPosition << " " << outdoor);
486 }
487 }
488
490
491 DoWalk(delayLeft);
492}
493
494void
500
501Vector
507
508void
516
517Vector
522
523int64_t
525{
526 m_speed->SetStream(stream);
527 m_direction->SetStream(stream + 1);
528 return 2;
529}
530
531} // namespace ns3
a 3d box
Definition box.h:24
double yMax
The y coordinate of the top bound of the box.
Definition box.h:105
bool IsInside(const Vector &position) const
Definition box.cc:43
double xMin
The x coordinate of the left bound of the box.
Definition box.h:99
double yMin
The y coordinate of the bottom bound of the box.
Definition box.h:103
double xMax
The x coordinate of the right bound of the box.
Definition box.h:101
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:31
Hold variables of type enum.
Definition enum.h:52
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
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:440
virtual void DoDispose()
Destructor implementation.
Definition object.cc:433
Smart pointer class similar to boost::intrusive_ptr.
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:24
Side GetClosestSideOrCorner(const Vector &position) const
Definition rectangle.cc:53
bool IsInside(const Vector &position) const
Definition rectangle.cc:39
Vector CalculateIntersection(const Vector &current, const Vector &speed) const
Definition rectangle.cc:141
Side
enum for naming sides
Definition rectangle.h:30
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
static void Remove(const EventId &id)
Remove an event from the event list.
Definition simulator.cc:264
Hold variables of type string.
Definition string.h:45
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:392
bool IsNegative() const
Exactly equivalent to t <= 0.
Definition nstime.h:313
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
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:248
Ptr< AttributeChecker > MakePointerChecker()
Create a PointerChecker for a type.
Definition pointer.h:269
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#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:264
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition nstime.h:1396
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition uinteger.h:35
Ptr< const AttributeChecker > MakeDoubleChecker()
Definition double.h:82
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition enum.h:179
Ptr< const AttributeChecker > MakeRectangleChecker()
Definition rectangle.cc:176
Ptr< const AttributeAccessor > MakeRectangleAccessor(T1 a1)
Definition rectangle.h:114
double CalculateDistance(const Vector3D &a, const Vector3D &b)
Definition vector.cc:98
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition double.h:32
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Definition enum.h:221
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1416