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