5G-LENA nr-v3.3-67-g8257369a
The 5G/NR module for the ns-3 simulator
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
file-scenario-helper.cc
1// Copyright (c) 2020 Lawrence Livermore National Laboratory
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "file-scenario-helper.h"
6
7#include <ns3/core-module.h>
8#include <ns3/mobility-module.h>
9
10#include <cmath> // M_PI (but non-standard)
11#include <sstream>
12
13using namespace ns3;
14
16namespace
17{
18
30void
31PlotDeployment(const Ptr<const ListPositionAllocator>& sitePositioner,
32 const Ptr<const ListPositionAllocator>& utPositioner,
33 const std::size_t numSectors,
34 const double maxRadius,
35 const double effIsd)
36{
37 std::size_t numSites = sitePositioner->GetSize();
38 std::size_t numUts = utPositioner->GetSize();
39 std::size_t numCells = numSites * numSectors;
40
41 NS_ASSERT(numSites);
42 NS_ASSERT(numUts);
43 NS_ASSERT(numSectors > 0);
44 NS_ASSERT(maxRadius > 0);
45 NS_ASSERT(effIsd < maxRadius);
46
47 // Try to open a new GNUPLOT file
48 std::ofstream topologyOutfile;
49 std::string topologyFileRoot = "./list-topology";
50 std::string topologyFileName = topologyFileRoot + ".gnuplot";
51 topologyOutfile.open(topologyFileName.c_str(), std::ios_base::out | std::ios_base::trunc);
52 if (!topologyOutfile.is_open())
53 {
54 NS_ABORT_MSG("Can't open " << topologyFileName);
55 }
56
57 topologyOutfile << "set term pdf" << std::endl;
58 topologyOutfile << "set output \"" << topologyFileRoot << ".pdf\"" << std::endl;
59 topologyOutfile << "set style arrow 1 linecolor \"red\" linewidth 2 head filled" << std::endl;
60 // topologyOutfile << "set autoscale" << std::endl;
61
62 // Extent of the plot, 5% margin all around
63 std::size_t radius = maxRadius * 1.05;
64 topologyOutfile << "set xrange [-" << radius << ":" << radius << "]" << std::endl;
65 topologyOutfile << "set yrange [-" << radius << ":" << radius << "]" << std::endl;
66
67 // Arrow style
70 // Plo the UEs first, so the sector arrows are on top
71 for (std::size_t utId = 0; utId < numUts; ++utId)
72 {
73 Vector utPos = utPositioner->GetNext();
74 // set label at xPos, yPos, zPos "" point pointtype 7 pointsize 2
75 topologyOutfile << "set label at " << utPos.x << " , " << utPos.y
76 << " point pointtype 7 pointsize 0.5 center" << std::endl;
77 }
78
79 // Control the arrow length that indicates
80 // the orientation of the sectorized antenna.
81 // Set it to 1/3 the ISD: x ---> <--- x
82 double arrowLength = effIsd;
83
84 const double sectorSize = 2 * M_PI / numSectors;
85 std::size_t cellId = 0;
86
87 for (std::size_t siteId = 0; siteId < numSites; ++siteId)
88 {
89 Vector site = sitePositioner->GetNext();
90
91 for (std::size_t sector = 0; sector < numSectors; ++sector)
92 {
93 double angle = 0.0;
94 if (numSectors > 1)
95 {
96 std::size_t sector = cellId % numSectors;
97 // 0'th sector spans from [0, sectorSize] in degrees,
98 // centered at sectorSize / 2
99 angle = sectorSize * (sector + 0.5);
100 }
101 double rx = arrowLength * std::cos(angle);
102 double ry = arrowLength * std::sin(angle);
103
104 topologyOutfile << "set arrow " << cellId + 1 << " from " << site.x << "," << site.y
105 << " rto " << rx << "," << ry << " arrowstyle 1\n";
106
107 topologyOutfile << "set label " << cellId + 1 << " \"" << (cellId + 1) << "\" at "
108 << site.x << "," << site.y << " center" << std::endl;
109 ++cellId;
110 }
111 }
112 NS_ASSERT(cellId == numCells);
113
114 topologyOutfile << "unset key" << std::endl; // Disable plot legends
115 topologyOutfile << "plot 1/0" << std::endl; // Need to plot a function
116
117} // PlotDeployment ()
118
119} // unnamed namespace
120
121namespace ns3
122{
123
127
128void
129FileScenarioHelper::Add(const std::string filePath, char delimiter /* = ',' */)
130{
131 if (!m_bsPositioner)
132 {
133 m_bsPositioner = CreateObject<ListPositionAllocator>();
134 }
135 m_bsPositioner->Add(filePath, m_bsHeight, delimiter);
136 auto numSites = m_bsPositioner->GetSize();
137 SetSitesNumber(numSites);
138}
139
140void
141FileScenarioHelper::CheckScenario(const char* where) const
142{
143 NS_ASSERT_MSG(m_scenarioCreated,
144 "Must Add() position file and CreateScenario() "
145 "before calling "
146 << where);
147}
148
149Vector
150FileScenarioHelper::GetSitePosition(std::size_t cellId) const
151{
152 CheckScenario(__FUNCTION__);
153
154 auto node = m_bs.Get(cellId);
155 auto mob = node->GetObject<MobilityModel>();
156 return mob->GetPosition();
157}
158
159void
161{
162 NS_ASSERT_MSG(m_bsPositioner, "Must Add() a position file before CreateScenario()");
163
164 // NS_ASSERT_MSG (m_numSites > 0,
165 // "Must have at least one site location in the position file.");
166
167 NS_ASSERT_MSG(m_sectorization != NONE, "Must SetSectorization() before CreateScenario()");
168 NS_ASSERT_MSG(m_bsHeight >= 0.0, "Must SetBsHeight() before CreateScenario()");
169 NS_ASSERT_MSG(m_utHeight >= 0.0, "Must SetUtHeight() before CreateScenario()");
170
171 auto sectors = GetNumSectorsPerSite();
172 std::cout << " creating BS" << std::endl;
173 m_bs.Create(m_numBs);
174 std::cout << " creating UE" << std::endl;
175 m_ut.Create(m_numUt);
176
177 // Accumulate maxRadius and effIsd
178 double maxRadius = 0;
179 // For effective ISD we have to iterate over all pairs of towers :(
180 // so cache the positions from m_bsPositioner
181 std::cout << " reserving sitePositions" << std::endl;
182 std::vector<Vector> sitePositions;
183 sitePositions.reserve(m_numSites);
184
185 // Position the BS cells
186 std::cout << " creating bsPositions" << std::endl;
187 Ptr<ListPositionAllocator> bsPositions = CreateObject<ListPositionAllocator>();
188 std::size_t cellId = 0;
189 for (std::size_t siteId = 0; siteId < m_numSites; ++siteId)
190 {
191 Vector site = m_bsPositioner->GetNext();
192 sitePositions.push_back(site);
193
194 maxRadius = std::max(maxRadius, std::abs(site.x));
195 maxRadius = std::max(maxRadius, std::abs(site.y));
196
197 std::cout << " sectors for site " << siteId << std::endl;
198 for (std::size_t sector = 0; sector < sectors; ++sector)
199 {
200 Vector bs{site};
201 double angle = GetAntennaOrientationRadians(cellId);
202 bs.x += m_antennaOffset * cos(angle);
203 bs.y += m_antennaOffset * sin(angle);
204
205 bsPositions->Add(bs);
206 ++cellId;
207
208 } // for cell
209
210 } // for site
211 NS_ASSERT_MSG(cellId == m_numBs,
212 "Computed positions for " << cellId << " cells, expected " << m_numBs);
213
214 std::cout << " BS mobility" << std::endl;
215 MobilityHelper mobility;
216 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
217 mobility.SetPositionAllocator(m_bsPositioner);
218 mobility.Install(m_bs);
219
220 // Compute effective ISD
221 double effIsd = 0;
222 std::cout << " computing average Isd..." << std::flush;
223 for (auto v = sitePositions.begin(); v != sitePositions.end(); ++v)
224 {
225 auto w = v;
226 ++w;
227 for (; w < sitePositions.end(); ++w)
228 {
229 NS_ASSERT(w != v);
230 double range = (*w - *v).GetLength();
231 effIsd += range;
232
233 } // for w : sitePositions
234
235 } // for v : sitePositions
236 effIsd /= m_numSites * (m_numSites - 1) / 2;
237 std::cout << effIsd << std::endl;
238
239 // Position the UEs uniformly in the sector annulus
240 std::cout << " UE positions" << std::endl;
241 // equivalent for a hexagonal laydown:
242 // between m_minBsUtDistance and m_isd / sqrt (3)
243 std::cout << " radius rng..." << std::flush;
244 Ptr<UniformRandomVariable> r = CreateObject<UniformRandomVariable>();
245 std::cout << "setting stream..." << std::flush;
246 r->SetStream(RngSeedManager::GetNextStreamIndex());
247 std::cout << "done" << std::endl;
248 const double outerR = std::min(effIsd, m_isd); // / std::sqrt(3);
249 NS_ASSERT(m_minBsUtDistance < outerR);
250 // Need to weight r to get uniform in the sector wedge
251 // See https://stackoverflow.com/questions/5837572
252 r->SetAttribute("Min", DoubleValue(m_minBsUtDistance * m_minBsUtDistance));
253 r->SetAttribute("Max", DoubleValue(outerR * outerR));
254 std::cout << "[" << m_minBsUtDistance << "," << outerR << "]" << std::endl;
255 std::cout << " using effIsd " << outerR << std::endl;
256
257 std::cout << " theta rng..." << std::flush;
258 Ptr<UniformRandomVariable> theta = CreateObject<UniformRandomVariable>();
259 std::cout << "setting stream..." << std::flush;
260 theta->SetStream(RngSeedManager::GetNextStreamIndex());
261 std::cout << "done" << std::endl;
262
263 Ptr<ListPositionAllocator> utPositioner = CreateObject<ListPositionAllocator>();
264 ;
265 for (uint32_t utId = 0; utId < m_numUt; ++utId)
266 {
267 auto cellId = GetCellIndex(utId);
268 auto siteId = GetSiteIndex(cellId);
269 Vector cellPos = sitePositions[siteId];
270
271 // Need to weight r to get uniform in the sector wedge
272 // See https://stackoverflow.com/questions/5837572
273 double d = std::sqrt(r->GetValue());
274 double boreSight = GetAntennaOrientationRadians(cellId);
275 double halfWidth = 2 * M_PI / sectors / 2;
276 theta->SetAttribute("Min", DoubleValue(boreSight - halfWidth));
277 theta->SetAttribute("Max", DoubleValue(boreSight + halfWidth));
278 double t = theta->GetValue();
279
280 Vector utPos(cellPos);
281 utPos.x += d * cos(t);
282 utPos.y += d * sin(t);
283 utPos.z = m_utHeight;
284
285 utPositioner->Add(utPos);
286 }
287
288 std::cout << " UE mobility" << std::endl;
289 mobility.SetPositionAllocator(utPositioner);
290 mobility.Install(m_ut);
291
292 std::cout << " plot deployment" << std::endl;
293 PlotDeployment(m_bsPositioner, utPositioner, sectors, maxRadius, outerR);
294
295 m_scenarioCreated = true;
296}
297
298} // namespace ns3
~FileScenarioHelper() override
~FileScenarioHelper
void CreateScenario() override
Create the scenario, with the configured parameter.
void Add(const std::string filePath, char delimiter=',')
Add the positions listed in a file. The file should be a simple text file, with one position per line...
Vector GetSitePosition(std::size_t cellId) const
Get the site position corresponding to a given cell.
std::size_t m_numBs
Number of base stations to create.
uint16_t GetSiteIndex(std::size_t cellId) const
Gets the site index the queried cell id belongs to.
uint16_t GetCellIndex(std::size_t ueId) const
Get the cell (base station) index the queried UE id belongs to.
std::size_t m_numUt
Number of user terminals to create.
std::size_t m_numSites
Number of sites with base stations.
void SetSitesNumber(std::size_t n)
Set number of sites/towers.
double GetAntennaOrientationRadians(std::size_t cellId) const
Returns the orientation in radians of the antenna array for the given cellId.
double m_isd
Inter-site distance (ISD) in meters.
uint32_t GetNumSectorsPerSite() const
Gets the number of sectors per site.
SiteSectorizationType m_sectorization
Number of sectors per site.
double m_bsHeight
Height of gNB nodes.
double m_minBsUtDistance
Minimum distance between BS and UT in meters.
@ NONE
Unconfigured value.
double m_utHeight
Height of UE nodes.
double m_antennaOffset
Cell antenna offset in meters w.r.t. site location.
void PlotDeployment(const Ptr< const ListPositionAllocator > &sitePositioner, const Ptr< const ListPositionAllocator > &utPositioner, const std::size_t numSectors, const double maxRadius, const double effIsd)
Creates a GNUPLOT with the deployment including base stations (BS) and user terminals (UT)....