A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
three-gpp-channel-model.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
3 * University of Padova
4 * Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
5 * New York University
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation;
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
23
24#include "ns3/double.h"
25#include "ns3/integer.h"
26#include "ns3/log.h"
27#include "ns3/mobility-model.h"
28#include "ns3/node.h"
29#include "ns3/phased-array-model.h"
30#include "ns3/pointer.h"
31#include "ns3/string.h"
32#include <ns3/simulator.h>
33
34#include <algorithm>
35#include <map>
36#include <random>
37
38namespace ns3
39{
40
41NS_LOG_COMPONENT_DEFINE("ThreeGppChannelModel");
42
43NS_OBJECT_ENSURE_REGISTERED(ThreeGppChannelModel);
44
45/// Conversion factor: degrees to radians
46static constexpr double DEG2RAD = M_PI / 180.0;
47
48/// The ray offset angles within a cluster, given for rms angle spread normalized to 1.
49/// (Table 7.5-3)
50static const double offSetAlpha[20] = {
51 0.0447, -0.0447, 0.1413, -0.1413, 0.2492, -0.2492, 0.3715, -0.3715, 0.5129, -0.5129,
52 0.6797, -0.6797, 0.8844, -0.8844, 1.1481, -1.1481, 1.5195, -1.5195, 2.1551, -2.1551,
53};
54
55/**
56 * The square root matrix for <em>RMa LOS</em>, which is generated using the
57 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
58 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
59 *
60 * The Matlab file to generate the matrices can be found in
61 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
62 */
63static const double sqrtC_RMa_LOS[7][7] = {
64 {1, 0, 0, 0, 0, 0, 0},
65 {0, 1, 0, 0, 0, 0, 0},
66 {-0.5, 0, 0.866025, 0, 0, 0, 0},
67 {0, 0, 0, 1, 0, 0, 0},
68 {0, 0, 0, 0, 1, 0, 0},
69 {0.01, 0, -0.0519615, 0.73, -0.2, 0.651383, 0},
70 {-0.17, -0.02, 0.21362, -0.14, 0.24, 0.142773, 0.909661},
71};
72
73/**
74 * The square root matrix for <em>RMa NLOS</em>, which is generated using the
75 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
76 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
77 * The parameter K is ignored.
78 *
79 * The Matlab file to generate the matrices can be found in
80 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
81 */
82static const double sqrtC_RMa_NLOS[6][6] = {
83 {1, 0, 0, 0, 0, 0},
84 {-0.5, 0.866025, 0, 0, 0, 0},
85 {0.6, -0.11547, 0.791623, 0, 0, 0},
86 {0, 0, 0, 1, 0, 0},
87 {-0.04, -0.138564, 0.540662, -0.18, 0.809003, 0},
88 {-0.25, -0.606218, -0.240013, 0.26, -0.231685, 0.625392},
89};
90
91/**
92 * The square root matrix for <em>RMa O2I</em>, which is generated using the
93 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
94 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
95 *
96 * The Matlab file to generate the matrices can be found in
97 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
98 */
99static const double sqrtC_RMa_O2I[6][6] = {
100 {1, 0, 0, 0, 0, 0},
101 {0, 1, 0, 0, 0, 0},
102 {0, 0, 1, 0, 0, 0},
103 {0, 0, -0.7, 0.714143, 0, 0},
104 {0, 0, 0.66, -0.123225, 0.741091, 0},
105 {0, 0, 0.47, 0.152631, -0.393194, 0.775373},
106};
107
108/**
109 * The square root matrix for <em>UMa LOS</em>, which is generated using the
110 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
111 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
112 *
113 * The Matlab file to generate the matrices can be found in
114 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
115 */
116static const double sqrtC_UMa_LOS[7][7] = {
117 {1, 0, 0, 0, 0, 0, 0},
118 {0, 1, 0, 0, 0, 0, 0},
119 {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
120 {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
121 {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
122 {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
123 {-0.8, 0, -0.388057, -0.367926, 0.238537, -3.58949e-15, 0.130931},
124};
125
126/**
127 * The square root matrix for <em>UMa NLOS</em>, which is generated using the
128 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
129 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
130 * The parameter K is ignored.
131 *
132 * The Matlab file to generate the matrices can be found in
133 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
134 */
135static const double sqrtC_UMa_NLOS[6][6] = {
136 {1, 0, 0, 0, 0, 0},
137 {-0.4, 0.916515, 0, 0, 0, 0},
138 {-0.6, 0.174574, 0.78072, 0, 0, 0},
139 {0, 0.654654, 0.365963, 0.661438, 0, 0},
140 {0, -0.545545, 0.762422, 0.118114, 0.327327, 0},
141 {-0.4, -0.174574, -0.396459, 0.392138, 0.49099, 0.507445},
142};
143
144/**
145 * The square root matrix for <em>UMa O2I</em>, which is generated using the
146 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
147 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
148 *
149 * The Matlab file to generate the matrices can be found in
150 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
151 */
152static const double sqrtC_UMa_O2I[6][6] = {
153 {1, 0, 0, 0, 0, 0},
154 {-0.5, 0.866025, 0, 0, 0, 0},
155 {0.2, 0.57735, 0.791623, 0, 0, 0},
156 {0, 0.46188, -0.336861, 0.820482, 0, 0},
157 {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
158 {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
159
160};
161
162/**
163 * The square root matrix for <em>UMi LOS</em>, which is generated using the
164 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
165 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
166 *
167 * The Matlab file to generate the matrices can be found in
168 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
169 */
170static const double sqrtC_UMi_LOS[7][7] = {
171 {1, 0, 0, 0, 0, 0, 0},
172 {0.5, 0.866025, 0, 0, 0, 0, 0},
173 {-0.4, -0.57735, 0.711805, 0, 0, 0, 0},
174 {-0.5, 0.057735, 0.468293, 0.726201, 0, 0, 0},
175 {-0.4, -0.11547, 0.805464, -0.23482, 0.350363, 0, 0},
176 {0, 0, 0, 0.688514, 0.461454, 0.559471, 0},
177 {0, 0, 0.280976, 0.231921, -0.490509, 0.11916, 0.782603},
178};
179
180/**
181 * The square root matrix for <em>UMi NLOS</em>, which is generated using the
182 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
183 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
184 * The parameter K is ignored.
185 *
186 * The Matlab file to generate the matrices can be found in
187 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
188 */
189static const double sqrtC_UMi_NLOS[6][6] = {
190 {1, 0, 0, 0, 0, 0},
191 {-0.7, 0.714143, 0, 0, 0, 0},
192 {0, 0, 1, 0, 0, 0},
193 {-0.4, 0.168034, 0, 0.90098, 0, 0},
194 {0, -0.70014, 0.5, 0.130577, 0.4927, 0},
195 {0, 0, 0.5, 0.221981, -0.566238, 0.616522},
196};
197
198/**
199 * The square root matrix for <em>UMi O2I</em>, which is generated using the
200 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
201 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
202 *
203 * The Matlab file to generate the matrices can be found in
204 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
205 */
206static const double sqrtC_UMi_O2I[6][6] = {
207 {1, 0, 0, 0, 0, 0},
208 {-0.5, 0.866025, 0, 0, 0, 0},
209 {0.2, 0.57735, 0.791623, 0, 0, 0},
210 {0, 0.46188, -0.336861, 0.820482, 0, 0},
211 {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
212 {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
213};
214
215/**
216 * The square root matrix for <em>Indoor-Office LOS</em>, which is generated
217 * using the Cholesky decomposition according to table 7.5-6 Part 2 and follows
218 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
219 *
220 * The Matlab file to generate the matrices can be found in
221 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
222 */
223static const double sqrtC_office_LOS[7][7] = {
224 {1, 0, 0, 0, 0, 0, 0},
225 {0.5, 0.866025, 0, 0, 0, 0, 0},
226 {-0.8, -0.11547, 0.588784, 0, 0, 0, 0},
227 {-0.4, 0.23094, 0.520847, 0.717903, 0, 0, 0},
228 {-0.5, 0.288675, 0.73598, -0.348236, 0.0610847, 0, 0},
229 {0.2, -0.11547, 0.418943, 0.541106, 0.219905, 0.655744, 0},
230 {0.3, -0.057735, 0.73598, -0.348236, 0.0610847, -0.304997, 0.383375},
231};
232
233/**
234 * The square root matrix for <em>Indoor-Office NLOS</em>, which is generated
235 * using the Cholesky decomposition according to table 7.5-6 Part 2 and follows
236 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
237 * The parameter K is ignored.
238 *
239 * The Matlab file to generate the matrices can be found in
240 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
241 */
242static const double sqrtC_office_NLOS[6][6] = {
243 {1, 0, 0, 0, 0, 0},
244 {-0.5, 0.866025, 0, 0, 0, 0},
245 {0, 0.46188, 0.886942, 0, 0, 0},
246 {-0.4, -0.23094, 0.120263, 0.878751, 0, 0},
247 {0, -0.311769, 0.55697, -0.249198, 0.728344, 0},
248 {0, -0.069282, 0.295397, 0.430696, 0.468462, 0.709214},
249};
250
252{
253 NS_LOG_FUNCTION(this);
254 m_uniformRv = CreateObject<UniformRandomVariable>();
255 m_uniformRvShuffle = CreateObject<UniformRandomVariable>();
256 m_uniformRvDoppler = CreateObject<UniformRandomVariable>();
257
258 m_normalRv = CreateObject<NormalRandomVariable>();
259 m_normalRv->SetAttribute("Mean", DoubleValue(0.0));
260 m_normalRv->SetAttribute("Variance", DoubleValue(1.0));
261}
262
264{
265 NS_LOG_FUNCTION(this);
266}
267
268void
270{
271 NS_LOG_FUNCTION(this);
273 {
274 m_channelConditionModel->Dispose();
275 }
276 m_channelMatrixMap.clear();
277 m_channelParamsMap.clear();
278 m_channelConditionModel = nullptr;
279}
280
281TypeId
283{
284 static TypeId tid =
285 TypeId("ns3::ThreeGppChannelModel")
286 .SetGroupName("Spectrum")
288 .AddConstructor<ThreeGppChannelModel>()
289 .AddAttribute("Frequency",
290 "The operating Frequency in Hz",
291 DoubleValue(500.0e6),
294 MakeDoubleChecker<double>())
295 .AddAttribute(
296 "Scenario",
297 "The 3GPP scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed)",
298 StringValue("UMa"),
302 .AddAttribute("ChannelConditionModel",
303 "Pointer to the channel condition model",
304 PointerValue(),
307 MakePointerChecker<ChannelConditionModel>())
308 .AddAttribute("UpdatePeriod",
309 "Specify the channel coherence time",
313 // attributes for the blockage model
314 .AddAttribute("Blockage",
315 "Enable blockage model A (sec 7.6.4.1)",
316 BooleanValue(false),
319 .AddAttribute("NumNonselfBlocking",
320 "number of non-self-blocking regions",
321 IntegerValue(4),
323 MakeIntegerChecker<uint16_t>())
324 .AddAttribute("PortraitMode",
325 "true for portrait mode, false for landscape mode",
326 BooleanValue(true),
329 .AddAttribute("BlockerSpeed",
330 "The speed of moving blockers, the unit is m/s",
331 DoubleValue(1),
333 MakeDoubleChecker<double>())
334 .AddAttribute("vScatt",
335 "Maximum speed of the vehicle in the layout (see 3GPP TR 37.885 v15.3.0, "
336 "Sec. 6.2.3)."
337 "Used to compute the additional contribution for the Doppler of"
338 "delayed (reflected) paths",
339 DoubleValue(0.0),
341 MakeDoubleChecker<double>(0.0))
342
343 ;
344 return tid;
345}
346
347void
349{
350 NS_LOG_FUNCTION(this);
352}
353
356{
357 NS_LOG_FUNCTION(this);
359}
360
361void
363{
364 NS_LOG_FUNCTION(this);
365 NS_ASSERT_MSG(f >= 500.0e6 && f <= 100.0e9,
366 "Frequency should be between 0.5 and 100 GHz but is " << f);
367 m_frequency = f;
368}
369
370double
372{
373 NS_LOG_FUNCTION(this);
374 return m_frequency;
375}
376
377void
378ThreeGppChannelModel::SetScenario(const std::string& scenario)
379{
380 NS_LOG_FUNCTION(this);
381 NS_ASSERT_MSG(scenario == "RMa" || scenario == "UMa" || scenario == "UMi-StreetCanyon" ||
382 scenario == "InH-OfficeOpen" || scenario == "InH-OfficeMixed" ||
383 scenario == "V2V-Urban" || scenario == "V2V-Highway",
384 "Unknown scenario, choose between: RMa, UMa, UMi-StreetCanyon, "
385 "InH-OfficeOpen, InH-OfficeMixed, V2V-Urban or V2V-Highway");
386 m_scenario = scenario;
387}
388
389std::string
391{
392 NS_LOG_FUNCTION(this);
393 return m_scenario;
394}
395
398 double hBS,
399 double hUT,
400 double distance2D) const
401{
402 NS_LOG_FUNCTION(this);
403
404 double fcGHz = m_frequency / 1.0e9;
405 Ptr<ParamsTable> table3gpp = Create<ParamsTable>();
406 // table3gpp includes the following parameters:
407 // numOfCluster, raysPerCluster, uLgDS, sigLgDS, uLgASD, sigLgASD,
408 // uLgASA, sigLgASA, uLgZSA, sigLgZSA, uLgZSD, sigLgZSD, offsetZOD,
409 // cDS, cASD, cASA, cZSA, uK, sigK, rTau, uXpr, sigXpr, shadowingStd
410
411 bool los = channelCondition->IsLos();
412 bool o2i = channelCondition->IsO2i();
413
414 // In NLOS case, parameter uK and sigK are not used and they are set to 0
415 if (m_scenario == "RMa")
416 {
417 if (los && !o2i)
418 {
419 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
420 // entry is N/A.
421 table3gpp->m_numOfCluster = 11;
422 table3gpp->m_raysPerCluster = 20;
423 table3gpp->m_uLgDS = -7.49;
424 table3gpp->m_sigLgDS = 0.55;
425 table3gpp->m_uLgASD = 0.90;
426 table3gpp->m_sigLgASD = 0.38;
427 table3gpp->m_uLgASA = 1.52;
428 table3gpp->m_sigLgASA = 0.24;
429 table3gpp->m_uLgZSA = 0.47;
430 table3gpp->m_sigLgZSA = 0.40;
431 table3gpp->m_uLgZSD = 0.34;
432 table3gpp->m_sigLgZSD =
433 std::max(-1.0, -0.17 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.22);
434 table3gpp->m_offsetZOD = 0;
435 table3gpp->m_cDS = 3.91e-9;
436 table3gpp->m_cASD = 2;
437 table3gpp->m_cASA = 3;
438 table3gpp->m_cZSA = 3;
439 table3gpp->m_uK = 7;
440 table3gpp->m_sigK = 4;
441 table3gpp->m_rTau = 3.8;
442 table3gpp->m_uXpr = 12;
443 table3gpp->m_sigXpr = 4;
444 table3gpp->m_perClusterShadowingStd = 3;
445
446 for (uint8_t row = 0; row < 7; row++)
447 {
448 for (uint8_t column = 0; column < 7; column++)
449 {
450 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_LOS[row][column];
451 }
452 }
453 }
454 else if (!los && !o2i)
455 {
456 table3gpp->m_numOfCluster = 10;
457 table3gpp->m_raysPerCluster = 20;
458 table3gpp->m_uLgDS = -7.43;
459 table3gpp->m_sigLgDS = 0.48;
460 table3gpp->m_uLgASD = 0.95;
461 table3gpp->m_sigLgASD = 0.45;
462 table3gpp->m_uLgASA = 1.52;
463 table3gpp->m_sigLgASA = 0.13;
464 table3gpp->m_uLgZSA = 0.58;
465 table3gpp->m_sigLgZSA = 0.37;
466 table3gpp->m_uLgZSD =
467 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
468 table3gpp->m_sigLgZSD = 0.30;
469 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
470 table3gpp->m_cDS = 3.91e-9;
471 table3gpp->m_cASD = 2;
472 table3gpp->m_cASA = 3;
473 table3gpp->m_cZSA = 3;
474 table3gpp->m_uK = 0;
475 table3gpp->m_sigK = 0;
476 table3gpp->m_rTau = 1.7;
477 table3gpp->m_uXpr = 7;
478 table3gpp->m_sigXpr = 3;
479 table3gpp->m_perClusterShadowingStd = 3;
480
481 for (uint8_t row = 0; row < 6; row++)
482 {
483 for (uint8_t column = 0; column < 6; column++)
484 {
485 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_NLOS[row][column];
486 }
487 }
488 }
489 else // o2i
490 {
491 table3gpp->m_numOfCluster = 10;
492 table3gpp->m_raysPerCluster = 20;
493 table3gpp->m_uLgDS = -7.47;
494 table3gpp->m_sigLgDS = 0.24;
495 table3gpp->m_uLgASD = 0.67;
496 table3gpp->m_sigLgASD = 0.18;
497 table3gpp->m_uLgASA = 1.66;
498 table3gpp->m_sigLgASA = 0.21;
499 table3gpp->m_uLgZSA = 0.93;
500 table3gpp->m_sigLgZSA = 0.22;
501 table3gpp->m_uLgZSD =
502 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
503 table3gpp->m_sigLgZSD = 0.30;
504 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
505 table3gpp->m_cDS = 3.91e-9;
506 table3gpp->m_cASD = 2;
507 table3gpp->m_cASA = 3;
508 table3gpp->m_cZSA = 3;
509 table3gpp->m_uK = 0;
510 table3gpp->m_sigK = 0;
511 table3gpp->m_rTau = 1.7;
512 table3gpp->m_uXpr = 7;
513 table3gpp->m_sigXpr = 3;
514 table3gpp->m_perClusterShadowingStd = 3;
515
516 for (uint8_t row = 0; row < 6; row++)
517 {
518 for (uint8_t column = 0; column < 6; column++)
519 {
520 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_O2I[row][column];
521 }
522 }
523 }
524 }
525 else if (m_scenario == "UMa")
526 {
527 if (los && !o2i)
528 {
529 table3gpp->m_numOfCluster = 12;
530 table3gpp->m_raysPerCluster = 20;
531 table3gpp->m_uLgDS = -6.955 - 0.0963 * log10(fcGHz);
532 table3gpp->m_sigLgDS = 0.66;
533 table3gpp->m_uLgASD = 1.06 + 0.1114 * log10(fcGHz);
534 table3gpp->m_sigLgASD = 0.28;
535 table3gpp->m_uLgASA = 1.81;
536 table3gpp->m_sigLgASA = 0.20;
537 table3gpp->m_uLgZSA = 0.95;
538 table3gpp->m_sigLgZSA = 0.16;
539 table3gpp->m_uLgZSD =
540 std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.75);
541 table3gpp->m_sigLgZSD = 0.40;
542 table3gpp->m_offsetZOD = 0;
543 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
544 table3gpp->m_cASD = 5;
545 table3gpp->m_cASA = 11;
546 table3gpp->m_cZSA = 7;
547 table3gpp->m_uK = 9;
548 table3gpp->m_sigK = 3.5;
549 table3gpp->m_rTau = 2.5;
550 table3gpp->m_uXpr = 8;
551 table3gpp->m_sigXpr = 4;
552 table3gpp->m_perClusterShadowingStd = 3;
553
554 for (uint8_t row = 0; row < 7; row++)
555 {
556 for (uint8_t column = 0; column < 7; column++)
557 {
558 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_LOS[row][column];
559 }
560 }
561 }
562 else
563 {
564 double uLgZSD = std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.9);
565
566 double afc = 0.208 * log10(fcGHz) - 0.782;
567 double bfc = 25;
568 double cfc = -0.13 * log10(fcGHz) + 2.03;
569 double efc = 7.66 * log10(fcGHz) - 5.96;
570
571 double offsetZOD = efc - std::pow(10, afc * log10(std::max(bfc, distance2D)) + cfc);
572
573 if (!los && !o2i)
574 {
575 table3gpp->m_numOfCluster = 20;
576 table3gpp->m_raysPerCluster = 20;
577 table3gpp->m_uLgDS = -6.28 - 0.204 * log10(fcGHz);
578 table3gpp->m_sigLgDS = 0.39;
579 table3gpp->m_uLgASD = 1.5 - 0.1144 * log10(fcGHz);
580 table3gpp->m_sigLgASD = 0.28;
581 table3gpp->m_uLgASA = 2.08 - 0.27 * log10(fcGHz);
582 table3gpp->m_sigLgASA = 0.11;
583 table3gpp->m_uLgZSA = -0.3236 * log10(fcGHz) + 1.512;
584 table3gpp->m_sigLgZSA = 0.16;
585 table3gpp->m_uLgZSD = uLgZSD;
586 table3gpp->m_sigLgZSD = 0.49;
587 table3gpp->m_offsetZOD = offsetZOD;
588 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
589 table3gpp->m_cASD = 2;
590 table3gpp->m_cASA = 15;
591 table3gpp->m_cZSA = 7;
592 table3gpp->m_uK = 0;
593 table3gpp->m_sigK = 0;
594 table3gpp->m_rTau = 2.3;
595 table3gpp->m_uXpr = 7;
596 table3gpp->m_sigXpr = 3;
597 table3gpp->m_perClusterShadowingStd = 3;
598
599 for (uint8_t row = 0; row < 6; row++)
600 {
601 for (uint8_t column = 0; column < 6; column++)
602 {
603 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_NLOS[row][column];
604 }
605 }
606 }
607 else //(o2i)
608 {
609 table3gpp->m_numOfCluster = 12;
610 table3gpp->m_raysPerCluster = 20;
611 table3gpp->m_uLgDS = -6.62;
612 table3gpp->m_sigLgDS = 0.32;
613 table3gpp->m_uLgASD = 1.25;
614 table3gpp->m_sigLgASD = 0.42;
615 table3gpp->m_uLgASA = 1.76;
616 table3gpp->m_sigLgASA = 0.16;
617 table3gpp->m_uLgZSA = 1.01;
618 table3gpp->m_sigLgZSA = 0.43;
619 table3gpp->m_uLgZSD = uLgZSD;
620 table3gpp->m_sigLgZSD = 0.49;
621 table3gpp->m_offsetZOD = offsetZOD;
622 table3gpp->m_cDS = 11e-9;
623 table3gpp->m_cASD = 5;
624 table3gpp->m_cASA = 8;
625 table3gpp->m_cZSA = 3;
626 table3gpp->m_uK = 0;
627 table3gpp->m_sigK = 0;
628 table3gpp->m_rTau = 2.2;
629 table3gpp->m_uXpr = 9;
630 table3gpp->m_sigXpr = 5;
631 table3gpp->m_perClusterShadowingStd = 4;
632
633 for (uint8_t row = 0; row < 6; row++)
634 {
635 for (uint8_t column = 0; column < 6; column++)
636 {
637 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_O2I[row][column];
638 }
639 }
640 }
641 }
642 }
643 else if (m_scenario == "UMi-StreetCanyon")
644 {
645 if (los && !o2i)
646 {
647 table3gpp->m_numOfCluster = 12;
648 table3gpp->m_raysPerCluster = 20;
649 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 7.14;
650 table3gpp->m_sigLgDS = 0.38;
651 table3gpp->m_uLgASD = -0.05 * log10(1 + fcGHz) + 1.21;
652 table3gpp->m_sigLgASD = 0.41;
653 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.73;
654 table3gpp->m_sigLgASA = 0.014 * log10(1 + fcGHz) + 0.28;
655 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
656 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
657 table3gpp->m_uLgZSD =
658 std::max(-0.21, -14.8 * distance2D / 1000.0 + 0.01 * std::abs(hUT - hBS) + 0.83);
659 table3gpp->m_sigLgZSD = 0.35;
660 table3gpp->m_offsetZOD = 0;
661 table3gpp->m_cDS = 5e-9;
662 table3gpp->m_cASD = 3;
663 table3gpp->m_cASA = 17;
664 table3gpp->m_cZSA = 7;
665 table3gpp->m_uK = 9;
666 table3gpp->m_sigK = 5;
667 table3gpp->m_rTau = 3;
668 table3gpp->m_uXpr = 9;
669 table3gpp->m_sigXpr = 3;
670 table3gpp->m_perClusterShadowingStd = 3;
671
672 for (uint8_t row = 0; row < 7; row++)
673 {
674 for (uint8_t column = 0; column < 7; column++)
675 {
676 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
677 }
678 }
679 }
680 else
681 {
682 double uLgZSD =
683 std::max(-0.5, -3.1 * distance2D / 1000.0 + 0.01 * std::max(hUT - hBS, 0.0) + 0.2);
684 double offsetZOD = -1 * std::pow(10, -1.5 * log10(std::max(10.0, distance2D)) + 3.3);
685 if (!los && !o2i)
686 {
687 table3gpp->m_numOfCluster = 19;
688 table3gpp->m_raysPerCluster = 20;
689 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 6.83;
690 table3gpp->m_sigLgDS = 0.16 * log10(1 + fcGHz) + 0.28;
691 table3gpp->m_uLgASD = -0.23 * log10(1 + fcGHz) + 1.53;
692 table3gpp->m_sigLgASD = 0.11 * log10(1 + fcGHz) + 0.33;
693 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
694 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
695 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
696 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
697 table3gpp->m_uLgZSD = uLgZSD;
698 table3gpp->m_sigLgZSD = 0.35;
699 table3gpp->m_offsetZOD = offsetZOD;
700 table3gpp->m_cDS = 11e-9;
701 table3gpp->m_cASD = 10;
702 table3gpp->m_cASA = 22;
703 table3gpp->m_cZSA = 7;
704 table3gpp->m_uK = 0;
705 table3gpp->m_sigK = 0;
706 table3gpp->m_rTau = 2.1;
707 table3gpp->m_uXpr = 8;
708 table3gpp->m_sigXpr = 3;
709 table3gpp->m_perClusterShadowingStd = 3;
710
711 for (uint8_t row = 0; row < 6; row++)
712 {
713 for (uint8_t column = 0; column < 6; column++)
714 {
715 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
716 }
717 }
718 }
719 else //(o2i)
720 {
721 table3gpp->m_numOfCluster = 12;
722 table3gpp->m_raysPerCluster = 20;
723 table3gpp->m_uLgDS = -6.62;
724 table3gpp->m_sigLgDS = 0.32;
725 table3gpp->m_uLgASD = 1.25;
726 table3gpp->m_sigLgASD = 0.42;
727 table3gpp->m_uLgASA = 1.76;
728 table3gpp->m_sigLgASA = 0.16;
729 table3gpp->m_uLgZSA = 1.01;
730 table3gpp->m_sigLgZSA = 0.43;
731 table3gpp->m_uLgZSD = uLgZSD;
732 table3gpp->m_sigLgZSD = 0.35;
733 table3gpp->m_offsetZOD = offsetZOD;
734 table3gpp->m_cDS = 11e-9;
735 table3gpp->m_cASD = 5;
736 table3gpp->m_cASA = 8;
737 table3gpp->m_cZSA = 3;
738 table3gpp->m_uK = 0;
739 table3gpp->m_sigK = 0;
740 table3gpp->m_rTau = 2.2;
741 table3gpp->m_uXpr = 9;
742 table3gpp->m_sigXpr = 5;
743 table3gpp->m_perClusterShadowingStd = 4;
744
745 for (uint8_t row = 0; row < 6; row++)
746 {
747 for (uint8_t column = 0; column < 6; column++)
748 {
749 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_O2I[row][column];
750 }
751 }
752 }
753 }
754 }
755 else if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
756 {
757 NS_ASSERT_MSG(!o2i, "The indoor scenario does out support outdoor to indoor");
758 if (los)
759 {
760 table3gpp->m_numOfCluster = 15;
761 table3gpp->m_raysPerCluster = 20;
762 table3gpp->m_uLgDS = -0.01 * log10(1 + fcGHz) - 7.692;
763 table3gpp->m_sigLgDS = 0.18;
764 table3gpp->m_uLgASD = 1.60;
765 table3gpp->m_sigLgASD = 0.18;
766 table3gpp->m_uLgASA = -0.19 * log10(1 + fcGHz) + 1.781;
767 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.119;
768 table3gpp->m_uLgZSA = -0.26 * log10(1 + fcGHz) + 1.44;
769 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.264;
770 table3gpp->m_uLgZSD = -1.43 * log10(1 + fcGHz) + 2.228;
771 table3gpp->m_sigLgZSD = 0.13 * log10(1 + fcGHz) + 0.30;
772 table3gpp->m_offsetZOD = 0;
773 table3gpp->m_cDS = 3.91e-9;
774 table3gpp->m_cASD = 5;
775 table3gpp->m_cASA = 8;
776 table3gpp->m_cZSA = 9;
777 table3gpp->m_uK = 7;
778 table3gpp->m_sigK = 4;
779 table3gpp->m_rTau = 3.6;
780 table3gpp->m_uXpr = 11;
781 table3gpp->m_sigXpr = 4;
782 table3gpp->m_perClusterShadowingStd = 6;
783
784 for (uint8_t row = 0; row < 7; row++)
785 {
786 for (uint8_t column = 0; column < 7; column++)
787 {
788 table3gpp->m_sqrtC[row][column] = sqrtC_office_LOS[row][column];
789 }
790 }
791 }
792 else
793 {
794 table3gpp->m_numOfCluster = 19;
795 table3gpp->m_raysPerCluster = 20;
796 table3gpp->m_uLgDS = -0.28 * log10(1 + fcGHz) - 7.173;
797 table3gpp->m_sigLgDS = 0.1 * log10(1 + fcGHz) + 0.055;
798 table3gpp->m_uLgASD = 1.62;
799 table3gpp->m_sigLgASD = 0.25;
800 table3gpp->m_uLgASA = -0.11 * log10(1 + fcGHz) + 1.863;
801 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.059;
802 table3gpp->m_uLgZSA = -0.15 * log10(1 + fcGHz) + 1.387;
803 table3gpp->m_sigLgZSA = -0.09 * log10(1 + fcGHz) + 0.746;
804 table3gpp->m_uLgZSD = 1.08;
805 table3gpp->m_sigLgZSD = 0.36;
806 table3gpp->m_offsetZOD = 0;
807 table3gpp->m_cDS = 3.91e-9;
808 table3gpp->m_cASD = 5;
809 table3gpp->m_cASA = 11;
810 table3gpp->m_cZSA = 9;
811 table3gpp->m_uK = 0;
812 table3gpp->m_sigK = 0;
813 table3gpp->m_rTau = 3;
814 table3gpp->m_uXpr = 10;
815 table3gpp->m_sigXpr = 4;
816 table3gpp->m_perClusterShadowingStd = 3;
817
818 for (uint8_t row = 0; row < 6; row++)
819 {
820 for (uint8_t column = 0; column < 6; column++)
821 {
822 table3gpp->m_sqrtC[row][column] = sqrtC_office_NLOS[row][column];
823 }
824 }
825 }
826 }
827 else if (m_scenario == "V2V-Urban")
828 {
829 if (channelCondition->IsLos())
830 {
831 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
832 // entry is N/A.
833 table3gpp->m_numOfCluster = 12;
834 table3gpp->m_raysPerCluster = 20;
835 table3gpp->m_uLgDS = -0.2 * log10(1 + fcGHz) - 7.5;
836 table3gpp->m_sigLgDS = 0.1;
837 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.6;
838 table3gpp->m_sigLgASD = 0.1;
839 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.6;
840 table3gpp->m_sigLgASA = 0.1;
841 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
842 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
843 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
844 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
845 table3gpp->m_offsetZOD = 0;
846 table3gpp->m_cDS = 5;
847 table3gpp->m_cASD = 17;
848 table3gpp->m_cASA = 17;
849 table3gpp->m_cZSA = 7;
850 table3gpp->m_uK = 3.48;
851 table3gpp->m_sigK = 2;
852 table3gpp->m_rTau = 3;
853 table3gpp->m_uXpr = 9;
854 table3gpp->m_sigXpr = 3;
855 table3gpp->m_perClusterShadowingStd = 4;
856
857 for (uint8_t row = 0; row < 7; row++)
858 {
859 for (uint8_t column = 0; column < 7; column++)
860 {
861 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
862 }
863 }
864 }
865 else if (channelCondition->IsNlos())
866 {
867 table3gpp->m_numOfCluster = 19;
868 table3gpp->m_raysPerCluster = 20;
869 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
870 table3gpp->m_sigLgDS = 0.28;
871 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
872 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
873 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
874 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
875 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
876 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
877 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
878 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
879 table3gpp->m_offsetZOD = 0;
880 table3gpp->m_cDS = 11;
881 table3gpp->m_cASD = 22;
882 table3gpp->m_cASA = 22;
883 table3gpp->m_cZSA = 7;
884 table3gpp->m_uK = 0; // N/A
885 table3gpp->m_sigK = 0; // N/A
886 table3gpp->m_rTau = 2.1;
887 table3gpp->m_uXpr = 8;
888 table3gpp->m_sigXpr = 3;
889 table3gpp->m_perClusterShadowingStd = 4;
890
891 for (uint8_t row = 0; row < 6; row++)
892 {
893 for (uint8_t column = 0; column < 6; column++)
894 {
895 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
896 }
897 }
898 }
899 else if (channelCondition->IsNlosv())
900 {
901 table3gpp->m_numOfCluster = 19;
902 table3gpp->m_raysPerCluster = 20;
903 table3gpp->m_uLgDS = -0.4 * log10(1 + fcGHz) - 7;
904 table3gpp->m_sigLgDS = 0.1;
905 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.7;
906 table3gpp->m_sigLgASD = 0.1;
907 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.7;
908 table3gpp->m_sigLgASA = 0.1;
909 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
910 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
911 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
912 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
913 table3gpp->m_offsetZOD = 0;
914 table3gpp->m_cDS = 11;
915 table3gpp->m_cASD = 22;
916 table3gpp->m_cASA = 22;
917 table3gpp->m_cZSA = 7;
918 table3gpp->m_uK = 0;
919 table3gpp->m_sigK = 4.5;
920 table3gpp->m_rTau = 2.1;
921 table3gpp->m_uXpr = 8;
922 table3gpp->m_sigXpr = 3;
923 table3gpp->m_perClusterShadowingStd = 4;
924
925 for (uint8_t row = 0; row < 6; row++)
926 {
927 for (uint8_t column = 0; column < 6; column++)
928 {
929 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
930 }
931 }
932 }
933 else
934 {
935 NS_FATAL_ERROR("Unknown channel condition");
936 }
937 }
938 else if (m_scenario == "V2V-Highway")
939 {
940 if (channelCondition->IsLos())
941 {
942 table3gpp->m_numOfCluster = 12;
943 table3gpp->m_raysPerCluster = 20;
944 table3gpp->m_uLgDS = -8.3;
945 table3gpp->m_sigLgDS = 0.2;
946 table3gpp->m_uLgASD = 1.4;
947 table3gpp->m_sigLgASD = 0.1;
948 table3gpp->m_uLgASA = 1.4;
949 table3gpp->m_sigLgASA = 0.1;
950 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
951 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
952 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
953 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
954 table3gpp->m_offsetZOD = 0;
955 table3gpp->m_cDS = 5;
956 table3gpp->m_cASD = 17;
957 table3gpp->m_cASA = 17;
958 table3gpp->m_cZSA = 7;
959 table3gpp->m_uK = 9;
960 table3gpp->m_sigK = 3.5;
961 table3gpp->m_rTau = 3;
962 table3gpp->m_uXpr = 9;
963 table3gpp->m_sigXpr = 3;
964 table3gpp->m_perClusterShadowingStd = 4;
965
966 for (uint8_t row = 0; row < 7; row++)
967 {
968 for (uint8_t column = 0; column < 7; column++)
969 {
970 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
971 }
972 }
973 }
974 else if (channelCondition->IsNlosv())
975 {
976 table3gpp->m_numOfCluster = 19;
977 table3gpp->m_raysPerCluster = 20;
978 table3gpp->m_uLgDS = -8.3;
979 table3gpp->m_sigLgDS = 0.3;
980 table3gpp->m_uLgASD = 1.5;
981 table3gpp->m_sigLgASD = 0.1;
982 table3gpp->m_uLgASA = 1.5;
983 table3gpp->m_sigLgASA = 0.1;
984 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
985 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
986 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
987 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
988 table3gpp->m_offsetZOD = 0;
989 table3gpp->m_cDS = 11;
990 table3gpp->m_cASD = 22;
991 table3gpp->m_cASA = 22;
992 table3gpp->m_cZSA = 7;
993 table3gpp->m_uK = 0;
994 table3gpp->m_sigK = 4.5;
995 table3gpp->m_rTau = 2.1;
996 table3gpp->m_uXpr = 8.0;
997 table3gpp->m_sigXpr = 3;
998 table3gpp->m_perClusterShadowingStd = 4;
999
1000 for (uint8_t row = 0; row < 6; row++)
1001 {
1002 for (uint8_t column = 0; column < 6; column++)
1003 {
1004 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1005 }
1006 }
1007 }
1008 else if (channelCondition->IsNlos())
1009 {
1011 "The fast fading parameters for the NLOS condition in the Highway scenario are not "
1012 "defined in TR 37.885, use the ones defined in TDoc R1-1803671 instead");
1013
1014 table3gpp->m_numOfCluster = 19;
1015 table3gpp->m_raysPerCluster = 20;
1016 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
1017 table3gpp->m_sigLgDS = 0.28;
1018 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
1019 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
1020 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
1021 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
1022 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1023 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1024 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
1025 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
1026 table3gpp->m_offsetZOD = 0;
1027 table3gpp->m_cDS = 11;
1028 table3gpp->m_cASD = 22;
1029 table3gpp->m_cASA = 22;
1030 table3gpp->m_cZSA = 7;
1031 table3gpp->m_uK = 0; // N/A
1032 table3gpp->m_sigK = 0; // N/A
1033 table3gpp->m_rTau = 2.1;
1034 table3gpp->m_uXpr = 8;
1035 table3gpp->m_sigXpr = 3;
1036 table3gpp->m_perClusterShadowingStd = 4;
1037
1038 for (uint8_t row = 0; row < 6; row++)
1039 {
1040 for (uint8_t column = 0; column < 6; column++)
1041 {
1042 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
1043 }
1044 }
1045 }
1046 else
1047 {
1048 NS_FATAL_ERROR("Unknown channel condition");
1049 }
1050 }
1051 else
1052 {
1053 NS_FATAL_ERROR("unknown scenarios");
1054 }
1055
1056 return table3gpp;
1057}
1058
1059bool
1061 Ptr<const ChannelCondition> channelCondition) const
1062{
1063 NS_LOG_FUNCTION(this);
1064
1065 bool update = false;
1066
1067 // if the channel condition is different the channel has to be updated
1068 if (!channelCondition->IsEqual(channelParams->m_losCondition, channelParams->m_o2iCondition))
1069 {
1070 NS_LOG_DEBUG("Update the channel condition");
1071 update = true;
1072 }
1073
1074 // if the coherence time is over the channel has to be updated
1075 if (!m_updatePeriod.IsZero() &&
1076 Simulator::Now() - channelParams->m_generatedTime > m_updatePeriod)
1077 {
1078 NS_LOG_DEBUG("Generation time " << channelParams->m_generatedTime.As(Time::NS) << " now "
1079 << Now().As(Time::NS));
1080 update = true;
1081 }
1082
1083 return update;
1084}
1085
1086bool
1088 Ptr<const ChannelMatrix> channelMatrix)
1089{
1090 return channelParams->m_generatedTime > channelMatrix->m_generatedTime;
1091}
1092
1093bool
1096 Ptr<const ChannelMatrix> channelMatrix)
1097{
1098 // This allows changing the number of antenna ports during execution,
1099 // which is used by nr's initial association.
1100 size_t sAntNumElems = aAntenna->GetNumElems();
1101 size_t uAntNumElems = bAntenna->GetNumElems();
1102 size_t chanNumRows = channelMatrix->m_channel.GetNumRows();
1103 size_t chanNumCols = channelMatrix->m_channel.GetNumCols();
1104 return ((uAntNumElems != chanNumRows) || (sAntNumElems != chanNumCols)) &&
1105 ((uAntNumElems != chanNumCols) || (sAntNumElems != chanNumRows));
1106}
1107
1113{
1114 NS_LOG_FUNCTION(this);
1115
1116 // Compute the channel params key. The key is reciprocal, i.e., key (a, b) = key (b, a)
1117 uint64_t channelParamsKey =
1118 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
1119 // Compute the channel matrix key. The key is reciprocal, i.e., key (a, b) = key (b, a)
1120 uint64_t channelMatrixKey = GetKey(aAntenna->GetId(), bAntenna->GetId());
1121
1122 // retrieve the channel condition
1123 Ptr<const ChannelCondition> condition =
1124 m_channelConditionModel->GetChannelCondition(aMob, bMob);
1125
1126 // Check if the channel is present in the map and return it, otherwise
1127 // generate a new channel
1128 bool updateParams = false;
1129 bool updateMatrix = false;
1130 bool notFoundParams = false;
1131 bool notFoundMatrix = false;
1132 Ptr<ChannelMatrix> channelMatrix;
1133 Ptr<ThreeGppChannelParams> channelParams;
1134
1135 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
1136 {
1137 channelParams = m_channelParamsMap[channelParamsKey];
1138 // check if it has to be updated
1139 updateParams = ChannelParamsNeedsUpdate(channelParams, condition);
1140 }
1141 else
1142 {
1143 NS_LOG_DEBUG("channel params not found");
1144 notFoundParams = true;
1145 }
1146
1147 double x = aMob->GetPosition().x - bMob->GetPosition().x;
1148 double y = aMob->GetPosition().y - bMob->GetPosition().y;
1149 double distance2D = sqrt(x * x + y * y);
1150
1151 // NOTE we assume hUT = min (height(a), height(b)) and
1152 // hBS = max (height (a), height (b))
1153 double hUt = std::min(aMob->GetPosition().z, bMob->GetPosition().z);
1154 double hBs = std::max(aMob->GetPosition().z, bMob->GetPosition().z);
1155
1156 // get the 3GPP parameters
1157 Ptr<const ParamsTable> table3gpp = GetThreeGppTable(condition, hBs, hUt, distance2D);
1158
1159 if (notFoundParams || updateParams)
1160 {
1161 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
1162 // Step 5: Generate Delays.
1163 // Step 6: Generate cluster powers.
1164 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
1165 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
1166 // shuffle all the arrays to perform random coupling
1167 // Step 9: Generate the cross polarization power ratios
1168 // Step 10: Draw initial phases
1169 channelParams = GenerateChannelParameters(condition, table3gpp, aMob, bMob);
1170 // store or replace the channel parameters
1171 m_channelParamsMap[channelParamsKey] = channelParams;
1172 }
1173
1174 if (m_channelMatrixMap.find(channelMatrixKey) != m_channelMatrixMap.end())
1175 {
1176 // channel matrix present in the map
1177 NS_LOG_DEBUG("channel matrix present in the map");
1178 channelMatrix = m_channelMatrixMap[channelMatrixKey];
1179 updateMatrix = ChannelMatrixNeedsUpdate(channelParams, channelMatrix);
1180 updateMatrix |= AntennaSetupChanged(aAntenna, bAntenna, channelMatrix);
1181 }
1182 else
1183 {
1184 NS_LOG_DEBUG("channel matrix not found");
1185 notFoundMatrix = true;
1186 }
1187
1188 // If the channel is not present in the map or if it has to be updated
1189 // generate a new realization
1190 if (notFoundMatrix || updateMatrix)
1191 {
1192 // channel matrix not found or has to be updated, generate a new one
1193 channelMatrix = GetNewChannel(channelParams, table3gpp, aMob, bMob, aAntenna, bAntenna);
1194 channelMatrix->m_antennaPair =
1195 std::make_pair(aAntenna->GetId(),
1196 bAntenna->GetId()); // save antenna pair, with the exact order of s and u
1197 // antennas at the moment of the channel generation
1198
1199 // store or replace the channel matrix in the channel map
1200 m_channelMatrixMap[channelMatrixKey] = channelMatrix;
1201 }
1202
1203 return channelMatrix;
1204}
1205
1208{
1209 NS_LOG_FUNCTION(this);
1210
1211 // Compute the channel key. The key is reciprocal, i.e., key (a, b) = key (b, a)
1212 uint64_t channelParamsKey =
1213 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
1214
1215 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
1216 {
1217 return m_channelParamsMap.find(channelParamsKey)->second;
1218 }
1219 else
1220 {
1221 NS_LOG_WARN("Channel params map not found. Returning a nullptr.");
1222 return nullptr;
1223 }
1224}
1225
1228 const Ptr<const ParamsTable> table3gpp,
1229 const Ptr<const MobilityModel> aMob,
1230 const Ptr<const MobilityModel> bMob) const
1231{
1232 NS_LOG_FUNCTION(this);
1233 // create a channel matrix instance
1234 Ptr<ThreeGppChannelParams> channelParams = Create<ThreeGppChannelParams>();
1235 channelParams->m_generatedTime = Simulator::Now();
1236 channelParams->m_nodeIds =
1237 std::make_pair(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
1238 channelParams->m_losCondition = channelCondition->GetLosCondition();
1239 channelParams->m_o2iCondition = channelCondition->GetO2iCondition();
1240
1241 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
1242 DoubleVector LSPsIndep;
1243 DoubleVector LSPs;
1244 uint8_t paramNum = 6;
1245 if (channelParams->m_losCondition == ChannelCondition::LOS)
1246 {
1247 paramNum = 7;
1248 }
1249
1250 // Generate paramNum independent LSPs.
1251 for (uint8_t iter = 0; iter < paramNum; iter++)
1252 {
1253 LSPsIndep.push_back(m_normalRv->GetValue());
1254 }
1255 for (uint8_t row = 0; row < paramNum; row++)
1256 {
1257 double temp = 0;
1258 for (uint8_t column = 0; column < paramNum; column++)
1259 {
1260 temp += table3gpp->m_sqrtC[row][column] * LSPsIndep[column];
1261 }
1262 LSPs.push_back(temp);
1263 }
1264
1265 // NOTE the shadowing is generated in the propagation loss model
1266 double DS;
1267 double ASD;
1268 double ASA;
1269 double ZSA;
1270 double ZSD;
1271 double kFactor = 0;
1272 if (channelParams->m_losCondition == ChannelCondition::LOS)
1273 {
1274 kFactor = LSPs[1] * table3gpp->m_sigK + table3gpp->m_uK;
1275 DS = pow(10, LSPs[2] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
1276 ASD = pow(10, LSPs[3] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
1277 ASA = pow(10, LSPs[4] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
1278 ZSD = pow(10, LSPs[5] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
1279 ZSA = pow(10, LSPs[6] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
1280 }
1281 else
1282 {
1283 DS = pow(10, LSPs[1] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
1284 ASD = pow(10, LSPs[2] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
1285 ASA = pow(10, LSPs[3] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
1286 ZSD = pow(10, LSPs[4] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
1287 ZSA = pow(10, LSPs[5] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
1288 }
1289 ASD = std::min(ASD, 104.0);
1290 ASA = std::min(ASA, 104.0);
1291 ZSD = std::min(ZSD, 52.0);
1292 ZSA = std::min(ZSA, 52.0);
1293
1294 // save DS and K_factor parameters in the structure
1295 channelParams->m_DS = DS;
1296 channelParams->m_K_factor = kFactor;
1297
1298 NS_LOG_INFO("K-factor=" << kFactor << ", DS=" << DS << ", ASD=" << ASD << ", ASA=" << ASA
1299 << ", ZSD=" << ZSD << ", ZSA=" << ZSA);
1300
1301 // Step 5: Generate Delays.
1302 DoubleVector clusterDelay;
1303 double minTau = 100.0;
1304 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1305 {
1306 double tau = -1 * table3gpp->m_rTau * DS * log(m_uniformRv->GetValue(0, 1)); //(7.5-1)
1307 if (minTau > tau)
1308 {
1309 minTau = tau;
1310 }
1311 clusterDelay.push_back(tau);
1312 }
1313
1314 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1315 {
1316 clusterDelay[cIndex] -= minTau;
1317 }
1318 std::sort(clusterDelay.begin(), clusterDelay.end()); //(7.5-2)
1319
1320 /* since the scaled Los delays are not to be used in cluster power generation,
1321 * we will generate cluster power first and resume to compute Los cluster delay later.*/
1322
1323 // Step 6: Generate cluster powers.
1324 DoubleVector clusterPower;
1325 double powerSum = 0;
1326 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1327 {
1328 double power =
1329 exp(-1 * clusterDelay[cIndex] * (table3gpp->m_rTau - 1) / table3gpp->m_rTau / DS) *
1330 pow(10,
1331 -1 * m_normalRv->GetValue() * table3gpp->m_perClusterShadowingStd / 10.0); //(7.5-5)
1332 powerSum += power;
1333 clusterPower.push_back(power);
1334 }
1335 channelParams->m_clusterPower = clusterPower;
1336
1337 double powerMax = 0;
1338
1339 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1340 {
1341 channelParams->m_clusterPower[cIndex] =
1342 channelParams->m_clusterPower[cIndex] / powerSum; //(7.5-6)
1343 }
1344
1345 DoubleVector clusterPowerForAngles; // this power is only for equation (7.5-9) and (7.5-14), not
1346 // for (7.5-22)
1347 if (channelParams->m_losCondition == ChannelCondition::LOS)
1348 {
1349 double kLinear = pow(10, kFactor / 10.0);
1350
1351 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1352 {
1353 if (cIndex == 0)
1354 {
1355 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
1356 (1 + kLinear) +
1357 kLinear / (1 + kLinear)); //(7.5-8)
1358 }
1359 else
1360 {
1361 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
1362 (1 + kLinear)); //(7.5-8)
1363 }
1364 if (powerMax < clusterPowerForAngles[cIndex])
1365 {
1366 powerMax = clusterPowerForAngles[cIndex];
1367 }
1368 }
1369 }
1370 else
1371 {
1372 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
1373 {
1374 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex]); //(7.5-6)
1375 if (powerMax < clusterPowerForAngles[cIndex])
1376 {
1377 powerMax = clusterPowerForAngles[cIndex];
1378 }
1379 }
1380 }
1381
1382 // remove clusters with less than -25 dB power compared to the maxim cluster power;
1383 // double thresh = pow(10, -2.5);
1384 double thresh = 0.0032;
1385 for (uint8_t cIndex = table3gpp->m_numOfCluster; cIndex > 0; cIndex--)
1386 {
1387 if (clusterPowerForAngles[cIndex - 1] < thresh * powerMax)
1388 {
1389 clusterPowerForAngles.erase(clusterPowerForAngles.begin() + cIndex - 1);
1390 channelParams->m_clusterPower.erase(channelParams->m_clusterPower.begin() + cIndex - 1);
1391 clusterDelay.erase(clusterDelay.begin() + cIndex - 1);
1392 }
1393 }
1394
1395 NS_ASSERT(channelParams->m_clusterPower.size() < UINT8_MAX);
1396 channelParams->m_reducedClusterNumber = channelParams->m_clusterPower.size();
1397 // Resume step 5 to compute the delay for LoS condition.
1398 if (channelParams->m_losCondition == ChannelCondition::LOS)
1399 {
1400 double cTau =
1401 0.7705 - 0.0433 * kFactor + 2e-4 * pow(kFactor, 2) + 17e-6 * pow(kFactor, 3); //(7.5-3)
1402 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1403 {
1404 clusterDelay[cIndex] = clusterDelay[cIndex] / cTau; //(7.5-4)
1405 }
1406 }
1407
1408 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
1409
1410 double cNlos;
1411 // According to table 7.5-6, only cluster number equals to 8, 10, 11, 12, 19 and 20 is valid.
1412 // Not sure why the other cases are in Table 7.5-2.
1413 switch (table3gpp->m_numOfCluster) // Table 7.5-2
1414 {
1415 case 4:
1416 cNlos = 0.779;
1417 break;
1418 case 5:
1419 cNlos = 0.860;
1420 break;
1421 case 8:
1422 cNlos = 1.018;
1423 break;
1424 case 10:
1425 cNlos = 1.090;
1426 break;
1427 case 11:
1428 cNlos = 1.123;
1429 break;
1430 case 12:
1431 cNlos = 1.146;
1432 break;
1433 case 14:
1434 cNlos = 1.190;
1435 break;
1436 case 15:
1437 cNlos = 1.221;
1438 break;
1439 case 16:
1440 cNlos = 1.226;
1441 break;
1442 case 19:
1443 cNlos = 1.273;
1444 break;
1445 case 20:
1446 cNlos = 1.289;
1447 break;
1448 default:
1449 NS_FATAL_ERROR("Invalid cluster number");
1450 }
1451
1452 double cPhi = cNlos;
1453
1454 if (channelParams->m_losCondition == ChannelCondition::LOS)
1455 {
1456 cPhi *= (1.1035 - 0.028 * kFactor - 2e-3 * pow(kFactor, 2) +
1457 1e-4 * pow(kFactor, 3)); //(7.5-10))
1458 }
1459
1460 switch (table3gpp->m_numOfCluster) // Table 7.5-4
1461 {
1462 case 8:
1463 cNlos = 0.889;
1464 break;
1465 case 10:
1466 cNlos = 0.957;
1467 break;
1468 case 11:
1469 cNlos = 1.031;
1470 break;
1471 case 12:
1472 cNlos = 1.104;
1473 break;
1474 case 15:
1475 cNlos = 1.1088;
1476 break;
1477 case 19:
1478 cNlos = 1.184;
1479 break;
1480 case 20:
1481 cNlos = 1.178;
1482 break;
1483 default:
1484 NS_FATAL_ERROR("Invalid cluster number");
1485 }
1486
1487 double cTheta = cNlos;
1488 if (channelCondition->IsLos())
1489 {
1490 cTheta *= (1.3086 + 0.0339 * kFactor - 0.0077 * pow(kFactor, 2) +
1491 2e-4 * pow(kFactor, 3)); //(7.5-15)
1492 }
1493
1494 DoubleVector clusterAoa;
1495 DoubleVector clusterAod;
1496 DoubleVector clusterZoa;
1497 DoubleVector clusterZod;
1498 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1499 {
1500 double logCalc = -1 * log(clusterPowerForAngles[cIndex] / powerMax);
1501 double angle = 2 * sqrt(logCalc) / 1.4 / cPhi; //(7.5-9)
1502 clusterAoa.push_back(ASA * angle);
1503 clusterAod.push_back(ASD * angle);
1504 angle = logCalc / cTheta; //(7.5-14)
1505 clusterZoa.push_back(ZSA * angle);
1506 clusterZod.push_back(ZSD * angle);
1507 }
1508
1509 Angles sAngle(bMob->GetPosition(), aMob->GetPosition());
1510 Angles uAngle(aMob->GetPosition(), bMob->GetPosition());
1511
1512 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1513 {
1514 int Xn = 1;
1515 if (m_uniformRv->GetValue(0, 1) < 0.5)
1516 {
1517 Xn = -1;
1518 }
1519 clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue() * ASA / 7.0) +
1520 RadiansToDegrees(uAngle.GetAzimuth()); //(7.5-11)
1521 clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue() * ASD / 7.0) +
1522 RadiansToDegrees(sAngle.GetAzimuth());
1523 if (channelCondition->IsO2i())
1524 {
1525 clusterZoa[cIndex] =
1526 clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) + 90; //(7.5-16)
1527 }
1528 else
1529 {
1530 clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) +
1531 RadiansToDegrees(uAngle.GetInclination()); //(7.5-16)
1532 }
1533 clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue() * ZSD / 7.0) +
1535 table3gpp->m_offsetZOD; //(7.5-19)
1536 }
1537
1538 if (channelParams->m_losCondition == ChannelCondition::LOS)
1539 {
1540 // The 7.5-12 can be rewrite as Theta_n,ZOA = Theta_n,ZOA - (Theta_1,ZOA - Theta_LOS,ZOA) =
1541 // Theta_n,ZOA - diffZOA, Similar as AOD, ZSA and ZSD.
1542 double diffAoa = clusterAoa[0] - RadiansToDegrees(uAngle.GetAzimuth());
1543 double diffAod = clusterAod[0] - RadiansToDegrees(sAngle.GetAzimuth());
1544 double diffZsa = clusterZoa[0] - RadiansToDegrees(uAngle.GetInclination());
1545 double diffZsd = clusterZod[0] - RadiansToDegrees(sAngle.GetInclination());
1546
1547 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1548 {
1549 clusterAoa[cIndex] -= diffAoa; //(7.5-12)
1550 clusterAod[cIndex] -= diffAod;
1551 clusterZoa[cIndex] -= diffZsa; //(7.5-17)
1552 clusterZod[cIndex] -= diffZsd;
1553 }
1554 }
1555
1556 double sizeTemp = clusterZoa.size();
1557 for (uint8_t ind = 0; ind < 4; ind++)
1558 {
1559 DoubleVector angleDegree;
1560 switch (ind)
1561 {
1562 case 0:
1563 angleDegree = clusterAoa;
1564 break;
1565 case 1:
1566 angleDegree = clusterZoa;
1567 break;
1568 case 2:
1569 angleDegree = clusterAod;
1570 break;
1571 case 3:
1572 angleDegree = clusterZod;
1573 break;
1574 default:
1575 NS_FATAL_ERROR("Programming Error");
1576 }
1577 for (uint8_t nIndex = 0; nIndex < sizeTemp; nIndex++)
1578 {
1579 while (angleDegree[nIndex] > 360)
1580 {
1581 angleDegree[nIndex] -= 360;
1582 }
1583
1584 while (angleDegree[nIndex] < 0)
1585 {
1586 angleDegree[nIndex] += 360;
1587 }
1588
1589 if (ind == 1 || ind == 3)
1590 {
1591 if (angleDegree[nIndex] > 180)
1592 {
1593 angleDegree[nIndex] = 360 - angleDegree[nIndex];
1594 }
1595 }
1596 }
1597 switch (ind)
1598 {
1599 case 0:
1600 clusterAoa = angleDegree;
1601 break;
1602 case 1:
1603 clusterZoa = angleDegree;
1604 break;
1605 case 2:
1606 clusterAod = angleDegree;
1607 break;
1608 case 3:
1609 clusterZod = angleDegree;
1610 break;
1611 default:
1612 NS_FATAL_ERROR("Programming Error");
1613 }
1614 }
1615
1616 DoubleVector attenuationDb;
1617 if (m_blockage)
1618 {
1619 attenuationDb = CalcAttenuationOfBlockage(channelParams, clusterAoa, clusterZoa);
1620 for (uint8_t cInd = 0; cInd < channelParams->m_reducedClusterNumber; cInd++)
1621 {
1622 channelParams->m_clusterPower[cInd] =
1623 channelParams->m_clusterPower[cInd] / pow(10, attenuationDb[cInd] / 10.0);
1624 }
1625 }
1626 else
1627 {
1628 attenuationDb.push_back(0);
1629 }
1630
1631 // store attenuation
1632 channelParams->m_attenuation_dB = attenuationDb;
1633
1634 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
1635 // shuffle all the arrays to perform random coupling
1637 channelParams->m_reducedClusterNumber,
1638 DoubleVector(table3gpp->m_raysPerCluster,
1639 0)); // rayAoaRadian[n][m], where n is cluster index, m is ray index
1641 channelParams->m_reducedClusterNumber,
1642 DoubleVector(table3gpp->m_raysPerCluster,
1643 0)); // rayAodRadian[n][m], where n is cluster index, m is ray index
1645 channelParams->m_reducedClusterNumber,
1646 DoubleVector(table3gpp->m_raysPerCluster,
1647 0)); // rayZoaRadian[n][m], where n is cluster index, m is ray index
1649 channelParams->m_reducedClusterNumber,
1650 DoubleVector(table3gpp->m_raysPerCluster,
1651 0)); // rayZodRadian[n][m], where n is cluster index, m is ray index
1652
1653 const double pow10_uLgZSD = pow(10, table3gpp->m_uLgZSD);
1654 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
1655 {
1656 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
1657 {
1658 double tempAoa = clusterAoa[nInd] + table3gpp->m_cASA * offSetAlpha[mInd]; //(7.5-13)
1659 double tempZoa = clusterZoa[nInd] + table3gpp->m_cZSA * offSetAlpha[mInd]; //(7.5-18)
1660 std::tie(rayAoaRadian[nInd][mInd], rayZoaRadian[nInd][mInd]) =
1661 WrapAngles(DegreesToRadians(tempAoa), DegreesToRadians(tempZoa));
1662
1663 double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd]; //(7.5-13)
1664 double tempZod = clusterZod[nInd] + 0.375 * pow10_uLgZSD * offSetAlpha[mInd]; //(7.5-20)
1665 std::tie(rayAodRadian[nInd][mInd], rayZodRadian[nInd][mInd]) =
1666 WrapAngles(DegreesToRadians(tempAod), DegreesToRadians(tempZod));
1667 }
1668 }
1669
1670 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1671 {
1672 Shuffle(&rayAodRadian[cIndex][0], &rayAodRadian[cIndex][table3gpp->m_raysPerCluster]);
1673 Shuffle(&rayAoaRadian[cIndex][0], &rayAoaRadian[cIndex][table3gpp->m_raysPerCluster]);
1674 Shuffle(&rayZodRadian[cIndex][0], &rayZodRadian[cIndex][table3gpp->m_raysPerCluster]);
1675 Shuffle(&rayZoaRadian[cIndex][0], &rayZoaRadian[cIndex][table3gpp->m_raysPerCluster]);
1676 }
1677
1678 // store values
1679 channelParams->m_rayAodRadian = rayAodRadian;
1680 channelParams->m_rayAoaRadian = rayAoaRadian;
1681 channelParams->m_rayZodRadian = rayZodRadian;
1682 channelParams->m_rayZoaRadian = rayZoaRadian;
1683
1684 // Step 9: Generate the cross polarization power ratios
1685 // Step 10: Draw initial phases
1686
1687 // vector containing the cross polarization power ratios, as defined by 7.5-21
1688 auto& crossPolarizationPowerRatios = channelParams->m_crossPolarizationPowerRatios;
1689 // rayAoaRadian[n][m], where n is cluster index, m is ray index
1690 auto& clusterPhase = channelParams->m_clusterPhase;
1691
1692 const double uXprLinear = pow(10, table3gpp->m_uXpr / 10.0); // convert to linear
1693 const double sigXprLinear = pow(10, table3gpp->m_sigXpr / 10.0); // convert to linear
1694
1695 // store the PHI values for all the possible combination of polarization
1696 clusterPhase.resize(channelParams->m_reducedClusterNumber);
1697 crossPolarizationPowerRatios.resize(channelParams->m_reducedClusterNumber);
1698 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
1699 {
1700 clusterPhase[nInd].resize(table3gpp->m_raysPerCluster);
1701 crossPolarizationPowerRatios[nInd].resize(table3gpp->m_raysPerCluster);
1702 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
1703 {
1704 clusterPhase[nInd][mInd].resize(4);
1705 // used to store the XPR values
1706 crossPolarizationPowerRatios[nInd][mInd] =
1707 std::pow(10, (m_normalRv->GetValue() * sigXprLinear + uXprLinear) / 10.0);
1708 for (uint8_t pInd = 0; pInd < 4; pInd++)
1709 {
1710 // used to store the PHI values
1711 clusterPhase[nInd][mInd][pInd] = m_uniformRv->GetValue(-1 * M_PI, M_PI);
1712 }
1713 }
1714 }
1715
1716 uint8_t cluster1st = 0;
1717 uint8_t cluster2nd = 0; // first and second strongest cluster;
1718 double maxPower = 0;
1719 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1720 {
1721 if (maxPower < channelParams->m_clusterPower[cIndex])
1722 {
1723 maxPower = channelParams->m_clusterPower[cIndex];
1724 cluster1st = cIndex;
1725 }
1726 }
1727 channelParams->m_cluster1st = cluster1st;
1728 maxPower = 0;
1729 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
1730 {
1731 if (maxPower < channelParams->m_clusterPower[cIndex] && cluster1st != cIndex)
1732 {
1733 maxPower = channelParams->m_clusterPower[cIndex];
1734 cluster2nd = cIndex;
1735 }
1736 }
1737 channelParams->m_cluster2nd = cluster2nd;
1738
1739 NS_LOG_INFO("1st strongest cluster:" << +cluster1st
1740 << ", 2nd strongest cluster:" << +cluster2nd);
1741
1742 // store the delays and the angles for the subclusters
1743 if (cluster1st == cluster2nd)
1744 {
1745 clusterDelay.push_back(clusterDelay[cluster1st] + 1.28 * table3gpp->m_cDS);
1746 clusterDelay.push_back(clusterDelay[cluster1st] + 2.56 * table3gpp->m_cDS);
1747
1748 clusterAoa.push_back(clusterAoa[cluster1st]);
1749 clusterAoa.push_back(clusterAoa[cluster1st]);
1750
1751 clusterZoa.push_back(clusterZoa[cluster1st]);
1752 clusterZoa.push_back(clusterZoa[cluster1st]);
1753
1754 clusterAod.push_back(clusterAod[cluster1st]);
1755 clusterAod.push_back(clusterAod[cluster1st]);
1756
1757 clusterZod.push_back(clusterZod[cluster1st]);
1758 clusterZod.push_back(clusterZod[cluster1st]);
1759 }
1760 else
1761 {
1762 double min;
1763 double max;
1764 if (cluster1st < cluster2nd)
1765 {
1766 min = cluster1st;
1767 max = cluster2nd;
1768 }
1769 else
1770 {
1771 min = cluster2nd;
1772 max = cluster1st;
1773 }
1774 clusterDelay.push_back(clusterDelay[min] + 1.28 * table3gpp->m_cDS);
1775 clusterDelay.push_back(clusterDelay[min] + 2.56 * table3gpp->m_cDS);
1776 clusterDelay.push_back(clusterDelay[max] + 1.28 * table3gpp->m_cDS);
1777 clusterDelay.push_back(clusterDelay[max] + 2.56 * table3gpp->m_cDS);
1778
1779 clusterAoa.push_back(clusterAoa[min]);
1780 clusterAoa.push_back(clusterAoa[min]);
1781 clusterAoa.push_back(clusterAoa[max]);
1782 clusterAoa.push_back(clusterAoa[max]);
1783
1784 clusterZoa.push_back(clusterZoa[min]);
1785 clusterZoa.push_back(clusterZoa[min]);
1786 clusterZoa.push_back(clusterZoa[max]);
1787 clusterZoa.push_back(clusterZoa[max]);
1788
1789 clusterAod.push_back(clusterAod[min]);
1790 clusterAod.push_back(clusterAod[min]);
1791 clusterAod.push_back(clusterAod[max]);
1792 clusterAod.push_back(clusterAod[max]);
1793
1794 clusterZod.push_back(clusterZod[min]);
1795 clusterZod.push_back(clusterZod[min]);
1796 clusterZod.push_back(clusterZod[max]);
1797 clusterZod.push_back(clusterZod[max]);
1798 }
1799
1800 channelParams->m_delay = clusterDelay;
1801 channelParams->m_angle.clear();
1802 channelParams->m_angle.push_back(clusterAoa);
1803 channelParams->m_angle.push_back(clusterZoa);
1804 channelParams->m_angle.push_back(clusterAod);
1805 channelParams->m_angle.push_back(clusterZod);
1806
1807 // Precompute angles sincos
1808 channelParams->m_cachedAngleSincos.resize(channelParams->m_angle.size());
1809 for (size_t direction = 0; direction < channelParams->m_angle.size(); direction++)
1810 {
1811 channelParams->m_cachedAngleSincos[direction].resize(
1812 channelParams->m_angle[direction].size());
1813 for (size_t cluster = 0; cluster < channelParams->m_angle[direction].size(); cluster++)
1814 {
1815 channelParams->m_cachedAngleSincos[direction][cluster] = {
1816 sin(channelParams->m_angle[direction][cluster] * DEG2RAD),
1817 cos(channelParams->m_angle[direction][cluster] * DEG2RAD)};
1818 }
1819 }
1820
1821 // Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
1822 // These terms account for an additional Doppler contribution due to the
1823 // presence of moving objects in the surrounding environment, such as in
1824 // vehicular scenarios.
1825 // This contribution is applied only to the delayed (reflected) paths and
1826 // must be properly configured by setting the value of
1827 // m_vScatt, which is defined as "maximum speed of the vehicle in the
1828 // layout".
1829 // By default, m_vScatt is set to 0, so there is no additional Doppler
1830 // contribution.
1831
1832 DoubleVector dopplerTermAlpha;
1833 DoubleVector dopplerTermD;
1834
1835 // 2 or 4 is added to account for additional subrays for the 1st and 2nd clusters, if there is
1836 // only one cluster then would be added 2 more subrays (see creation of Husn channel matrix)
1837 uint8_t updatedClusterNumber = (channelParams->m_reducedClusterNumber == 1)
1838 ? channelParams->m_reducedClusterNumber + 2
1839 : channelParams->m_reducedClusterNumber + 4;
1840
1841 for (uint8_t cIndex = 0; cIndex < updatedClusterNumber; cIndex++)
1842 {
1843 double alpha = 0;
1844 double D = 0;
1845 if (cIndex != 0)
1846 {
1847 alpha = m_uniformRvDoppler->GetValue(-1, 1);
1849 }
1850 dopplerTermAlpha.push_back(alpha);
1851 dopplerTermD.push_back(D);
1852 }
1853 channelParams->m_alpha = dopplerTermAlpha;
1854 channelParams->m_D = dopplerTermD;
1855
1856 return channelParams;
1857}
1858
1861 Ptr<const ParamsTable> table3gpp,
1862 const Ptr<const MobilityModel> sMob,
1863 const Ptr<const MobilityModel> uMob,
1865 Ptr<const PhasedArrayModel> uAntenna) const
1866{
1867 NS_LOG_FUNCTION(this);
1868
1869 NS_ASSERT_MSG(m_frequency > 0.0, "Set the operating frequency first!");
1870
1871 // create a channel matrix instance
1872 Ptr<ChannelMatrix> channelMatrix = Create<ChannelMatrix>();
1873 channelMatrix->m_generatedTime = Simulator::Now();
1874 // save in which order is generated this matrix
1875 channelMatrix->m_nodeIds =
1876 std::make_pair(sMob->GetObject<Node>()->GetId(), uMob->GetObject<Node>()->GetId());
1877 // check if channelParams structure is generated in direction s-to-u or u-to-s
1878 bool isSameDirection = (channelParams->m_nodeIds == channelMatrix->m_nodeIds);
1879
1884
1885 // if channel params is generated in the same direction in which we
1886 // generate the channel matrix, angles and zenith od departure and arrival are ok,
1887 // just set them to corresponding variable that will be used for the generation
1888 // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
1889 if (isSameDirection)
1890 {
1891 rayAodRadian = channelParams->m_rayAodRadian;
1892 rayAoaRadian = channelParams->m_rayAoaRadian;
1893 rayZodRadian = channelParams->m_rayZodRadian;
1894 rayZoaRadian = channelParams->m_rayZoaRadian;
1895 }
1896 else
1897 {
1898 rayAodRadian = channelParams->m_rayAoaRadian;
1899 rayAoaRadian = channelParams->m_rayAodRadian;
1900 rayZodRadian = channelParams->m_rayZoaRadian;
1901 rayZoaRadian = channelParams->m_rayZodRadian;
1902 }
1903
1904 // Step 11: Generate channel coefficients for each cluster n and each receiver
1905 // and transmitter element pair u,s.
1906 // where n is cluster index, u and s are receive and transmit antenna element.
1907 size_t uSize = uAntenna->GetNumElems();
1908 size_t sSize = sAntenna->GetNumElems();
1909
1910 // NOTE: Since each of the strongest 2 clusters are divided into 3 sub-clusters,
1911 // the total cluster will generally be numReducedCLuster + 4.
1912 // However, it might be that m_cluster1st = m_cluster2nd. In this case the
1913 // total number of clusters will be numReducedCLuster + 2.
1914 uint16_t numOverallCluster = (channelParams->m_cluster1st != channelParams->m_cluster2nd)
1915 ? channelParams->m_reducedClusterNumber + 4
1916 : channelParams->m_reducedClusterNumber + 2;
1917 Complex3DVector hUsn(uSize, sSize, numOverallCluster); // channel coefficient hUsn (u, s, n);
1918 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPhase.size());
1919 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPower.size());
1920 NS_ASSERT(channelParams->m_reducedClusterNumber <=
1921 channelParams->m_crossPolarizationPowerRatios.size());
1922 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZoaRadian.size());
1923 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZodRadian.size());
1924 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAoaRadian.size());
1925 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAodRadian.size());
1926 NS_ASSERT(table3gpp->m_raysPerCluster <= channelParams->m_clusterPhase[0].size());
1927 NS_ASSERT(table3gpp->m_raysPerCluster <=
1928 channelParams->m_crossPolarizationPowerRatios[0].size());
1929 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZoaRadian[0].size());
1930 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZodRadian[0].size());
1931 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAoaRadian[0].size());
1932 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAodRadian[0].size());
1933
1934 double x = sMob->GetPosition().x - uMob->GetPosition().x;
1935 double y = sMob->GetPosition().y - uMob->GetPosition().y;
1936 double distance2D = sqrt(x * x + y * y);
1937 // NOTE we assume hUT = min (height(a), height(b)) and
1938 // hBS = max (height (a), height (b))
1939 double hUt = std::min(sMob->GetPosition().z, uMob->GetPosition().z);
1940 double hBs = std::max(sMob->GetPosition().z, uMob->GetPosition().z);
1941 // compute the 3D distance using eq. 7.4-1
1942 double distance3D = std::sqrt(distance2D * distance2D + (hBs - hUt) * (hBs - hUt));
1943
1944 Angles sAngle(uMob->GetPosition(), sMob->GetPosition());
1945 Angles uAngle(sMob->GetPosition(), uMob->GetPosition());
1946
1947 Double2DVector sinCosA; // cached multiplications of sin and cos of the ZoA and AoA angles
1948 Double2DVector sinSinA; // cached multiplications of sines of the ZoA and AoA angles
1949 Double2DVector cosZoA; // cached cos of the ZoA angle
1950 Double2DVector sinCosD; // cached multiplications of sin and cos of the ZoD and AoD angles
1951 Double2DVector sinSinD; // cached multiplications of the cosines of the ZoA and AoA angles
1952 Double2DVector cosZoD; // cached cos of the ZoD angle
1953
1954 // contains part of the ray expression, cached as independent from the u- and s-indexes,
1955 // but calculate it for different polarization angles of s and u
1956 std::map<std::pair<uint8_t, uint8_t>, Complex2DVector> raysPreComp;
1957 for (size_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
1958 {
1959 for (size_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
1960 {
1961 raysPreComp[std::make_pair(polSa, polUa)] =
1962 Complex2DVector(channelParams->m_reducedClusterNumber, table3gpp->m_raysPerCluster);
1963 }
1964 }
1965
1966 // resize to appropriate dimensions
1967 sinCosA.resize(channelParams->m_reducedClusterNumber);
1968 sinSinA.resize(channelParams->m_reducedClusterNumber);
1969 cosZoA.resize(channelParams->m_reducedClusterNumber);
1970 sinCosD.resize(channelParams->m_reducedClusterNumber);
1971 sinSinD.resize(channelParams->m_reducedClusterNumber);
1972 cosZoD.resize(channelParams->m_reducedClusterNumber);
1973 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
1974 {
1975 sinCosA[nIndex].resize(table3gpp->m_raysPerCluster);
1976 sinSinA[nIndex].resize(table3gpp->m_raysPerCluster);
1977 cosZoA[nIndex].resize(table3gpp->m_raysPerCluster);
1978 sinCosD[nIndex].resize(table3gpp->m_raysPerCluster);
1979 sinSinD[nIndex].resize(table3gpp->m_raysPerCluster);
1980 cosZoD[nIndex].resize(table3gpp->m_raysPerCluster);
1981 }
1982 // pre-compute the terms which are independent from uIndex and sIndex
1983 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
1984 {
1985 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
1986 {
1987 DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
1988 NS_ASSERT(4 <= initialPhase.size());
1989 double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
1990
1991 // cache the component of the "rays" terms which depend on the random angle of arrivals
1992 // and departures and initial phases only
1993 for (uint8_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
1994 {
1995 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
1996 Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
1997 channelParams->m_rayZoaRadian[nIndex][mIndex]),
1998 polUa);
1999 for (uint8_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
2000 {
2001 auto [txFieldPatternPhi, txFieldPatternTheta] =
2002 sAntenna->GetElementFieldPattern(
2003 Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
2004 channelParams->m_rayZodRadian[nIndex][mIndex]),
2005 polSa);
2006 raysPreComp[std::make_pair(polSa, polUa)](nIndex, mIndex) =
2007 std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
2008 rxFieldPatternTheta * txFieldPatternTheta +
2009 std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
2010 std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
2011 std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
2012 std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
2013 std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
2014 rxFieldPatternPhi * txFieldPatternPhi;
2015 }
2016 }
2017
2018 // cache the component of the "rxPhaseDiff" terms which depend on the random angle of
2019 // arrivals only
2020 double sinRayZoa = sin(rayZoaRadian[nIndex][mIndex]);
2021 double sinRayAoa = sin(rayAoaRadian[nIndex][mIndex]);
2022 double cosRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
2023 sinCosA[nIndex][mIndex] = sinRayZoa * cosRayAoa;
2024 sinSinA[nIndex][mIndex] = sinRayZoa * sinRayAoa;
2025 cosZoA[nIndex][mIndex] = cos(rayZoaRadian[nIndex][mIndex]);
2026
2027 // cache the component of the "txPhaseDiff" terms which depend on the random angle of
2028 // departure only
2029 double sinRayZod = sin(rayZodRadian[nIndex][mIndex]);
2030 double sinRayAod = sin(rayAodRadian[nIndex][mIndex]);
2031 double cosRayAod = cos(rayAodRadian[nIndex][mIndex]);
2032 sinCosD[nIndex][mIndex] = sinRayZod * cosRayAod;
2033 sinSinD[nIndex][mIndex] = sinRayZod * sinRayAod;
2034 cosZoD[nIndex][mIndex] = cos(rayZodRadian[nIndex][mIndex]);
2035 }
2036 }
2037
2038 // The following for loops computes the channel coefficients
2039 // Keeps track of how many sub-clusters have been added up to now
2040 uint8_t numSubClustersAdded = 0;
2041 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
2042 {
2043 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
2044 {
2045 Vector uLoc = uAntenna->GetElementLocation(uIndex);
2046
2047 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
2048 {
2049 Vector sLoc = sAntenna->GetElementLocation(sIndex);
2050 // Compute the N-2 weakest cluster, assuming 0 slant angle and a
2051 // polarization slant angle configured in the array (7.5-22)
2052 if (nIndex != channelParams->m_cluster1st && nIndex != channelParams->m_cluster2nd)
2053 {
2054 std::complex<double> rays(0, 0);
2055 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
2056 {
2057 // lambda_0 is accounted in the antenna spacing uLoc and sLoc.
2058 double rxPhaseDiff =
2059 2 * M_PI *
2060 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
2061 cosZoA[nIndex][mIndex] * uLoc.z);
2062
2063 double txPhaseDiff =
2064 2 * M_PI *
2065 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
2066 cosZoD[nIndex][mIndex] * sLoc.z);
2067 // NOTE Doppler is computed in the CalcBeamformingGain function and is
2068 // simplified to only account for the center angle of each cluster.
2069 rays += raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
2070 uAntenna->GetElemPol(uIndex))](nIndex,
2071 mIndex) *
2072 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
2073 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
2074 }
2075 rays *=
2076 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
2077 hUsn(uIndex, sIndex, nIndex) = rays;
2078 }
2079 else //(7.5-28)
2080 {
2081 std::complex<double> raysSub1(0, 0);
2082 std::complex<double> raysSub2(0, 0);
2083 std::complex<double> raysSub3(0, 0);
2084
2085 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
2086 {
2087 // ZML:Just remind me that the angle offsets for the 3 subclusters were not
2088 // generated correctly.
2089 double rxPhaseDiff =
2090 2 * M_PI *
2091 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
2092 cosZoA[nIndex][mIndex] * uLoc.z);
2093
2094 double txPhaseDiff =
2095 2 * M_PI *
2096 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
2097 cosZoD[nIndex][mIndex] * sLoc.z);
2098
2099 std::complex<double> raySub =
2100 raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
2101 uAntenna->GetElemPol(uIndex))](nIndex,
2102 mIndex) *
2103 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
2104 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
2105
2106 switch (mIndex)
2107 {
2108 case 9:
2109 case 10:
2110 case 11:
2111 case 12:
2112 case 17:
2113 case 18:
2114 raysSub2 += raySub;
2115 break;
2116 case 13:
2117 case 14:
2118 case 15:
2119 case 16:
2120 raysSub3 += raySub;
2121 break;
2122 default: // case 1,2,3,4,5,6,7,8,19,20
2123 raysSub1 += raySub;
2124 break;
2125 }
2126 }
2127 raysSub1 *=
2128 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
2129 raysSub2 *=
2130 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
2131 raysSub3 *=
2132 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
2133 hUsn(uIndex, sIndex, nIndex) = raysSub1;
2134 hUsn(uIndex,
2135 sIndex,
2136 channelParams->m_reducedClusterNumber + numSubClustersAdded) = raysSub2;
2137 hUsn(uIndex,
2138 sIndex,
2139 channelParams->m_reducedClusterNumber + numSubClustersAdded + 1) =
2140 raysSub3;
2141 }
2142 }
2143 }
2144 if (nIndex == channelParams->m_cluster1st || nIndex == channelParams->m_cluster2nd)
2145 {
2146 numSubClustersAdded += 2;
2147 }
2148 }
2149
2150 if (channelParams->m_losCondition == ChannelCondition::LOS) //(7.5-29) && (7.5-30)
2151 {
2152 double lambda = 3.0e8 / m_frequency; // the wavelength of the carrier frequency
2153 std::complex<double> phaseDiffDueToDistance(cos(-2 * M_PI * distance3D / lambda),
2154 sin(-2 * M_PI * distance3D / lambda));
2155
2156 const double sinUAngleIncl = sin(uAngle.GetInclination());
2157 const double cosUAngleIncl = cos(uAngle.GetInclination());
2158 const double sinUAngleAz = sin(uAngle.GetAzimuth());
2159 const double cosUAngleAz = cos(uAngle.GetAzimuth());
2160 const double sinSAngleIncl = sin(sAngle.GetInclination());
2161 const double cosSAngleIncl = cos(sAngle.GetInclination());
2162 const double sinSAngleAz = sin(sAngle.GetAzimuth());
2163 const double cosSAngleAz = cos(sAngle.GetAzimuth());
2164
2165 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
2166 {
2167 Vector uLoc = uAntenna->GetElementLocation(uIndex);
2168 double rxPhaseDiff = 2 * M_PI *
2169 (sinUAngleIncl * cosUAngleAz * uLoc.x +
2170 sinUAngleIncl * sinUAngleAz * uLoc.y + cosUAngleIncl * uLoc.z);
2171
2172 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
2173 {
2174 Vector sLoc = sAntenna->GetElementLocation(sIndex);
2175 std::complex<double> ray(0, 0);
2176 double txPhaseDiff =
2177 2 * M_PI *
2178 (sinSAngleIncl * cosSAngleAz * sLoc.x + sinSAngleIncl * sinSAngleAz * sLoc.y +
2179 cosSAngleIncl * sLoc.z);
2180
2181 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
2182 Angles(uAngle.GetAzimuth(), uAngle.GetInclination()),
2183 uAntenna->GetElemPol(uIndex));
2184 auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
2185 Angles(sAngle.GetAzimuth(), sAngle.GetInclination()),
2186 sAntenna->GetElemPol(sIndex));
2187
2188 ray = (rxFieldPatternTheta * txFieldPatternTheta -
2189 rxFieldPatternPhi * txFieldPatternPhi) *
2190 phaseDiffDueToDistance *
2191 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
2192 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
2193
2194 double kLinear = pow(10, channelParams->m_K_factor / 10.0);
2195 // the LOS path should be attenuated if blockage is enabled.
2196 hUsn(uIndex, sIndex, 0) =
2197 sqrt(1.0 / (kLinear + 1)) * hUsn(uIndex, sIndex, 0) +
2198 sqrt(kLinear / (1 + kLinear)) * ray /
2199 pow(10,
2200 channelParams->m_attenuation_dB[0] / 10.0); //(7.5-30) for tau = tau1
2201 for (size_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
2202 {
2203 hUsn(uIndex, sIndex, nIndex) *=
2204 sqrt(1.0 / (kLinear + 1)); //(7.5-30) for tau = tau2...tauN
2205 }
2206 }
2207 }
2208 }
2209
2210 NS_LOG_DEBUG("Husn (sAntenna, uAntenna):" << sAntenna->GetId() << ", " << uAntenna->GetId());
2211 for (size_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
2212 {
2213 for (size_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
2214 {
2215 for (size_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
2216 {
2217 NS_LOG_DEBUG(" " << hUsn(rowIdx, colIdx, cIndex) << ",");
2218 }
2219 }
2220 }
2221
2222 NS_LOG_INFO("size of coefficient matrix (rows, columns, clusters) = ("
2223 << hUsn.GetNumRows() << ", " << hUsn.GetNumCols() << ", " << hUsn.GetNumPages()
2224 << ")");
2225 channelMatrix->m_channel = hUsn;
2226 return channelMatrix;
2227}
2228
2229std::pair<double, double>
2230ThreeGppChannelModel::WrapAngles(double azimuthRad, double inclinationRad)
2231{
2232 inclinationRad = WrapTo2Pi(inclinationRad);
2233 if (inclinationRad > M_PI)
2234 {
2235 // inclination must be in [0, M_PI]
2236 inclinationRad -= M_PI;
2237 azimuthRad += M_PI;
2238 }
2239
2240 azimuthRad = WrapTo2Pi(azimuthRad);
2241
2242 NS_ASSERT_MSG(0 <= inclinationRad && inclinationRad <= M_PI,
2243 "inclinationRad=" << inclinationRad << " not valid, should be in [0, pi]");
2244 NS_ASSERT_MSG(0 <= azimuthRad && azimuthRad <= 2 * M_PI,
2245 "azimuthRad=" << azimuthRad << " not valid, should be in [0, 2*pi]");
2246
2247 return std::make_pair(azimuthRad, inclinationRad);
2248}
2249
2253 const DoubleVector& clusterAOA,
2254 const DoubleVector& clusterZOA) const
2255{
2256 NS_LOG_FUNCTION(this);
2257
2258 auto clusterNum = clusterAOA.size();
2259
2260 // Initial power attenuation for all clusters to be 0 dB
2261 DoubleVector powerAttenuation(clusterNum, 0);
2262
2263 // step a: the number of non-self blocking blockers is stored in m_numNonSelfBlocking.
2264
2265 // step b:Generate the size and location of each blocker
2266 // generate self blocking (i.e., for blockage from the human body)
2267 // table 7.6.4.1-1 Self-blocking region parameters.
2268 // Defaults: landscape mode
2269 double phiSb = 40;
2270 double xSb = 160;
2271 double thetaSb = 110;
2272 double ySb = 75;
2273 if (m_portraitMode)
2274 {
2275 phiSb = 260;
2276 xSb = 120;
2277 thetaSb = 100;
2278 ySb = 80;
2279 }
2280
2281 // generate or update non-self blocking
2282 if (channelParams->m_nonSelfBlocking.empty()) // generate new blocking regions
2283 {
2284 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
2285 {
2286 // draw value from table 7.6.4.1-2 Blocking region parameters
2287 DoubleVector table;
2288 table.push_back(m_normalRv->GetValue()); // phi_k: store the normal RV that will be
2289 // mapped to uniform (0,360) later.
2290 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
2291 {
2292 table.push_back(m_uniformRv->GetValue(15, 45)); // x_k
2293 table.push_back(90); // Theta_k
2294 table.push_back(m_uniformRv->GetValue(5, 15)); // y_k
2295 table.push_back(2); // r
2296 }
2297 else
2298 {
2299 table.push_back(m_uniformRv->GetValue(5, 15)); // x_k
2300 table.push_back(90); // Theta_k
2301 table.push_back(5); // y_k
2302 table.push_back(10); // r
2303 }
2304 channelParams->m_nonSelfBlocking.push_back(table);
2305 }
2306 }
2307 else
2308 {
2309 double deltaX = sqrt(pow(channelParams->m_preLocUT.x - channelParams->m_locUT.x, 2) +
2310 pow(channelParams->m_preLocUT.y - channelParams->m_locUT.y, 2));
2311 // if deltaX and speed are both 0, the autocorrelation is 1, skip updating
2312 if (deltaX > 1e-6 || m_blockerSpeed > 1e-6)
2313 {
2314 double corrDis;
2315 // draw value from table 7.6.4.1-4: Spatial correlation distance for different
2316 // m_scenarios.
2317 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
2318 {
2319 // InH, correlation distance = 5;
2320 corrDis = 5;
2321 }
2322 else
2323 {
2324 if (channelParams->m_o2iCondition == ChannelCondition::O2I) // outdoor to indoor
2325 {
2326 corrDis = 5;
2327 }
2328 else // LOS or NLOS
2329 {
2330 corrDis = 10;
2331 }
2332 }
2333 double R;
2334 if (m_blockerSpeed > 1e-6) // speed not equal to 0
2335 {
2336 double corrT = corrDis / m_blockerSpeed;
2337 R = exp(-1 * (deltaX / corrDis +
2338 (Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()) /
2339 corrT));
2340 }
2341 else
2342 {
2343 R = exp(-1 * (deltaX / corrDis));
2344 }
2345
2346 NS_LOG_INFO("Distance change:"
2347 << deltaX << " Speed:" << m_blockerSpeed << " Time difference:"
2348 << Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()
2349 << " correlation:" << R);
2350
2351 // In order to generate correlated uniform random variables, we first generate
2352 // correlated normal random variables and map the normal RV to uniform RV. Notice the
2353 // correlation will change if the RV is transformed from normal to uniform. To
2354 // compensate the distortion, the correlation of the normal RV is computed such that the
2355 // uniform RV would have the desired correlation when transformed from normal RV.
2356
2357 // The following formula was obtained from MATLAB numerical simulation.
2358
2359 if (R * R * (-0.069) + R * 1.074 - 0.002 <
2360 1) // transform only when the correlation of normal RV is smaller than 1
2361 {
2362 R = R * R * (-0.069) + R * 1.074 - 0.002;
2363 }
2364 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
2365 {
2366 // Generate a new correlated normal RV with the following formula
2367 channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] =
2368 R * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] +
2369 sqrt(1 - R * R) * m_normalRv->GetValue();
2370 }
2371 }
2372 }
2373
2374 // step c: Determine the attenuation of each blocker due to blockers
2375 for (std::size_t cInd = 0; cInd < clusterNum; cInd++)
2376 {
2377 NS_ASSERT_MSG(clusterAOA[cInd] >= 0 && clusterAOA[cInd] <= 360,
2378 "the AOA should be the range of [0,360]");
2379 NS_ASSERT_MSG(clusterZOA[cInd] >= 0 && clusterZOA[cInd] <= 180,
2380 "the ZOA should be the range of [0,180]");
2381
2382 // check self blocking
2383 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiSb - xSb / 2.0 << ","
2384 << phiSb + xSb / 2.0 << "]");
2385 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaSb - ySb / 2.0 << ","
2386 << thetaSb + ySb / 2.0 << "]");
2387 if (std::abs(clusterAOA[cInd] - phiSb) < (xSb / 2.0) &&
2388 std::abs(clusterZOA[cInd] - thetaSb) < (ySb / 2.0))
2389 {
2390 powerAttenuation[cInd] += 30; // attenuate by 30 dB.
2391 NS_LOG_INFO("Cluster[" << +cInd
2392 << "] is blocked by self blocking region and reduce 30 dB power,"
2393 "the attenuation is ["
2394 << powerAttenuation[cInd] << " dB]");
2395 }
2396
2397 // check non-self blocking
2398 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
2399 {
2400 // The normal RV is transformed to uniform RV with the desired correlation.
2401 double phiK =
2402 (0.5 * erfc(-1 * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] / sqrt(2))) *
2403 360;
2404 while (phiK > 360)
2405 {
2406 phiK -= 360;
2407 }
2408
2409 while (phiK < 0)
2410 {
2411 phiK += 360;
2412 }
2413
2414 double xK = channelParams->m_nonSelfBlocking[blockInd][X_INDEX];
2415 double thetaK = channelParams->m_nonSelfBlocking[blockInd][THETA_INDEX];
2416 double yK = channelParams->m_nonSelfBlocking[blockInd][Y_INDEX];
2417
2418 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiK - xK << ","
2419 << phiK + xK << "]");
2420 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaK - yK << ","
2421 << thetaK + yK << "]");
2422
2423 if (std::abs(clusterAOA[cInd] - phiK) < (xK) &&
2424 std::abs(clusterZOA[cInd] - thetaK) < (yK))
2425 {
2426 double A1 = clusterAOA[cInd] - (phiK + xK / 2.0); //(7.6-24)
2427 double A2 = clusterAOA[cInd] - (phiK - xK / 2.0); //(7.6-25)
2428 double Z1 = clusterZOA[cInd] - (thetaK + yK / 2.0); //(7.6-26)
2429 double Z2 = clusterZOA[cInd] - (thetaK - yK / 2.0); //(7.6-27)
2430 int signA1;
2431 int signA2;
2432 int signZ1;
2433 int signZ2;
2434 // draw sign for the above parameters according to table 7.6.4.1-3 Description of
2435 // signs
2436 if (xK / 2.0 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
2437 {
2438 signA1 = -1;
2439 }
2440 else
2441 {
2442 signA1 = 1;
2443 }
2444 if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2.0)
2445 {
2446 signA2 = -1;
2447 }
2448 else
2449 {
2450 signA2 = 1;
2451 }
2452
2453 if (yK / 2.0 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
2454 {
2455 signZ1 = -1;
2456 }
2457 else
2458 {
2459 signZ1 = 1;
2460 }
2461 if (-1 * yK < clusterZOA[cInd] - thetaK &&
2462 clusterZOA[cInd] - thetaK <= -1 * yK / 2.0)
2463 {
2464 signZ2 = -1;
2465 }
2466 else
2467 {
2468 signZ2 = 1;
2469 }
2470 double lambda = 3e8 / m_frequency;
2471 double fA1 =
2472 atan(signA1 * M_PI / 2.0 *
2473 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
2474 (1.0 / cos(DegreesToRadians(A1)) - 1))) /
2475 M_PI; //(7.6-23)
2476 double fA2 =
2477 atan(signA2 * M_PI / 2.0 *
2478 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
2479 (1.0 / cos(DegreesToRadians(A2)) - 1))) /
2480 M_PI;
2481 double fZ1 =
2482 atan(signZ1 * M_PI / 2.0 *
2483 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
2484 (1.0 / cos(DegreesToRadians(Z1)) - 1))) /
2485 M_PI;
2486 double fZ2 =
2487 atan(signZ2 * M_PI / 2.0 *
2488 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
2489 (1.0 / cos(DegreesToRadians(Z2)) - 1))) /
2490 M_PI;
2491 double lDb = -20 * log10(1 - (fA1 + fA2) * (fZ1 + fZ2)); //(7.6-22)
2492 powerAttenuation[cInd] += lDb;
2493 NS_LOG_INFO("Cluster[" << +cInd << "] is blocked by no-self blocking, the loss is ["
2494 << lDb << "] dB");
2495 }
2496 }
2497 }
2498 return powerAttenuation;
2499}
2500
2501void
2502ThreeGppChannelModel::Shuffle(double* first, double* last) const
2503{
2504 for (auto i = (last - first) - 1; i > 0; --i)
2505 {
2506 std::swap(first[i], first[m_uniformRvShuffle->GetInteger(0, i)]);
2507 }
2508}
2509
2510int64_t
2512{
2513 NS_LOG_FUNCTION(this << stream);
2514 m_normalRv->SetStream(stream);
2515 m_uniformRv->SetStream(stream + 1);
2516 m_uniformRvShuffle->SetStream(stream + 2);
2517 m_uniformRvDoppler->SetStream(stream + 3);
2518 return 4;
2519}
2520
2521} // namespace ns3
Class holding the azimuth and inclination angles of spherical coordinates.
Definition: angles.h:118
double GetInclination() const
Getter for inclination angle.
Definition: angles.cc:246
double GetAzimuth() const
Getter for azimuth angle.
Definition: angles.cc:240
AttributeValue implementation for Boolean.
Definition: boolean.h:37
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Hold a signed integer type.
Definition: integer.h:45
This is an interface for a channel model that can be described by a channel matrix,...
std::vector< double > DoubleVector
Type definition for vectors of doubles.
ComplexMatrixArray Complex2DVector
Create an alias for 2D complex vectors.
std::vector< DoubleVector > Double2DVector
Type definition for matrices of doubles.
static uint64_t GetKey(uint32_t a, uint32_t b)
Generate a unique value for the pair of unsigned integer of 32 bits, where the order does not matter,...
A network Node.
Definition: node.h:57
uint32_t GetId() const
Definition: node.cc:117
AttributeValue implementation for Pointer.
Definition: pointer.h:48
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Hold variables of type string.
Definition: string.h:56
DoubleVector CalcAttenuationOfBlockage(const Ptr< ThreeGppChannelModel::ThreeGppChannelParams > channelParams, const DoubleVector &clusterAOA, const DoubleVector &clusterZOA) const
Applies the blockage model A described in 3GPP TR 38.901.
int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model.
bool AntennaSetupChanged(Ptr< const PhasedArrayModel > aAntenna, Ptr< const PhasedArrayModel > bAntenna, Ptr< const ChannelMatrix > channelMatrix)
Check if the channel matrix has to be updated due to changes in the number of antenna ports.
bool m_portraitMode
true if portrait mode, false if landscape
bool ChannelParamsNeedsUpdate(Ptr< const ThreeGppChannelParams > channelParams, Ptr< const ChannelCondition > channelCondition) const
Check if the channel params has to be updated.
virtual Ptr< const ParamsTable > GetThreeGppTable(Ptr< const ChannelCondition > channelCondition, double hBS, double hUT, double distance2D) const
Get the parameters needed to apply the channel generation procedure.
Ptr< NormalRandomVariable > m_normalRv
normal random variable
static const uint8_t Y_INDEX
index of the Y value in the m_nonSelfBlocking array
bool m_blockage
enables the blockage model A
Ptr< const ChannelParams > GetParams(Ptr< const MobilityModel > aMob, Ptr< const MobilityModel > bMob) const override
Looks for the channel params associated to the aMob and bMob pair in m_channelParamsMap.
~ThreeGppChannelModel() override
Destructor.
bool ChannelMatrixNeedsUpdate(Ptr< const ThreeGppChannelParams > channelParams, Ptr< const ChannelMatrix > channelMatrix)
Check if the channel matrix has to be updated (it needs update when the channel params generation tim...
static const uint8_t THETA_INDEX
index of the THETA value in the m_nonSelfBlocking array
std::unordered_map< uint64_t, Ptr< ThreeGppChannelParams > > m_channelParamsMap
map containing the common channel parameters per pair of nodes, the key of this map is reciprocal and...
static std::pair< double, double > WrapAngles(double azimuthRad, double inclinationRad)
Wrap an (azimuth, inclination) angle pair in a valid range.
double m_blockerSpeed
the blocker speed
Ptr< const ChannelMatrix > GetChannel(Ptr< const MobilityModel > aMob, Ptr< const MobilityModel > bMob, Ptr< const PhasedArrayModel > aAntenna, Ptr< const PhasedArrayModel > bAntenna) override
Looks for the channel matrix associated to the aMob and bMob pair in m_channelMatrixMap.
void SetFrequency(double f)
Sets the center frequency of the model.
std::unordered_map< uint64_t, Ptr< ChannelMatrix > > m_channelMatrixMap
map containing the channel realizations per pair of PhasedAntennaArray instances, the key of this map...
Ptr< UniformRandomVariable > m_uniformRv
uniform random variable
void DoDispose() override
Destructor implementation.
void SetScenario(const std::string &scenario)
Sets the propagation scenario.
void SetChannelConditionModel(Ptr< ChannelConditionModel > model)
Set the channel condition model.
Ptr< UniformRandomVariable > m_uniformRvDoppler
uniform random variable, used to compute the additional Doppler contribution
uint16_t m_numNonSelfBlocking
number of non-self-blocking regions
std::string GetScenario() const
Returns the propagation scenario.
virtual Ptr< ChannelMatrix > GetNewChannel(Ptr< const ThreeGppChannelParams > channelParams, Ptr< const ParamsTable > table3gpp, const Ptr< const MobilityModel > sMob, const Ptr< const MobilityModel > uMob, Ptr< const PhasedArrayModel > sAntenna, Ptr< const PhasedArrayModel > uAntenna) const
Compute the channel matrix between two nodes a and b, and their antenna arrays aAntenna and bAntenna ...
static const uint8_t PHI_INDEX
index of the PHI value in the m_nonSelfBlocking array
double m_frequency
the operating frequency
double m_vScatt
value used to compute the additional Doppler contribution for the delayed paths
Ptr< ChannelConditionModel > GetChannelConditionModel() const
Get the associated channel condition model.
Ptr< ChannelConditionModel > m_channelConditionModel
the channel condition model
std::string m_scenario
the 3GPP scenario
static const uint8_t R_INDEX
index of the R value in the m_nonSelfBlocking array
static TypeId GetTypeId()
Get the type ID.
void Shuffle(double *first, double *last) const
Shuffle the elements of a simple sequence container of type double.
Ptr< ThreeGppChannelParams > GenerateChannelParameters(const Ptr< const ChannelCondition > channelCondition, const Ptr< const ParamsTable > table3gpp, const Ptr< const MobilityModel > aMob, const Ptr< const MobilityModel > bMob) const
Prepare 3gpp channel parameters among the nodes a and b.
double GetFrequency() const
Returns the center frequency.
Time m_updatePeriod
the channel update period
static const uint8_t X_INDEX
index of the X value in the m_nonSelfBlocking array
Ptr< UniformRandomVariable > m_uniformRvShuffle
uniform random variable used to shuffle array in GetNewChannel
@ NS
nanosecond
Definition: nstime.h:119
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
AttributeValue implementation for Time.
Definition: nstime.h:1406
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetGroupName(std::string groupName)
Set the group name.
Definition: type-id.cc:940
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
double GetValue(double min, double max)
Get the next random value drawn from the distribution.
uint32_t GetInteger(uint32_t min, uint32_t max)
Get the next random value drawn from the distribution.
#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
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Definition: integer.h:46
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Definition: pointer.h:259
Ptr< const AttributeChecker > MakeStringChecker()
Definition: string.cc:30
Ptr< const AttributeAccessor > MakeStringAccessor(T1 a1)
Definition: string.h:57
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1407
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
#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 Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:305
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1331
Definition: first.py:1
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static constexpr double DEG2RAD
Conversion factor: degrees to radians.
static const double offSetAlpha[20]
The ray offset angles within a cluster, given for rms angle spread normalized to 1.
static const double sqrtC_RMa_O2I[6][6]
The square root matrix for RMa O2I, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_UMi_LOS[7][7]
The square root matrix for UMi LOS, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_office_LOS[7][7]
The square root matrix for Indoor-Office LOS, which is generated using the Cholesky decomposition acc...
static const double sqrtC_UMa_O2I[6][6]
The square root matrix for UMa O2I, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_RMa_NLOS[6][6]
The square root matrix for RMa NLOS, which is generated using the Cholesky decomposition according to...
static const double sqrtC_UMa_LOS[7][7]
The square root matrix for UMa LOS, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_UMi_NLOS[6][6]
The square root matrix for UMi NLOS, which is generated using the Cholesky decomposition according to...
static const double sqrtC_RMa_LOS[7][7]
The square root matrix for RMa LOS, which is generated using the Cholesky decomposition according to ...
double DegreesToRadians(double degrees)
converts degrees to radians
Definition: angles.cc:39
static const double sqrtC_UMi_O2I[6][6]
The square root matrix for UMi O2I, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_office_NLOS[6][6]
The square root matrix for Indoor-Office NLOS, which is generated using the Cholesky decomposition ac...
static const double sqrtC_UMa_NLOS[6][6]
The square root matrix for UMa NLOS, which is generated using the Cholesky decomposition according to...
double WrapTo2Pi(double a)
Wrap angle in [0, 2*M_PI)
Definition: angles.cc:117
double RadiansToDegrees(double radians)
converts radians to degrees
Definition: angles.cc:45