A Discrete-Event Network Simulator
API
three-gpp-channel-model.cc
Go to the documentation of this file.
1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
4  * University of Padova
5  * Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
6  * New York University
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation;
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  */
22 
24 #include "ns3/log.h"
25 #include "ns3/three-gpp-antenna-array-model.h"
26 #include "ns3/node.h"
27 #include "ns3/double.h"
28 #include "ns3/string.h"
29 #include "ns3/integer.h"
30 #include <algorithm>
31 #include <random>
32 #include "ns3/log.h"
33 #include <ns3/simulator.h>
34 #include "ns3/mobility-model.h"
35 #include "ns3/pointer.h"
36 
37 namespace ns3 {
38 
39 NS_LOG_COMPONENT_DEFINE ("ThreeGppChannelModel");
40 
41 NS_OBJECT_ENSURE_REGISTERED (ThreeGppChannelModel);
42 
43 //Table 7.5-3: Ray offset angles within a cluster, given for rms angle spread normalized to 1.
44 static const double offSetAlpha[20] = {
45  0.0447,-0.0447,0.1413,-0.1413,0.2492,-0.2492,0.3715,-0.3715,0.5129,-0.5129,0.6797,-0.6797,0.8844,-0.8844,1.1481,-1.1481,1.5195,-1.5195,2.1551,-2.1551
46 };
47 
48 /*
49  * The cross correlation matrix is constructed according to table 7.5-6.
50  * All the square root matrix is being generated using the Cholesky decomposition
51  * and following the order of [SF,K,DS,ASD,ASA,ZSD,ZSA].
52  * The parameter K is ignored in NLOS.
53  *
54  * The Matlab file to generate the matrices can be found in
55  * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
56  *
57  */
58 static 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 
68 static const double sqrtC_RMa_NLOS[6][6] = {
69  {1, 0, 0, 0, 0, 0},
70  {-0.5, 0.866025, 0, 0, 0, 0},
71  {0.6, -0.11547, 0.791623, 0, 0, 0},
72  {0, 0, 0, 1, 0, 0},
73  {-0.04, -0.138564, 0.540662, -0.18, 0.809003, 0},
74  {-0.25, -0.606218, -0.240013, 0.26, -0.231685, 0.625392},
75 };
76 
77 static const double sqrtC_RMa_O2I[6][6] = {
78  {1, 0, 0, 0, 0, 0},
79  {0, 1, 0, 0, 0, 0},
80  {0, 0, 1, 0, 0, 0},
81  {0, 0, -0.7, 0.714143, 0, 0},
82  {0, 0, 0.66, -0.123225, 0.741091, 0},
83  {0, 0, 0.47, 0.152631, -0.393194, 0.775373},
84 };
85 
86 static const double sqrtC_UMa_LOS[7][7] = {
87  {1, 0, 0, 0, 0, 0, 0},
88  {0, 1, 0, 0, 0, 0, 0},
89  {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
90  {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
91  {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
92  {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
93  {-0.8, 0, -0.388057, -0.367926, 0.238537, -3.58949e-15, 0.130931},
94 };
95 
96 
97 static const double sqrtC_UMa_NLOS[6][6] = {
98  {1, 0, 0, 0, 0, 0},
99  {-0.4, 0.916515, 0, 0, 0, 0},
100  {-0.6, 0.174574, 0.78072, 0, 0, 0},
101  {0, 0.654654, 0.365963, 0.661438, 0, 0},
102  {0, -0.545545, 0.762422, 0.118114, 0.327327, 0},
103  {-0.4, -0.174574, -0.396459, 0.392138, 0.49099, 0.507445},
104 };
105 
106 static const double sqrtC_UMa_O2I[6][6] = {
107  {1, 0, 0, 0, 0, 0},
108  {-0.5, 0.866025, 0, 0, 0, 0},
109  {0.2, 0.57735, 0.791623, 0, 0, 0},
110  {0, 0.46188, -0.336861, 0.820482, 0, 0},
111  {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
112  {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
113 
114 };
115 
116 static const double sqrtC_UMi_LOS[7][7] = {
117  {1, 0, 0, 0, 0, 0, 0},
118  {0.5, 0.866025, 0, 0, 0, 0, 0},
119  {-0.4, -0.57735, 0.711805, 0, 0, 0, 0},
120  {-0.5, 0.057735, 0.468293, 0.726201, 0, 0, 0},
121  {-0.4, -0.11547, 0.805464, -0.23482, 0.350363, 0, 0},
122  {0, 0, 0, 0.688514, 0.461454, 0.559471, 0},
123  {0, 0, 0.280976, 0.231921, -0.490509, 0.11916, 0.782603},
124 };
125 
126 static const double sqrtC_UMi_NLOS[6][6] = {
127  {1, 0, 0, 0, 0, 0},
128  {-0.7, 0.714143, 0, 0, 0, 0},
129  {0, 0, 1, 0, 0, 0},
130  {-0.4, 0.168034, 0, 0.90098, 0, 0},
131  {0, -0.70014, 0.5, 0.130577, 0.4927, 0},
132  {0, 0, 0.5, 0.221981, -0.566238, 0.616522},
133 };
134 
135 static const double sqrtC_UMi_O2I[6][6] = {
136  {1, 0, 0, 0, 0, 0},
137  {-0.5, 0.866025, 0, 0, 0, 0},
138  {0.2, 0.57735, 0.791623, 0, 0, 0},
139  {0, 0.46188, -0.336861, 0.820482, 0, 0},
140  {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
141  {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
142 };
143 
144 static const double sqrtC_office_LOS[7][7] = {
145  {1, 0, 0, 0, 0, 0, 0},
146  {0.5, 0.866025, 0, 0, 0, 0, 0},
147  {-0.8, -0.11547, 0.588784, 0, 0, 0, 0},
148  {-0.4, 0.23094, 0.520847, 0.717903, 0, 0, 0},
149  {-0.5, 0.288675, 0.73598, -0.348236, 0.0610847, 0, 0},
150  {0.2, -0.11547, 0.418943, 0.541106, 0.219905, 0.655744, 0},
151  {0.3, -0.057735, 0.73598, -0.348236, 0.0610847, -0.304997, 0.383375},
152 };
153 
154 static const double sqrtC_office_NLOS[6][6] = {
155  {1, 0, 0, 0, 0, 0},
156  {-0.5, 0.866025, 0, 0, 0, 0},
157  {0, 0.46188, 0.886942, 0, 0, 0},
158  {-0.4, -0.23094, 0.120263, 0.878751, 0, 0},
159  {0, -0.311769, 0.55697, -0.249198, 0.728344, 0},
160  {0, -0.069282, 0.295397, 0.430696, 0.468462, 0.709214},
161 };
162 
164 {
165  NS_LOG_FUNCTION (this);
166  m_uniformRv = CreateObject<UniformRandomVariable> ();
167 
168  m_normalRv = CreateObject<NormalRandomVariable> ();
169  m_normalRv->SetAttribute ("Mean", DoubleValue (0.0));
170  m_normalRv->SetAttribute ("Variance", DoubleValue (1.0));
171 }
172 
174 {
175  NS_LOG_FUNCTION (this);
176 }
177 
178 void
180 {
181  m_channelMap.clear ();
182  m_channelConditionModel->Dispose ();
183  m_channelConditionModel = nullptr;
184 }
185 
186 TypeId
188 {
189  static TypeId tid = TypeId ("ns3::ThreeGppChannelModel")
190  .SetParent<Object> ()
191  .SetGroupName ("Spectrum")
192  .SetParent<MatrixBasedChannelModel> ()
193  .AddConstructor<ThreeGppChannelModel> ()
194  .AddAttribute ("Frequency",
195  "The operating Frequency in Hz",
196  DoubleValue (500.0e6),
199  MakeDoubleChecker<double> ())
200  .AddAttribute ("Scenario",
201  "The 3GPP scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed)",
202  StringValue ("UMa"),
206  .AddAttribute ("ChannelConditionModel",
207  "Pointer to the channel condition model",
208  PointerValue (),
211  MakePointerChecker<ChannelConditionModel> ())
212  .AddAttribute ("UpdatePeriod",
213  "Specify the channel coherence time",
214  TimeValue (MilliSeconds (0)),
216  MakeTimeChecker ())
217  // attributes for the blockage model
218  .AddAttribute ("Blockage",
219  "Enable blockage model A (sec 7.6.4.1)",
220  BooleanValue (false),
223  .AddAttribute ("NumNonselfBlocking",
224  "number of non-self-blocking regions",
225  IntegerValue (4),
227  MakeIntegerChecker<uint16_t> ())
228  .AddAttribute ("PortraitMode",
229  "true for portrait mode, false for landscape mode",
230  BooleanValue (true),
233  .AddAttribute ("BlockerSpeed",
234  "The speed of moving blockers, the unit is m/s",
235  DoubleValue (1),
237  MakeDoubleChecker<double> ())
238  ;
239  return tid;
240 }
241 
242 void
244 {
245  NS_LOG_FUNCTION (this);
246  m_channelConditionModel = model;
247 }
248 
251 {
252  NS_LOG_FUNCTION (this);
254 }
255 
256 void
258 {
259  NS_LOG_FUNCTION (this);
260  NS_ASSERT_MSG (f >= 500.0e6 && f <= 100.0e9, "Frequency should be between 0.5 and 100 GHz but is " << f);
261  m_frequency = f;
262 }
263 
264 double
266 {
267  NS_LOG_FUNCTION (this);
268  return m_frequency;
269 }
270 
271 void
272 ThreeGppChannelModel::SetScenario (const std::string &scenario)
273 {
274  NS_LOG_FUNCTION (this);
275  NS_ASSERT_MSG (scenario == "RMa" || scenario == "UMa" || scenario == "UMi-StreetCanyon" ||
276  scenario == "InH-OfficeOpen" || scenario == "InH-OfficeMixed",
277  "Unknown scenario, choose between RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen or InH-OfficeMixed");
278  m_scenario = scenario;
279 }
280 
281 std::string
283 {
284  NS_LOG_FUNCTION (this);
285  return m_scenario;
286 }
287 
289 ThreeGppChannelModel::GetThreeGppTable (bool los, bool o2i, double hBS, double hUT, double distance2D) const
290 {
291  NS_LOG_FUNCTION (this);
292 
293  double fcGHz = m_frequency / 1e9;
294  Ptr<ParamsTable> table3gpp = Create<ParamsTable> ();
295  // table3gpp includes the following parameters:
296  // numOfCluster, raysPerCluster, uLgDS, sigLgDS, uLgASD, sigLgASD,
297  // uLgASA, sigLgASA, uLgZSA, sigLgZSA, uLgZSD, sigLgZSD, offsetZOD,
298  // cDS, cASD, cASA, cZSA, uK, sigK, rTau, uXpr, sigXpr, shadowingStd
299 
300  // In NLOS case, parameter uK and sigK are not used and they are set to 0
301  if (m_scenario == "RMa")
302  {
303  if (los && !o2i)
304  {
305  // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
306  // entry is N/A.
307  table3gpp->m_numOfCluster = 11;
308  table3gpp->m_raysPerCluster = 20;
309  table3gpp->m_uLgDS = -7.49;
310  table3gpp->m_sigLgDS = 0.55;
311  table3gpp->m_uLgASD = 0.90;
312  table3gpp->m_sigLgASD = 0.38;
313  table3gpp->m_uLgASA = 1.52;
314  table3gpp->m_sigLgASA = 0.24;
315  table3gpp->m_uLgZSA = 0.47;
316  table3gpp->m_sigLgZSA = 0.40;
317  table3gpp->m_uLgZSD = 0.34;
318  table3gpp->m_sigLgZSD = std::max (-1.0, -0.17 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.22);
319  table3gpp->m_offsetZOD = 0;
320  table3gpp->m_cDS = 3.91e-9;
321  table3gpp->m_cASD = 2;
322  table3gpp->m_cASA = 3;
323  table3gpp->m_cZSA = 3;
324  table3gpp->m_uK = 7;
325  table3gpp->m_sigK = 4;
326  table3gpp->m_rTau = 3.8;
327  table3gpp->m_uXpr = 12;
328  table3gpp->m_sigXpr = 4;
329  table3gpp->m_perClusterShadowingStd = 3;
330 
331  for (uint8_t row = 0; row < 7; row++)
332  {
333  for (uint8_t column = 0; column < 7; column++)
334  {
335  table3gpp->m_sqrtC[row][column] = sqrtC_RMa_LOS[row][column];
336  }
337  }
338  }
339  else if (!los && !o2i)
340  {
341  table3gpp->m_numOfCluster = 10;
342  table3gpp->m_raysPerCluster = 20;
343  table3gpp->m_uLgDS = -7.43;
344  table3gpp->m_sigLgDS = 0.48;
345  table3gpp->m_uLgASD = 0.95;
346  table3gpp->m_sigLgASD = 0.45;
347  table3gpp->m_uLgASA = 1.52;
348  table3gpp->m_sigLgASA = 0.13;
349  table3gpp->m_uLgZSA = 0.58,
350  table3gpp->m_sigLgZSA = 0.37;
351  table3gpp->m_uLgZSD = std::max (-1.0, -0.19 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.28);
352  table3gpp->m_sigLgZSD = 0.30;
353  table3gpp->m_offsetZOD = atan ((35 - 3.5) / distance2D) - atan ((35 - 1.5) / distance2D);
354  table3gpp->m_cDS = 3.91e-9;
355  table3gpp->m_cASD = 2;
356  table3gpp->m_cASA = 3;
357  table3gpp->m_cZSA = 3;
358  table3gpp->m_uK = 0;
359  table3gpp->m_sigK = 0;
360  table3gpp->m_rTau = 1.7;
361  table3gpp->m_uXpr = 7;
362  table3gpp->m_sigXpr = 3;
363  table3gpp->m_perClusterShadowingStd = 3;
364 
365  for (uint8_t row = 0; row < 6; row++)
366  {
367  for (uint8_t column = 0; column < 6; column++)
368  {
369  table3gpp->m_sqrtC[row][column] = sqrtC_RMa_NLOS[row][column];
370  }
371  }
372  }
373  else // o2i
374  {
375  table3gpp->m_numOfCluster = 10;
376  table3gpp->m_raysPerCluster = 20;
377  table3gpp->m_uLgDS = -7.47;
378  table3gpp->m_sigLgDS = 0.24;
379  table3gpp->m_uLgASD = 0.67;
380  table3gpp->m_sigLgASD = 0.18;
381  table3gpp->m_uLgASA = 1.66;
382  table3gpp->m_sigLgASA = 0.21;
383  table3gpp->m_uLgZSA = 0.93,
384  table3gpp->m_sigLgZSA = 0.22;
385  table3gpp->m_uLgZSD = std::max (-1.0, -0.19 * (distance2D / 1000) - 0.01 * (hUT - 1.5) + 0.28);
386  table3gpp->m_sigLgZSD = 0.30;
387  table3gpp->m_offsetZOD = atan ((35 - 3.5) / distance2D) - atan ((35 - 1.5) / distance2D);
388  table3gpp->m_cDS = 3.91e-9;
389  table3gpp->m_cASD = 2;
390  table3gpp->m_cASA = 3;
391  table3gpp->m_cZSA = 3;
392  table3gpp->m_uK = 0;
393  table3gpp->m_sigK = 0;
394  table3gpp->m_rTau = 1.7;
395  table3gpp->m_uXpr = 7;
396  table3gpp->m_sigXpr = 3;
397  table3gpp->m_perClusterShadowingStd = 3;
398 
399  for (uint8_t row = 0; row < 6; row++)
400  {
401  for (uint8_t column = 0; column < 6; column++)
402  {
403  table3gpp->m_sqrtC[row][column] = sqrtC_RMa_O2I[row][column];
404  }
405  }
406  }
407  }
408  else if (m_scenario == "UMa")
409  {
410  if (los && !o2i)
411  {
412  table3gpp->m_numOfCluster = 12;
413  table3gpp->m_raysPerCluster = 20;
414  table3gpp->m_uLgDS = -6.955 - 0.0963 * log10 (fcGHz);
415  table3gpp->m_sigLgDS = 0.66;
416  table3gpp->m_uLgASD = 1.06 + 0.1114 * log10 (fcGHz);
417  table3gpp->m_sigLgASD = 0.28;
418  table3gpp->m_uLgASA = 1.81;
419  table3gpp->m_sigLgASA = 0.20;
420  table3gpp->m_uLgZSA = 0.95;
421  table3gpp->m_sigLgZSA = 0.16;
422  table3gpp->m_uLgZSD = std::max (-0.5, -2.1 * distance2D / 1000 - 0.01 * (hUT - 1.5) + 0.75);
423  table3gpp->m_sigLgZSD = 0.40;
424  table3gpp->m_offsetZOD = 0;
425  table3gpp->m_cDS = std::max (0.25, -3.4084 * log10 (fcGHz) + 6.5622) * 1e-9;
426  table3gpp->m_cASD = 5;
427  table3gpp->m_cASA = 11;
428  table3gpp->m_cZSA = 7;
429  table3gpp->m_uK = 9;
430  table3gpp->m_sigK = 3.5;
431  table3gpp->m_rTau = 2.5;
432  table3gpp->m_uXpr = 8;
433  table3gpp->m_sigXpr = 4;
434  table3gpp->m_perClusterShadowingStd = 3;
435 
436  for (uint8_t row = 0; row < 7; row++)
437  {
438  for (uint8_t column = 0; column < 7; column++)
439  {
440  table3gpp->m_sqrtC[row][column] = sqrtC_UMa_LOS[row][column];
441  }
442  }
443  }
444  else
445  {
446  double uLgZSD = std::max (-0.5, -2.1 * distance2D / 1000 - 0.01 * (hUT - 1.5) + 0.9);
447 
448  double afc = 0.208 * log10 (fcGHz) - 0.782;
449  double bfc = 25;
450  double cfc = -0.13 * log10 (fcGHz) + 2.03;
451  double efc = 7.66 * log10 (fcGHz) - 5.96;
452 
453  double offsetZOD = efc - std::pow (10, afc * log10 (std::max (bfc,distance2D)) + cfc);
454 
455  if (!los && !o2i)
456  {
457  table3gpp->m_numOfCluster = 20;
458  table3gpp->m_raysPerCluster = 20;
459  table3gpp->m_uLgDS = -6.28 - 0.204 * log10 (fcGHz);
460  table3gpp->m_sigLgDS = 0.39;
461  table3gpp->m_uLgASD = 1.5 - 0.1144 * log10 (fcGHz);
462  table3gpp->m_sigLgASD = 0.28;
463  table3gpp->m_uLgASA = 2.08 - 0.27 * log10 (fcGHz);
464  table3gpp->m_sigLgASA = 0.11;
465  table3gpp->m_uLgZSA = -0.3236 * log10 (fcGHz) + 1.512;
466  table3gpp->m_sigLgZSA = 0.16;
467  table3gpp->m_uLgZSD = uLgZSD;
468  table3gpp->m_sigLgZSD = 0.49;
469  table3gpp->m_offsetZOD = offsetZOD;
470  table3gpp->m_cDS = std::max (0.25, -3.4084 * log10 (fcGHz) + 6.5622) * 1e-9;;
471  table3gpp->m_cASD = 2;
472  table3gpp->m_cASA = 15;
473  table3gpp->m_cZSA = 7;
474  table3gpp->m_uK = 0;
475  table3gpp->m_sigK = 0;
476  table3gpp->m_rTau = 2.3;
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_UMa_NLOS[row][column];
486  }
487  }
488  }
489  else //(o2i)
490  {
491  table3gpp->m_numOfCluster = 12;
492  table3gpp->m_raysPerCluster = 20;
493  table3gpp->m_uLgDS = -6.62;
494  table3gpp->m_sigLgDS = 0.32;
495  table3gpp->m_uLgASD = 1.25;
496  table3gpp->m_sigLgASD = 0.42;
497  table3gpp->m_uLgASA = 1.76;
498  table3gpp->m_sigLgASA = 0.16;
499  table3gpp->m_uLgZSA = 1.01;
500  table3gpp->m_sigLgZSA = 0.43;
501  table3gpp->m_uLgZSD = uLgZSD;
502  table3gpp->m_sigLgZSD = 0.49;
503  table3gpp->m_offsetZOD = offsetZOD;
504  table3gpp->m_cDS = 11e-9;
505  table3gpp->m_cASD = 5;
506  table3gpp->m_cASA = 8;
507  table3gpp->m_cZSA = 3;
508  table3gpp->m_uK = 0;
509  table3gpp->m_sigK = 0;
510  table3gpp->m_rTau = 2.2;
511  table3gpp->m_uXpr = 9;
512  table3gpp->m_sigXpr = 5;
513  table3gpp->m_perClusterShadowingStd = 4;
514 
515  for (uint8_t row = 0; row < 6; row++)
516  {
517  for (uint8_t column = 0; column < 6; column++)
518  {
519  table3gpp->m_sqrtC[row][column] = sqrtC_UMa_O2I[row][column];
520  }
521  }
522 
523  }
524 
525  }
526 
527  }
528  else if (m_scenario == "UMi-StreetCanyon")
529  {
530  if (los && !o2i)
531  {
532  table3gpp->m_numOfCluster = 12;
533  table3gpp->m_raysPerCluster = 20;
534  table3gpp->m_uLgDS = -0.24 * log10 (1 + fcGHz) - 7.14;
535  table3gpp->m_sigLgDS = 0.38;
536  table3gpp->m_uLgASD = -0.05 * log10 (1 + fcGHz) + 1.21;
537  table3gpp->m_sigLgASD = 0.41;
538  table3gpp->m_uLgASA = -0.08 * log10 (1 + fcGHz) + 1.73;
539  table3gpp->m_sigLgASA = 0.014 * log10 (1 + fcGHz) + 0.28;
540  table3gpp->m_uLgZSA = -0.1 * log10 (1 + fcGHz) + 0.73;
541  table3gpp->m_sigLgZSA = -0.04 * log10 (1 + fcGHz) + 0.34;
542  table3gpp->m_uLgZSD = std::max (-0.21, -14.8 * distance2D / 1000 + 0.01 * std::abs (hUT - hBS) + 0.83);
543  table3gpp->m_sigLgZSD = 0.35;
544  table3gpp->m_offsetZOD = 0;
545  table3gpp->m_cDS = 5e-9;
546  table3gpp->m_cASD = 3;
547  table3gpp->m_cASA = 17;
548  table3gpp->m_cZSA = 7;
549  table3gpp->m_uK = 9;
550  table3gpp->m_sigK = 5;
551  table3gpp->m_rTau = 3;
552  table3gpp->m_uXpr = 9;
553  table3gpp->m_sigXpr = 3;
554  table3gpp->m_perClusterShadowingStd = 3;
555 
556  for (uint8_t row = 0; row < 7; row++)
557  {
558  for (uint8_t column = 0; column < 7; column++)
559  {
560  table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
561  }
562  }
563  }
564  else
565  {
566  double uLgZSD = std::max (-0.5, -3.1 * distance2D / 1000 + 0.01 * std::max (hUT - hBS,0.0) + 0.2);
567  double offsetZOD = -1 * std::pow (10, -1.5 * log10 (std::max (10.0, distance2D)) + 3.3);
568  if (!los && !o2i)
569  {
570  table3gpp->m_numOfCluster = 19;
571  table3gpp->m_raysPerCluster = 20;
572  table3gpp->m_uLgDS = -0.24 * log10 (1 + fcGHz) - 6.83;
573  table3gpp->m_sigLgDS = 0.16 * log10 (1 + fcGHz) + 0.28;
574  table3gpp->m_uLgASD = -0.23 * log10 (1 + fcGHz) + 1.53;
575  table3gpp->m_sigLgASD = 0.11 * log10 (1 + fcGHz) + 0.33;
576  table3gpp->m_uLgASA = -0.08 * log10 (1 + fcGHz) + 1.81;
577  table3gpp->m_sigLgASA = 0.05 * log10 (1 + fcGHz) + 0.3;
578  table3gpp->m_uLgZSA = -0.04 * log10 (1 + fcGHz) + 0.92;
579  table3gpp->m_sigLgZSA = -0.07 * log10 (1 + fcGHz) + 0.41;
580  table3gpp->m_uLgZSD = uLgZSD;
581  table3gpp->m_sigLgZSD = 0.35;
582  table3gpp->m_offsetZOD = offsetZOD;
583  table3gpp->m_cDS = 11e-9;
584  table3gpp->m_cASD = 10;
585  table3gpp->m_cASA = 22;
586  table3gpp->m_cZSA = 7;
587  table3gpp->m_uK = 0;
588  table3gpp->m_sigK = 0;
589  table3gpp->m_rTau = 2.1;
590  table3gpp->m_uXpr = 8;
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_UMi_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.35;
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_UMi_O2I[row][column];
633  }
634  }
635  }
636  }
637  }
638  else if (m_scenario == "InH-OfficeMixed"||m_scenario == "InH-OfficeOpen")
639  {
640  NS_ASSERT_MSG (!o2i, "The indoor scenario does out support outdoor to indoor");
641  if (los)
642  {
643  table3gpp->m_numOfCluster = 15;
644  table3gpp->m_raysPerCluster = 20;
645  table3gpp->m_uLgDS = -0.01 * log10 (1 + fcGHz) - 7.692;
646  table3gpp->m_sigLgDS = 0.18;
647  table3gpp->m_uLgASD = 1.60;
648  table3gpp->m_sigLgASD = 0.18;
649  table3gpp->m_uLgASA = -0.19 * log10 (1 + fcGHz) + 1.781;
650  table3gpp->m_sigLgASA = 0.12 * log10 (1 + fcGHz) + 0.119;
651  table3gpp->m_uLgZSA = -0.26 * log10 (1 + fcGHz) + 1.44;
652  table3gpp->m_sigLgZSA = -0.04 * log10 (1 + fcGHz) + 0.264;
653  table3gpp->m_uLgZSD = -1.43 * log10 (1 + fcGHz) + 2.228;
654  table3gpp->m_sigLgZSD = 0.13 * log10 (1 + fcGHz) + 0.30;
655  table3gpp->m_offsetZOD = 0;
656  table3gpp->m_cDS = 3.91e-9;
657  table3gpp->m_cASD = 5;
658  table3gpp->m_cASA = 8;
659  table3gpp->m_cZSA = 9;
660  table3gpp->m_uK = 7;
661  table3gpp->m_sigK = 4;
662  table3gpp->m_rTau = 3.6;
663  table3gpp->m_uXpr = 11;
664  table3gpp->m_sigXpr = 4;
665  table3gpp->m_perClusterShadowingStd = 6;
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_office_LOS[row][column];
672  }
673  }
674  }
675  else
676  {
677  table3gpp->m_numOfCluster = 19;
678  table3gpp->m_raysPerCluster = 20;
679  table3gpp->m_uLgDS = -0.28 * log10 (1 + fcGHz) - 7.173;
680  table3gpp->m_sigLgDS = 0.1 * log10 (1 + fcGHz) + 0.055;
681  table3gpp->m_uLgASD = 1.62;
682  table3gpp->m_sigLgASD = 0.25;
683  table3gpp->m_uLgASA = -0.11 * log10 (1 + fcGHz) + 1.863;
684  table3gpp->m_sigLgASA = 0.12 * log10 (1 + fcGHz) + 0.059;
685  table3gpp->m_uLgZSA = -0.15 * log10 (1 + fcGHz) + 1.387;
686  table3gpp->m_sigLgZSA = -0.09 * log10 (1 + fcGHz) + 0.746;
687  table3gpp->m_uLgZSD = 1.08;
688  table3gpp->m_sigLgZSD = 0.36;
689  table3gpp->m_offsetZOD = 0;
690  table3gpp->m_cDS = 3.91e-9;
691  table3gpp->m_cASD = 5;
692  table3gpp->m_cASA = 11;
693  table3gpp->m_cZSA = 9;
694  table3gpp->m_uK = 0;
695  table3gpp->m_sigK = 0;
696  table3gpp->m_rTau = 3;
697  table3gpp->m_uXpr = 10;
698  table3gpp->m_sigXpr = 4;
699  table3gpp->m_perClusterShadowingStd = 3;
700 
701  for (uint8_t row = 0; row < 6; row++)
702  {
703  for (uint8_t column = 0; column < 6; column++)
704  {
705  table3gpp->m_sqrtC[row][column] = sqrtC_office_NLOS[row][column];
706  }
707  }
708  }
709  }
710  else
711  {
712  NS_FATAL_ERROR ("unkonw scenarios");
713  }
714 
715  return table3gpp;
716 }
717 
718 bool
720 {
721  NS_LOG_FUNCTION (this);
722 
723  bool update = false;
724 
725  // if the los condition is different the channel has to be updated
726  if (channelMatrix->m_los != los)
727  {
728  NS_LOG_DEBUG ("old los condition " << channelMatrix->m_los << " new los condition " << los);
729  update = true;
730  }
731 
732  // if the coherence time is over the channel has to be updated
733  if (!m_updatePeriod.IsZero () && Simulator::Now() - channelMatrix->m_generatedTime > m_updatePeriod)
734  {
735  NS_LOG_DEBUG ("Generation time " << channelMatrix->m_generatedTime.GetNanoSeconds () << " now " << Simulator::Now ().GetNanoSeconds ());
736  update = true;
737  }
738 
739  return update;
740 }
741 
747 {
748  NS_LOG_FUNCTION (this);
749 
750  // Compute the channel key. The key is reciprocal, i.e., key (a, b) = key (b, a)
751  uint32_t x1 = std::min (aMob->GetObject<Node> ()->GetId (), bMob->GetObject<Node> ()->GetId ());
752  uint32_t x2 = std::max (aMob->GetObject<Node> ()->GetId (), bMob->GetObject<Node> ()->GetId ());
753  uint32_t channelId = GetKey (x1, x2);
754 
755  // retrieve the channel condition
756  Ptr<const ChannelCondition> condition = m_channelConditionModel->GetChannelCondition (aMob, bMob);
757  bool los = (condition->GetLosCondition () == ChannelCondition::LosConditionValue::LOS);
758  bool o2i = false; // TODO include the o2i condition in the channel condition model
759 
760  // Check if the channel is present in the map and return it, otherwise
761  // generate a new channel
762  bool update = false;
763  bool notFound = false;
764  Ptr<ThreeGppChannelMatrix> channelMatrix;
765  if (m_channelMap.find (channelId) != m_channelMap.end ())
766  {
767  // channel matrix present in the map
768  NS_LOG_DEBUG ("channel matrix present in the map");
769  channelMatrix = m_channelMap[channelId];
770 
771  // check if it has to be updated
772  update = ChannelMatrixNeedsUpdate (channelMatrix, los);
773  }
774  else
775  {
776  NS_LOG_DEBUG ("channel matrix not found");
777  notFound = true;
778  }
779 
780  // If the channel is not present in the map or if it has to be updated
781  // generate a new realization
782  if (notFound || update)
783  {
784  // channel matrix not found or has to be updated, generate a new one
785  Angles txAngle (bMob->GetPosition (), aMob->GetPosition ());
786  Angles rxAngle (aMob->GetPosition (), bMob->GetPosition ());
787 
788  double x = aMob->GetPosition ().x - bMob->GetPosition ().x;
789  double y = aMob->GetPosition ().y - bMob->GetPosition ().y;
790  double distance2D = sqrt (x * x + y * y);
791 
792  // NOTE we assume hUT = min (height(a), height(b)) and
793  // hBS = max (height (a), height (b))
794  double hUt = std::min (aMob->GetPosition ().z, bMob->GetPosition ().z);
795  double hBs = std::max (aMob->GetPosition ().z, bMob->GetPosition ().z);
796 
797  // TODO this is not currently used, it is needed for the computation of the
798  // additional blockage in case of spatial consistent update
799  // I do not know who is the UT, I can use the relative distance between
800  // tx and rx instead
801  Vector locUt = Vector (0.0, 0.0, 0.0);
802 
803  channelMatrix = GetNewChannel (locUt, los, o2i, aAntenna, bAntenna, rxAngle, txAngle, distance2D, hBs, hUt);
804  channelMatrix->m_nodeIds = std::make_pair (aMob->GetObject<Node> ()->GetId (), bMob->GetObject<Node> ()->GetId ());
805 
806  // store or replace the channel matrix in the channel map
807  m_channelMap[channelId] = channelMatrix;
808  }
809 
810  return channelMatrix;
811 }
812 
814 ThreeGppChannelModel::GetNewChannel (Vector locUT, bool los, bool o2i,
817  Angles &uAngle, Angles &sAngle,
818  double dis2D, double hBS, double hUT) const
819 {
820  NS_LOG_FUNCTION (this);
821 
822  NS_ASSERT_MSG (m_frequency > 0.0, "Set the operating frequency first!");
823 
824  // get the 3GPP parameters
825  Ptr<const ParamsTable> table3gpp = GetThreeGppTable (los, o2i, hBS, hUT, dis2D);
826 
827  // get the number of clusters and the number of rays per cluster
828  uint8_t numOfCluster = table3gpp->m_numOfCluster;
829  uint8_t raysPerCluster = table3gpp->m_raysPerCluster;
830 
831  // create a channel matrix instance
832  Ptr<ThreeGppChannelMatrix> channelParams = Create<ThreeGppChannelMatrix> ();
833  channelParams->m_los = los; // set the LOS condition
834  channelParams->m_o2i = o2i; // set the O2I condition
835  channelParams->m_generatedTime = Simulator::Now ();
836 
837  // compute the 3D distance using eq. 7.4-1
838  double dis3D = std::sqrt (dis2D * dis2D + (hBS - hUT) * (hBS - hUT));
839 
840  //Step 4: Generate large scale parameters. All LSPS are uncorrelated.
841  DoubleVector LSPsIndep, LSPs;
842  uint8_t paramNum;
843  if (los)
844  {
845  paramNum = 7;
846  }
847  else
848  {
849  paramNum = 6;
850  }
851  //Generate paramNum independent LSPs.
852  for (uint8_t iter = 0; iter < paramNum; iter++)
853  {
854  LSPsIndep.push_back (m_normalRv->GetValue ());
855  }
856  for (uint8_t row = 0; row < paramNum; row++)
857  {
858  double temp = 0;
859  for (uint8_t column = 0; column < paramNum; column++)
860  {
861  temp += table3gpp->m_sqrtC[row][column] * LSPsIndep[column];
862  }
863  LSPs.push_back (temp);
864  }
865 
866  // NOTE the shadowing is generated in the propagation loss model
867 
868  double DS,ASD,ASA,ZSA,ZSD,K_factor = 0;
869  if (los)
870  {
871  K_factor = LSPs[1] * table3gpp->m_sigK + table3gpp->m_uK;
872  DS = pow (10, LSPs[2] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
873  ASD = pow (10, LSPs[3] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
874  ASA = pow (10, LSPs[4] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
875  ZSD = pow (10, LSPs[5] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
876  ZSA = pow (10, LSPs[6] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
877  }
878  else
879  {
880  DS = pow (10, LSPs[1] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
881  ASD = pow (10, LSPs[2] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
882  ASA = pow (10, LSPs[3] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
883  ZSD = pow (10, LSPs[4] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
884  ZSA = pow (10, LSPs[5] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
885 
886  }
887  ASD = std::min (ASD, 104.0);
888  ASA = std::min (ASA, 104.0);
889  ZSD = std::min (ZSD, 52.0);
890  ZSA = std::min (ZSA, 52.0);
891 
892  channelParams->m_DS = DS;
893  channelParams->m_K = K_factor;
894 
895  NS_LOG_INFO ("K-factor=" << K_factor << ",DS=" << DS << ", ASD=" << ASD << ", ASA=" << ASA << ", ZSD=" << ZSD << ", ZSA=" << ZSA);
896 
897  //Step 5: Generate Delays.
898  DoubleVector clusterDelay;
899  double minTau = 100.0;
900  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
901  {
902  double tau = -1*table3gpp->m_rTau*DS*log (m_uniformRv->GetValue (0,1)); //(7.5-1)
903  if (minTau > tau)
904  {
905  minTau = tau;
906  }
907  clusterDelay.push_back (tau);
908  }
909 
910  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
911  {
912  clusterDelay[cIndex] -= minTau;
913  }
914  std::sort (clusterDelay.begin (), clusterDelay.end ()); //(7.5-2)
915 
916  /* since the scaled Los delays are not to be used in cluster power generation,
917  * we will generate cluster power first and resume to compute Los cluster delay later.*/
918 
919  //Step 6: Generate cluster powers.
920  DoubleVector clusterPower;
921  double powerSum = 0;
922  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
923  {
924  double power = exp (-1 * clusterDelay[cIndex] * (table3gpp->m_rTau - 1) / table3gpp->m_rTau / DS) *
925  pow (10,-1 * m_normalRv->GetValue () * table3gpp->m_perClusterShadowingStd / 10); //(7.5-5)
926  powerSum += power;
927  clusterPower.push_back (power);
928  }
929  double powerMax = 0;
930 
931  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
932  {
933  clusterPower[cIndex] = clusterPower[cIndex] / powerSum; //(7.5-6)
934  }
935 
936  DoubleVector clusterPowerForAngles; // this power is only for equation (7.5-9) and (7.5-14), not for (7.5-22)
937  if (los)
938  {
939  double K_linear = pow (10,K_factor / 10);
940 
941  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
942  {
943  if (cIndex == 0)
944  {
945  clusterPowerForAngles.push_back (clusterPower[cIndex] / (1 + K_linear) + K_linear / (1 + K_linear)); //(7.5-8)
946  }
947  else
948  {
949  clusterPowerForAngles.push_back (clusterPower[cIndex] / (1 + K_linear)); //(7.5-8)
950  }
951  if (powerMax < clusterPowerForAngles[cIndex])
952  {
953  powerMax = clusterPowerForAngles[cIndex];
954  }
955  }
956  }
957  else
958  {
959  for (uint8_t cIndex = 0; cIndex < numOfCluster; cIndex++)
960  {
961  clusterPowerForAngles.push_back (clusterPower[cIndex]); //(7.5-6)
962  if (powerMax < clusterPowerForAngles[cIndex])
963  {
964  powerMax = clusterPowerForAngles[cIndex];
965  }
966  }
967  }
968 
969  //remove clusters with less than -25 dB power compared to the maxim cluster power;
970  //double thresh = pow(10,-2.5);
971  double thresh = 0.0032;
972  for (uint8_t cIndex = numOfCluster; cIndex > 0; cIndex--)
973  {
974  if (clusterPowerForAngles[cIndex - 1] < thresh * powerMax )
975  {
976  clusterPowerForAngles.erase (clusterPowerForAngles.begin () + cIndex - 1);
977  clusterPower.erase (clusterPower.begin () + cIndex - 1);
978  clusterDelay.erase (clusterDelay.begin () + cIndex - 1);
979  }
980  }
981  uint8_t numReducedCluster = clusterPower.size ();
982 
983  channelParams->m_numCluster = numReducedCluster;
984  // Resume step 5 to compute the delay for LoS condition.
985  if (los)
986  {
987  double C_tau = 0.7705 - 0.0433 * K_factor + 2e-4 * pow (K_factor,2) + 17e-6 * pow (K_factor,3); //(7.5-3)
988  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
989  {
990  clusterDelay[cIndex] = clusterDelay[cIndex] / C_tau; //(7.5-4)
991  }
992  }
993 
994  //Step 7: Generate arrival and departure angles for both azimuth and elevation.
995 
996  double C_NLOS, C_phi;
997  //According to table 7.5-6, only cluster number equals to 8, 10, 11, 12, 19 and 20 is valid.
998  //Not sure why the other cases are in Table 7.5-2.
999  switch (numOfCluster) // Table 7.5-2
1000  {
1001  case 4:
1002  C_NLOS = 0.779;
1003  break;
1004  case 5:
1005  C_NLOS = 0.860;
1006  break;
1007  case 8:
1008  C_NLOS = 1.018;
1009  break;
1010  case 10:
1011  C_NLOS = 1.090;
1012  break;
1013  case 11:
1014  C_NLOS = 1.123;
1015  break;
1016  case 12:
1017  C_NLOS = 1.146;
1018  break;
1019  case 14:
1020  C_NLOS = 1.190;
1021  break;
1022  case 15:
1023  C_NLOS = 1.221;
1024  break;
1025  case 16:
1026  C_NLOS = 1.226;
1027  break;
1028  case 19:
1029  C_NLOS = 1.273;
1030  break;
1031  case 20:
1032  C_NLOS = 1.289;
1033  break;
1034  default:
1035  NS_FATAL_ERROR ("Invalide cluster number");
1036  }
1037 
1038  if (los)
1039  {
1040  C_phi = C_NLOS * (1.1035 - 0.028 * K_factor - 2e-3 * pow (K_factor,2) + 1e-4 * pow (K_factor,3)); //(7.5-10))
1041  }
1042  else
1043  {
1044  C_phi = C_NLOS; //(7.5-10)
1045  }
1046 
1047  double C_theta;
1048  switch (numOfCluster) //Table 7.5-4
1049  {
1050  case 8:
1051  C_NLOS = 0.889;
1052  break;
1053  case 10:
1054  C_NLOS = 0.957;
1055  break;
1056  case 11:
1057  C_NLOS = 1.031;
1058  break;
1059  case 12:
1060  C_NLOS = 1.104;
1061  break;
1062  case 15:
1063  C_NLOS = 1.1088;
1064  break;
1065  case 19:
1066  C_NLOS = 1.184;
1067  break;
1068  case 20:
1069  C_NLOS = 1.178;
1070  break;
1071  default:
1072  NS_FATAL_ERROR ("Invalide cluster number");
1073  }
1074 
1075  if (los)
1076  {
1077  C_theta = C_NLOS * (1.3086 + 0.0339 * K_factor - 0.0077 * pow (K_factor,2) + 2e-4 * pow (K_factor,3)); //(7.5-15)
1078  }
1079  else
1080  {
1081  C_theta = C_NLOS;
1082  }
1083 
1084 
1085  DoubleVector clusterAoa, clusterAod, clusterZoa, clusterZod;
1086  double angle;
1087  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1088  {
1089  angle = 2*ASA*sqrt (-1 * log (clusterPowerForAngles[cIndex] / powerMax)) / 1.4 / C_phi; //(7.5-9)
1090  clusterAoa.push_back (angle);
1091  angle = 2*ASD*sqrt (-1 * log (clusterPowerForAngles[cIndex] / powerMax)) / 1.4 / C_phi; //(7.5-9)
1092  clusterAod.push_back (angle);
1093  angle = -1*ZSA*log (clusterPowerForAngles[cIndex] / powerMax) / C_theta; //(7.5-14)
1094  clusterZoa.push_back (angle);
1095  angle = -1*ZSD*log (clusterPowerForAngles[cIndex] / powerMax) / C_theta;
1096  clusterZod.push_back (angle);
1097  }
1098 
1099  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1100  {
1101  int Xn = 1;
1102  if (m_uniformRv->GetValue (0,1) < 0.5)
1103  {
1104  Xn = -1;
1105  }
1106  clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue () * ASA / 7) + uAngle.phi * 180 / M_PI; //(7.5-11)
1107  clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue () * ASD / 7) + sAngle.phi * 180 / M_PI;
1108  if (o2i)
1109  {
1110  clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue () * ZSA / 7) + 90; //(7.5-16)
1111  }
1112  else
1113  {
1114  clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue () * ZSA / 7) + uAngle.theta * 180 / M_PI; //(7.5-16)
1115  }
1116  clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue () * ZSD / 7) + sAngle.theta * 180 / M_PI + table3gpp->m_offsetZOD; //(7.5-19)
1117 
1118  }
1119 
1120  if (los)
1121  {
1122  //The 7.5-12 can be rewrite as Theta_n,ZOA = Theta_n,ZOA - (Theta_1,ZOA - Theta_LOS,ZOA) = Theta_n,ZOA - diffZOA,
1123  //Similar as AOD, ZSA and ZSD.
1124  double diffAoa = clusterAoa[0] - uAngle.phi * 180 / M_PI;
1125  double diffAod = clusterAod[0] - sAngle.phi * 180 / M_PI;
1126  double diffZsa = clusterZoa[0] - uAngle.theta * 180 / M_PI;
1127  double diffZsd = clusterZod[0] - sAngle.theta * 180 / M_PI;
1128 
1129  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1130  {
1131  clusterAoa[cIndex] -= diffAoa; //(7.5-12)
1132  clusterAod[cIndex] -= diffAod;
1133  clusterZoa[cIndex] -= diffZsa; //(7.5-17)
1134  clusterZod[cIndex] -= diffZsd;
1135 
1136  }
1137  }
1138 
1139  double rayAoa_radian[numReducedCluster][raysPerCluster]; //rayAoa_radian[n][m], where n is cluster index, m is ray index
1140  double rayAod_radian[numReducedCluster][raysPerCluster]; //rayAod_radian[n][m], where n is cluster index, m is ray index
1141  double rayZoa_radian[numReducedCluster][raysPerCluster]; //rayZoa_radian[n][m], where n is cluster index, m is ray index
1142  double rayZod_radian[numReducedCluster][raysPerCluster]; //rayZod_radian[n][m], where n is cluster index, m is ray index
1143 
1144  for (uint8_t nInd = 0; nInd < numReducedCluster; nInd++)
1145  {
1146  for (uint8_t mInd = 0; mInd < raysPerCluster; mInd++)
1147  {
1148  double tempAoa = clusterAoa[nInd] + table3gpp->m_cASA * offSetAlpha[mInd]; //(7.5-13)
1149  while (tempAoa > 360)
1150  {
1151  tempAoa -= 360;
1152  }
1153 
1154  while (tempAoa < 0)
1155  {
1156  tempAoa += 360;
1157 
1158  }
1159  NS_ASSERT_MSG (tempAoa >= 0 && tempAoa <= 360, "the AOA should be the range of [0,360]");
1160  rayAoa_radian[nInd][mInd] = tempAoa * M_PI / 180;
1161 
1162  double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd];
1163  while (tempAod > 360)
1164  {
1165  tempAod -= 360;
1166  }
1167 
1168  while (tempAod < 0)
1169  {
1170  tempAod += 360;
1171  }
1172  NS_ASSERT_MSG (tempAod >= 0 && tempAod <= 360, "the AOD should be the range of [0,360]");
1173  rayAod_radian[nInd][mInd] = tempAod * M_PI / 180;
1174 
1175  double tempZoa = clusterZoa[nInd] + table3gpp->m_cZSA * offSetAlpha[mInd]; //(7.5-18)
1176 
1177  while (tempZoa > 360)
1178  {
1179  tempZoa -= 360;
1180  }
1181 
1182  while (tempZoa < 0)
1183  {
1184  tempZoa += 360;
1185  }
1186 
1187  if (tempZoa > 180)
1188  {
1189  tempZoa = 360 - tempZoa;
1190  }
1191 
1192  NS_ASSERT_MSG (tempZoa >= 0&&tempZoa <= 180, "the ZOA should be the range of [0,180]");
1193  rayZoa_radian[nInd][mInd] = tempZoa * M_PI / 180;
1194 
1195  double tempZod = clusterZod[nInd] + 0.375 * pow (10,table3gpp->m_uLgZSD) * offSetAlpha[mInd]; //(7.5-20)
1196 
1197  while (tempZod > 360)
1198  {
1199  tempZod -= 360;
1200  }
1201 
1202  while (tempZod < 0)
1203  {
1204  tempZod += 360;
1205  }
1206  if (tempZod > 180)
1207  {
1208  tempZod = 360 - tempZod;
1209  }
1210  NS_ASSERT_MSG (tempZod >= 0&&tempZod <= 180, "the ZOD should be the range of [0,180]");
1211  rayZod_radian[nInd][mInd] = tempZod * M_PI / 180;
1212  }
1213  }
1214  DoubleVector angle_degree;
1215  double sizeTemp = clusterZoa.size ();
1216  for (uint8_t ind = 0; ind < 4; ind++)
1217  {
1218  switch (ind)
1219  {
1220  case 0:
1221  angle_degree = clusterAoa;
1222  break;
1223  case 1:
1224  angle_degree = clusterZoa;
1225  break;
1226  case 2:
1227  angle_degree = clusterAod;
1228  break;
1229  case 3:
1230  angle_degree = clusterZod;
1231  break;
1232  default:
1233  NS_FATAL_ERROR ("Programming Error");
1234  }
1235 
1236  for (uint8_t nIndex = 0; nIndex < sizeTemp; nIndex++)
1237  {
1238  while (angle_degree[nIndex] > 360)
1239  {
1240  angle_degree[nIndex] -= 360;
1241  }
1242 
1243  while (angle_degree[nIndex] < 0)
1244  {
1245  angle_degree[nIndex] += 360;
1246  }
1247 
1248  if (ind == 1 || ind == 3)
1249  {
1250  if (angle_degree[nIndex] > 180)
1251  {
1252  angle_degree[nIndex] = 360 - angle_degree[nIndex];
1253  }
1254  }
1255  }
1256  switch (ind)
1257  {
1258  case 0:
1259  clusterAoa = angle_degree;
1260  break;
1261  case 1:
1262  clusterZoa = angle_degree;
1263  break;
1264  case 2:
1265  clusterAod = angle_degree;
1266  break;
1267  case 3:
1268  clusterZod = angle_degree;
1269  break;
1270  default:
1271  NS_FATAL_ERROR ("Programming Error");
1272  }
1273  }
1274 
1275  DoubleVector attenuation_dB;
1276  if (m_blockage)
1277  {
1278  attenuation_dB = CalcAttenuationOfBlockage (channelParams, clusterAoa, clusterZoa);
1279  for (uint8_t cInd = 0; cInd < numReducedCluster; cInd++)
1280  {
1281  clusterPower[cInd] = clusterPower[cInd] / pow (10,attenuation_dB[cInd] / 10);
1282  }
1283  }
1284  else
1285  {
1286  attenuation_dB.push_back (0);
1287  }
1288 
1289  //Step 8: Coupling of rays within a cluster for both azimuth and elevation
1290  //shuffle all the arrays to perform random coupling
1291  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1292  {
1293  std::shuffle (&rayAod_radian[cIndex][0],&rayAod_radian[cIndex][raysPerCluster],std::default_random_engine (cIndex * 1000 + 100));
1294  std::shuffle (&rayAoa_radian[cIndex][0],&rayAoa_radian[cIndex][raysPerCluster],std::default_random_engine (cIndex * 1000 + 200));
1295  std::shuffle (&rayZod_radian[cIndex][0],&rayZod_radian[cIndex][raysPerCluster],std::default_random_engine (cIndex * 1000 + 300));
1296  std::shuffle (&rayZoa_radian[cIndex][0],&rayZoa_radian[cIndex][raysPerCluster],std::default_random_engine (cIndex * 1000 + 400));
1297  }
1298 
1299  //Step 9: Generate the cross polarization power ratios
1300  //Step 10: Draw initial phases
1301  Double2DVector crossPolarizationPowerRatios; // vector containing the cross polarization power ratios, as defined by 7.5-21
1302  Double3DVector clusterPhase; //rayAoa_radian[n][m], where n is cluster index, m is ray index
1303  for (uint8_t nInd = 0; nInd < numReducedCluster; nInd++)
1304  {
1305  DoubleVector temp; // used to store the XPR values
1306  Double2DVector temp2; // used to store the PHI values for all the possible combination of polarization
1307  for (uint8_t mInd = 0; mInd < raysPerCluster; mInd++)
1308  {
1309  double uXprLinear = pow (10, table3gpp->m_uXpr / 10); // convert to linear
1310  double sigXprLinear = pow (10, table3gpp->m_sigXpr / 10); // convert to linear
1311 
1312  temp.push_back (std::pow (10, (m_normalRv->GetValue () * sigXprLinear + uXprLinear) / 10));
1313  DoubleVector temp3; // used to store the PHI valuse
1314  for (uint8_t pInd = 0; pInd < 4; pInd++)
1315  {
1316  temp3.push_back (m_uniformRv->GetValue (-1 * M_PI, M_PI));
1317  }
1318  temp2.push_back (temp3);
1319  }
1320  crossPolarizationPowerRatios.push_back (temp);
1321  clusterPhase.push_back (temp2);
1322  }
1323  channelParams->m_clusterPhase = clusterPhase;
1324 
1325  //Step 11: Generate channel coefficients for each cluster n and each receiver
1326  // and transmitter element pair u,s.
1327 
1328  Complex3DVector H_NLOS; // channel coefficients H_NLOS [u][s][n],
1329  // where u and s are receive and transmit antenna element, n is cluster index.
1330  uint64_t uSize = uAntenna->GetNumberOfElements ();
1331  uint64_t sSize = sAntenna->GetNumberOfElements ();
1332 
1333  uint8_t cluster1st = 0, cluster2nd = 0; // first and second strongest cluster;
1334  double maxPower = 0;
1335  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1336  {
1337  if (maxPower < clusterPower[cIndex])
1338  {
1339  maxPower = clusterPower[cIndex];
1340  cluster1st = cIndex;
1341  }
1342  }
1343  maxPower = 0;
1344  for (uint8_t cIndex = 0; cIndex < numReducedCluster; cIndex++)
1345  {
1346  if (maxPower < clusterPower[cIndex] && cluster1st != cIndex)
1347  {
1348  maxPower = clusterPower[cIndex];
1349  cluster2nd = cIndex;
1350  }
1351  }
1352 
1353  NS_LOG_INFO ("1st strongest cluster:" << (int)cluster1st << ", 2nd strongest cluster:" << (int)cluster2nd);
1354 
1355  Complex3DVector H_usn; //channel coffecient H_usn[u][s][n];
1356  // NOTE Since each of the strongest 2 clusters are divided into 3 sub-clusters,
1357  // the total cluster will be numReducedCLuster + 4.
1358 
1359  H_usn.resize (uSize);
1360  for (uint64_t uIndex = 0; uIndex < uSize; uIndex++)
1361  {
1362  H_usn[uIndex].resize (sSize);
1363  for (uint64_t sIndex = 0; sIndex < sSize; sIndex++)
1364  {
1365  H_usn[uIndex][sIndex].resize (numReducedCluster);
1366  }
1367  }
1368 
1369  // The following for loops computes the channel coefficients
1370  for (uint64_t uIndex = 0; uIndex < uSize; uIndex++)
1371  {
1372  Vector uLoc = uAntenna->GetElementLocation (uIndex);
1373 
1374  for (uint64_t sIndex = 0; sIndex < sSize; sIndex++)
1375  {
1376 
1377  Vector sLoc = sAntenna->GetElementLocation (sIndex);
1378 
1379  for (uint8_t nIndex = 0; nIndex < numReducedCluster; nIndex++)
1380  {
1381  //Compute the N-2 weakest cluster, only vertical polarization. (7.5-22)
1382  if (nIndex != cluster1st && nIndex != cluster2nd)
1383  {
1384  std::complex<double> rays (0,0);
1385  for (uint8_t mIndex = 0; mIndex < raysPerCluster; mIndex++)
1386  {
1387  DoubleVector initialPhase = clusterPhase[nIndex][mIndex];
1388  double k = crossPolarizationPowerRatios[nIndex][mIndex];
1389  //lambda_0 is accounted in the antenna spacing uLoc and sLoc.
1390  double rxPhaseDiff = 2 * M_PI * (sin (rayZoa_radian[nIndex][mIndex]) * cos (rayAoa_radian[nIndex][mIndex]) * uLoc.x
1391  + sin (rayZoa_radian[nIndex][mIndex]) * sin (rayAoa_radian[nIndex][mIndex]) * uLoc.y
1392  + cos (rayZoa_radian[nIndex][mIndex]) * uLoc.z);
1393 
1394  double txPhaseDiff = 2 * M_PI * (sin (rayZod_radian[nIndex][mIndex]) * cos (rayAod_radian[nIndex][mIndex]) * sLoc.x
1395  + sin (rayZod_radian[nIndex][mIndex]) * sin (rayAod_radian[nIndex][mIndex]) * sLoc.y
1396  + cos (rayZod_radian[nIndex][mIndex]) * sLoc.z);
1397  // NOTE Doppler is computed in the CalcBeamformingGain function and is simplified to only account for the center anngle of each cluster.
1398 
1399  double rxFieldPatternPhi, rxFieldPatternTheta, txFieldPatternPhi, txFieldPatternTheta;
1400  std::tie (rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern (Angles (rayAoa_radian[nIndex][mIndex], rayZoa_radian[nIndex][mIndex]));
1401  std::tie (txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern (Angles (rayAod_radian[nIndex][mIndex], rayZod_radian[nIndex][mIndex]));
1402 
1403  rays += (exp (std::complex<double> (0, initialPhase[0])) * rxFieldPatternTheta * txFieldPatternTheta +
1404  +exp (std::complex<double> (0, initialPhase[1])) * std::sqrt (1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
1405  +exp (std::complex<double> (0, initialPhase[2])) * std::sqrt (1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
1406  +exp (std::complex<double> (0, initialPhase[3])) * rxFieldPatternPhi * txFieldPatternPhi)
1407  * exp (std::complex<double> (0, rxPhaseDiff))
1408  * exp (std::complex<double> (0, txPhaseDiff));
1409  }
1410  rays *= sqrt (clusterPower[nIndex] / raysPerCluster);
1411  H_usn[uIndex][sIndex][nIndex] = rays;
1412  }
1413  else //(7.5-28)
1414  {
1415  std::complex<double> raysSub1 (0,0);
1416  std::complex<double> raysSub2 (0,0);
1417  std::complex<double> raysSub3 (0,0);
1418 
1419  for (uint8_t mIndex = 0; mIndex < raysPerCluster; mIndex++)
1420  {
1421  double k = crossPolarizationPowerRatios[nIndex][mIndex];
1422 
1423  //ZML:Just remind me that the angle offsets for the 3 subclusters were not generated correctly.
1424 
1425  DoubleVector initialPhase = clusterPhase[nIndex][mIndex];
1426  double rxPhaseDiff = 2 * M_PI * (sin (rayZoa_radian[nIndex][mIndex]) * cos (rayAoa_radian[nIndex][mIndex]) * uLoc.x
1427  + sin (rayZoa_radian[nIndex][mIndex]) * sin (rayAoa_radian[nIndex][mIndex]) * uLoc.y
1428  + cos (rayZoa_radian[nIndex][mIndex]) * uLoc.z);
1429  double txPhaseDiff = 2 * M_PI * (sin (rayZod_radian[nIndex][mIndex]) * cos (rayAod_radian[nIndex][mIndex]) * sLoc.x
1430  + sin (rayZod_radian[nIndex][mIndex]) * sin (rayAod_radian[nIndex][mIndex]) * sLoc.y
1431  + cos (rayZod_radian[nIndex][mIndex]) * sLoc.z);
1432 
1433  double rxFieldPatternPhi, rxFieldPatternTheta, txFieldPatternPhi, txFieldPatternTheta;
1434  std::tie (rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern (Angles (rayAoa_radian[nIndex][mIndex], rayZoa_radian[nIndex][mIndex]));
1435  std::tie (txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern (Angles (rayAod_radian[nIndex][mIndex], rayZod_radian[nIndex][mIndex]));
1436 
1437  switch (mIndex)
1438  {
1439  case 9:
1440  case 10:
1441  case 11:
1442  case 12:
1443  case 17:
1444  case 18:
1445  raysSub2 += (exp (std::complex<double> (0, initialPhase[0])) * rxFieldPatternTheta * txFieldPatternTheta +
1446  +exp (std::complex<double> (0, initialPhase[1])) * std::sqrt (1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
1447  +exp (std::complex<double> (0, initialPhase[2])) * std::sqrt (1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
1448  +exp (std::complex<double> (0, initialPhase[3])) * rxFieldPatternPhi * txFieldPatternPhi)
1449  * exp (std::complex<double> (0, rxPhaseDiff))
1450  * exp (std::complex<double> (0, txPhaseDiff));
1451  break;
1452  case 13:
1453  case 14:
1454  case 15:
1455  case 16:
1456  raysSub3 += (exp (std::complex<double> (0, initialPhase[0])) * rxFieldPatternTheta * txFieldPatternTheta +
1457  +exp (std::complex<double> (0, initialPhase[1])) * std::sqrt (1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
1458  +exp (std::complex<double> (0, initialPhase[2])) * std::sqrt (1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
1459  +exp (std::complex<double> (0, initialPhase[3])) * rxFieldPatternPhi * txFieldPatternPhi)
1460  * exp (std::complex<double> (0, rxPhaseDiff))
1461  * exp (std::complex<double> (0, txPhaseDiff));
1462  break;
1463  default: //case 1,2,3,4,5,6,7,8,19,20
1464  raysSub1 += (exp (std::complex<double> (0, initialPhase[0])) * rxFieldPatternTheta * txFieldPatternTheta +
1465  +exp (std::complex<double> (0, initialPhase[1])) * std::sqrt (1 / k) * rxFieldPatternTheta * txFieldPatternPhi +
1466  +exp (std::complex<double> (0, initialPhase[2])) * std::sqrt (1 / k) * rxFieldPatternPhi * txFieldPatternTheta +
1467  +exp (std::complex<double> (0, initialPhase[3])) * rxFieldPatternPhi * txFieldPatternPhi)
1468  * exp (std::complex<double> (0, rxPhaseDiff))
1469  * exp (std::complex<double> (0, txPhaseDiff));
1470  break;
1471  }
1472  }
1473  raysSub1 *= sqrt (clusterPower[nIndex] / raysPerCluster);
1474  raysSub2 *= sqrt (clusterPower[nIndex] / raysPerCluster);
1475  raysSub3 *= sqrt (clusterPower[nIndex] / raysPerCluster);
1476  H_usn[uIndex][sIndex][nIndex] = raysSub1;
1477  H_usn[uIndex][sIndex].push_back (raysSub2);
1478  H_usn[uIndex][sIndex].push_back (raysSub3);
1479 
1480  }
1481  }
1482  if (los) //(7.5-29) && (7.5-30)
1483  {
1484  std::complex<double> ray (0,0);
1485  double rxPhaseDiff = 2 * M_PI * (sin (uAngle.theta) * cos (uAngle.phi) * uLoc.x
1486  + sin (uAngle.theta) * sin (uAngle.phi) * uLoc.y
1487  + cos (uAngle.theta) * uLoc.z);
1488  double txPhaseDiff = 2 * M_PI * (sin (sAngle.theta) * cos (sAngle.phi) * sLoc.x
1489  + sin (sAngle.theta) * sin (sAngle.phi) * sLoc.y
1490  + cos (sAngle.theta) * sLoc.z);
1491 
1492  double rxFieldPatternPhi, rxFieldPatternTheta, txFieldPatternPhi, txFieldPatternTheta;
1493  std::tie (rxFieldPatternPhi, rxFieldPatternTheta) = uAntenna->GetElementFieldPattern (Angles (uAngle.phi, uAngle.theta));
1494  std::tie (txFieldPatternPhi, txFieldPatternTheta) = sAntenna->GetElementFieldPattern (Angles (sAngle.phi, sAngle.theta));
1495 
1496  double lambda = 3e8 / m_frequency; // the wavelength of the carrier frequency
1497 
1498  ray = (rxFieldPatternTheta * txFieldPatternTheta - rxFieldPatternPhi * txFieldPatternPhi)
1499  * exp (std::complex<double> (0, - 2 * M_PI * dis3D / lambda))
1500  * exp (std::complex<double> (0, rxPhaseDiff))
1501  * exp (std::complex<double> (0, txPhaseDiff));
1502 
1503  double K_linear = pow (10,K_factor / 10);
1504  // the LOS path should be attenuated if blockage is enabled.
1505  H_usn[uIndex][sIndex][0] = sqrt (1 / (K_linear + 1)) * H_usn[uIndex][sIndex][0] + sqrt (K_linear / (1 + K_linear)) * ray / pow (10,attenuation_dB[0] / 10); //(7.5-30) for tau = tau1
1506  double tempSize = H_usn[uIndex][sIndex].size ();
1507  for (uint8_t nIndex = 1; nIndex < tempSize; nIndex++)
1508  {
1509  H_usn[uIndex][sIndex][nIndex] *= sqrt (1 / (K_linear + 1)); //(7.5-30) for tau = tau2...taunN
1510  }
1511 
1512  }
1513  }
1514  }
1515 
1516  // store the delays and the angles for the subclusters
1517  if (cluster1st == cluster2nd)
1518  {
1519  clusterDelay.push_back (clusterDelay[cluster1st] + 1.28 * table3gpp->m_cDS);
1520  clusterDelay.push_back (clusterDelay[cluster1st] + 2.56 * table3gpp->m_cDS);
1521 
1522  clusterAoa.push_back (clusterAoa[cluster1st]);
1523  clusterAoa.push_back (clusterAoa[cluster1st]);
1524 
1525  clusterZoa.push_back (clusterZoa[cluster1st]);
1526  clusterZoa.push_back (clusterZoa[cluster1st]);
1527 
1528  clusterAod.push_back (clusterAod[cluster1st]);
1529  clusterAod.push_back (clusterAod[cluster1st]);
1530 
1531  clusterZod.push_back (clusterZod[cluster1st]);
1532  clusterZod.push_back (clusterZod[cluster1st]);
1533  }
1534  else
1535  {
1536  double min, max;
1537  if (cluster1st < cluster2nd)
1538  {
1539  min = cluster1st;
1540  max = cluster2nd;
1541  }
1542  else
1543  {
1544  min = cluster2nd;
1545  max = cluster1st;
1546  }
1547  clusterDelay.push_back (clusterDelay[min] + 1.28 * table3gpp->m_cDS);
1548  clusterDelay.push_back (clusterDelay[min] + 2.56 * table3gpp->m_cDS);
1549  clusterDelay.push_back (clusterDelay[max] + 1.28 * table3gpp->m_cDS);
1550  clusterDelay.push_back (clusterDelay[max] + 2.56 * table3gpp->m_cDS);
1551 
1552  clusterAoa.push_back (clusterAoa[min]);
1553  clusterAoa.push_back (clusterAoa[min]);
1554  clusterAoa.push_back (clusterAoa[max]);
1555  clusterAoa.push_back (clusterAoa[max]);
1556 
1557  clusterZoa.push_back (clusterZoa[min]);
1558  clusterZoa.push_back (clusterZoa[min]);
1559  clusterZoa.push_back (clusterZoa[max]);
1560  clusterZoa.push_back (clusterZoa[max]);
1561 
1562  clusterAod.push_back (clusterAod[min]);
1563  clusterAod.push_back (clusterAod[min]);
1564  clusterAod.push_back (clusterAod[max]);
1565  clusterAod.push_back (clusterAod[max]);
1566 
1567  clusterZod.push_back (clusterZod[min]);
1568  clusterZod.push_back (clusterZod[min]);
1569  clusterZod.push_back (clusterZod[max]);
1570  clusterZod.push_back (clusterZod[max]);
1571 
1572 
1573  }
1574 
1575  NS_LOG_INFO ("size of coefficient matrix =[" << H_usn.size () << "][" << H_usn[0].size () << "][" << H_usn[0][0].size () << "]");
1576 
1577  channelParams->m_channel = H_usn;
1578  channelParams->m_delay = clusterDelay;
1579 
1580  channelParams->m_angle.clear ();
1581  channelParams->m_angle.push_back (clusterAoa);
1582  channelParams->m_angle.push_back (clusterZoa);
1583  channelParams->m_angle.push_back (clusterAod);
1584  channelParams->m_angle.push_back (clusterZod);
1585 
1586  return channelParams;
1587 }
1588 
1591  const DoubleVector &clusterAOA,
1592  const DoubleVector &clusterZOA) const
1593 {
1594  NS_LOG_FUNCTION (this);
1595 
1596  DoubleVector powerAttenuation;
1597  uint8_t clusterNum = clusterAOA.size ();
1598  for (uint8_t cInd = 0; cInd < clusterNum; cInd++)
1599  {
1600  powerAttenuation.push_back (0); //Initial power attenuation for all clusters to be 0 dB;
1601  }
1602  //step a: the number of non-self blocking blockers is stored in m_numNonSelfBlocking.
1603 
1604  //step b:Generate the size and location of each blocker
1605  //generate self blocking (i.e., for blockage from the human body)
1606  double phi_sb, x_sb, theta_sb, y_sb;
1607  //table 7.6.4.1-1 Self-blocking region parameters.
1608  if (m_portraitMode)
1609  {
1610  phi_sb = 260;
1611  x_sb = 120;
1612  theta_sb = 100;
1613  y_sb = 80;
1614  }
1615  else // landscape mode
1616  {
1617  phi_sb = 40;
1618  x_sb = 160;
1619  theta_sb = 110;
1620  y_sb = 75;
1621  }
1622 
1623  //generate or update non-self blocking
1624  if (params->m_nonSelfBlocking.size () == 0) //generate new blocking regions
1625  {
1626  for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
1627  {
1628  //draw value from table 7.6.4.1-2 Blocking region parameters
1629  DoubleVector table;
1630  table.push_back (m_normalRv->GetValue ()); //phi_k: store the normal RV that will be mapped to uniform (0,360) later.
1631  if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
1632  {
1633  table.push_back (m_uniformRv->GetValue (15, 45)); //x_k
1634  table.push_back (90); //Theta_k
1635  table.push_back (m_uniformRv->GetValue (5, 15)); //y_k
1636  table.push_back (2); //r
1637  }
1638  else
1639  {
1640  table.push_back (m_uniformRv->GetValue (5, 15)); //x_k
1641  table.push_back (90); //Theta_k
1642  table.push_back (5); //y_k
1643  table.push_back (10); //r
1644  }
1645  params->m_nonSelfBlocking.push_back (table);
1646  }
1647  }
1648  else
1649  {
1650  double deltaX = sqrt (pow (params->m_preLocUT.x - params->m_locUT.x, 2) + pow (params->m_preLocUT.y - params->m_locUT.y, 2));
1651  //if deltaX and speed are both 0, the autocorrelation is 1, skip updating
1652  if (deltaX > 1e-6 || m_blockerSpeed > 1e-6)
1653  {
1654  double corrDis;
1655  //draw value from table 7.6.4.1-4: Spatial correlation distance for different m_scenarios.
1656  if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
1657  {
1658  //InH, correlation distance = 5;
1659  corrDis = 5;
1660  }
1661  else
1662  {
1663  if (params->m_o2i) // outdoor to indoor
1664  {
1665  corrDis = 5;
1666  }
1667  else //LOS or NLOS
1668  {
1669  corrDis = 10;
1670  }
1671  }
1672  double R;
1673  if (m_blockerSpeed > 1e-6) // speed not equal to 0
1674  {
1675  double corrT = corrDis / m_blockerSpeed;
1676  R = exp (-1 * (deltaX / corrDis + (Now ().GetSeconds () - params->m_generatedTime.GetSeconds ()) / corrT));
1677  }
1678  else
1679  {
1680  R = exp (-1 * (deltaX / corrDis));
1681  }
1682 
1683  NS_LOG_INFO ("Distance change:" << deltaX << " Speed:" << m_blockerSpeed
1684  << " Time difference:" << Now ().GetSeconds () - params->m_generatedTime.GetSeconds ()
1685  << " correlation:" << R);
1686 
1687  //In order to generate correlated uniform random variables, we first generate correlated normal random variables and map the normal RV to uniform RV.
1688  //Notice the correlation will change if the RV is transformed from normal to uniform.
1689  //To compensate the distortion, the correlation of the normal RV is computed
1690  //such that the uniform RV would have the desired correlation when transformed from normal RV.
1691 
1692  //The following formula was obtained from MATLAB numerical simulation.
1693 
1694  if (R * R * (-0.069) + R * 1.074 - 0.002 < 1) //transform only when the correlation of normal RV is smaller than 1
1695  {
1696  R = R * R * (-0.069) + R * 1.074 - 0.002;
1697  }
1698  for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
1699  {
1700 
1701  //Generate a new correlated normal RV with the following formula
1702  params->m_nonSelfBlocking[blockInd][PHI_INDEX] =
1703  R * params->m_nonSelfBlocking[blockInd][PHI_INDEX] + sqrt (1 - R * R) * m_normalRv->GetValue ();
1704  }
1705  }
1706 
1707  }
1708 
1709  //step c: Determine the attenuation of each blocker due to blockers
1710  for (uint8_t cInd = 0; cInd < clusterNum; cInd++)
1711  {
1712  NS_ASSERT_MSG (clusterAOA[cInd] >= 0 && clusterAOA[cInd] <= 360, "the AOA should be the range of [0,360]");
1713  NS_ASSERT_MSG (clusterZOA[cInd] >= 0 && clusterZOA[cInd] <= 180, "the ZOA should be the range of [0,180]");
1714 
1715  //check self blocking
1716  NS_LOG_INFO ("AOA=" << clusterAOA[cInd] << " Block Region[" << phi_sb - x_sb / 2 << "," << phi_sb + x_sb / 2 << "]");
1717  NS_LOG_INFO ("ZOA=" << clusterZOA[cInd] << " Block Region[" << theta_sb - y_sb / 2 << "," << theta_sb + y_sb / 2 << "]");
1718  if ( std::abs (clusterAOA[cInd] - phi_sb) < (x_sb / 2) && std::abs (clusterZOA[cInd] - theta_sb) < (y_sb / 2))
1719  {
1720  powerAttenuation[cInd] += 30; //anttenuate by 30 dB.
1721  NS_LOG_INFO ("Cluster[" << (int)cInd << "] is blocked by self blocking region and reduce 30 dB power,"
1722  "the attenuation is [" << powerAttenuation[cInd] << " dB]");
1723  }
1724 
1725  //check non-self blocking
1726  double phiK, xK, thetaK, yK;
1727  for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
1728  {
1729  //The normal RV is transformed to uniform RV with the desired correlation.
1730  phiK = (0.5 * erfc (-1 * params->m_nonSelfBlocking[blockInd][PHI_INDEX] / sqrt (2))) * 360;
1731  while (phiK > 360)
1732  {
1733  phiK -= 360;
1734  }
1735 
1736  while (phiK < 0)
1737  {
1738  phiK += 360;
1739  }
1740 
1741  xK = params->m_nonSelfBlocking[blockInd][X_INDEX];
1742  thetaK = params->m_nonSelfBlocking[blockInd][THETA_INDEX];
1743  yK = params->m_nonSelfBlocking[blockInd][Y_INDEX];
1744  NS_LOG_INFO ("AOA=" << clusterAOA[cInd] << " Block Region[" << phiK - xK << "," << phiK + xK << "]");
1745  NS_LOG_INFO ("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaK - yK << "," << thetaK + yK << "]");
1746 
1747  if ( std::abs (clusterAOA[cInd] - phiK) < (xK)
1748  && std::abs (clusterZOA[cInd] - thetaK) < (yK))
1749  {
1750  double A1 = clusterAOA[cInd] - (phiK + xK / 2); //(7.6-24)
1751  double A2 = clusterAOA[cInd] - (phiK - xK / 2); //(7.6-25)
1752  double Z1 = clusterZOA[cInd] - (thetaK + yK / 2); //(7.6-26)
1753  double Z2 = clusterZOA[cInd] - (thetaK - yK / 2); //(7.6-27)
1754  int signA1, signA2, signZ1, signZ2;
1755  //draw sign for the above parameters according to table 7.6.4.1-3 Description of signs
1756  if (xK / 2 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
1757  {
1758  signA1 = -1;
1759  }
1760  else
1761  {
1762  signA1 = 1;
1763  }
1764  if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2)
1765  {
1766  signA2 = -1;
1767  }
1768  else
1769  {
1770  signA2 = 1;
1771  }
1772 
1773  if (yK / 2 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
1774  {
1775  signZ1 = -1;
1776  }
1777  else
1778  {
1779  signZ1 = 1;
1780  }
1781  if (-1 * yK < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= -1 * yK / 2)
1782  {
1783  signZ2 = -1;
1784  }
1785  else
1786  {
1787  signZ2 = 1;
1788  }
1789  double lambda = 3e8 / m_frequency;
1790  double F_A1 = atan (signA1 * M_PI / 2 * sqrt (M_PI / lambda *
1791  params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (A1 * M_PI / 180) - 1))) / M_PI; //(7.6-23)
1792  double F_A2 = atan (signA2 * M_PI / 2 * sqrt (M_PI / lambda *
1793  params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (A2 * M_PI / 180) - 1))) / M_PI;
1794  double F_Z1 = atan (signZ1 * M_PI / 2 * sqrt (M_PI / lambda *
1795  params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (Z1 * M_PI / 180) - 1))) / M_PI;
1796  double F_Z2 = atan (signZ2 * M_PI / 2 * sqrt (M_PI / lambda *
1797  params->m_nonSelfBlocking[blockInd][R_INDEX] * (1 / cos (Z2 * M_PI / 180) - 1))) / M_PI;
1798  double L_dB = -20 * log10 (1 - (F_A1 + F_A2) * (F_Z1 + F_Z2)); //(7.6-22)
1799  powerAttenuation[cInd] += L_dB;
1800  NS_LOG_INFO ("Cluster[" << (int)cInd << "] is blocked by no-self blocking, "
1801  "the loss is [" << L_dB << "]" << " dB");
1802 
1803  }
1804  }
1805  }
1806  return powerAttenuation;
1807 }
1808 
1809 int64_t
1811 {
1812  NS_LOG_FUNCTION (this << stream);
1813  m_normalRv->SetStream (stream);
1814  m_uniformRv->SetStream (stream + 1);
1815  return 2;
1816 }
1817 
1818 } // namespace ns3
static const double offSetAlpha[20]
static const double sqrtC_UMa_LOS[7][7]
This is an interface for a channel model that can be described by a channel matrix, e.g., the 3GPP Spatial Channel Models, which is generally used in combination with antenna arrays.
std::unordered_map< uint32_t, Ptr< ThreeGppChannelMatrix > > m_channelMap
map containing the channel realizations
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
AttributeValue implementation for Boolean.
Definition: boolean.h:36
static const double sqrtC_office_NLOS[6][6]
uint32_t GetId(void) const
Definition: node.cc:109
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
NS_ASSERT_MSG(false, "Ipv4AddressGenerator::MaskToIndex(): Impossible")
Hold variables of type string.
Definition: string.h:41
#define min(a, b)
Definition: 80211b.c:42
static const double sqrtC_UMi_O2I[6][6]
void DoDispose() override
Destructor implementation.
static const uint8_t THETA_INDEX
index of the THETA value in the m_nonSelfBlocking array
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: boolean.h:85
Ptr< NormalRandomVariable > m_normalRv
normal random variable
std::string GetScenario(void) const
Returns the propagation scenario.
Hold a signed integer type.
Definition: integer.h:44
std::vector< Complex2DVector > Complex3DVector
type definition for complex 3D matrices
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1070
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
double theta
the inclination angle in radians
Definition: angles.h:117
std::vector< double > DoubleVector
type definition for vectors of doubles
static const uint8_t R_INDEX
index of the R value in the m_nonSelfBlocking array
DoubleVector m_delay
cluster delay in nanoseconds.
void SetChannelConditionModel(Ptr< ChannelConditionModel > model)
Set the channel condition model.
Ptr< ChannelConditionModel > m_channelConditionModel
the channel condition model
std::string m_scenario
the 3GPP scenario
static const double sqrtC_UMa_O2I[6][6]
std::pair< uint32_t, uint32_t > m_nodeIds
the first element is the s-node ID (the transmitter when the channel was generated), the second element is the u-node ID (the receiver when the channel was generated)
static const uint8_t X_INDEX
index of the X value in the m_nonSelfBlocking array
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: integer.h:45
Ptr< const ParamsTable > GetThreeGppTable(bool los, bool o2i, double hBS, double hUT, double distance2D) const
Get the parameters needed to apply the channel generation procedure.
Ptr< const AttributeChecker > MakeStringChecker(void)
Definition: string.cc:30
static const double sqrtC_office_LOS[7][7]
void SetFrequency(double f)
Sets the center frequency of the model.
double GetFrequency(void) const
Returns the center frequency.
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: pointer.h:227
#define max(a, b)
Definition: 80211b.c:43
bool IsZero(void) const
Definition: nstime.h:294
AttributeValue implementation for Time.
Definition: nstime.h:1124
uint16_t m_numNonSelfBlocking
number of non-self-blocking regions
static const double sqrtC_RMa_LOS[7][7]
Time m_updatePeriod
the channel update period
int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model...
Ptr< const ChannelMatrix > GetChannel(Ptr< const MobilityModel > aMob, Ptr< const MobilityModel > bMob, Ptr< const ThreeGppAntennaArrayModel > aAntenna, Ptr< const ThreeGppAntennaArrayModel > bAntenna) override
Looks for the channel matrix associated to the aMob and bMob pair in m_channelMap.
bool m_portraitMode
true if potrait mode, false if landscape
bool ChannelMatrixNeedsUpdate(Ptr< const ThreeGppChannelMatrix > channelMatrix, bool isLos) const
Check if the channel matrix has to be updated.
Complex3DVector m_channel
channel matrix H[u][s][n].
MatrixBasedChannelModel::Double3DVector m_clusterPhase
the initial random phases
double m_blockerSpeed
the blocker speed
double f(double x, void *params)
Definition: 80211b.c:70
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Hold objects of type Ptr<T>.
Definition: pointer.h:36
int64_t GetNanoSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:373
double GetValue(double min, double max)
Get the next random value, as a double in the specified range .
Ptr< UniformRandomVariable > m_uniformRv
uniform random variable
static constexpr uint32_t GetKey(uint32_t x1, uint32_t x2)
Calculate the channel key using the Cantor function.
std::vector< DoubleVector > Double2DVector
type definition for matrices of doubles
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: nstime.h:1125
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:195
static const double sqrtC_RMa_NLOS[6][6]
Double2DVector m_angle
cluster angle angle[direction][n], where direction = 0(AOA), 1(ZOA), 2(AOD), 3(ZOD) in degree...
static const double sqrtC_RMa_O2I[6][6]
std::vector< Double2DVector > Double3DVector
type definition for 3D matrices of doubles
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: double.h:42
double max(double x, double y)
void SetScenario(const std::string &scenario)
Sets the propagation scenario.
static const uint8_t PHI_INDEX
index of the PHI value in the m_nonSelfBlocking array
if(desigRtr==addrLocal)
bool m_blockage
enables the blockage model A
static TypeId GetTypeId()
Get the type ID.
A network Node.
Definition: node.h:56
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
Ptr< const AttributeChecker > MakeBooleanChecker(void)
Definition: boolean.cc:121
double phi
the azimuth angle in radians
Definition: angles.h:111
double min(double x, double y)
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:449
Time Now(void)
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:287
struct holding the azimuth and inclination angles of spherical coordinates.
Definition: angles.h:71
A base class which provides memory management and object aggregation.
Definition: object.h:87
Ptr< const AttributeAccessor > MakeStringAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: string.h:42
static const double sqrtC_UMa_NLOS[6][6]
This class can be used to hold variables of floating point type such as &#39;double&#39; or &#39;float&#39;...
Definition: double.h:41
Ptr< ChannelConditionModel > GetChannelConditionModel() const
Get the associated channel condition model.
static const uint8_t Y_INDEX
index of the Y value in the m_nonSelfBlocking array
a unique identifier for an interface.
Definition: type-id.h:58
static const double sqrtC_UMi_LOS[7][7]
Ptr< ThreeGppChannelMatrix > GetNewChannel(Vector locUT, bool los, bool o2i, Ptr< const ThreeGppAntennaArrayModel > sAntenna, Ptr< const ThreeGppAntennaArrayModel > uAntenna, Angles &uAngle, Angles &sAngle, double dis2D, double hBS, double hUT) const
Compute the channel matrix between two devices using the procedure described in 3GPP TR 38...
DoubleVector CalcAttenuationOfBlockage(Ptr< ThreeGppChannelMatrix > params, const DoubleVector &clusterAOA, const DoubleVector &clusterZOA) const
Applies the blockage model A described in 3GPP TR 38.901.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
static const double sqrtC_UMi_NLOS[6][6]
double m_frequency
the operating frequency