A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
three-gpp-channel-model.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
3 * University of Padova
4 * Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering,
5 * New York University
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation;
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
23
24#include "ns3/double.h"
25#include "ns3/geocentric-constant-position-mobility-model.h"
26#include "ns3/integer.h"
27#include "ns3/log.h"
28#include "ns3/mobility-model.h"
29#include "ns3/node.h"
30#include "ns3/phased-array-model.h"
31#include "ns3/pointer.h"
32#include "ns3/string.h"
33#include <ns3/simulator.h>
34
35#include <algorithm>
36#include <map>
37#include <random>
38
39namespace ns3
40{
41
42NS_LOG_COMPONENT_DEFINE("ThreeGppChannelModel");
43
44NS_OBJECT_ENSURE_REGISTERED(ThreeGppChannelModel);
45
46/// Conversion factor: degrees to radians
47static constexpr double DEG2RAD = M_PI / 180.0;
48
49/// The ray offset angles within a cluster, given for rms angle spread normalized to 1.
50/// (Table 7.5-3)
51static const double offSetAlpha[20] = {
52 0.0447, -0.0447, 0.1413, -0.1413, 0.2492, -0.2492, 0.3715, -0.3715, 0.5129, -0.5129,
53 0.6797, -0.6797, 0.8844, -0.8844, 1.1481, -1.1481, 1.5195, -1.5195, 2.1551, -2.1551,
54};
55
56/**
57 * The square root matrix for <em>RMa LOS</em>, which is generated using the
58 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
59 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
60 *
61 * The Matlab file to generate the matrices can be found in
62 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
63 */
64static const double sqrtC_RMa_LOS[7][7] = {
65 {1, 0, 0, 0, 0, 0, 0},
66 {0, 1, 0, 0, 0, 0, 0},
67 {-0.5, 0, 0.866025, 0, 0, 0, 0},
68 {0, 0, 0, 1, 0, 0, 0},
69 {0, 0, 0, 0, 1, 0, 0},
70 {0.01, 0, -0.0519615, 0.73, -0.2, 0.651383, 0},
71 {-0.17, -0.02, 0.21362, -0.14, 0.24, 0.142773, 0.909661},
72};
73
74/**
75 * The square root matrix for <em>RMa NLOS</em>, which is generated using the
76 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
77 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
78 * The parameter K is ignored.
79 *
80 * The Matlab file to generate the matrices can be found in
81 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
82 */
83static const double sqrtC_RMa_NLOS[6][6] = {
84 {1, 0, 0, 0, 0, 0},
85 {-0.5, 0.866025, 0, 0, 0, 0},
86 {0.6, -0.11547, 0.791623, 0, 0, 0},
87 {0, 0, 0, 1, 0, 0},
88 {-0.04, -0.138564, 0.540662, -0.18, 0.809003, 0},
89 {-0.25, -0.606218, -0.240013, 0.26, -0.231685, 0.625392},
90};
91
92/**
93 * The square root matrix for <em>RMa O2I</em>, which is generated using the
94 * Cholesky decomposition according to table 7.5-6 Part 2 and follows the order
95 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
96 *
97 * The Matlab file to generate the matrices can be found in
98 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
99 */
100static const double sqrtC_RMa_O2I[6][6] = {
101 {1, 0, 0, 0, 0, 0},
102 {0, 1, 0, 0, 0, 0},
103 {0, 0, 1, 0, 0, 0},
104 {0, 0, -0.7, 0.714143, 0, 0},
105 {0, 0, 0.66, -0.123225, 0.741091, 0},
106 {0, 0, 0.47, 0.152631, -0.393194, 0.775373},
107};
108
109/**
110 * The square root matrix for <em>UMa LOS</em>, which is generated using the
111 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
112 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
113 *
114 * The Matlab file to generate the matrices can be found in
115 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
116 */
117static const double sqrtC_UMa_LOS[7][7] = {
118 {1, 0, 0, 0, 0, 0, 0},
119 {0, 1, 0, 0, 0, 0, 0},
120 {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
121 {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
122 {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
123 {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
124 {-0.8, 0, -0.388057, -0.367926, 0.238537, -3.58949e-15, 0.130931},
125};
126
127/**
128 * The square root matrix for <em>UMa NLOS</em>, which is generated using the
129 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
130 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
131 * The parameter K is ignored.
132 *
133 * The Matlab file to generate the matrices can be found in
134 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
135 */
136static const double sqrtC_UMa_NLOS[6][6] = {
137 {1, 0, 0, 0, 0, 0},
138 {-0.4, 0.916515, 0, 0, 0, 0},
139 {-0.6, 0.174574, 0.78072, 0, 0, 0},
140 {0, 0.654654, 0.365963, 0.661438, 0, 0},
141 {0, -0.545545, 0.762422, 0.118114, 0.327327, 0},
142 {-0.4, -0.174574, -0.396459, 0.392138, 0.49099, 0.507445},
143};
144
145/**
146 * The square root matrix for <em>UMa O2I</em>, which is generated using the
147 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
148 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
149 *
150 * The Matlab file to generate the matrices can be found in
151 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
152 */
153static const double sqrtC_UMa_O2I[6][6] = {
154 {1, 0, 0, 0, 0, 0},
155 {-0.5, 0.866025, 0, 0, 0, 0},
156 {0.2, 0.57735, 0.791623, 0, 0, 0},
157 {0, 0.46188, -0.336861, 0.820482, 0, 0},
158 {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
159 {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
160
161};
162
163/**
164 * The square root matrix for <em>UMi LOS</em>, which is generated using the
165 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
166 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
167 *
168 * The Matlab file to generate the matrices can be found in
169 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
170 */
171static const double sqrtC_UMi_LOS[7][7] = {
172 {1, 0, 0, 0, 0, 0, 0},
173 {0.5, 0.866025, 0, 0, 0, 0, 0},
174 {-0.4, -0.57735, 0.711805, 0, 0, 0, 0},
175 {-0.5, 0.057735, 0.468293, 0.726201, 0, 0, 0},
176 {-0.4, -0.11547, 0.805464, -0.23482, 0.350363, 0, 0},
177 {0, 0, 0, 0.688514, 0.461454, 0.559471, 0},
178 {0, 0, 0.280976, 0.231921, -0.490509, 0.11916, 0.782603},
179};
180
181/**
182 * The square root matrix for <em>UMi NLOS</em>, which is generated using the
183 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
184 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
185 * The parameter K is ignored.
186 *
187 * The Matlab file to generate the matrices can be found in
188 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
189 */
190static const double sqrtC_UMi_NLOS[6][6] = {
191 {1, 0, 0, 0, 0, 0},
192 {-0.7, 0.714143, 0, 0, 0, 0},
193 {0, 0, 1, 0, 0, 0},
194 {-0.4, 0.168034, 0, 0.90098, 0, 0},
195 {0, -0.70014, 0.5, 0.130577, 0.4927, 0},
196 {0, 0, 0.5, 0.221981, -0.566238, 0.616522},
197};
198
199/**
200 * The square root matrix for <em>UMi O2I</em>, which is generated using the
201 * Cholesky decomposition according to table 7.5-6 Part 1 and follows the order
202 * of [SF, K, DS, ASD, ASA, ZSD, ZSA].
203 *
204 * The Matlab file to generate the matrices can be found in
205 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
206 */
207static const double sqrtC_UMi_O2I[6][6] = {
208 {1, 0, 0, 0, 0, 0},
209 {-0.5, 0.866025, 0, 0, 0, 0},
210 {0.2, 0.57735, 0.791623, 0, 0, 0},
211 {0, 0.46188, -0.336861, 0.820482, 0, 0},
212 {0, -0.69282, 0.252646, 0.493742, 0.460857, 0},
213 {0, -0.23094, 0.16843, 0.808554, -0.220827, 0.464515},
214};
215
216/**
217 * The square root matrix for <em>Indoor-Office LOS</em>, which is generated
218 * using the Cholesky decomposition according to table 7.5-6 Part 2 and follows
219 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
220 *
221 * The Matlab file to generate the matrices can be found in
222 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
223 */
224static const double sqrtC_office_LOS[7][7] = {
225 {1, 0, 0, 0, 0, 0, 0},
226 {0.5, 0.866025, 0, 0, 0, 0, 0},
227 {-0.8, -0.11547, 0.588784, 0, 0, 0, 0},
228 {-0.4, 0.23094, 0.520847, 0.717903, 0, 0, 0},
229 {-0.5, 0.288675, 0.73598, -0.348236, 0.0610847, 0, 0},
230 {0.2, -0.11547, 0.418943, 0.541106, 0.219905, 0.655744, 0},
231 {0.3, -0.057735, 0.73598, -0.348236, 0.0610847, -0.304997, 0.383375},
232};
233
234/**
235 * The square root matrix for <em>Indoor-Office NLOS</em>, which is generated
236 * using the Cholesky decomposition according to table 7.5-6 Part 2 and follows
237 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
238 * The parameter K is ignored.
239 *
240 * The Matlab file to generate the matrices can be found in
241 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
242 */
243static const double sqrtC_office_NLOS[6][6] = {
244 {1, 0, 0, 0, 0, 0},
245 {-0.5, 0.866025, 0, 0, 0, 0},
246 {0, 0.46188, 0.886942, 0, 0, 0},
247 {-0.4, -0.23094, 0.120263, 0.878751, 0, 0},
248 {0, -0.311769, 0.55697, -0.249198, 0.728344, 0},
249 {0, -0.069282, 0.295397, 0.430696, 0.468462, 0.709214},
250};
251
252/**
253 * The square root matrix for <em>NTN Dense Urban LOS</em>, which is generated
254 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-1 and follows
255 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
256 *
257 * The Matlab file to generate the matrices can be found in
258 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
259 */
260static const double sqrtC_NTN_DenseUrban_LOS[7][7] = {
261 {1, 0, 0, 0, 0, 0, 0},
262 {0, 1, 0, 0, 0, 0, 0},
263 {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
264 {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
265 {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
266 {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
267 {-0.8, 0, -0.388057, -0.367926, 0.238537, -4.09997e-15, 0.130931},
268};
269
270/**
271 * The square root matrix for <em>NTN Dense Urban NLOS</em>, which is generated
272 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-2 and follows
273 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
274 *
275 * The Matlab file to generate the matrices can be found in
276 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
277 */
278static const double sqrtC_NTN_DenseUrban_NLOS[6][6] = {
279 {1, 0, 0, 0, 0, 0},
280 {-0.4, 0.916515, 0, 0, 0, 0},
281 {-0.6, 0.174574, 0.78072, 0, 0, 0},
282 {0, 0.654654, 0.365963, 0.661438, 0, 0},
283 {0, -0.545545, 0.762422, 0.118114, 0.327327, 0},
284 {-0.4, -0.174574, -0.396459, 0.392138, 0.49099, 0.507445},
285};
286
287/**
288 * The square root matrix for <em>NTN Urban LOS</em>, which is generated
289 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-3 and follows
290 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
291 *
292 * The Matlab file to generate the matrices can be found in
293 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
294 */
295static const double sqrtC_NTN_Urban_LOS[7][7] = {
296 {1, 0, 0, 0, 0, 0, 0},
297 {0, 1, 0, 0, 0, 0, 0},
298 {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
299 {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
300 {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
301 {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
302 {-0.8, 0, -0.388057, -0.367926, 0.238537, -4.09997e-15, 0.130931},
303};
304
305/**
306 * The square root matrix for <em>NTN Urban NLOS</em>, which is generated
307 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-4 and follows
308 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
309 *
310 * The square root matrix is dependent on the elevation angle, thus requiring a map.
311 *
312 * The Matlab file to generate the matrices can be found in
313 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
314 */
315static const std::map<int, std::vector<std::vector<double>>> sqrtC_NTN_Urban_NLOS{
316 {10,
317 {
318 {1, 0, 0, 0, 0, 0},
319 {-0.21, 0.977701, 0, 0, 0, 0},
320 {-0.48, 0.459445, 0.747335, 0, 0, 0},
321 {-0.05, 0.377927, 0.28416, 0.879729, 0, 0},
322 {-0.02, 0.691213, 0.258017, 0.073265, 0.670734, 0},
323 {-0.31, -0.00521632, -0.115615, 0.0788023, 0.00218104, 0.940368},
324 }},
325 {20,
326 {
327 {1, 0, 0, 0, 0, 0},
328 {-0.25, 0.968246, 0, 0, 0, 0},
329 {-0.52, 0.35115, 0.778648, 0, 0, 0},
330 {-0.04, 0.371806, 0.345008, 0.860889, 0, 0},
331 {0, 0.743613, 0.281102, 0.0424415, 0.605161, 0},
332 {-0.32, 0.0206559, -0.0689057, 0.154832, 0.061865, 0.929852},
333 }},
334 {30,
335 {
336 {1, 0, 0, 0, 0, 0},
337 {-0.21, 0.977701, 0, 0, 0, 0},
338 {-0.52, 0.450853, 0.725487, 0, 0, 0},
339 {-0.04, 0.288023, 0.260989, 0.920504, 0, 0},
340 {0.01, 0.697657, 0.386856, 0.0418183, 0.601472, 0},
341 {-0.33, 0.0416283, -0.0694268, 0.166137, 0.139937, 0.915075},
342 }},
343 {40,
344 {
345 {1, 0, 0, 0, 0, 0},
346 {-0.26, 0.965609, 0, 0, 0, 0},
347 {-0.53, 0.395813, 0.749955, 0, 0, 0},
348 {-0.04, 0.299914, 0.320139, 0.897754, 0, 0},
349 {0.01, 0.696556, 0.372815, 0.0580784, 0.610202, 0},
350 {-0.33, 0.0457742, -0.0173584, 0.154417, 0.129332, 0.920941},
351 }},
352 {50,
353 {
354 {1, 0, 0, 0, 0, 0},
355 {-0.25, 0.968246, 0, 0, 0, 0},
356 {-0.57, 0.420864, 0.705672, 0, 0, 0},
357 {-0.03, 0.229797, 0.235501, 0.943839, 0, 0},
358 {0.03, 0.679063, 0.384466, 0.0681379, 0.6209, 0},
359 {-0.41, -0.147173, -0.229228, 0.270707, 0.293002, 0.773668},
360 }},
361 {60,
362 {
363 {1, 0, 0, 0, 0, 0},
364 {-0.2, 0.979796, 0, 0, 0, 0},
365 {-0.53, 0.473568, 0.703444, 0, 0, 0},
366 {-0.05, 0.204124, 0.109225, 0.971547, 0, 0},
367 {0.03, 0.68994, 0.411073, 0.0676935, 0.591202, 0},
368 {-0.4, -0.224537, -0.292371, 0.275609, 0.301835, 0.732828},
369 }},
370 {70,
371 {
372 {1, 0, 0, 0, 0, 0},
373 {-0.19, 0.981784, 0, 0, 0, 0},
374 {-0.5, 0.524555, 0.689088, 0, 0, 0},
375 {-0.03, 0.228462, 0.18163, 0.955989, 0, 0},
376 {-0.02, 0.637818, 0.428725, 0.00608114, 0.639489, 0},
377 {-0.36, -0.18171, -0.282523, 0.106726, 0.123808, 0.854894},
378 }},
379 {80,
380 {
381 {1, 0, 0, 0, 0, 0},
382 {-0.2, 0.979796, 0, 0, 0, 0},
383 {-0.49, 0.502145, 0.712566, 0, 0, 0},
384 {-0.01, 0.232702, 0.151916, 0.960558, 0, 0},
385 {-0.05, 0.612372, 0.376106, 0.0206792, 0.693265, 0},
386 {-0.37, -0.320475, -0.365405, -0.00376264, 0.0364343, 0.790907},
387 }},
388 {90,
389 {
390 {1, 0, 0, 0, 0, 0},
391 {-0.19, 0.981784, 0, 0, 0, 0},
392 {-0.38, 0.58852, 0.713613, 0, 0, 0},
393 {-0.03, 0.360874, 0.12082, 0.924269, 0, 0},
394 {-0.12, 0.526796, 0.34244, 0.0594196, 0.766348, 0},
395 {-0.33, -0.257389, -0.24372, -0.257035, -0.176521, 0.817451},
396 }},
397};
398
399/**
400 * The square root matrix for <em>NTN Suburban LOS</em>, which is generated
401 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-5 and follows
402 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
403 *
404 * The Matlab file to generate the matrices can be found in
405 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
406 */
407static const double sqrtC_NTN_Suburban_LOS[7][7] = {
408 {1, 0, 0, 0, 0, 0, 0},
409 {0, 1, 0, 0, 0, 0, 0},
410 {-0.4, -0.4, 0.824621, 0, 0, 0, 0},
411 {-0.5, 0, 0.242536, 0.83137, 0, 0, 0},
412 {-0.5, -0.2, 0.630593, -0.484671, 0.278293, 0, 0},
413 {0, 0, -0.242536, 0.672172, 0.642214, 0.27735, 0},
414 {-0.8, 0, -0.388057, -0.367926, 0.238537, -4.09997e-15, 0.130931},
415};
416
417/**
418 * The square root matrix for <em>NTN Suburban NLOS</em>, which is generated
419 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-6 and follows
420 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
421 *
422 * The Matlab file to generate the matrices can be found in
423 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
424 */
425static const double sqrtC_NTN_Suburban_NLOS[6][6] = {
426 {1, 0, 0, 0, 0, 0},
427 {-0.4, 0.916515, 0, 0, 0, 0},
428 {-0.6, 0.174574, 0.78072, 0, 0, 0},
429 {0, 0.654654, 0.365963, 0.661438, 0, 0},
430 {0, -0.545545, 0.762422, 0.118114, 0.327327, 0},
431 {-0.4, -0.174574, -0.396459, 0.392138, 0.49099, 0.507445},
432};
433
434/**
435 * The square root matrix for <em>NTN Rural LOS</em>, which is generated
436 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-7 and follows
437 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
438 *
439 * The Matlab file to generate the matrices can be found in
440 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
441 */
442static const double sqrtC_NTN_Rural_LOS[7][7] = {
443 {1, 0, 0, 0, 0, 0, 0},
444 {0, 1, 0, 0, 0, 0, 0},
445 {-0.5, 0, 0.866025, 0, 0, 0, 0},
446 {0, 0, 0, 1, 0, 0, 0},
447 {0, 0, 0, 0, 1, 0, 0},
448 {0.01, 0, -0.0519615, 0.73, -0.2, 0.651383, 0},
449 {-0.17, -0.02, 0.21362, -0.14, 0.24, 0.142773, 0.909661},
450};
451
452/**
453 * The square root matrix for <em>NTN Rural NLOS S Band</em>, which is generated
454 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-8a and follows
455 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
456 *
457 * The square root matrix is dependent on the elevation angle, thus requiring a map.
458 *
459 * The Matlab file to generate the matrices can be found in
460 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
461 */
462static const std::map<int, std::vector<std::vector<double>>> sqrtC_NTN_Rural_NLOS_S{
463 {10,
464 {
465 {1, 0, 0, 0, 0, 0},
466 {-0.36, 0.932952, 0, 0, 0, 0},
467 {0.45, 0.516639, 0.728412, 0, 0, 0},
468 {0.02, 0.329277, 0.371881, 0.867687, 0, 0},
469 {-0.06, 0.59853, 0.436258, -0.0324062, 0.668424, 0},
470 {-0.07, 0.0373009, 0.305087, -0.0280496, -0.225204, 0.921481},
471 }},
472 {20,
473 {
474 {1, 0, 0, 0, 0, 0},
475 {-0.39, 0.920815, 0, 0, 0, 0},
476 {0.52, 0.426579, 0.740021, 0, 0, 0},
477 {0, 0.347518, -0.0381664, 0.936896, 0, 0},
478 {-0.04, 0.710675, 0.172483, 0.116993, 0.670748, 0},
479 {-0.17, -0.0394216, 0.115154, 0.243458, -0.0702635, 0.944498},
480 }},
481 {30,
482 {
483 {1, 0, 0, 0, 0, 0},
484 {-0.41, 0.912086, 0, 0, 0, 0},
485 {0.54, 0.49491, 0.680782, 0, 0, 0},
486 {0, 0.350844, -0.152231, 0.923977, 0, 0},
487 {-0.04, 0.694672, 0.0702137, 0.0832998, 0.709903, 0},
488 {-0.19, -0.0854087, 0.0805978, 0.283811, -0.137441, 0.922318},
489 }},
490 {40,
491 {
492 {1, 0, 0, 0, 0, 0},
493 {-0.37, 0.929032, 0, 0, 0, 0},
494 {0.53, 0.480177, 0.698949, 0, 0, 0},
495 {0.01, 0.434538, 0.00864797, 0.900556, 0, 0},
496 {-0.05, 0.765851, -0.0303947, 0.0421641, 0.63896, 0},
497 {-0.17, -0.16458, 0.0989022, 0.158081, -0.150425, 0.941602},
498 }},
499 {50,
500 {
501 {1, 0, 0, 0, 0, 0},
502 {-0.4, 0.916515, 0, 0, 0, 0},
503 {0.55, 0.403703, 0.731111, 0, 0, 0},
504 {0.02, 0.499719, -0.0721341, 0.862947, 0, 0},
505 {-0.06, 0.835775, -0.156481, 0.0373835, 0.521534, 0},
506 {-0.19, -0.301141, 0.145082, 0.144564, -0.0238067, 0.911427},
507 }},
508 {60,
509 {
510 {1, 0, 0, 0, 0, 0},
511 {-0.41, 0.912086, 0, 0, 0, 0},
512 {0.56, 0.339442, 0.755764, 0, 0, 0},
513 {0.02, 0.436582, -0.0256617, 0.899076, 0, 0},
514 {-0.07, 0.856608, -0.12116, 0.0715303, 0.491453, 0},
515 {-0.2, -0.331109, 0.15136, 0.036082, 0.031313, 0.908391},
516 }},
517 {70,
518 {
519 {1, 0, 0, 0, 0, 0},
520 {-0.4, 0.916515, 0, 0, 0, 0},
521 {0.56, 0.386246, 0.732949, 0, 0, 0},
522 {0.04, 0.573913, -0.0601289, 0.815726, 0, 0},
523 {-0.11, 0.813953, -0.0720183, 0.0281118, 0.565158, 0},
524 {-0.19, -0.432071, 0.236423, -0.0247788, -0.0557206, 0.847113},
525 }},
526 {80,
527 {
528 {1, 0, 0, 0, 0, 0},
529 {-0.46, 0.887919, 0, 0, 0, 0},
530 {0.58, 0.469412, 0.665772, 0, 0, 0},
531 {0.01, 0.309262, -0.286842, 0.90663, 0, 0},
532 {-0.05, 0.762457, -0.268721, -0.0467443, 0.584605, 0},
533 {-0.23, -0.580909, 0.399665, 0.0403629, 0.326208, 0.584698},
534 }},
535 {90,
536 {
537 {1, 0, 0, 0, 0, 0},
538 {-0.3, 0.953939, 0, 0, 0, 0},
539 {0.47, 0.81871, 0.329868, 0, 0, 0},
540 {0.06, 0.0712834, -0.595875, 0.797654, 0, 0},
541 {-0.1, 0.408831, -0.0233859, 0.0412736, 0.905873, 0},
542 {-0.13, -0.407783, 0.439436, -0.0768289, -0.212875, 0.756631},
543 }},
544};
545
546/**
547 * The square root matrix for <em>NTN Rural NLOS Ka Band</em>, which is generated
548 * using the Cholesky decomposition according to 3GPP TR 38.811 v15.4.0 table 6.7.2-8b and follows
549 * the order of [SF, K, DS, ASD, ASA, ZSD, ZSA].
550 *
551 * The square root matrix is dependent on the elevation angle, which acts as the corresponding map's
552 * key..
553 *
554 * The Matlab file to generate the matrices can be found in
555 * https://github.com/nyuwireless-unipd/ns3-mmwave/blob/master/src/mmwave/model/BeamFormingMatrix/SqrtMatrix.m
556 */
557static const std::map<int, std::vector<std::vector<double>>> sqrtC_NTN_Rural_NLOS_Ka{
558 {10,
559 {
560 {1, 0, 0, 0, 0, 0},
561 {-0.36, 0.932952, 0, 0, 0, 0},
562 {0.45, 0.527358, 0.72069, 0, 0, 0},
563 {0.02, 0.350715, 0.355282, 0.866241, 0, 0},
564 {-0.07, 0.562515, 0.478504, 0.0162932, 0.670406, 0},
565 {-0.06, 0.0411597, 0.270982, 0.0121094, -0.159927, 0.946336},
566 }},
567 {20,
568 {
569 {1, 0, 0, 0, 0, 0},
570 {-0.38, 0.924986, 0, 0, 0, 0},
571 {0.52, 0.473088, 0.711188, 0, 0, 0},
572 {0, 0.367573, -0.0617198, 0.927944, 0, 0},
573 {-0.04, 0.68628, 0.149228, 0.115257, 0.701332, 0},
574 {-0.16, -0.0441088, 0.118207, 0.251641, -0.0752458, 0.943131},
575 }},
576 {30,
577 {
578 {1, 0, 0, 0, 0, 0},
579 {-0.42, 0.907524, 0, 0, 0, 0},
580 {0.54, 0.48131, 0.690464, 0, 0, 0},
581 {0, 0.363627, -0.137613, 0.921324, 0, 0},
582 {-0.04, 0.686704, 0.117433, 0.104693, 0.708581, 0},
583 {-0.19, -0.0438556, 0.0922685, 0.269877, -0.136292, 0.928469},
584 }},
585 {40,
586 {
587 {1, 0, 0, 0, 0, 0},
588 {-0.36, 0.932952, 0, 0, 0, 0},
589 {0.53, 0.483197, 0.696865, 0, 0, 0},
590 {0.01, 0.464761, -0.0285153, 0.88492, 0, 0},
591 {-0.05, 0.763169, 0.140255, 0.0562856, 0.626286, 0},
592 {-0.16, -0.126051, 0.0942905, 0.195354, -0.217188, 0.92967},
593 }},
594 {50,
595 {
596 {1, 0, 0, 0, 0, 0},
597 {-0.39, 0.920815, 0, 0, 0, 0},
598 {0.55, 0.406705, 0.729446, 0, 0, 0},
599 {0.01, 0.503793, -0.123923, 0.854831, 0, 0},
600 {-0.06, 0.821664, -0.207246, 0.0245302, 0.526988, 0},
601 {-0.19, -0.254231, 0.10679, 0.190931, -0.0665276, 0.920316},
602 }},
603 {60,
604 {
605 {1, 0, 0, 0, 0, 0},
606 {-0.42, 0.907524, 0, 0, 0, 0},
607 {0.56, 0.391395, 0.730213, 0, 0, 0},
608 {0.02, 0.427978, -0.0393147, 0.902712, 0, 0},
609 {-0.06, 0.820694, -0.119986, 0.105509, 0.545281, 0},
610 {-0.2, -0.279882, 0.180145, 0.0563477, -0.0121631, 0.919723},
611 }},
612 {70,
613 {
614 {1, 0, 0, 0, 0, 0},
615 {-0.36, 0.932952, 0, 0, 0, 0},
616 {0.54, 0.519212, 0.662434, 0, 0, 0},
617 {0.04, 0.412025, -0.0234416, 0.909992, 0, 0},
618 {-0.09, 0.758452, -0.0682296, 0.0214276, 0.64151, 0},
619 {-0.17, -0.387158, 0.306169, -0.0291255, -0.109344, 0.845378},
620 }},
621 {80,
622 {
623 {1, 0, 0, 0, 0, 0},
624 {-0.44, 0.897998, 0, 0, 0, 0},
625 {0.57, 0.43519, 0.696928, 0, 0, 0},
626 {0.01, 0.316705, -0.248988, 0.915207, 0, 0},
627 {-0.06, 0.805793, -0.296262, -0.0419182, 0.507514, 0},
628 {-0.22, -0.497551, 0.289742, 0.0785823, 0.328773, 0.711214},
629 }},
630 {90,
631 {
632 {1, 0, 0, 0, 0, 0},
633 {-0.27, 0.96286, 0, 0, 0, 0},
634 {0.46, 0.741748, 0.488067, 0, 0, 0},
635 {0.04, 0.0735309, -0.374828, 0.923308, 0, 0},
636 {-0.08, 0.517624, 0.128779, 0.0795063, 0.838308, 0},
637 {-0.11, -0.321646, 0.0802763, -0.131981, -0.193429, 0.907285},
638 }},
639};
640
641/**
642 * The enumerator used for code clarity when performing parameter assignment in GetThreeGppTable
643 */
645{
669
670/**
671 * The nested map containing the threegpp value tables for the NTN Dense Urban LOS scenario
672 */
673static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNDenseUrbanLOS{
674 {"S",
675 {
676 {10,
677 {
678 -7.12, 0.8, -3.06, 0.48, 0.94, 0.7, 0.82, 0.03, -2.52, 0.5, 4.4,
679 3.3, 2.5, 24.4, 3.8, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
680 }},
681 {20,
682 {
683 -7.28, 0.67, -2.68, 0.36, 0.87, 0.66, 0.5, 0.09, -2.29, 0.53, 9.0,
684 6.6, 2.5, 23.6, 4.7, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
685 }},
686 {30,
687 {
688 -7.45, 0.68, -2.51, 0.38, 0.92, 0.68, 0.82, 0.05, -2.19, 0.58, 9.3,
689 6.1, 2.5, 23.2, 4.6, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
690 }},
691 {40,
692 {
693 -7.73, 0.66, -2.4, 0.32, 0.79, 0.64, 1.23, 0.03, -2.24, 0.51, 7.9,
694 4.0, 2.5, 22.6, 4.9, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
695 }},
696 {50,
697 {
698 -7.91, 0.62, -2.31, 0.33, 0.72, 0.63, 1.43, 0.06, -2.3, 0.46, 7.4,
699 3.0, 2.5, 21.8, 5.7, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
700 }},
701 {60,
702 {
703 -8.14, 0.51, -2.2, 0.39, 0.6, 0.54, 1.56, 0.05, -2.48, 0.35, 7.0,
704 2.6, 2.5, 20.5, 6.9, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
705 }},
706 {70,
707 {
708 -8.23, 0.45, -2.0, 0.4, 0.55, 0.52, 1.66, 0.05, -2.64, 0.31, 6.9,
709 2.2, 2.5, 19.3, 8.1, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
710 }},
711 {80,
712 {
713 -8.28, 0.31, -1.64, 0.32, 0.71, 0.53, 1.73, 0.02, -2.68, 0.39, 6.5,
714 2.1, 2.5, 17.4, 10.3, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
715 }},
716 {90,
717 {
718 -8.36, 0.08, -0.63, 0.53, 0.81, 0.62, 1.79, 0.01, -2.61, 0.28, 6.8,
719 1.9, 2.5, 12.3, 15.2, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0,
720 }},
721 }},
722 {"Ka",
723 {
724 {10,
725 {
726 -7.43, 0.9, -3.43, 0.54, 0.65, 0.82, 0.82, 0.05, -2.75, 0.55, 6.1,
727 2.6, 2.5, 24.7, 2.1, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
728 }},
729 {20,
730 {
731 -7.62, 0.78, -3.06, 0.41, 0.53, 0.78, 0.47, 0.11, -2.64, 0.64, 13.7,
732 6.8, 2.5, 24.4, 2.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
733 }},
734 {30,
735 {
736 -7.76, 0.8, -2.91, 0.42, 0.6, 0.83, 0.8, 0.05, -2.49, 0.69, 12.9,
737 6.0, 2.5, 24.4, 2.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
738 }},
739 {40,
740 {
741 -8.02, 0.72, -2.81, 0.34, 0.43, 0.78, 1.23, 0.04, -2.51, 0.57, 10.3,
742 3.3, 2.5, 24.2, 2.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
743 }},
744 {50,
745 {
746 -8.13, 0.61, -2.74, 0.34, 0.36, 0.77, 1.42, 0.1, -2.54, 0.5, 9.2,
747 2.2, 2.5, 23.9, 3.1, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
748 }},
749 {60,
750 {
751 -8.3, 0.47, -2.72, 0.7, 0.16, 0.84, 1.56, 0.06, -2.71, 0.37, 8.4,
752 1.9, 2.5, 23.3, 3.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
753 }},
754 {70,
755 {
756 -8.34, 0.39, -2.46, 0.4, 0.18, 0.64, 1.65, 0.07, -2.85, 0.31, 8.0,
757 1.5, 2.5, 22.6, 4.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
758 }},
759 {80,
760 {
761 -8.39, 0.26, -2.3, 0.78, 0.24, 0.81, 1.73, 0.02, -3.01, 0.45, 7.4,
762 1.6, 2.5, 21.2, 6.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
763 }},
764 {90,
765 {
766 -8.45, 0.01, -1.11, 0.51, 0.36, 0.65, 1.79, 0.01, -3.08, 0.27, 7.6,
767 1.3, 2.5, 17.6, 12.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
768 }},
769 }},
770};
771
772/**
773 * The nested map containing the threegpp value tables for the NTN Dense Urban NLOS scenario
774 */
775static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNDenseUrbanNLOS{
776 {"S",
777 {
778 {10,
779 {
780 -6.84, 0.82, -2.08, 0.87, 1.0, 1.6, 1.0, 0.63, -2.08, 0.58,
781 2.3, 23.8, 4.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
782 }},
783 {20,
784 {
785 -6.81, 0.61, -1.68, 0.73, 1.44, 0.87, 0.94, 0.65, -1.66, 0.5,
786 2.3, 21.9, 6.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
787 }},
788 {30,
789 {
790 -6.94, 0.49, -1.46, 0.53, 1.54, 0.64, 1.15, 0.42, -1.48, 0.4,
791 2.3, 19.7, 8.1, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
792 }},
793 {40,
794 {
795 -7.14, 0.49, -1.43, 0.5, 1.53, 0.56, 1.35, 0.28, -1.46, 0.37,
796 2.3, 18.1, 9.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
797 }},
798 {50,
799 {
800 -7.34, 0.51, -1.44, 0.58, 1.48, 0.54, 1.44, 0.25, -1.53, 0.47,
801 2.3, 16.3, 11.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
802 }},
803 {60,
804 {
805 -7.53, 0.47, -1.33, 0.49, 1.39, 0.68, 1.56, 0.16, -1.61, 0.43,
806 2.3, 14.0, 13.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
807 }},
808 {70,
809 {
810 -7.67, 0.44, -1.31, 0.65, 1.42, 0.55, 1.64, 0.18, -1.77, 0.5,
811 2.3, 12.1, 14.9, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
812 }},
813 {80,
814 {
815 -7.82, 0.42, -1.11, 0.69, 1.38, 0.6, 1.7, 0.09, -1.9, 0.42,
816 2.3, 8.7, 17.0, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
817 }},
818 {90,
819 {
820 -7.84, 0.55, -0.11, 0.53, 1.23, 0.6, 1.7, 0.17, -1.99, 0.5,
821 2.3, 6.4, 12.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
822 }},
823 }},
824 {"Ka",
825 {
826 {10,
827 {
828 -6.86, 0.81, -2.12, 0.94, 1.02, 1.44, 1.01, 0.56, -2.11, 0.59,
829 2.3, 23.7, 4.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
830 }},
831 {20,
832 {
833 -6.84, 0.61, -1.74, 0.79, 1.44, 0.77, 0.96, 0.55, -1.69, 0.51,
834 2.3, 21.8, 6.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
835 }},
836 {30,
837 {
838 -7.0, 0.56, -1.56, 0.66, 1.48, 0.7, 1.13, 0.43, -1.52, 0.46,
839 2.3, 19.6, 8.2, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
840 }},
841 {40,
842 {
843 -7.21, 0.56, -1.54, 0.63, 1.46, 0.6, 1.3, 0.37, -1.51, 0.43,
844 2.3, 18.0, 9.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
845 }},
846 {50,
847 {
848 -7.42, 0.57, -1.45, 0.56, 1.4, 0.59, 1.4, 0.32, -1.54, 0.45,
849 2.3, 16.3, 11.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
850 }},
851 {60,
852 {
853 -7.86, 0.55, -1.64, 0.78, 0.97, 1.27, 1.41, 0.45, -1.84, 0.63,
854 2.3, 15.9, 12.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
855 }},
856 {70,
857 {
858 -7.76, 0.47, -1.37, 0.56, 1.33, 0.56, 1.63, 0.17, -1.86, 0.51,
859 2.3, 12.3, 15.0, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
860 }},
861 {80,
862 {
863 -8.07, 0.42, -1.29, 0.76, 1.12, 1.04, 1.68, 0.14, -2.16, 0.74,
864 2.3, 10.5, 15.7, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
865 }},
866 {90,
867 {
868 -7.95, 0.59, -0.41, 0.59, 1.04, 0.63, 1.7, 0.17, -2.21, 0.61,
869 2.3, 10.5, 15.7, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0,
870 }},
871 }},
872};
873
874/**
875 * The nested map containing the threegpp value tables for the NTN Urban LOS scenario
876 */
877static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNUrbanLOS{
878 {"S",
879 {
880 {10,
881 {
882 -7.97, 1.0, -2.6, 0.79, 0.18, 0.74, -0.63, 2.6, -2.54, 2.62, 31.83,
883 13.84, 2.5, 8.0, 4.0, 4.0, 20.0, 3.9, 0.09, 12.55, 1.25, 3.0,
884 }},
885 {20,
886 {
887 -8.12, 0.83, -2.48, 0.8, 0.42, 0.9, -0.15, 3.31, -2.67, 2.96, 18.78,
888 13.78, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.09, 12.76, 3.23, 3.0,
889 }},
890 {30,
891 {
892 -8.21, 0.68, -2.44, 0.91, 0.41, 1.3, 0.54, 1.1, -2.03, 0.86, 10.49,
893 10.42, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.12, 14.36, 4.39, 3.0,
894 }},
895 {40,
896 {
897 -8.31, 0.48, -2.6, 1.02, 0.18, 1.69, 0.35, 1.59, -2.28, 1.19, 7.46,
898 8.01, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.16, 16.42, 5.72, 3.0,
899 }},
900 {50,
901 {
902 -8.37, 0.38, -2.71, 1.17, -0.07, 2.04, 0.27, 1.62, -2.48, 1.4, 6.52,
903 8.27, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.2, 17.13, 6.17, 3.0,
904 }},
905 {60,
906 {
907 -8.39, 0.24, -2.76, 1.17, -0.43, 2.54, 0.26, 0.97, -2.56, 0.85, 5.47,
908 7.26, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.28, 19.01, 7.36, 3.0,
909 }},
910 {70,
911 {
912 -8.38, 0.18, -2.78, 1.2, -0.64, 2.47, -0.12, 1.99, -2.96, 1.61, 4.54,
913 5.53, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.44, 19.31, 7.3, 3.0,
914 }},
915 {80,
916 {
917 -8.35, 0.13, -2.65, 1.45, -0.91, 2.69, -0.21, 1.82, -3.08, 1.49, 4.03,
918 4.49, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.9, 22.39, 7.7, 3.0,
919 }},
920 {90,
921 {
922 -8.34, 0.09, -2.27, 1.85, -0.54, 1.66, -0.07, 1.43, -3.0, 1.09, 3.68,
923 3.14, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 2.87, 27.8, 9.25, 3.0,
924 }},
925 }},
926 {"Ka",
927 {
928 {10,
929 {
930 -8.52, 0.92, -3.18, 0.79, -0.4, 0.77, -0.67, 2.22, -2.61, 2.41, 40.18,
931 16.99, 2.5, 8.0, 4.0, 4.0, 20.0, 1.6, 0.09, 11.8, 1.14, 3.0,
932 }},
933 {20,
934 {
935 -8.59, 0.79, -3.05, 0.87, -0.15, 0.97, -0.34, 3.04, -2.82, 2.59, 23.62,
936 18.96, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.09, 11.6, 2.78, 3.0,
937 }},
938 {30,
939 {
940 -8.51, 0.65, -2.98, 1.04, -0.18, 1.58, 0.07, 1.33, -2.48, 1.02, 12.48,
941 14.23, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.11, 13.05, 3.87, 3.0,
942 }},
943 {40,
944 {
945 -8.49, 0.48, -3.11, 1.06, -0.31, 1.69, -0.08, 1.45, -2.76, 1.27, 8.56,
946 11.06, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.15, 14.56, 4.94, 3.0,
947 }},
948 {50,
949 {
950 -8.48, 0.46, -3.19, 1.12, -0.58, 2.13, -0.21, 1.62, -2.93, 1.38, 7.42,
951 11.21, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.18, 15.35, 5.41, 3.0,
952 }},
953 {60,
954 {
955 -8.44, 0.34, -3.25, 1.14, -0.9, 2.51, -0.25, 1.06, -3.05, 0.96, 5.97,
956 9.47, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.27, 16.97, 6.31, 3.0,
957 }},
958 {70,
959 {
960 -8.4, 0.27, -3.33, 1.25, -1.16, 2.47, -0.61, 1.88, -3.45, 1.51, 4.88,
961 7.24, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.42, 17.96, 6.66, 3.0,
962 }},
963 {80,
964 {
965 -8.37, 0.19, -3.22, 1.35, -1.48, 2.61, -0.79, 1.87, -3.66, 1.49, 4.22,
966 5.79, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.86, 20.68, 7.31, 3.0,
967 }},
968 {90,
969 {
970 -8.35, 0.14, -2.83, 1.62, -1.14, 1.7, -0.58, 1.19, -3.56, 0.89, 3.81,
971 4.25, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 2.55, 25.08, 9.23, 3.0,
972 }},
973 }},
974};
975
976/**
977 * The nested map containing the threegpp value tables for the NTN Urban NLOS scenario
978 */
979static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNUrbanNLOS{
980 {"S",
981 {
982 {10,
983 {
984 -7.24, 1.26, -1.58, 0.89, 0.13, 2.99, -1.13, 2.66, -2.87, 2.76, 0.0,
985 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.08, 14.72, 1.57, 3.0,
986 }},
987 {20,
988 {
989 -7.7, 0.99, -1.67, 0.89, 0.19, 3.12, 0.49, 2.03, -2.68, 2.76, 0.0,
990 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.1, 14.62, 4.3, 3.0,
991 }},
992 {30,
993 {
994 -7.82, 0.86, -1.84, 1.3, 0.44, 2.69, 0.95, 1.54, -2.12, 1.54, 0.0,
995 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.14, 16.4, 6.64, 3.0,
996 }},
997 {40,
998 {
999 -8.04, 0.75, -2.02, 1.15, 0.48, 2.45, 1.15, 1.02, -2.27, 1.77, 0.0,
1000 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.22, 17.86, 9.21, 3.0,
1001 }},
1002 {50,
1003 {
1004 -8.08, 0.77, -2.06, 1.23, 0.56, 2.17, 1.14, 1.61, -2.5, 2.36, 0.0,
1005 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.31, 19.74, 10.32, 3.0,
1006 }},
1007 {60,
1008 {
1009 -8.1, 0.76, -1.99, 1.02, 0.55, 1.93, 1.13, 1.84, -2.47, 2.33, 0.0,
1010 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.49, 19.73, 10.3, 3.0,
1011 }},
1012 {70,
1013 {
1014 -8.16, 0.73, -2.19, 1.78, 0.48, 1.72, 1.16, 1.81, -2.83, 2.84, 0.0,
1015 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 0.97, 20.5, 10.2, 3.0,
1016 }},
1017 {80,
1018 {
1019 -8.03, 0.79, -1.88, 1.55, 0.53, 1.51, 1.28, 1.35, -2.82, 2.87, 0.0,
1020 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 1.52, 26.16, 12.27, 3.0,
1021 }},
1022 {90,
1023 {
1024 -8.33, 0.7, -2.0, 1.4, 0.32, 1.2, 1.42, 0.6, -4.55, 4.27, 0.0,
1025 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 5.36, 25.83, 12.75, 3.0,
1026 }},
1027 }},
1028 {"Ka",
1029 {
1030 {10,
1031 {
1032 -7.24, 1.26, -1.58, 0.89, 0.13, 2.99, -1.13, 2.66, -2.87, 2.76, 0.0,
1033 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.08, 14.72, 1.57, 3.0,
1034 }},
1035 {20,
1036 {
1037 -7.7, 0.99, -1.67, 0.89, 0.19, 3.12, 0.49, 2.03, -2.68, 2.76, 0.0,
1038 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.1, 14.62, 4.3, 3.0,
1039 }},
1040 {30,
1041 {
1042 -7.82, 0.86, -1.84, 1.3, 0.44, 2.69, 0.95, 1.54, -2.12, 1.54, 0.0,
1043 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.14, 16.4, 6.64, 3.0,
1044 }},
1045 {40,
1046 {
1047 -8.04, 0.75, -2.02, 1.15, 0.48, 2.45, 1.15, 1.02, -2.27, 1.77, 0.0,
1048 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.22, 17.86, 9.21, 3.0,
1049 }},
1050 {50,
1051 {
1052 -8.08, 0.77, -2.06, 1.23, 0.56, 2.17, 1.14, 1.61, -2.5, 2.36, 0.0,
1053 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.31, 19.74, 10.32, 3.0,
1054 }},
1055 {60,
1056 {
1057 -8.1, 0.76, -1.99, 1.02, 0.55, 1.93, 1.13, 1.84, -2.47, 2.33, 0.0,
1058 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.49, 19.73, 10.3, 3.0,
1059 }},
1060 {70,
1061 {
1062 -8.16, 0.73, -2.19, 1.78, 0.48, 1.72, 1.16, 1.81, -2.83, 2.84, 0.0,
1063 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 0.97, 20.5, 10.2, 3.0,
1064 }},
1065 {80,
1066 {
1067 -8.03, 0.79, -1.88, 1.55, 0.53, 1.51, 1.28, 1.35, -2.82, 2.87, 0.0,
1068 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 1.52, 26.16, 12.27, 3.0,
1069 }},
1070 {90,
1071 {
1072 -8.33, 0.7, -2.0, 1.4, 0.32, 1.2, 1.42, 0.6, -4.55, 4.27, 0.0,
1073 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 5.36, 25.83, 12.75, 3.0,
1074 }},
1075 }},
1076};
1077
1078/**
1079 * The nested map containing the threegpp value tables for the NTN Suburban LOS scenario
1080 */
1081static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNSuburbanLOS{
1082 {"S",
1083 {
1084 {10,
1085 {
1086 -8.16, 0.99, -3.57, 1.62, 0.05, 1.84, -1.78, 0.62, -1.06, 0.96, 11.4,
1087 6.26, 2.2, 21.3, 7.6, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1088 }},
1089 {20,
1090 {
1091 -8.56, 0.96, -3.8, 1.74, -0.38, 1.94, -1.84, 0.81, -1.21, 0.95, 19.45,
1092 10.32, 3.36, 21.0, 8.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1093 }},
1094 {30,
1095 {
1096 -8.72, 0.79, -3.77, 1.72, -0.56, 1.75, -1.67, 0.57, -1.28, 0.49, 20.8,
1097 16.34, 3.5, 21.2, 8.5, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1098 }},
1099 {40,
1100 {
1101 -8.71, 0.81, -3.57, 1.6, -0.59, 1.82, -1.59, 0.86, -1.32, 0.79, 21.2,
1102 15.63, 2.81, 21.1, 8.4, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1103 }},
1104 {50,
1105 {
1106 -8.72, 1.12, -3.42, 1.49, -0.58, 1.87, -1.55, 1.05, -1.39, 0.97, 21.6,
1107 14.22, 2.39, 20.7, 9.2, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1108 }},
1109 {60,
1110 {
1111 -8.66, 1.23, -3.27, 1.43, -0.55, 1.92, -1.51, 1.23, -1.36, 1.17, 19.75,
1112 14.19, 2.73, 20.6, 9.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1113 }},
1114 {70,
1115 {
1116 -8.38, 0.55, -3.08, 1.36, -0.28, 1.16, -1.27, 0.54, -1.08, 0.62, 12.0,
1117 5.7, 2.07, 20.3, 10.8, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1118 }},
1119 {80,
1120 {
1121 -8.34, 0.63, -2.75, 1.26, -0.17, 1.09, -1.28, 0.67, -1.31, 0.76, 12.85,
1122 9.91, 2.04, 19.8, 12.2, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1123 }},
1124 {90,
1125 {
1126 -8.34, 0.63, -2.75, 1.26, -0.17, 1.09, -1.28, 0.67, -1.31, 0.76, 12.85,
1127 9.91, 2.04, 19.1, 13.0, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1128 }},
1129 }},
1130 {"Ka",
1131 {
1132 {10,
1133 {
1134 -8.07, 0.46, -3.55, 0.48, 0.89, 0.67, 0.63, 0.35, -3.37, 0.28, 8.9,
1135 4.4, 2.5, 23.2, 5.0, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1136 }},
1137 {20,
1138 {
1139 -8.61, 0.45, -3.69, 0.41, 0.31, 0.78, 0.76, 0.3, -3.28, 0.27, 14.0,
1140 4.6, 2.5, 23.6, 4.5, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1141 }},
1142 {30,
1143 {
1144 -8.72, 0.28, -3.59, 0.41, 0.02, 0.75, 1.11, 0.28, -3.04, 0.26, 11.3,
1145 3.7, 2.5, 23.5, 4.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1146 }},
1147 {40,
1148 {
1149 -8.63, 0.17, -3.38, 0.35, -0.1, 0.65, 1.37, 0.23, -2.88, 0.21, 9.0,
1150 3.5, 2.5, 23.4, 5.2, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1151 }},
1152 {50,
1153 {
1154 -8.54, 0.14, -3.23, 0.35, -0.19, 0.55, 1.53, 0.23, -2.83, 0.18, 7.5,
1155 3.0, 2.5, 23.2, 5.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1156 }},
1157 {60,
1158 {
1159 -8.48, 0.15, -3.19, 0.43, -0.54, 0.96, 1.65, 0.17, -2.86, 0.17, 6.6,
1160 2.6, 2.5, 23.3, 5.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1161 }},
1162 {70,
1163 {
1164 -8.42, 0.09, -2.83, 0.33, -0.24, 0.43, 1.74, 0.11, -2.95, 0.1, 5.9,
1165 1.7, 2.5, 23.4, 6.2, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1166 }},
1167 {80,
1168 {
1169 -8.39, 0.05, -2.66, 0.44, -0.52, 0.93, 1.82, 0.05, -3.21, 0.07, 5.5,
1170 0.7, 2.5, 23.2, 7.0, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1171 }},
1172 {90,
1173 {
1174 -8.37, 0.02, -1.22, 0.31, -0.15, 0.44, 1.87, 0.02, -3.49, 0.24, 5.4,
1175 0.3, 2.5, 23.1, 7.6, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0,
1176 }},
1177 }},
1178};
1179
1180/**
1181 * The nested map containing the threegpp value tables for the NTN Suburban NLOS scenario
1182 */
1183static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNSuburbanNLOS{
1184 {"S",
1185 {
1186 {10,
1187 {
1188 -7.43, 0.5, -2.89, 0.41, 1.49, 0.4, 0.81, 0.36, -3.09, 0.32, 0.0,
1189 0.0, 2.3, 22.5, 5.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1190 }},
1191 {20,
1192 {
1193 -7.63, 0.61, -2.76, 0.41, 1.24, 0.82, 1.06, 0.41, -2.93, 0.47, 0.0,
1194 0.0, 2.3, 19.4, 8.5, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1195 }},
1196 {30,
1197 {
1198 -7.86, 0.56, -2.64, 0.41, 1.06, 0.71, 1.12, 0.4, -2.91, 0.46, 0.0,
1199 0.0, 2.3, 15.5, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1200 }},
1201 {40,
1202 {
1203 -7.96, 0.58, -2.41, 0.52, 0.91, 0.55, 1.14, 0.39, -2.78, 0.54, 0.0,
1204 0.0, 2.3, 13.9, 10.6, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1205 }},
1206 {50,
1207 {
1208 -7.98, 0.59, -2.42, 0.7, 0.98, 0.58, 1.29, 0.35, -2.7, 0.45, 0.0,
1209 0.0, 2.3, 11.7, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1210 }},
1211 {60,
1212 {
1213 -8.45, 0.47, -2.53, 0.5, 0.49, 1.37, 1.38, 0.36, -3.03, 0.36, 0.0,
1214 0.0, 2.3, 9.8, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1215 }},
1216 {70,
1217 {
1218 -8.21, 0.36, -2.35, 0.58, 0.73, 0.49, 1.36, 0.29, -2.9, 0.42, 0.0,
1219 0.0, 2.3, 10.3, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1220 }},
1221 {80,
1222 {
1223 -8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
1224 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1225 }},
1226 {90,
1227 {
1228 -8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
1229 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1230 }},
1231 }},
1232 {"Ka",
1233 {
1234 {10,
1235 {
1236 -7.43, 0.5, -2.89, 0.41, 1.49, 0.4, 0.81, 0.36, -3.09, 0.32, 0.0,
1237 0.0, 2.3, 22.5, 5.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1238 }},
1239 {20,
1240 {
1241 -7.63, 0.61, -2.76, 0.41, 1.24, 0.82, 1.06, 0.41, -2.93, 0.47, 0.0,
1242 0.0, 2.3, 19.4, 8.5, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1243 }},
1244 {30,
1245 {
1246 -7.86, 0.56, -2.64, 0.41, 1.06, 0.71, 1.12, 0.4, -2.91, 0.46, 0.0,
1247 0.0, 2.3, 15.5, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1248 }},
1249 {40,
1250 {
1251 -7.96, 0.58, -2.41, 0.52, 0.91, 0.55, 1.14, 0.39, -2.78, 0.54, 0.0,
1252 0.0, 2.3, 13.9, 10.6, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1253 }},
1254 {50,
1255 {
1256 -7.98, 0.59, -2.42, 0.7, 0.98, 0.58, 1.29, 0.35, -2.7, 0.45, 0.0,
1257 0.0, 2.3, 11.7, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1258 }},
1259 {60,
1260 {
1261 -8.45, 0.47, -2.53, 0.5, 0.49, 1.37, 1.38, 0.36, -3.03, 0.36, 0.0,
1262 0.0, 2.3, 9.8, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1263 }},
1264 {70,
1265 {
1266 -8.21, 0.36, -2.35, 0.58, 0.73, 0.49, 1.36, 0.29, -2.9, 0.42, 0.0,
1267 0.0, 2.3, 10.3, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1268 }},
1269 {80,
1270 {
1271 -8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
1272 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1273 }},
1274 {90,
1275 {
1276 -8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
1277 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0,
1278 }},
1279 }},
1280};
1281
1282/**
1283 * The nested map containing the threegpp value tables for the NTN Rural LOS scenario
1284 */
1285static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNRuralLOS{
1286 {"S",
1287 {
1288 {10,
1289 {
1290 -9.55, 0.66, -3.42, 0.89, -9.45, 7.83, -4.2, 6.3, -6.03, 5.19, 24.72,
1291 5.07, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.39, 10.81, 1.94, 3.0,
1292 }},
1293 {20,
1294 {
1295 -8.68, 0.44, -3.0, 0.63, -4.45, 6.86, -2.31, 5.04, -4.31, 4.18, 12.31,
1296 5.75, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.31, 8.09, 1.83, 3.0,
1297 }},
1298 {30,
1299 {
1300 -8.46, 0.28, -2.86, 0.52, -2.39, 5.14, -0.28, 0.81, -2.57, 0.61, 8.05,
1301 5.46, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.29, 13.7, 2.28, 3.0,
1302 }},
1303 {40,
1304 {
1305 -8.36, 0.19, -2.78, 0.45, -1.28, 3.44, -0.38, 1.16, -2.59, 0.79, 6.21,
1306 5.23, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.37, 20.05, 2.93, 3.0,
1307 }},
1308 {50,
1309 {
1310 -8.29, 0.14, -2.7, 0.42, -0.99, 2.59, -0.38, 0.82, -2.59, 0.65, 5.04,
1311 3.95, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.61, 24.51, 2.84, 3.0,
1312 }},
1313 {60,
1314 {
1315 -8.26, 0.1, -2.66, 0.41, -1.05, 2.42, -0.46, 0.67, -2.65, 0.52, 4.42,
1316 3.75, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.9, 26.35, 3.17, 3.0,
1317 }},
1318 {70,
1319 {
1320 -8.22, 0.1, -2.53, 0.42, -0.9, 1.78, -0.49, 1.0, -2.69, 0.78, 3.92,
1321 2.56, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 1.43, 31.84, 3.88, 3.0,
1322 }},
1323 {80,
1324 {
1325 -8.2, 0.05, -2.21, 0.5, -0.89, 1.65, -0.53, 1.18, -2.65, 1.01, 3.65,
1326 1.77, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 2.87, 36.62, 4.17, 3.0,
1327 }},
1328 {90,
1329 {
1330 -8.19, 0.06, -1.78, 0.91, -0.81, 1.26, -0.46, 0.91, -2.65, 0.71, 3.59,
1331 1.77, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 5.48, 36.77, 4.29, 3.0,
1332 }},
1333 }},
1334 {"Ka",
1335 {
1336 {10,
1337 {
1338 -9.68, 0.46, -4.03, 0.91, -9.74, 7.52, -5.85, 6.51, -7.45, 5.3, 25.43,
1339 7.04, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.36, 4.63, 0.75, 3.0,
1340 }},
1341 {20,
1342 {
1343 -8.86, 0.29, -3.55, 0.7, -4.88, 6.67, -3.27, 5.36, -5.25, 4.42, 12.72,
1344 7.47, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.3, 6.83, 1.25, 3.0,
1345 }},
1346 {30,
1347 {
1348 -8.59, 0.18, -3.45, 0.55, -2.6, 4.63, -0.88, 0.93, -3.16, 0.68, 8.4,
1349 7.18, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.25, 12.91, 1.93, 3.0,
1350 }},
1351 {40,
1352 {
1353 -8.46, 0.19, -3.38, 0.52, -1.92, 3.45, -0.93, 0.96, -3.15, 0.73, 6.52,
1354 6.88, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.35, 18.9, 2.37, 3.0,
1355 }},
1356 {50,
1357 {
1358 -8.36, 0.14, -3.33, 0.46, -1.56, 2.44, -0.99, 0.97, -3.2, 0.77, 5.24,
1359 5.28, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.53, 22.44, 2.66, 3.0,
1360 }},
1361 {60,
1362 {
1363 -8.3, 0.15, -3.29, 0.43, -1.66, 2.38, -1.04, 0.83, -3.27, 0.61, 4.57,
1364 4.92, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.88, 25.69, 3.23, 3.0,
1365 }},
1366 {70,
1367 {
1368 -8.26, 0.13, -3.24, 0.46, -1.59, 1.67, -1.17, 1.01, -3.42, 0.74, 4.02,
1369 3.4, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 1.39, 27.95, 3.71, 3.0,
1370 }},
1371 {80,
1372 {
1373 -8.22, 0.03, -2.9, 0.44, -1.58, 1.44, -1.19, 1.01, -3.36, 0.79, 3.7,
1374 2.22, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 2.7, 31.45, 4.17, 3.0,
1375 }},
1376 {90,
1377 {
1378 -8.21, 0.07, -2.5, 0.82, -1.51, 1.13, -1.13, 0.85, -3.35, 0.65, 3.62,
1379 2.28, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 4.97, 28.01, 4.14, 3.0,
1380 }},
1381 }},
1382};
1383
1384/**
1385 * The nested map containing the threegpp value tables for the NTN Rural NLOS scenario
1386 */
1387static const std::map<std::string, std::map<int, std::vector<float>>> m_NTNRuralNLOS{
1388 {"S",
1389 {
1390 {10,
1391 {
1392 -9.01, 1.59, -2.9, 1.34, -3.33, 6.22, -0.88, 3.26, -4.92, 3.96, 0.0,
1393 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.03, 18.16, 2.32, 3.0,
1394 }},
1395 {20,
1396 {
1397 -8.37, 0.95, -2.5, 1.18, -0.74, 4.22, -0.07, 3.29, -4.06, 4.07, 0.0,
1398 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.05, 26.82, 7.34, 3.0,
1399 }},
1400 {30,
1401 {
1402 -8.05, 0.92, -2.12, 1.08, 0.08, 3.02, 0.75, 1.92, -2.33, 1.7, 0.0,
1403 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.07, 21.99, 8.28, 3.0,
1404 }},
1405 {40,
1406 {
1407 -7.92, 0.92, -1.99, 1.06, 0.32, 2.45, 0.72, 1.92, -2.24, 2.01, 0.0,
1408 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.1, 22.86, 8.76, 3.0,
1409 }},
1410 {50,
1411 {
1412 -7.92, 0.87, -1.9, 1.05, 0.53, 1.63, 0.95, 1.45, -2.24, 2.0, 0.0,
1413 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.15, 25.93, 9.68, 3.0,
1414 }},
1415 {60,
1416 {
1417 -7.96, 0.87, -1.85, 1.06, 0.33, 2.08, 0.97, 1.62, -2.22, 1.82, 0.0,
1418 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.22, 27.79, 9.94, 3.0,
1419 }},
1420 {70,
1421 {
1422 -7.91, 0.82, -1.69, 1.14, 0.55, 1.58, 1.1, 1.43, -2.19, 1.66, 0.0,
1423 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.5, 28.5, 8.9, 3.0,
1424 }},
1425 {80,
1426 {
1427 -7.79, 0.86, -1.46, 1.16, 0.45, 2.01, 0.97, 1.88, -2.41, 2.58, 0.0,
1428 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 1.04, 37.53, 13.74, 3.0,
1429 }},
1430 {90,
1431 {
1432 -7.74, 0.81, -1.32, 1.3, 0.4, 2.19, 1.35, 0.62, -2.45, 2.52, 0.0,
1433 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 2.11, 29.23, 12.16, 3.0,
1434 }},
1435 }},
1436 {"Ka",
1437 {
1438 {10,
1439 {
1440 -9.13, 1.91, -2.9, 1.32, -3.4, 6.28, -1.19, 3.81, -5.47, 4.39, 0.0,
1441 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.03, 18.21, 2.13, 3.0,
1442 }},
1443 {20,
1444 {
1445 -8.39, 0.94, -2.53, 1.18, -0.51, 3.75, -0.11, 3.33, -4.06, 4.04, 0.0,
1446 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.05, 24.08, 6.52, 3.0,
1447 }},
1448 {30,
1449 {
1450 -8.1, 0.92, -2.16, 1.08, 0.06, 2.95, 0.72, 1.93, -2.32, 1.54, 0.0,
1451 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.07, 22.06, 7.72, 3.0,
1452 }},
1453 {40,
1454 {
1455 -7.96, 0.94, -2.04, 1.09, 0.2, 2.65, 0.69, 1.91, -2.19, 1.73, 0.0,
1456 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.09, 21.4, 8.45, 3.0,
1457 }},
1458 {50,
1459 {
1460 -7.99, 0.89, -1.99, 1.08, 0.4, 1.85, 0.84, 1.7, -2.16, 1.5, 0.0,
1461 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.16, 24.26, 8.92, 3.0,
1462 }},
1463 {60,
1464 {
1465 -8.05, 0.87, -1.95, 1.06, 0.32, 1.83, 0.99, 1.27, -2.24, 1.64, 0.0,
1466 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.22, 24.15, 8.76, 3.0,
1467 }},
1468 {70,
1469 {
1470 -8.01, 0.82, -1.81, 1.17, 0.46, 1.57, 0.95, 1.86, -2.29, 1.66, 0.0,
1471 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.51, 25.99, 9.0, 3.0,
1472 }},
1473 {80,
1474 {
1475 -8.05, 1.65, -1.56, 1.2, 0.33, 1.99, 0.92, 1.84, -2.65, 2.86, 0.0,
1476 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.89, 36.07, 13.6, 3.0,
1477 }},
1478 {90,
1479 {
1480 -7.91, 0.76, -1.53, 1.27, 0.24, 2.18, 1.29, 0.59, -2.23, 1.12, 0.0,
1481 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 1.68, 24.51, 10.56, 3.0,
1482 }},
1483 }},
1484};
1485
1487{
1488 NS_LOG_FUNCTION(this);
1489 m_uniformRv = CreateObject<UniformRandomVariable>();
1490 m_uniformRvShuffle = CreateObject<UniformRandomVariable>();
1491 m_uniformRvDoppler = CreateObject<UniformRandomVariable>();
1492
1493 m_normalRv = CreateObject<NormalRandomVariable>();
1494 m_normalRv->SetAttribute("Mean", DoubleValue(0.0));
1495 m_normalRv->SetAttribute("Variance", DoubleValue(1.0));
1496}
1497
1499{
1500 NS_LOG_FUNCTION(this);
1501}
1502
1503void
1505{
1506 NS_LOG_FUNCTION(this);
1508 {
1509 m_channelConditionModel->Dispose();
1510 }
1511 m_channelMatrixMap.clear();
1512 m_channelParamsMap.clear();
1513 m_channelConditionModel = nullptr;
1514}
1515
1516TypeId
1518{
1519 static TypeId tid =
1520 TypeId("ns3::ThreeGppChannelModel")
1521 .SetGroupName("Spectrum")
1523 .AddConstructor<ThreeGppChannelModel>()
1524 .AddAttribute("Frequency",
1525 "The operating Frequency in Hz",
1526 DoubleValue(500.0e6),
1529 MakeDoubleChecker<double>())
1530 .AddAttribute(
1531 "Scenario",
1532 "The 3GPP scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed, "
1533 "NTN-DenseUrban, NTN-Urban, NTN-Suburban, NTN-Rural)",
1534 StringValue("UMa"),
1538 .AddAttribute("ChannelConditionModel",
1539 "Pointer to the channel condition model",
1540 PointerValue(),
1543 MakePointerChecker<ChannelConditionModel>())
1544 .AddAttribute("UpdatePeriod",
1545 "Specify the channel coherence time",
1549 // attributes for the blockage model
1550 .AddAttribute("Blockage",
1551 "Enable blockage model A (sec 7.6.4.1)",
1552 BooleanValue(false),
1555 .AddAttribute("NumNonselfBlocking",
1556 "number of non-self-blocking regions",
1557 IntegerValue(4),
1559 MakeIntegerChecker<uint16_t>())
1560 .AddAttribute("PortraitMode",
1561 "true for portrait mode, false for landscape mode",
1562 BooleanValue(true),
1565 .AddAttribute("BlockerSpeed",
1566 "The speed of moving blockers, the unit is m/s",
1567 DoubleValue(1),
1569 MakeDoubleChecker<double>())
1570 .AddAttribute("vScatt",
1571 "Maximum speed of the vehicle in the layout (see 3GPP TR 37.885 v15.3.0, "
1572 "Sec. 6.2.3)."
1573 "Used to compute the additional contribution for the Doppler of"
1574 "delayed (reflected) paths",
1575 DoubleValue(0.0),
1577 MakeDoubleChecker<double>(0.0))
1578
1579 ;
1580 return tid;
1581}
1582
1583void
1585{
1586 NS_LOG_FUNCTION(this);
1588}
1589
1592{
1593 NS_LOG_FUNCTION(this);
1595}
1596
1597void
1599{
1600 NS_LOG_FUNCTION(this);
1601 NS_ASSERT_MSG(f >= 500.0e6 && f <= 100.0e9,
1602 "Frequency should be between 0.5 and 100 GHz but is " << f);
1603 m_frequency = f;
1604}
1605
1606double
1608{
1609 NS_LOG_FUNCTION(this);
1610 return m_frequency;
1611}
1612
1613void
1614ThreeGppChannelModel::SetScenario(const std::string& scenario)
1615{
1616 NS_LOG_FUNCTION(this);
1617 NS_ASSERT_MSG(scenario == "RMa" || scenario == "UMa" || scenario == "UMi-StreetCanyon" ||
1618 scenario == "InH-OfficeOpen" || scenario == "InH-OfficeMixed" ||
1619 scenario == "V2V-Urban" || scenario == "V2V-Highway" ||
1620 scenario == "NTN-DenseUrban" || scenario == "NTN-Urban" ||
1621 scenario == "NTN-Suburban" || scenario == "NTN-Rural",
1622 "Unknown scenario, choose between: RMa, UMa, UMi-StreetCanyon, "
1623 "InH-OfficeOpen, InH-OfficeMixed, V2V-Urban, V2V-Highway, "
1624 "NTN-DenseUrban, NTN-Urban, NTN-Suburban or NTN-Rural");
1625 m_scenario = scenario;
1626}
1627
1628std::string
1630{
1631 NS_LOG_FUNCTION(this);
1632 return m_scenario;
1633}
1634
1637 const Ptr<const MobilityModel> bMob,
1638 Ptr<const ChannelCondition> channelCondition) const
1639{
1640 NS_LOG_FUNCTION(this);
1641
1642 // NOTE we assume hUT = min (height(a), height(b)) and
1643 // hBS = max (height (a), height (b))
1644 double hUT = std::min(aMob->GetPosition().z, bMob->GetPosition().z);
1645 double hBS = std::max(aMob->GetPosition().z, bMob->GetPosition().z);
1646
1647 double distance2D = sqrt(pow(aMob->GetPosition().x - bMob->GetPosition().x, 2) +
1648 pow(aMob->GetPosition().y - bMob->GetPosition().y, 2));
1649
1650 double fcGHz = m_frequency / 1.0e9;
1651 Ptr<ParamsTable> table3gpp = Create<ParamsTable>();
1652 // table3gpp includes the following parameters:
1653 // numOfCluster, raysPerCluster, uLgDS, sigLgDS, uLgASD, sigLgASD,
1654 // uLgASA, sigLgASA, uLgZSA, sigLgZSA, uLgZSD, sigLgZSD, offsetZOD,
1655 // cDS, cASD, cASA, cZSA, uK, sigK, rTau, uXpr, sigXpr, shadowingStd
1656
1657 bool los = channelCondition->IsLos();
1658 bool o2i = channelCondition->IsO2i();
1659
1660 // In NLOS case, parameter uK and sigK are not used and they are set to 0
1661 if (m_scenario == "RMa")
1662 {
1663 if (los && !o2i)
1664 {
1665 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
1666 // entry is N/A.
1667 table3gpp->m_numOfCluster = 11;
1668 table3gpp->m_raysPerCluster = 20;
1669 table3gpp->m_uLgDS = -7.49;
1670 table3gpp->m_sigLgDS = 0.55;
1671 table3gpp->m_uLgASD = 0.90;
1672 table3gpp->m_sigLgASD = 0.38;
1673 table3gpp->m_uLgASA = 1.52;
1674 table3gpp->m_sigLgASA = 0.24;
1675 table3gpp->m_uLgZSA = 0.47;
1676 table3gpp->m_sigLgZSA = 0.40;
1677 table3gpp->m_uLgZSD = 0.34;
1678 table3gpp->m_sigLgZSD =
1679 std::max(-1.0, -0.17 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.22);
1680 table3gpp->m_offsetZOD = 0;
1681 table3gpp->m_cDS = 3.91e-9;
1682 table3gpp->m_cASD = 2;
1683 table3gpp->m_cASA = 3;
1684 table3gpp->m_cZSA = 3;
1685 table3gpp->m_uK = 7;
1686 table3gpp->m_sigK = 4;
1687 table3gpp->m_rTau = 3.8;
1688 table3gpp->m_uXpr = 12;
1689 table3gpp->m_sigXpr = 4;
1690 table3gpp->m_perClusterShadowingStd = 3;
1691
1692 for (uint8_t row = 0; row < 7; row++)
1693 {
1694 for (uint8_t column = 0; column < 7; column++)
1695 {
1696 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_LOS[row][column];
1697 }
1698 }
1699 }
1700 else if (!los && !o2i)
1701 {
1702 table3gpp->m_numOfCluster = 10;
1703 table3gpp->m_raysPerCluster = 20;
1704 table3gpp->m_uLgDS = -7.43;
1705 table3gpp->m_sigLgDS = 0.48;
1706 table3gpp->m_uLgASD = 0.95;
1707 table3gpp->m_sigLgASD = 0.45;
1708 table3gpp->m_uLgASA = 1.52;
1709 table3gpp->m_sigLgASA = 0.13;
1710 table3gpp->m_uLgZSA = 0.58;
1711 table3gpp->m_sigLgZSA = 0.37;
1712 table3gpp->m_uLgZSD =
1713 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
1714 table3gpp->m_sigLgZSD = 0.30;
1715 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
1716 table3gpp->m_cDS = 3.91e-9;
1717 table3gpp->m_cASD = 2;
1718 table3gpp->m_cASA = 3;
1719 table3gpp->m_cZSA = 3;
1720 table3gpp->m_uK = 0;
1721 table3gpp->m_sigK = 0;
1722 table3gpp->m_rTau = 1.7;
1723 table3gpp->m_uXpr = 7;
1724 table3gpp->m_sigXpr = 3;
1725 table3gpp->m_perClusterShadowingStd = 3;
1726
1727 for (uint8_t row = 0; row < 6; row++)
1728 {
1729 for (uint8_t column = 0; column < 6; column++)
1730 {
1731 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_NLOS[row][column];
1732 }
1733 }
1734 }
1735 else // o2i
1736 {
1737 table3gpp->m_numOfCluster = 10;
1738 table3gpp->m_raysPerCluster = 20;
1739 table3gpp->m_uLgDS = -7.47;
1740 table3gpp->m_sigLgDS = 0.24;
1741 table3gpp->m_uLgASD = 0.67;
1742 table3gpp->m_sigLgASD = 0.18;
1743 table3gpp->m_uLgASA = 1.66;
1744 table3gpp->m_sigLgASA = 0.21;
1745 table3gpp->m_uLgZSA = 0.93;
1746 table3gpp->m_sigLgZSA = 0.22;
1747 table3gpp->m_uLgZSD =
1748 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
1749 table3gpp->m_sigLgZSD = 0.30;
1750 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
1751 table3gpp->m_cDS = 3.91e-9;
1752 table3gpp->m_cASD = 2;
1753 table3gpp->m_cASA = 3;
1754 table3gpp->m_cZSA = 3;
1755 table3gpp->m_uK = 0;
1756 table3gpp->m_sigK = 0;
1757 table3gpp->m_rTau = 1.7;
1758 table3gpp->m_uXpr = 7;
1759 table3gpp->m_sigXpr = 3;
1760 table3gpp->m_perClusterShadowingStd = 3;
1761
1762 for (uint8_t row = 0; row < 6; row++)
1763 {
1764 for (uint8_t column = 0; column < 6; column++)
1765 {
1766 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_O2I[row][column];
1767 }
1768 }
1769 }
1770 }
1771 else if (m_scenario == "UMa")
1772 {
1773 if (los && !o2i)
1774 {
1775 table3gpp->m_numOfCluster = 12;
1776 table3gpp->m_raysPerCluster = 20;
1777 table3gpp->m_uLgDS = -6.955 - 0.0963 * log10(fcGHz);
1778 table3gpp->m_sigLgDS = 0.66;
1779 table3gpp->m_uLgASD = 1.06 + 0.1114 * log10(fcGHz);
1780 table3gpp->m_sigLgASD = 0.28;
1781 table3gpp->m_uLgASA = 1.81;
1782 table3gpp->m_sigLgASA = 0.20;
1783 table3gpp->m_uLgZSA = 0.95;
1784 table3gpp->m_sigLgZSA = 0.16;
1785 table3gpp->m_uLgZSD =
1786 std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.75);
1787 table3gpp->m_sigLgZSD = 0.40;
1788 table3gpp->m_offsetZOD = 0;
1789 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
1790 table3gpp->m_cASD = 5;
1791 table3gpp->m_cASA = 11;
1792 table3gpp->m_cZSA = 7;
1793 table3gpp->m_uK = 9;
1794 table3gpp->m_sigK = 3.5;
1795 table3gpp->m_rTau = 2.5;
1796 table3gpp->m_uXpr = 8;
1797 table3gpp->m_sigXpr = 4;
1798 table3gpp->m_perClusterShadowingStd = 3;
1799
1800 for (uint8_t row = 0; row < 7; row++)
1801 {
1802 for (uint8_t column = 0; column < 7; column++)
1803 {
1804 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_LOS[row][column];
1805 }
1806 }
1807 }
1808 else
1809 {
1810 double uLgZSD = std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.9);
1811
1812 double afc = 0.208 * log10(fcGHz) - 0.782;
1813 double bfc = 25;
1814 double cfc = -0.13 * log10(fcGHz) + 2.03;
1815 double efc = 7.66 * log10(fcGHz) - 5.96;
1816
1817 double offsetZOD = efc - std::pow(10, afc * log10(std::max(bfc, distance2D)) + cfc);
1818
1819 if (!los && !o2i)
1820 {
1821 table3gpp->m_numOfCluster = 20;
1822 table3gpp->m_raysPerCluster = 20;
1823 table3gpp->m_uLgDS = -6.28 - 0.204 * log10(fcGHz);
1824 table3gpp->m_sigLgDS = 0.39;
1825 table3gpp->m_uLgASD = 1.5 - 0.1144 * log10(fcGHz);
1826 table3gpp->m_sigLgASD = 0.28;
1827 table3gpp->m_uLgASA = 2.08 - 0.27 * log10(fcGHz);
1828 table3gpp->m_sigLgASA = 0.11;
1829 table3gpp->m_uLgZSA = -0.3236 * log10(fcGHz) + 1.512;
1830 table3gpp->m_sigLgZSA = 0.16;
1831 table3gpp->m_uLgZSD = uLgZSD;
1832 table3gpp->m_sigLgZSD = 0.49;
1833 table3gpp->m_offsetZOD = offsetZOD;
1834 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
1835 table3gpp->m_cASD = 2;
1836 table3gpp->m_cASA = 15;
1837 table3gpp->m_cZSA = 7;
1838 table3gpp->m_uK = 0;
1839 table3gpp->m_sigK = 0;
1840 table3gpp->m_rTau = 2.3;
1841 table3gpp->m_uXpr = 7;
1842 table3gpp->m_sigXpr = 3;
1843 table3gpp->m_perClusterShadowingStd = 3;
1844
1845 for (uint8_t row = 0; row < 6; row++)
1846 {
1847 for (uint8_t column = 0; column < 6; column++)
1848 {
1849 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_NLOS[row][column];
1850 }
1851 }
1852 }
1853 else //(o2i)
1854 {
1855 table3gpp->m_numOfCluster = 12;
1856 table3gpp->m_raysPerCluster = 20;
1857 table3gpp->m_uLgDS = -6.62;
1858 table3gpp->m_sigLgDS = 0.32;
1859 table3gpp->m_uLgASD = 1.25;
1860 table3gpp->m_sigLgASD = 0.42;
1861 table3gpp->m_uLgASA = 1.76;
1862 table3gpp->m_sigLgASA = 0.16;
1863 table3gpp->m_uLgZSA = 1.01;
1864 table3gpp->m_sigLgZSA = 0.43;
1865 table3gpp->m_uLgZSD = uLgZSD;
1866 table3gpp->m_sigLgZSD = 0.49;
1867 table3gpp->m_offsetZOD = offsetZOD;
1868 table3gpp->m_cDS = 11e-9;
1869 table3gpp->m_cASD = 5;
1870 table3gpp->m_cASA = 8;
1871 table3gpp->m_cZSA = 3;
1872 table3gpp->m_uK = 0;
1873 table3gpp->m_sigK = 0;
1874 table3gpp->m_rTau = 2.2;
1875 table3gpp->m_uXpr = 9;
1876 table3gpp->m_sigXpr = 5;
1877 table3gpp->m_perClusterShadowingStd = 4;
1878
1879 for (uint8_t row = 0; row < 6; row++)
1880 {
1881 for (uint8_t column = 0; column < 6; column++)
1882 {
1883 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_O2I[row][column];
1884 }
1885 }
1886 }
1887 }
1888 }
1889 else if (m_scenario == "UMi-StreetCanyon")
1890 {
1891 if (los && !o2i)
1892 {
1893 table3gpp->m_numOfCluster = 12;
1894 table3gpp->m_raysPerCluster = 20;
1895 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 7.14;
1896 table3gpp->m_sigLgDS = 0.38;
1897 table3gpp->m_uLgASD = -0.05 * log10(1 + fcGHz) + 1.21;
1898 table3gpp->m_sigLgASD = 0.41;
1899 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.73;
1900 table3gpp->m_sigLgASA = 0.014 * log10(1 + fcGHz) + 0.28;
1901 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
1902 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
1903 table3gpp->m_uLgZSD =
1904 std::max(-0.21, -14.8 * distance2D / 1000.0 + 0.01 * std::abs(hUT - hBS) + 0.83);
1905 table3gpp->m_sigLgZSD = 0.35;
1906 table3gpp->m_offsetZOD = 0;
1907 table3gpp->m_cDS = 5e-9;
1908 table3gpp->m_cASD = 3;
1909 table3gpp->m_cASA = 17;
1910 table3gpp->m_cZSA = 7;
1911 table3gpp->m_uK = 9;
1912 table3gpp->m_sigK = 5;
1913 table3gpp->m_rTau = 3;
1914 table3gpp->m_uXpr = 9;
1915 table3gpp->m_sigXpr = 3;
1916 table3gpp->m_perClusterShadowingStd = 3;
1917
1918 for (uint8_t row = 0; row < 7; row++)
1919 {
1920 for (uint8_t column = 0; column < 7; column++)
1921 {
1922 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1923 }
1924 }
1925 }
1926 else
1927 {
1928 double uLgZSD =
1929 std::max(-0.5, -3.1 * distance2D / 1000.0 + 0.01 * std::max(hUT - hBS, 0.0) + 0.2);
1930 double offsetZOD = -1 * std::pow(10, -1.5 * log10(std::max(10.0, distance2D)) + 3.3);
1931 if (!los && !o2i)
1932 {
1933 table3gpp->m_numOfCluster = 19;
1934 table3gpp->m_raysPerCluster = 20;
1935 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 6.83;
1936 table3gpp->m_sigLgDS = 0.16 * log10(1 + fcGHz) + 0.28;
1937 table3gpp->m_uLgASD = -0.23 * log10(1 + fcGHz) + 1.53;
1938 table3gpp->m_sigLgASD = 0.11 * log10(1 + fcGHz) + 0.33;
1939 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
1940 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
1941 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1942 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1943 table3gpp->m_uLgZSD = uLgZSD;
1944 table3gpp->m_sigLgZSD = 0.35;
1945 table3gpp->m_offsetZOD = offsetZOD;
1946 table3gpp->m_cDS = 11e-9;
1947 table3gpp->m_cASD = 10;
1948 table3gpp->m_cASA = 22;
1949 table3gpp->m_cZSA = 7;
1950 table3gpp->m_uK = 0;
1951 table3gpp->m_sigK = 0;
1952 table3gpp->m_rTau = 2.1;
1953 table3gpp->m_uXpr = 8;
1954 table3gpp->m_sigXpr = 3;
1955 table3gpp->m_perClusterShadowingStd = 3;
1956
1957 for (uint8_t row = 0; row < 6; row++)
1958 {
1959 for (uint8_t column = 0; column < 6; column++)
1960 {
1961 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
1962 }
1963 }
1964 }
1965 else //(o2i)
1966 {
1967 table3gpp->m_numOfCluster = 12;
1968 table3gpp->m_raysPerCluster = 20;
1969 table3gpp->m_uLgDS = -6.62;
1970 table3gpp->m_sigLgDS = 0.32;
1971 table3gpp->m_uLgASD = 1.25;
1972 table3gpp->m_sigLgASD = 0.42;
1973 table3gpp->m_uLgASA = 1.76;
1974 table3gpp->m_sigLgASA = 0.16;
1975 table3gpp->m_uLgZSA = 1.01;
1976 table3gpp->m_sigLgZSA = 0.43;
1977 table3gpp->m_uLgZSD = uLgZSD;
1978 table3gpp->m_sigLgZSD = 0.35;
1979 table3gpp->m_offsetZOD = offsetZOD;
1980 table3gpp->m_cDS = 11e-9;
1981 table3gpp->m_cASD = 5;
1982 table3gpp->m_cASA = 8;
1983 table3gpp->m_cZSA = 3;
1984 table3gpp->m_uK = 0;
1985 table3gpp->m_sigK = 0;
1986 table3gpp->m_rTau = 2.2;
1987 table3gpp->m_uXpr = 9;
1988 table3gpp->m_sigXpr = 5;
1989 table3gpp->m_perClusterShadowingStd = 4;
1990
1991 for (uint8_t row = 0; row < 6; row++)
1992 {
1993 for (uint8_t column = 0; column < 6; column++)
1994 {
1995 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_O2I[row][column];
1996 }
1997 }
1998 }
1999 }
2000 }
2001 else if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
2002 {
2003 NS_ASSERT_MSG(!o2i, "The indoor scenario does out support outdoor to indoor");
2004 if (los)
2005 {
2006 table3gpp->m_numOfCluster = 15;
2007 table3gpp->m_raysPerCluster = 20;
2008 table3gpp->m_uLgDS = -0.01 * log10(1 + fcGHz) - 7.692;
2009 table3gpp->m_sigLgDS = 0.18;
2010 table3gpp->m_uLgASD = 1.60;
2011 table3gpp->m_sigLgASD = 0.18;
2012 table3gpp->m_uLgASA = -0.19 * log10(1 + fcGHz) + 1.781;
2013 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.119;
2014 table3gpp->m_uLgZSA = -0.26 * log10(1 + fcGHz) + 1.44;
2015 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.264;
2016 table3gpp->m_uLgZSD = -1.43 * log10(1 + fcGHz) + 2.228;
2017 table3gpp->m_sigLgZSD = 0.13 * log10(1 + fcGHz) + 0.30;
2018 table3gpp->m_offsetZOD = 0;
2019 table3gpp->m_cDS = 3.91e-9;
2020 table3gpp->m_cASD = 5;
2021 table3gpp->m_cASA = 8;
2022 table3gpp->m_cZSA = 9;
2023 table3gpp->m_uK = 7;
2024 table3gpp->m_sigK = 4;
2025 table3gpp->m_rTau = 3.6;
2026 table3gpp->m_uXpr = 11;
2027 table3gpp->m_sigXpr = 4;
2028 table3gpp->m_perClusterShadowingStd = 6;
2029
2030 for (uint8_t row = 0; row < 7; row++)
2031 {
2032 for (uint8_t column = 0; column < 7; column++)
2033 {
2034 table3gpp->m_sqrtC[row][column] = sqrtC_office_LOS[row][column];
2035 }
2036 }
2037 }
2038 else
2039 {
2040 table3gpp->m_numOfCluster = 19;
2041 table3gpp->m_raysPerCluster = 20;
2042 table3gpp->m_uLgDS = -0.28 * log10(1 + fcGHz) - 7.173;
2043 table3gpp->m_sigLgDS = 0.1 * log10(1 + fcGHz) + 0.055;
2044 table3gpp->m_uLgASD = 1.62;
2045 table3gpp->m_sigLgASD = 0.25;
2046 table3gpp->m_uLgASA = -0.11 * log10(1 + fcGHz) + 1.863;
2047 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.059;
2048 table3gpp->m_uLgZSA = -0.15 * log10(1 + fcGHz) + 1.387;
2049 table3gpp->m_sigLgZSA = -0.09 * log10(1 + fcGHz) + 0.746;
2050 table3gpp->m_uLgZSD = 1.08;
2051 table3gpp->m_sigLgZSD = 0.36;
2052 table3gpp->m_offsetZOD = 0;
2053 table3gpp->m_cDS = 3.91e-9;
2054 table3gpp->m_cASD = 5;
2055 table3gpp->m_cASA = 11;
2056 table3gpp->m_cZSA = 9;
2057 table3gpp->m_uK = 0;
2058 table3gpp->m_sigK = 0;
2059 table3gpp->m_rTau = 3;
2060 table3gpp->m_uXpr = 10;
2061 table3gpp->m_sigXpr = 4;
2062 table3gpp->m_perClusterShadowingStd = 3;
2063
2064 for (uint8_t row = 0; row < 6; row++)
2065 {
2066 for (uint8_t column = 0; column < 6; column++)
2067 {
2068 table3gpp->m_sqrtC[row][column] = sqrtC_office_NLOS[row][column];
2069 }
2070 }
2071 }
2072 }
2073 else if (m_scenario == "V2V-Urban")
2074 {
2075 if (channelCondition->IsLos())
2076 {
2077 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
2078 // entry is N/A.
2079 table3gpp->m_numOfCluster = 12;
2080 table3gpp->m_raysPerCluster = 20;
2081 table3gpp->m_uLgDS = -0.2 * log10(1 + fcGHz) - 7.5;
2082 table3gpp->m_sigLgDS = 0.1;
2083 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.6;
2084 table3gpp->m_sigLgASD = 0.1;
2085 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.6;
2086 table3gpp->m_sigLgASA = 0.1;
2087 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
2088 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
2089 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
2090 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
2091 table3gpp->m_offsetZOD = 0;
2092 table3gpp->m_cDS = 5;
2093 table3gpp->m_cASD = 17;
2094 table3gpp->m_cASA = 17;
2095 table3gpp->m_cZSA = 7;
2096 table3gpp->m_uK = 3.48;
2097 table3gpp->m_sigK = 2;
2098 table3gpp->m_rTau = 3;
2099 table3gpp->m_uXpr = 9;
2100 table3gpp->m_sigXpr = 3;
2101 table3gpp->m_perClusterShadowingStd = 4;
2102
2103 for (uint8_t row = 0; row < 7; row++)
2104 {
2105 for (uint8_t column = 0; column < 7; column++)
2106 {
2107 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
2108 }
2109 }
2110 }
2111 else if (channelCondition->IsNlos())
2112 {
2113 table3gpp->m_numOfCluster = 19;
2114 table3gpp->m_raysPerCluster = 20;
2115 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
2116 table3gpp->m_sigLgDS = 0.28;
2117 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
2118 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
2119 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
2120 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
2121 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
2122 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
2123 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
2124 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
2125 table3gpp->m_offsetZOD = 0;
2126 table3gpp->m_cDS = 11;
2127 table3gpp->m_cASD = 22;
2128 table3gpp->m_cASA = 22;
2129 table3gpp->m_cZSA = 7;
2130 table3gpp->m_uK = 0; // N/A
2131 table3gpp->m_sigK = 0; // N/A
2132 table3gpp->m_rTau = 2.1;
2133 table3gpp->m_uXpr = 8;
2134 table3gpp->m_sigXpr = 3;
2135 table3gpp->m_perClusterShadowingStd = 4;
2136
2137 for (uint8_t row = 0; row < 6; row++)
2138 {
2139 for (uint8_t column = 0; column < 6; column++)
2140 {
2141 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
2142 }
2143 }
2144 }
2145 else if (channelCondition->IsNlosv())
2146 {
2147 table3gpp->m_numOfCluster = 19;
2148 table3gpp->m_raysPerCluster = 20;
2149 table3gpp->m_uLgDS = -0.4 * log10(1 + fcGHz) - 7;
2150 table3gpp->m_sigLgDS = 0.1;
2151 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.7;
2152 table3gpp->m_sigLgASD = 0.1;
2153 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.7;
2154 table3gpp->m_sigLgASA = 0.1;
2155 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
2156 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
2157 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
2158 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
2159 table3gpp->m_offsetZOD = 0;
2160 table3gpp->m_cDS = 11;
2161 table3gpp->m_cASD = 22;
2162 table3gpp->m_cASA = 22;
2163 table3gpp->m_cZSA = 7;
2164 table3gpp->m_uK = 0;
2165 table3gpp->m_sigK = 4.5;
2166 table3gpp->m_rTau = 2.1;
2167 table3gpp->m_uXpr = 8;
2168 table3gpp->m_sigXpr = 3;
2169 table3gpp->m_perClusterShadowingStd = 4;
2170
2171 for (uint8_t row = 0; row < 6; row++)
2172 {
2173 for (uint8_t column = 0; column < 6; column++)
2174 {
2175 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
2176 }
2177 }
2178 }
2179 else
2180 {
2181 NS_FATAL_ERROR("Unknown channel condition");
2182 }
2183 }
2184 else if (m_scenario == "V2V-Highway")
2185 {
2186 if (channelCondition->IsLos())
2187 {
2188 table3gpp->m_numOfCluster = 12;
2189 table3gpp->m_raysPerCluster = 20;
2190 table3gpp->m_uLgDS = -8.3;
2191 table3gpp->m_sigLgDS = 0.2;
2192 table3gpp->m_uLgASD = 1.4;
2193 table3gpp->m_sigLgASD = 0.1;
2194 table3gpp->m_uLgASA = 1.4;
2195 table3gpp->m_sigLgASA = 0.1;
2196 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
2197 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
2198 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
2199 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
2200 table3gpp->m_offsetZOD = 0;
2201 table3gpp->m_cDS = 5;
2202 table3gpp->m_cASD = 17;
2203 table3gpp->m_cASA = 17;
2204 table3gpp->m_cZSA = 7;
2205 table3gpp->m_uK = 9;
2206 table3gpp->m_sigK = 3.5;
2207 table3gpp->m_rTau = 3;
2208 table3gpp->m_uXpr = 9;
2209 table3gpp->m_sigXpr = 3;
2210 table3gpp->m_perClusterShadowingStd = 4;
2211
2212 for (uint8_t row = 0; row < 7; row++)
2213 {
2214 for (uint8_t column = 0; column < 7; column++)
2215 {
2216 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
2217 }
2218 }
2219 }
2220 else if (channelCondition->IsNlosv())
2221 {
2222 table3gpp->m_numOfCluster = 19;
2223 table3gpp->m_raysPerCluster = 20;
2224 table3gpp->m_uLgDS = -8.3;
2225 table3gpp->m_sigLgDS = 0.3;
2226 table3gpp->m_uLgASD = 1.5;
2227 table3gpp->m_sigLgASD = 0.1;
2228 table3gpp->m_uLgASA = 1.5;
2229 table3gpp->m_sigLgASA = 0.1;
2230 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
2231 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
2232 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
2233 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
2234 table3gpp->m_offsetZOD = 0;
2235 table3gpp->m_cDS = 11;
2236 table3gpp->m_cASD = 22;
2237 table3gpp->m_cASA = 22;
2238 table3gpp->m_cZSA = 7;
2239 table3gpp->m_uK = 0;
2240 table3gpp->m_sigK = 4.5;
2241 table3gpp->m_rTau = 2.1;
2242 table3gpp->m_uXpr = 8.0;
2243 table3gpp->m_sigXpr = 3;
2244 table3gpp->m_perClusterShadowingStd = 4;
2245
2246 for (uint8_t row = 0; row < 6; row++)
2247 {
2248 for (uint8_t column = 0; column < 6; column++)
2249 {
2250 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
2251 }
2252 }
2253 }
2254 else if (channelCondition->IsNlos())
2255 {
2257 "The fast fading parameters for the NLOS condition in the Highway scenario are not "
2258 "defined in TR 37.885, use the ones defined in TDoc R1-1803671 instead");
2259
2260 table3gpp->m_numOfCluster = 19;
2261 table3gpp->m_raysPerCluster = 20;
2262 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
2263 table3gpp->m_sigLgDS = 0.28;
2264 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
2265 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
2266 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
2267 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
2268 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
2269 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
2270 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
2271 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
2272 table3gpp->m_offsetZOD = 0;
2273 table3gpp->m_cDS = 11;
2274 table3gpp->m_cASD = 22;
2275 table3gpp->m_cASA = 22;
2276 table3gpp->m_cZSA = 7;
2277 table3gpp->m_uK = 0; // N/A
2278 table3gpp->m_sigK = 0; // N/A
2279 table3gpp->m_rTau = 2.1;
2280 table3gpp->m_uXpr = 8;
2281 table3gpp->m_sigXpr = 3;
2282 table3gpp->m_perClusterShadowingStd = 4;
2283
2284 for (uint8_t row = 0; row < 6; row++)
2285 {
2286 for (uint8_t column = 0; column < 6; column++)
2287 {
2288 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
2289 }
2290 }
2291 }
2292 else
2293 {
2294 NS_FATAL_ERROR("Unknown channel condition");
2295 }
2296 }
2297 else if (m_scenario.substr(0, 3) == "NTN")
2298 {
2299 std::string freqBand = (fcGHz < 13) ? "S" : "Ka";
2300
2301 double elevAngle = 0;
2302 bool isSatellite = false; // flag to indicate if one of the two nodes is a satellite
2303 // if so, parameters will be set accordingly to NOTE 8 of
2304 // Table 6.7.2 from 3GPP 38.811 V15.4.0 (2020-09)
2305
2306 Ptr<MobilityModel> aMobNonConst = ConstCast<MobilityModel>(aMob);
2307 Ptr<MobilityModel> bMobNonConst = ConstCast<MobilityModel>(bMob);
2308
2309 if (DynamicCast<GeocentricConstantPositionMobilityModel>(
2310 ConstCast<MobilityModel>(aMob)) && // Transform to NS_ASSERT
2311 DynamicCast<GeocentricConstantPositionMobilityModel>(
2312 ConstCast<MobilityModel>(bMob))) // check if aMob and bMob are of type
2313 // GeocentricConstantPositionMobilityModel
2314 {
2316 DynamicCast<GeocentricConstantPositionMobilityModel>(aMobNonConst);
2318 DynamicCast<GeocentricConstantPositionMobilityModel>(bMobNonConst);
2319
2320 if (aNTNMob->GetGeographicPosition().z <
2321 bNTNMob->GetGeographicPosition().z) // b is the HAPS/Satellite
2322 {
2323 elevAngle = aNTNMob->GetElevationAngle(bNTNMob);
2324 if (bNTNMob->GetGeographicPosition().z > 50000)
2325 {
2326 isSatellite = true;
2327 }
2328 }
2329 else // a is the HAPS/Satellite
2330 {
2331 elevAngle = bNTNMob->GetElevationAngle(aNTNMob);
2332 if (aNTNMob->GetGeographicPosition().z > 50000)
2333 {
2334 isSatellite = true;
2335 }
2336 }
2337 }
2338 else
2339 {
2340 NS_FATAL_ERROR("Mobility Models needs to be of type Geocentric for NTN scenarios");
2341 }
2342
2343 // Round the elevation angle into a two-digits integer between 10 and 90.
2344 int elevAngleQuantized = (elevAngle < 10) ? 10 : round(elevAngle / 10) * 10;
2345
2346 if (m_scenario == "NTN-DenseUrban")
2347 {
2348 if (channelCondition->IsLos())
2349 {
2350 table3gpp->m_uLgDS = (*m_NTNDenseUrbanLOS)
2351 .at(freqBand)
2352 .at(elevAngleQuantized)[Table3gppParams::uLgDS];
2353 table3gpp->m_sigLgDS = (*m_NTNDenseUrbanLOS)
2354 .at(freqBand)
2355 .at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2356
2357 // table3gpp->m_uLgASD=-1.79769e+308; //FOR SATELLITES
2358 table3gpp->m_uLgASD = (*m_NTNDenseUrbanLOS)
2359 .at(freqBand)
2360 .at(elevAngleQuantized)[Table3gppParams::uLgASD];
2361 // table3gpp->m_sigLgASD=0; //FOR SATELLITES
2362 table3gpp->m_sigLgASD = (*m_NTNDenseUrbanLOS)
2363 .at(freqBand)
2364 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2365
2366 table3gpp->m_uLgASA = (*m_NTNDenseUrbanLOS)
2367 .at(freqBand)
2368 .at(elevAngleQuantized)[Table3gppParams::uLgASA];
2369 table3gpp->m_sigLgASA = (*m_NTNDenseUrbanLOS)
2370 .at(freqBand)
2371 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2372 table3gpp->m_uLgZSA = (*m_NTNDenseUrbanLOS)
2373 .at(freqBand)
2374 .at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2375 table3gpp->m_sigLgZSA = (*m_NTNDenseUrbanLOS)
2376 .at(freqBand)
2377 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2378
2379 // table3gpp->m_uLgZSD=-1.79769e+308; //FOR SATELLITES
2380 table3gpp->m_uLgZSD = (*m_NTNDenseUrbanLOS)
2381 .at(freqBand)
2382 .at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2383 // table3gpp->m_sigLgZSD= 0; //FOR SATELLITES
2384 table3gpp->m_sigLgZSD = (*m_NTNDenseUrbanLOS)
2385 .at(freqBand)
2386 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2387
2388 table3gpp->m_uK =
2389 (*m_NTNDenseUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2390 table3gpp->m_sigK = (*m_NTNDenseUrbanLOS)
2391 .at(freqBand)
2392 .at(elevAngleQuantized)[Table3gppParams::sigK];
2393 table3gpp->m_rTau = (*m_NTNDenseUrbanLOS)
2394 .at(freqBand)
2395 .at(elevAngleQuantized)[Table3gppParams::rTau];
2396 table3gpp->m_uXpr = (*m_NTNDenseUrbanLOS)
2397 .at(freqBand)
2398 .at(elevAngleQuantized)[Table3gppParams::uXpr];
2399 table3gpp->m_sigXpr = (*m_NTNDenseUrbanLOS)
2400 .at(freqBand)
2401 .at(elevAngleQuantized)[Table3gppParams::sigXpr];
2402 table3gpp->m_numOfCluster =
2403 (*m_NTNDenseUrbanLOS)
2404 .at(freqBand)
2405 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2406 table3gpp->m_raysPerCluster =
2407 (*m_NTNDenseUrbanLOS)
2408 .at(freqBand)
2409 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2410 table3gpp->m_cDS =
2411 (*m_NTNDenseUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2412 table3gpp->m_cASD = (*m_NTNDenseUrbanLOS)
2413 .at(freqBand)
2414 .at(elevAngleQuantized)[Table3gppParams::cASD];
2415 table3gpp->m_cASA = (*m_NTNDenseUrbanLOS)
2416 .at(freqBand)
2417 .at(elevAngleQuantized)[Table3gppParams::cASA];
2418 table3gpp->m_cZSA = (*m_NTNDenseUrbanLOS)
2419 .at(freqBand)
2420 .at(elevAngleQuantized)[Table3gppParams::cZSA];
2421 table3gpp->m_perClusterShadowingStd =
2422 (*m_NTNDenseUrbanLOS)
2423 .at(freqBand)
2424 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2425
2426 for (uint8_t row = 0; row < 7; row++)
2427 {
2428 for (uint8_t column = 0; column < 7; column++)
2429 {
2430 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_DenseUrban_LOS[row][column];
2431 }
2432 }
2433 }
2434 else if (channelCondition->IsNlos())
2435 {
2436 NS_LOG_UNCOND("Dense Urban NLOS");
2437 table3gpp->m_uLgDS = (*m_NTNDenseUrbanNLOS)
2438 .at(freqBand)
2439 .at(elevAngleQuantized)[Table3gppParams::uLgDS];
2440 table3gpp->m_sigLgDS = (*m_NTNDenseUrbanNLOS)
2441 .at(freqBand)
2442 .at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2443 table3gpp->m_uLgASD = (*m_NTNDenseUrbanNLOS)
2444 .at(freqBand)
2445 .at(elevAngleQuantized)[Table3gppParams::uLgASD];
2446 table3gpp->m_sigLgASD = (*m_NTNDenseUrbanNLOS)
2447 .at(freqBand)
2448 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2449 table3gpp->m_uLgASA = (*m_NTNDenseUrbanNLOS)
2450 .at(freqBand)
2451 .at(elevAngleQuantized)[Table3gppParams::uLgASA];
2452 table3gpp->m_sigLgASA = (*m_NTNDenseUrbanNLOS)
2453 .at(freqBand)
2454 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2455 table3gpp->m_uLgZSA = (*m_NTNDenseUrbanNLOS)
2456 .at(freqBand)
2457 .at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2458 table3gpp->m_sigLgZSA = (*m_NTNDenseUrbanNLOS)
2459 .at(freqBand)
2460 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2461 table3gpp->m_uLgZSD = (*m_NTNDenseUrbanNLOS)
2462 .at(freqBand)
2463 .at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2464 table3gpp->m_sigLgZSD = (*m_NTNDenseUrbanNLOS)
2465 .at(freqBand)
2466 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2467 table3gpp->m_rTau = (*m_NTNDenseUrbanNLOS)
2468 .at(freqBand)
2469 .at(elevAngleQuantized)[Table3gppParams::rTau];
2470 table3gpp->m_uXpr = (*m_NTNDenseUrbanNLOS)
2471 .at(freqBand)
2472 .at(elevAngleQuantized)[Table3gppParams::uXpr];
2473 table3gpp->m_sigXpr = (*m_NTNDenseUrbanNLOS)
2474 .at(freqBand)
2475 .at(elevAngleQuantized)[Table3gppParams::sigXpr];
2476 table3gpp->m_numOfCluster =
2477 (*m_NTNDenseUrbanNLOS)
2478 .at(freqBand)
2479 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2480 table3gpp->m_raysPerCluster =
2481 (*m_NTNDenseUrbanNLOS)
2482 .at(freqBand)
2483 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2484 table3gpp->m_cDS = (*m_NTNDenseUrbanNLOS)
2485 .at(freqBand)
2486 .at(elevAngleQuantized)[Table3gppParams::cDS];
2487 table3gpp->m_cASD = (*m_NTNDenseUrbanNLOS)
2488 .at(freqBand)
2489 .at(elevAngleQuantized)[Table3gppParams::cASD];
2490 table3gpp->m_cASA = (*m_NTNDenseUrbanNLOS)
2491 .at(freqBand)
2492 .at(elevAngleQuantized)[Table3gppParams::cASA];
2493 table3gpp->m_cZSA = (*m_NTNDenseUrbanNLOS)
2494 .at(freqBand)
2495 .at(elevAngleQuantized)[Table3gppParams::cZSA];
2496 table3gpp->m_perClusterShadowingStd =
2497 (*m_NTNDenseUrbanNLOS)
2498 .at(freqBand)
2499 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2500
2501 for (uint8_t row = 0; row < 6; row++)
2502 {
2503 for (uint8_t column = 0; column < 6; column++)
2504 {
2505 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_DenseUrban_NLOS[row][column];
2506 }
2507 }
2508 }
2509 }
2510 else if (m_scenario == "NTN-Urban")
2511 {
2512 if (channelCondition->IsLos())
2513 {
2514 table3gpp->m_uLgDS =
2515 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2516 table3gpp->m_sigLgDS =
2517 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2518 table3gpp->m_uLgASD =
2519 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2520 table3gpp->m_sigLgASD =
2521 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2522 table3gpp->m_uLgASA =
2523 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2524 table3gpp->m_sigLgASA =
2525 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2526 table3gpp->m_uLgZSA =
2527 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2528 table3gpp->m_sigLgZSA =
2529 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2530 table3gpp->m_uLgZSD =
2531 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2532 table3gpp->m_sigLgZSD =
2533 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2534 table3gpp->m_uK =
2535 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2536 table3gpp->m_sigK =
2537 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2538 table3gpp->m_rTau =
2539 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2540 table3gpp->m_uXpr =
2541 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2542 table3gpp->m_sigXpr =
2543 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2544 table3gpp->m_numOfCluster =
2545 (*m_NTNUrbanLOS)
2546 .at(freqBand)
2547 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2548 table3gpp->m_raysPerCluster =
2549 (*m_NTNUrbanLOS)
2550 .at(freqBand)
2551 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2552 table3gpp->m_cDS =
2553 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2554 table3gpp->m_cASD =
2555 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2556 table3gpp->m_cASA =
2557 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2558 table3gpp->m_cZSA =
2559 (*m_NTNUrbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2560 table3gpp->m_perClusterShadowingStd =
2561 (*m_NTNUrbanLOS)
2562 .at(freqBand)
2563 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2564
2565 for (uint8_t row = 0; row < 7; row++)
2566 {
2567 for (uint8_t column = 0; column < 7; column++)
2568 {
2569 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Urban_LOS[row][column];
2570 }
2571 }
2572 }
2573 else if (channelCondition->IsNlos())
2574 {
2575 table3gpp->m_uLgDS =
2576 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2577 table3gpp->m_sigLgDS =
2578 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2579 table3gpp->m_uLgASD =
2580 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2581 table3gpp->m_sigLgASD = (*m_NTNUrbanNLOS)
2582 .at(freqBand)
2583 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2584 table3gpp->m_uLgASA =
2585 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2586 table3gpp->m_sigLgASA = (*m_NTNUrbanNLOS)
2587 .at(freqBand)
2588 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2589 table3gpp->m_uLgZSA =
2590 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2591 table3gpp->m_sigLgZSA = (*m_NTNUrbanNLOS)
2592 .at(freqBand)
2593 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2594 table3gpp->m_uLgZSD =
2595 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2596 table3gpp->m_sigLgZSD = (*m_NTNUrbanNLOS)
2597 .at(freqBand)
2598 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2599 table3gpp->m_uK =
2600 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2601 table3gpp->m_sigK =
2602 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2603 table3gpp->m_rTau =
2604 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2605 table3gpp->m_uXpr =
2606 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2607 table3gpp->m_sigXpr =
2608 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2609 table3gpp->m_numOfCluster =
2610 (*m_NTNUrbanNLOS)
2611 .at(freqBand)
2612 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2613 table3gpp->m_raysPerCluster =
2614 (*m_NTNUrbanNLOS)
2615 .at(freqBand)
2616 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2617 table3gpp->m_cDS =
2618 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2619 table3gpp->m_cASD =
2620 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2621 table3gpp->m_cASA =
2622 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2623 table3gpp->m_cZSA =
2624 (*m_NTNUrbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2625 table3gpp->m_perClusterShadowingStd =
2626 (*m_NTNUrbanNLOS)
2627 .at(freqBand)
2628 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2629
2630 for (uint8_t row = 0; row < 6; row++)
2631 {
2632 for (uint8_t column = 0; column < 6; column++)
2633 {
2634 table3gpp->m_sqrtC[row][column] =
2635 sqrtC_NTN_Urban_NLOS.at(elevAngleQuantized)[row][column];
2636 }
2637 }
2638 }
2639 }
2640 else if (m_scenario == "NTN-Suburban")
2641 {
2642 if (channelCondition->IsLos())
2643 {
2644 table3gpp->m_uLgDS =
2645 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2646 table3gpp->m_sigLgDS = (*m_NTNSuburbanLOS)
2647 .at(freqBand)
2648 .at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2649 table3gpp->m_uLgASD = (*m_NTNSuburbanLOS)
2650 .at(freqBand)
2651 .at(elevAngleQuantized)[Table3gppParams::uLgASD];
2652 table3gpp->m_sigLgASD = (*m_NTNSuburbanLOS)
2653 .at(freqBand)
2654 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2655 table3gpp->m_uLgASA = (*m_NTNSuburbanLOS)
2656 .at(freqBand)
2657 .at(elevAngleQuantized)[Table3gppParams::uLgASA];
2658 table3gpp->m_sigLgASA = (*m_NTNSuburbanLOS)
2659 .at(freqBand)
2660 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2661 table3gpp->m_uLgZSA = (*m_NTNSuburbanLOS)
2662 .at(freqBand)
2663 .at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2664 table3gpp->m_sigLgZSA = (*m_NTNSuburbanLOS)
2665 .at(freqBand)
2666 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2667 table3gpp->m_uLgZSD = (*m_NTNSuburbanLOS)
2668 .at(freqBand)
2669 .at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2670 table3gpp->m_sigLgZSD = (*m_NTNSuburbanLOS)
2671 .at(freqBand)
2672 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2673 table3gpp->m_uK =
2674 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2675 table3gpp->m_sigK =
2676 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2677 table3gpp->m_rTau =
2678 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2679 table3gpp->m_uXpr =
2680 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2681 table3gpp->m_sigXpr = (*m_NTNSuburbanLOS)
2682 .at(freqBand)
2683 .at(elevAngleQuantized)[Table3gppParams::sigXpr];
2684 table3gpp->m_numOfCluster =
2685 (*m_NTNSuburbanLOS)
2686 .at(freqBand)
2687 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2688 table3gpp->m_raysPerCluster =
2689 (*m_NTNSuburbanLOS)
2690 .at(freqBand)
2691 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2692 table3gpp->m_cDS =
2693 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2694 table3gpp->m_cASD =
2695 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2696 table3gpp->m_cASA =
2697 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2698 table3gpp->m_cZSA =
2699 (*m_NTNSuburbanLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2700 table3gpp->m_perClusterShadowingStd =
2701 (*m_NTNSuburbanLOS)
2702 .at(freqBand)
2703 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2704
2705 for (uint8_t row = 0; row < 7; row++)
2706 {
2707 for (uint8_t column = 0; column < 7; column++)
2708 {
2709 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Suburban_LOS[row][column];
2710 }
2711 }
2712 }
2713 else if (channelCondition->IsNlos())
2714 {
2715 table3gpp->m_uLgDS = (*m_NTNSuburbanNLOS)
2716 .at(freqBand)
2717 .at(elevAngleQuantized)[Table3gppParams::uLgDS];
2718 table3gpp->m_sigLgDS = (*m_NTNSuburbanNLOS)
2719 .at(freqBand)
2720 .at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2721 table3gpp->m_uLgASD = (*m_NTNSuburbanNLOS)
2722 .at(freqBand)
2723 .at(elevAngleQuantized)[Table3gppParams::uLgASD];
2724 table3gpp->m_sigLgASD = (*m_NTNSuburbanNLOS)
2725 .at(freqBand)
2726 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2727 table3gpp->m_uLgASA = (*m_NTNSuburbanNLOS)
2728 .at(freqBand)
2729 .at(elevAngleQuantized)[Table3gppParams::uLgASA];
2730 table3gpp->m_sigLgASA = (*m_NTNSuburbanNLOS)
2731 .at(freqBand)
2732 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2733 table3gpp->m_uLgZSA = (*m_NTNSuburbanNLOS)
2734 .at(freqBand)
2735 .at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2736 table3gpp->m_sigLgZSA = (*m_NTNSuburbanNLOS)
2737 .at(freqBand)
2738 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2739 table3gpp->m_uLgZSD = (*m_NTNSuburbanNLOS)
2740 .at(freqBand)
2741 .at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2742 table3gpp->m_sigLgZSD = (*m_NTNSuburbanNLOS)
2743 .at(freqBand)
2744 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2745 table3gpp->m_uK =
2746 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2747 table3gpp->m_sigK =
2748 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2749 table3gpp->m_rTau =
2750 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2751 table3gpp->m_uXpr =
2752 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2753 table3gpp->m_sigXpr = (*m_NTNSuburbanNLOS)
2754 .at(freqBand)
2755 .at(elevAngleQuantized)[Table3gppParams::sigXpr];
2756 table3gpp->m_numOfCluster =
2757 (*m_NTNSuburbanNLOS)
2758 .at(freqBand)
2759 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2760 table3gpp->m_raysPerCluster =
2761 (*m_NTNSuburbanNLOS)
2762 .at(freqBand)
2763 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2764 table3gpp->m_cDS =
2765 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2766 table3gpp->m_cASD =
2767 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2768 table3gpp->m_cASA =
2769 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2770 table3gpp->m_cZSA =
2771 (*m_NTNSuburbanNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2772 table3gpp->m_perClusterShadowingStd =
2773 (*m_NTNSuburbanNLOS)
2774 .at(freqBand)
2775 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2776
2777 for (uint8_t row = 0; row < 6; row++)
2778 {
2779 for (uint8_t column = 0; column < 6; column++)
2780 {
2781 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Suburban_NLOS[row][column];
2782 }
2783 }
2784 }
2785 }
2786 else if (m_scenario == "NTN-Rural")
2787 {
2788 if (channelCondition->IsLos())
2789 {
2790 table3gpp->m_uLgDS =
2791 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2792 table3gpp->m_sigLgDS =
2793 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2794 table3gpp->m_uLgASD =
2795 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2796 table3gpp->m_sigLgASD =
2797 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2798 table3gpp->m_uLgASA =
2799 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2800 table3gpp->m_sigLgASA =
2801 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2802 table3gpp->m_uLgZSA =
2803 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2804 table3gpp->m_sigLgZSA =
2805 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2806 table3gpp->m_uLgZSD =
2807 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2808 table3gpp->m_sigLgZSD =
2809 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2810 table3gpp->m_uK =
2811 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2812 table3gpp->m_sigK =
2813 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2814 table3gpp->m_rTau =
2815 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2816 table3gpp->m_uXpr =
2817 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2818 table3gpp->m_sigXpr =
2819 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2820 table3gpp->m_numOfCluster =
2821 (*m_NTNRuralLOS)
2822 .at(freqBand)
2823 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2824 table3gpp->m_raysPerCluster =
2825 (*m_NTNRuralLOS)
2826 .at(freqBand)
2827 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2828 table3gpp->m_cDS =
2829 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2830 table3gpp->m_cASD =
2831 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2832 table3gpp->m_cASA =
2833 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2834 table3gpp->m_cZSA =
2835 (*m_NTNRuralLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2836 table3gpp->m_perClusterShadowingStd =
2837 (*m_NTNRuralLOS)
2838 .at(freqBand)
2839 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2840
2841 for (uint8_t row = 0; row < 7; row++)
2842 {
2843 for (uint8_t column = 0; column < 7; column++)
2844 {
2845 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Rural_LOS[row][column];
2846 }
2847 }
2848 }
2849 else if (channelCondition->IsNlos())
2850 {
2851 table3gpp->m_uLgDS =
2852 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2853 table3gpp->m_sigLgDS =
2854 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2855 table3gpp->m_uLgASD =
2856 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2857 table3gpp->m_sigLgASD = (*m_NTNRuralNLOS)
2858 .at(freqBand)
2859 .at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2860 table3gpp->m_uLgASA =
2861 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2862 table3gpp->m_sigLgASA = (*m_NTNRuralNLOS)
2863 .at(freqBand)
2864 .at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2865 table3gpp->m_uLgZSA =
2866 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2867 table3gpp->m_sigLgZSA = (*m_NTNRuralNLOS)
2868 .at(freqBand)
2869 .at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2870 table3gpp->m_uLgZSD =
2871 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2872 table3gpp->m_sigLgZSD = (*m_NTNRuralNLOS)
2873 .at(freqBand)
2874 .at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2875 table3gpp->m_uK =
2876 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2877 table3gpp->m_sigK =
2878 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2879 table3gpp->m_rTau =
2880 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2881 table3gpp->m_uXpr =
2882 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2883 table3gpp->m_sigXpr =
2884 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2885 table3gpp->m_numOfCluster =
2886 (*m_NTNRuralNLOS)
2887 .at(freqBand)
2888 .at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2889 table3gpp->m_raysPerCluster =
2890 (*m_NTNRuralNLOS)
2891 .at(freqBand)
2892 .at(elevAngleQuantized)[Table3gppParams::raysPerCluster];
2893 table3gpp->m_cDS =
2894 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2895 table3gpp->m_cASD =
2896 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2897 table3gpp->m_cASA =
2898 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2899 table3gpp->m_cZSA =
2900 (*m_NTNRuralNLOS).at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2901 table3gpp->m_perClusterShadowingStd =
2902 (*m_NTNRuralNLOS)
2903 .at(freqBand)
2904 .at(elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2905
2906 if (freqBand == "S")
2907 {
2908 for (uint8_t row = 0; row < 6; row++)
2909 {
2910 for (uint8_t column = 0; column < 6; column++)
2911 {
2912 table3gpp->m_sqrtC[row][column] =
2913 sqrtC_NTN_Rural_NLOS_S.at(elevAngleQuantized)[row][column];
2914 }
2915 }
2916 }
2917 else if (freqBand == "Ka")
2918 {
2919 for (uint8_t row = 0; row < 6; row++)
2920 {
2921 for (uint8_t column = 0; column < 6; column++)
2922 {
2923 table3gpp->m_sqrtC[row][column] =
2924 sqrtC_NTN_Rural_NLOS_Ka.at(elevAngleQuantized)[row][column];
2925 }
2926 }
2927 }
2928 }
2929 }
2930 // Parameters that should be set to -inf are instead set to the minimum
2931 // value of double
2932 if (isSatellite)
2933 {
2934 table3gpp->m_uLgASD = std::numeric_limits<double>::min();
2935 table3gpp->m_sigLgASD = 0;
2936 table3gpp->m_uLgZSD = std::numeric_limits<double>::min();
2937 table3gpp->m_sigLgZSD = 0;
2938 }
2939 }
2940 else
2941 {
2942 NS_FATAL_ERROR("unknown scenarios");
2943 }
2944
2945 return table3gpp;
2946}
2947
2948bool
2950 Ptr<const ChannelCondition> channelCondition) const
2951{
2952 NS_LOG_FUNCTION(this);
2953
2954 bool update = false;
2955
2956 // if the channel condition is different the channel has to be updated
2957 if (!channelCondition->IsEqual(channelParams->m_losCondition, channelParams->m_o2iCondition))
2958 {
2959 NS_LOG_DEBUG("Update the channel condition");
2960 update = true;
2961 }
2962
2963 // if the coherence time is over the channel has to be updated
2964 if (!m_updatePeriod.IsZero() &&
2965 Simulator::Now() - channelParams->m_generatedTime > m_updatePeriod)
2966 {
2967 NS_LOG_DEBUG("Generation time " << channelParams->m_generatedTime.As(Time::NS) << " now "
2968 << Now().As(Time::NS));
2969 update = true;
2970 }
2971
2972 return update;
2973}
2974
2975bool
2977 Ptr<const ChannelMatrix> channelMatrix)
2978{
2979 return channelParams->m_generatedTime > channelMatrix->m_generatedTime;
2980}
2981
2982bool
2985 Ptr<const ChannelMatrix> channelMatrix)
2986{
2987 // This allows changing the number of antenna ports during execution,
2988 // which is used by nr's initial association.
2989 size_t sAntNumElems = aAntenna->GetNumElems();
2990 size_t uAntNumElems = bAntenna->GetNumElems();
2991 size_t chanNumRows = channelMatrix->m_channel.GetNumRows();
2992 size_t chanNumCols = channelMatrix->m_channel.GetNumCols();
2993 return ((uAntNumElems != chanNumRows) || (sAntNumElems != chanNumCols)) &&
2994 ((uAntNumElems != chanNumCols) || (sAntNumElems != chanNumRows));
2995}
2996
3002{
3003 NS_LOG_FUNCTION(this);
3004
3005 // Compute the channel params key. The key is reciprocal, i.e., key (a, b) = key (b, a)
3006 uint64_t channelParamsKey =
3007 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
3008 // Compute the channel matrix key. The key is reciprocal, i.e., key (a, b) = key (b, a)
3009 uint64_t channelMatrixKey = GetKey(aAntenna->GetId(), bAntenna->GetId());
3010
3011 // retrieve the channel condition
3012 Ptr<const ChannelCondition> condition =
3013 m_channelConditionModel->GetChannelCondition(aMob, bMob);
3014
3015 // Check if the channel is present in the map and return it, otherwise
3016 // generate a new channel
3017 bool updateParams = false;
3018 bool updateMatrix = false;
3019 bool notFoundParams = false;
3020 bool notFoundMatrix = false;
3021 Ptr<ChannelMatrix> channelMatrix;
3022 Ptr<ThreeGppChannelParams> channelParams;
3023
3024 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
3025 {
3026 channelParams = m_channelParamsMap[channelParamsKey];
3027 // check if it has to be updated
3028 updateParams = ChannelParamsNeedsUpdate(channelParams, condition);
3029 }
3030 else
3031 {
3032 NS_LOG_DEBUG("channel params not found");
3033 notFoundParams = true;
3034 }
3035
3036 // get the 3GPP parameters
3037 Ptr<const ParamsTable> table3gpp = GetThreeGppTable(aMob, bMob, condition);
3038
3039 if (notFoundParams || updateParams)
3040 {
3041 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
3042 // Step 5: Generate Delays.
3043 // Step 6: Generate cluster powers.
3044 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
3045 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
3046 // shuffle all the arrays to perform random coupling
3047 // Step 9: Generate the cross polarization power ratios
3048 // Step 10: Draw initial phases
3049 channelParams = GenerateChannelParameters(condition, table3gpp, aMob, bMob);
3050 // store or replace the channel parameters
3051 m_channelParamsMap[channelParamsKey] = channelParams;
3052 }
3053
3054 if (m_channelMatrixMap.find(channelMatrixKey) != m_channelMatrixMap.end())
3055 {
3056 // channel matrix present in the map
3057 NS_LOG_DEBUG("channel matrix present in the map");
3058 channelMatrix = m_channelMatrixMap[channelMatrixKey];
3059 updateMatrix = ChannelMatrixNeedsUpdate(channelParams, channelMatrix);
3060 updateMatrix |= AntennaSetupChanged(aAntenna, bAntenna, channelMatrix);
3061 }
3062 else
3063 {
3064 NS_LOG_DEBUG("channel matrix not found");
3065 notFoundMatrix = true;
3066 }
3067
3068 // If the channel is not present in the map or if it has to be updated
3069 // generate a new realization
3070 if (notFoundMatrix || updateMatrix)
3071 {
3072 // channel matrix not found or has to be updated, generate a new one
3073 channelMatrix = GetNewChannel(channelParams, table3gpp, aMob, bMob, aAntenna, bAntenna);
3074 channelMatrix->m_antennaPair =
3075 std::make_pair(aAntenna->GetId(),
3076 bAntenna->GetId()); // save antenna pair, with the exact order of s and u
3077 // antennas at the moment of the channel generation
3078
3079 // store or replace the channel matrix in the channel map
3080 m_channelMatrixMap[channelMatrixKey] = channelMatrix;
3081 }
3082
3083 return channelMatrix;
3084}
3085
3088{
3089 NS_LOG_FUNCTION(this);
3090
3091 // Compute the channel key. The key is reciprocal, i.e., key (a, b) = key (b, a)
3092 uint64_t channelParamsKey =
3093 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
3094
3095 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
3096 {
3097 return m_channelParamsMap.find(channelParamsKey)->second;
3098 }
3099 else
3100 {
3101 NS_LOG_WARN("Channel params map not found. Returning a nullptr.");
3102 return nullptr;
3103 }
3104}
3105
3108 const Ptr<const ParamsTable> table3gpp,
3109 const Ptr<const MobilityModel> aMob,
3110 const Ptr<const MobilityModel> bMob) const
3111{
3112 NS_LOG_FUNCTION(this);
3113 // create a channel matrix instance
3114 Ptr<ThreeGppChannelParams> channelParams = Create<ThreeGppChannelParams>();
3115 channelParams->m_generatedTime = Simulator::Now();
3116 channelParams->m_nodeIds =
3117 std::make_pair(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
3118 channelParams->m_losCondition = channelCondition->GetLosCondition();
3119 channelParams->m_o2iCondition = channelCondition->GetO2iCondition();
3120
3121 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
3122 DoubleVector LSPsIndep;
3123 DoubleVector LSPs;
3124 uint8_t paramNum = 6;
3125 if (channelParams->m_losCondition == ChannelCondition::LOS)
3126 {
3127 paramNum = 7;
3128 }
3129
3130 // Generate paramNum independent LSPs.
3131 for (uint8_t iter = 0; iter < paramNum; iter++)
3132 {
3133 LSPsIndep.push_back(m_normalRv->GetValue());
3134 }
3135 for (uint8_t row = 0; row < paramNum; row++)
3136 {
3137 double temp = 0;
3138 for (uint8_t column = 0; column < paramNum; column++)
3139 {
3140 temp += table3gpp->m_sqrtC[row][column] * LSPsIndep[column];
3141 }
3142 LSPs.push_back(temp);
3143 }
3144
3145 // NOTE the shadowing is generated in the propagation loss model
3146 double DS;
3147 double ASD;
3148 double ASA;
3149 double ZSA;
3150 double ZSD;
3151 double kFactor = 0;
3152 if (channelParams->m_losCondition == ChannelCondition::LOS)
3153 {
3154 kFactor = LSPs[1] * table3gpp->m_sigK + table3gpp->m_uK;
3155 DS = pow(10, LSPs[2] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
3156 ASD = pow(10, LSPs[3] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
3157 ASA = pow(10, LSPs[4] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
3158 ZSD = pow(10, LSPs[5] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
3159 ZSA = pow(10, LSPs[6] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
3160 }
3161 else
3162 {
3163 DS = pow(10, LSPs[1] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
3164 ASD = pow(10, LSPs[2] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
3165 ASA = pow(10, LSPs[3] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
3166 ZSD = pow(10, LSPs[4] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
3167 ZSA = pow(10, LSPs[5] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
3168 }
3169 ASD = std::min(ASD, 104.0);
3170 ASA = std::min(ASA, 104.0);
3171 ZSD = std::min(ZSD, 52.0);
3172 ZSA = std::min(ZSA, 52.0);
3173
3174 // save DS and K_factor parameters in the structure
3175 channelParams->m_DS = DS;
3176 channelParams->m_K_factor = kFactor;
3177
3178 NS_LOG_INFO("K-factor=" << kFactor << ", DS=" << DS << ", ASD=" << ASD << ", ASA=" << ASA
3179 << ", ZSD=" << ZSD << ", ZSA=" << ZSA);
3180
3181 // Step 5: Generate Delays.
3182 DoubleVector clusterDelay;
3183 double minTau = 100.0;
3184 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3185 {
3186 double tau = -1 * table3gpp->m_rTau * DS * log(m_uniformRv->GetValue(0, 1)); //(7.5-1)
3187 if (minTau > tau)
3188 {
3189 minTau = tau;
3190 }
3191 clusterDelay.push_back(tau);
3192 }
3193
3194 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3195 {
3196 clusterDelay[cIndex] -= minTau;
3197 }
3198 std::sort(clusterDelay.begin(), clusterDelay.end()); //(7.5-2)
3199
3200 /* since the scaled Los delays are not to be used in cluster power generation,
3201 * we will generate cluster power first and resume to compute Los cluster delay later.*/
3202
3203 // Step 6: Generate cluster powers.
3204 DoubleVector clusterPower;
3205 double powerSum = 0;
3206 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3207 {
3208 double power =
3209 exp(-1 * clusterDelay[cIndex] * (table3gpp->m_rTau - 1) / table3gpp->m_rTau / DS) *
3210 pow(10,
3211 -1 * m_normalRv->GetValue() * table3gpp->m_perClusterShadowingStd / 10.0); //(7.5-5)
3212 powerSum += power;
3213 clusterPower.push_back(power);
3214 }
3215 channelParams->m_clusterPower = clusterPower;
3216
3217 double powerMax = 0;
3218
3219 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3220 {
3221 channelParams->m_clusterPower[cIndex] =
3222 channelParams->m_clusterPower[cIndex] / powerSum; //(7.5-6)
3223 }
3224
3225 DoubleVector clusterPowerForAngles; // this power is only for equation (7.5-9) and (7.5-14), not
3226 // for (7.5-22)
3227 if (channelParams->m_losCondition == ChannelCondition::LOS)
3228 {
3229 double kLinear = pow(10, kFactor / 10.0);
3230
3231 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3232 {
3233 if (cIndex == 0)
3234 {
3235 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
3236 (1 + kLinear) +
3237 kLinear / (1 + kLinear)); //(7.5-8)
3238 }
3239 else
3240 {
3241 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
3242 (1 + kLinear)); //(7.5-8)
3243 }
3244 if (powerMax < clusterPowerForAngles[cIndex])
3245 {
3246 powerMax = clusterPowerForAngles[cIndex];
3247 }
3248 }
3249 }
3250 else
3251 {
3252 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
3253 {
3254 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex]); //(7.5-6)
3255 if (powerMax < clusterPowerForAngles[cIndex])
3256 {
3257 powerMax = clusterPowerForAngles[cIndex];
3258 }
3259 }
3260 }
3261
3262 // remove clusters with less than -25 dB power compared to the maxim cluster power;
3263 // double thresh = pow(10, -2.5);
3264 double thresh = 0.0032;
3265 for (uint8_t cIndex = table3gpp->m_numOfCluster; cIndex > 0; cIndex--)
3266 {
3267 if (clusterPowerForAngles[cIndex - 1] < thresh * powerMax)
3268 {
3269 clusterPowerForAngles.erase(clusterPowerForAngles.begin() + cIndex - 1);
3270 channelParams->m_clusterPower.erase(channelParams->m_clusterPower.begin() + cIndex - 1);
3271 clusterDelay.erase(clusterDelay.begin() + cIndex - 1);
3272 }
3273 }
3274
3275 NS_ASSERT(channelParams->m_clusterPower.size() < UINT8_MAX);
3276 channelParams->m_reducedClusterNumber = channelParams->m_clusterPower.size();
3277 // Resume step 5 to compute the delay for LoS condition.
3278 if (channelParams->m_losCondition == ChannelCondition::LOS)
3279 {
3280 double cTau =
3281 0.7705 - 0.0433 * kFactor + 2e-4 * pow(kFactor, 2) + 17e-6 * pow(kFactor, 3); //(7.5-3)
3282 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3283 {
3284 clusterDelay[cIndex] = clusterDelay[cIndex] / cTau; //(7.5-4)
3285 }
3286 }
3287
3288 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
3289
3290 double cNlos;
3291 // According to table 7.5-6, only cluster number equals to 8, 10, 11, 12, 19 and 20 is valid.
3292 // Not sure why the other cases are in Table 7.5-2.
3293 // Added case 2 and 3 for the NTN according to table 6.7.2-1aa (28.811)
3294 switch (table3gpp->m_numOfCluster) // Table 7.5-2
3295 {
3296 case 2:
3297 cNlos = 0.501;
3298 break;
3299 case 3:
3300 cNlos = 0.680;
3301 break;
3302 case 4:
3303 cNlos = 0.779;
3304 break;
3305 case 5:
3306 cNlos = 0.860;
3307 break;
3308 case 8:
3309 cNlos = 1.018;
3310 break;
3311 case 10:
3312 cNlos = 1.090;
3313 break;
3314 case 11:
3315 cNlos = 1.123;
3316 break;
3317 case 12:
3318 cNlos = 1.146;
3319 break;
3320 case 14:
3321 cNlos = 1.190;
3322 break;
3323 case 15:
3324 cNlos = 1.221;
3325 break;
3326 case 16:
3327 cNlos = 1.226;
3328 break;
3329 case 19:
3330 cNlos = 1.273;
3331 break;
3332 case 20:
3333 cNlos = 1.289;
3334 break;
3335 default:
3336 NS_FATAL_ERROR("Invalid cluster number");
3337 }
3338
3339 double cPhi = cNlos;
3340
3341 if (channelParams->m_losCondition == ChannelCondition::LOS)
3342 {
3343 cPhi *= (1.1035 - 0.028 * kFactor - 2e-3 * pow(kFactor, 2) +
3344 1e-4 * pow(kFactor, 3)); //(7.5-10))
3345 }
3346
3347 // Added case 2, 3 and 4 for the NTN according to table 6.7.2-1ab (28.811)
3348 switch (table3gpp->m_numOfCluster) // Table 7.5-4
3349 {
3350 case 2:
3351 cNlos = 0.430;
3352 break;
3353 case 3:
3354 cNlos = 0.594;
3355 break;
3356 case 4:
3357 cNlos = 0.697;
3358 break;
3359 case 8:
3360 cNlos = 0.889;
3361 break;
3362 case 10:
3363 cNlos = 0.957;
3364 break;
3365 case 11:
3366 cNlos = 1.031;
3367 break;
3368 case 12:
3369 cNlos = 1.104;
3370 break;
3371 case 15:
3372 cNlos = 1.1088;
3373 break;
3374 case 19:
3375 cNlos = 1.184;
3376 break;
3377 case 20:
3378 cNlos = 1.178;
3379 break;
3380 default:
3381 NS_FATAL_ERROR("Invalid cluster number");
3382 }
3383
3384 double cTheta = cNlos;
3385 if (channelCondition->IsLos())
3386 {
3387 cTheta *= (1.3086 + 0.0339 * kFactor - 0.0077 * pow(kFactor, 2) +
3388 2e-4 * pow(kFactor, 3)); //(7.5-15)
3389 }
3390
3391 DoubleVector clusterAoa;
3392 DoubleVector clusterAod;
3393 DoubleVector clusterZoa;
3394 DoubleVector clusterZod;
3395 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3396 {
3397 double logCalc = -1 * log(clusterPowerForAngles[cIndex] / powerMax);
3398 double angle = 2 * sqrt(logCalc) / 1.4 / cPhi; //(7.5-9)
3399 clusterAoa.push_back(ASA * angle);
3400 clusterAod.push_back(ASD * angle);
3401 angle = logCalc / cTheta; //(7.5-14)
3402 clusterZoa.push_back(ZSA * angle);
3403 clusterZod.push_back(ZSD * angle);
3404 }
3405
3406 Angles sAngle(bMob->GetPosition(), aMob->GetPosition());
3407 Angles uAngle(aMob->GetPosition(), bMob->GetPosition());
3408
3409 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3410 {
3411 int Xn = 1;
3412 if (m_uniformRv->GetValue(0, 1) < 0.5)
3413 {
3414 Xn = -1;
3415 }
3416 clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue() * ASA / 7.0) +
3417 RadiansToDegrees(uAngle.GetAzimuth()); //(7.5-11)
3418 clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue() * ASD / 7.0) +
3419 RadiansToDegrees(sAngle.GetAzimuth());
3420 if (channelCondition->IsO2i())
3421 {
3422 clusterZoa[cIndex] =
3423 clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) + 90; //(7.5-16)
3424 }
3425 else
3426 {
3427 clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) +
3428 RadiansToDegrees(uAngle.GetInclination()); //(7.5-16)
3429 }
3430 clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue() * ZSD / 7.0) +
3432 table3gpp->m_offsetZOD; //(7.5-19)
3433 }
3434
3435 if (channelParams->m_losCondition == ChannelCondition::LOS)
3436 {
3437 // The 7.5-12 can be rewrite as Theta_n,ZOA = Theta_n,ZOA - (Theta_1,ZOA - Theta_LOS,ZOA) =
3438 // Theta_n,ZOA - diffZOA, Similar as AOD, ZSA and ZSD.
3439 double diffAoa = clusterAoa[0] - RadiansToDegrees(uAngle.GetAzimuth());
3440 double diffAod = clusterAod[0] - RadiansToDegrees(sAngle.GetAzimuth());
3441 double diffZsa = clusterZoa[0] - RadiansToDegrees(uAngle.GetInclination());
3442 double diffZsd = clusterZod[0] - RadiansToDegrees(sAngle.GetInclination());
3443
3444 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3445 {
3446 clusterAoa[cIndex] -= diffAoa; //(7.5-12)
3447 clusterAod[cIndex] -= diffAod;
3448 clusterZoa[cIndex] -= diffZsa; //(7.5-17)
3449 clusterZod[cIndex] -= diffZsd;
3450 }
3451 }
3452
3453 double sizeTemp = clusterZoa.size();
3454 for (uint8_t ind = 0; ind < 4; ind++)
3455 {
3456 DoubleVector angleDegree;
3457 switch (ind)
3458 {
3459 case 0:
3460 angleDegree = clusterAoa;
3461 break;
3462 case 1:
3463 angleDegree = clusterZoa;
3464 break;
3465 case 2:
3466 angleDegree = clusterAod;
3467 break;
3468 case 3:
3469 angleDegree = clusterZod;
3470 break;
3471 default:
3472 NS_FATAL_ERROR("Programming Error");
3473 }
3474 for (uint8_t nIndex = 0; nIndex < sizeTemp; nIndex++)
3475 {
3476 while (angleDegree[nIndex] > 360)
3477 {
3478 angleDegree[nIndex] -= 360;
3479 }
3480
3481 while (angleDegree[nIndex] < 0)
3482 {
3483 angleDegree[nIndex] += 360;
3484 }
3485
3486 if (ind == 1 || ind == 3)
3487 {
3488 if (angleDegree[nIndex] > 180)
3489 {
3490 angleDegree[nIndex] = 360 - angleDegree[nIndex];
3491 }
3492 }
3493 }
3494 switch (ind)
3495 {
3496 case 0:
3497 clusterAoa = angleDegree;
3498 break;
3499 case 1:
3500 clusterZoa = angleDegree;
3501 break;
3502 case 2:
3503 clusterAod = angleDegree;
3504 break;
3505 case 3:
3506 clusterZod = angleDegree;
3507 break;
3508 default:
3509 NS_FATAL_ERROR("Programming Error");
3510 }
3511 }
3512
3513 DoubleVector attenuationDb;
3514 if (m_blockage)
3515 {
3516 attenuationDb = CalcAttenuationOfBlockage(channelParams, clusterAoa, clusterZoa);
3517 for (uint8_t cInd = 0; cInd < channelParams->m_reducedClusterNumber; cInd++)
3518 {
3519 channelParams->m_clusterPower[cInd] =
3520 channelParams->m_clusterPower[cInd] / pow(10, attenuationDb[cInd] / 10.0);
3521 }
3522 }
3523 else
3524 {
3525 attenuationDb.push_back(0);
3526 }
3527
3528 // store attenuation
3529 channelParams->m_attenuation_dB = attenuationDb;
3530
3531 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
3532 // shuffle all the arrays to perform random coupling
3534 channelParams->m_reducedClusterNumber,
3535 DoubleVector(table3gpp->m_raysPerCluster,
3536 0)); // rayAoaRadian[n][m], where n is cluster index, m is ray index
3538 channelParams->m_reducedClusterNumber,
3539 DoubleVector(table3gpp->m_raysPerCluster,
3540 0)); // rayAodRadian[n][m], where n is cluster index, m is ray index
3542 channelParams->m_reducedClusterNumber,
3543 DoubleVector(table3gpp->m_raysPerCluster,
3544 0)); // rayZoaRadian[n][m], where n is cluster index, m is ray index
3546 channelParams->m_reducedClusterNumber,
3547 DoubleVector(table3gpp->m_raysPerCluster,
3548 0)); // rayZodRadian[n][m], where n is cluster index, m is ray index
3549
3550 const double pow10_uLgZSD = pow(10, table3gpp->m_uLgZSD);
3551 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
3552 {
3553 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
3554 {
3555 double tempAoa = clusterAoa[nInd] + table3gpp->m_cASA * offSetAlpha[mInd]; //(7.5-13)
3556 double tempZoa = clusterZoa[nInd] + table3gpp->m_cZSA * offSetAlpha[mInd]; //(7.5-18)
3557 std::tie(rayAoaRadian[nInd][mInd], rayZoaRadian[nInd][mInd]) =
3558 WrapAngles(DegreesToRadians(tempAoa), DegreesToRadians(tempZoa));
3559
3560 double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd]; //(7.5-13)
3561 double tempZod = clusterZod[nInd] + 0.375 * pow10_uLgZSD * offSetAlpha[mInd]; //(7.5-20)
3562 std::tie(rayAodRadian[nInd][mInd], rayZodRadian[nInd][mInd]) =
3563 WrapAngles(DegreesToRadians(tempAod), DegreesToRadians(tempZod));
3564 }
3565 }
3566
3567 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3568 {
3569 Shuffle(&rayAodRadian[cIndex][0], &rayAodRadian[cIndex][table3gpp->m_raysPerCluster]);
3570 Shuffle(&rayAoaRadian[cIndex][0], &rayAoaRadian[cIndex][table3gpp->m_raysPerCluster]);
3571 Shuffle(&rayZodRadian[cIndex][0], &rayZodRadian[cIndex][table3gpp->m_raysPerCluster]);
3572 Shuffle(&rayZoaRadian[cIndex][0], &rayZoaRadian[cIndex][table3gpp->m_raysPerCluster]);
3573 }
3574
3575 // store values
3576 channelParams->m_rayAodRadian = rayAodRadian;
3577 channelParams->m_rayAoaRadian = rayAoaRadian;
3578 channelParams->m_rayZodRadian = rayZodRadian;
3579 channelParams->m_rayZoaRadian = rayZoaRadian;
3580
3581 // Step 9: Generate the cross polarization power ratios
3582 // Step 10: Draw initial phases
3583
3584 // vector containing the cross polarization power ratios, as defined by 7.5-21
3585 auto& crossPolarizationPowerRatios = channelParams->m_crossPolarizationPowerRatios;
3586 // rayAoaRadian[n][m], where n is cluster index, m is ray index
3587 auto& clusterPhase = channelParams->m_clusterPhase;
3588
3589 const double uXprLinear = pow(10, table3gpp->m_uXpr / 10.0); // convert to linear
3590 const double sigXprLinear = pow(10, table3gpp->m_sigXpr / 10.0); // convert to linear
3591
3592 // store the PHI values for all the possible combination of polarization
3593 clusterPhase.resize(channelParams->m_reducedClusterNumber);
3594 crossPolarizationPowerRatios.resize(channelParams->m_reducedClusterNumber);
3595 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
3596 {
3597 clusterPhase[nInd].resize(table3gpp->m_raysPerCluster);
3598 crossPolarizationPowerRatios[nInd].resize(table3gpp->m_raysPerCluster);
3599 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
3600 {
3601 clusterPhase[nInd][mInd].resize(4);
3602 // used to store the XPR values
3603 crossPolarizationPowerRatios[nInd][mInd] =
3604 std::pow(10, (m_normalRv->GetValue() * sigXprLinear + uXprLinear) / 10.0);
3605 for (uint8_t pInd = 0; pInd < 4; pInd++)
3606 {
3607 // used to store the PHI values
3608 clusterPhase[nInd][mInd][pInd] = m_uniformRv->GetValue(-1 * M_PI, M_PI);
3609 }
3610 }
3611 }
3612
3613 uint8_t cluster1st = 0;
3614 uint8_t cluster2nd = 0; // first and second strongest cluster;
3615 double maxPower = 0;
3616 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3617 {
3618 if (maxPower < channelParams->m_clusterPower[cIndex])
3619 {
3620 maxPower = channelParams->m_clusterPower[cIndex];
3621 cluster1st = cIndex;
3622 }
3623 }
3624 channelParams->m_cluster1st = cluster1st;
3625 maxPower = 0;
3626 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3627 {
3628 if (maxPower < channelParams->m_clusterPower[cIndex] && cluster1st != cIndex)
3629 {
3630 maxPower = channelParams->m_clusterPower[cIndex];
3631 cluster2nd = cIndex;
3632 }
3633 }
3634 channelParams->m_cluster2nd = cluster2nd;
3635
3636 NS_LOG_INFO("1st strongest cluster:" << +cluster1st
3637 << ", 2nd strongest cluster:" << +cluster2nd);
3638
3639 // store the delays and the angles for the subclusters
3640 if (cluster1st == cluster2nd)
3641 {
3642 clusterDelay.push_back(clusterDelay[cluster1st] + 1.28 * table3gpp->m_cDS);
3643 clusterDelay.push_back(clusterDelay[cluster1st] + 2.56 * table3gpp->m_cDS);
3644
3645 clusterAoa.push_back(clusterAoa[cluster1st]);
3646 clusterAoa.push_back(clusterAoa[cluster1st]);
3647
3648 clusterZoa.push_back(clusterZoa[cluster1st]);
3649 clusterZoa.push_back(clusterZoa[cluster1st]);
3650
3651 clusterAod.push_back(clusterAod[cluster1st]);
3652 clusterAod.push_back(clusterAod[cluster1st]);
3653
3654 clusterZod.push_back(clusterZod[cluster1st]);
3655 clusterZod.push_back(clusterZod[cluster1st]);
3656 }
3657 else
3658 {
3659 double min;
3660 double max;
3661 if (cluster1st < cluster2nd)
3662 {
3663 min = cluster1st;
3664 max = cluster2nd;
3665 }
3666 else
3667 {
3668 min = cluster2nd;
3669 max = cluster1st;
3670 }
3671 clusterDelay.push_back(clusterDelay[min] + 1.28 * table3gpp->m_cDS);
3672 clusterDelay.push_back(clusterDelay[min] + 2.56 * table3gpp->m_cDS);
3673 clusterDelay.push_back(clusterDelay[max] + 1.28 * table3gpp->m_cDS);
3674 clusterDelay.push_back(clusterDelay[max] + 2.56 * table3gpp->m_cDS);
3675
3676 clusterAoa.push_back(clusterAoa[min]);
3677 clusterAoa.push_back(clusterAoa[min]);
3678 clusterAoa.push_back(clusterAoa[max]);
3679 clusterAoa.push_back(clusterAoa[max]);
3680
3681 clusterZoa.push_back(clusterZoa[min]);
3682 clusterZoa.push_back(clusterZoa[min]);
3683 clusterZoa.push_back(clusterZoa[max]);
3684 clusterZoa.push_back(clusterZoa[max]);
3685
3686 clusterAod.push_back(clusterAod[min]);
3687 clusterAod.push_back(clusterAod[min]);
3688 clusterAod.push_back(clusterAod[max]);
3689 clusterAod.push_back(clusterAod[max]);
3690
3691 clusterZod.push_back(clusterZod[min]);
3692 clusterZod.push_back(clusterZod[min]);
3693 clusterZod.push_back(clusterZod[max]);
3694 clusterZod.push_back(clusterZod[max]);
3695 }
3696
3697 channelParams->m_delay = clusterDelay;
3698 channelParams->m_angle.clear();
3699 channelParams->m_angle.push_back(clusterAoa);
3700 channelParams->m_angle.push_back(clusterZoa);
3701 channelParams->m_angle.push_back(clusterAod);
3702 channelParams->m_angle.push_back(clusterZod);
3703
3704 // Precompute angles sincos
3705 channelParams->m_cachedAngleSincos.resize(channelParams->m_angle.size());
3706 for (size_t direction = 0; direction < channelParams->m_angle.size(); direction++)
3707 {
3708 channelParams->m_cachedAngleSincos[direction].resize(
3709 channelParams->m_angle[direction].size());
3710 for (size_t cluster = 0; cluster < channelParams->m_angle[direction].size(); cluster++)
3711 {
3712 channelParams->m_cachedAngleSincos[direction][cluster] = {
3713 sin(channelParams->m_angle[direction][cluster] * DEG2RAD),
3714 cos(channelParams->m_angle[direction][cluster] * DEG2RAD)};
3715 }
3716 }
3717
3718 // Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
3719 // These terms account for an additional Doppler contribution due to the
3720 // presence of moving objects in the surrounding environment, such as in
3721 // vehicular scenarios.
3722 // This contribution is applied only to the delayed (reflected) paths and
3723 // must be properly configured by setting the value of
3724 // m_vScatt, which is defined as "maximum speed of the vehicle in the
3725 // layout".
3726 // By default, m_vScatt is set to 0, so there is no additional Doppler
3727 // contribution.
3728
3729 DoubleVector dopplerTermAlpha;
3730 DoubleVector dopplerTermD;
3731
3732 // 2 or 4 is added to account for additional subrays for the 1st and 2nd clusters, if there is
3733 // only one cluster then would be added 2 more subrays (see creation of Husn channel matrix)
3734 uint8_t updatedClusterNumber = (channelParams->m_reducedClusterNumber == 1)
3735 ? channelParams->m_reducedClusterNumber + 2
3736 : channelParams->m_reducedClusterNumber + 4;
3737
3738 for (uint8_t cIndex = 0; cIndex < updatedClusterNumber; cIndex++)
3739 {
3740 double alpha = 0;
3741 double D = 0;
3742 if (cIndex != 0)
3743 {
3744 alpha = m_uniformRvDoppler->GetValue(-1, 1);
3746 }
3747 dopplerTermAlpha.push_back(alpha);
3748 dopplerTermD.push_back(D);
3749 }
3750 channelParams->m_alpha = dopplerTermAlpha;
3751 channelParams->m_D = dopplerTermD;
3752
3753 return channelParams;
3754}
3755
3758 Ptr<const ParamsTable> table3gpp,
3759 const Ptr<const MobilityModel> sMob,
3760 const Ptr<const MobilityModel> uMob,
3762 Ptr<const PhasedArrayModel> uAntenna) const
3763{
3764 NS_LOG_FUNCTION(this);
3765
3766 NS_ASSERT_MSG(m_frequency > 0.0, "Set the operating frequency first!");
3767
3768 // create a channel matrix instance
3769 Ptr<ChannelMatrix> channelMatrix = Create<ChannelMatrix>();
3770 channelMatrix->m_generatedTime = Simulator::Now();
3771 // save in which order is generated this matrix
3772 channelMatrix->m_nodeIds =
3773 std::make_pair(sMob->GetObject<Node>()->GetId(), uMob->GetObject<Node>()->GetId());
3774 // check if channelParams structure is generated in direction s-to-u or u-to-s
3775 bool isSameDirection = (channelParams->m_nodeIds == channelMatrix->m_nodeIds);
3776
3781
3782 // if channel params is generated in the same direction in which we
3783 // generate the channel matrix, angles and zenith od departure and arrival are ok,
3784 // just set them to corresponding variable that will be used for the generation
3785 // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
3786 if (isSameDirection)
3787 {
3788 rayAodRadian = channelParams->m_rayAodRadian;
3789 rayAoaRadian = channelParams->m_rayAoaRadian;
3790 rayZodRadian = channelParams->m_rayZodRadian;
3791 rayZoaRadian = channelParams->m_rayZoaRadian;
3792 }
3793 else
3794 {
3795 rayAodRadian = channelParams->m_rayAoaRadian;
3796 rayAoaRadian = channelParams->m_rayAodRadian;
3797 rayZodRadian = channelParams->m_rayZoaRadian;
3798 rayZoaRadian = channelParams->m_rayZodRadian;
3799 }
3800
3801 // Step 11: Generate channel coefficients for each cluster n and each receiver
3802 // and transmitter element pair u,s.
3803 // where n is cluster index, u and s are receive and transmit antenna element.
3804 size_t uSize = uAntenna->GetNumElems();
3805 size_t sSize = sAntenna->GetNumElems();
3806
3807 // NOTE: Since each of the strongest 2 clusters are divided into 3 sub-clusters,
3808 // the total cluster will generally be numReducedCLuster + 4.
3809 // However, it might be that m_cluster1st = m_cluster2nd. In this case the
3810 // total number of clusters will be numReducedCLuster + 2.
3811 uint16_t numOverallCluster = (channelParams->m_cluster1st != channelParams->m_cluster2nd)
3812 ? channelParams->m_reducedClusterNumber + 4
3813 : channelParams->m_reducedClusterNumber + 2;
3814 Complex3DVector hUsn(uSize, sSize, numOverallCluster); // channel coefficient hUsn (u, s, n);
3815 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPhase.size());
3816 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPower.size());
3817 NS_ASSERT(channelParams->m_reducedClusterNumber <=
3818 channelParams->m_crossPolarizationPowerRatios.size());
3819 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZoaRadian.size());
3820 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZodRadian.size());
3821 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAoaRadian.size());
3822 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAodRadian.size());
3823 NS_ASSERT(table3gpp->m_raysPerCluster <= channelParams->m_clusterPhase[0].size());
3824 NS_ASSERT(table3gpp->m_raysPerCluster <=
3825 channelParams->m_crossPolarizationPowerRatios[0].size());
3826 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZoaRadian[0].size());
3827 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZodRadian[0].size());
3828 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAoaRadian[0].size());
3829 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAodRadian[0].size());
3830
3831 double x = sMob->GetPosition().x - uMob->GetPosition().x;
3832 double y = sMob->GetPosition().y - uMob->GetPosition().y;
3833 double distance2D = sqrt(x * x + y * y);
3834 // NOTE we assume hUT = min (height(a), height(b)) and
3835 // hBS = max (height (a), height (b))
3836 double hUt = std::min(sMob->GetPosition().z, uMob->GetPosition().z);
3837 double hBs = std::max(sMob->GetPosition().z, uMob->GetPosition().z);
3838 // compute the 3D distance using eq. 7.4-1
3839 double distance3D = std::sqrt(distance2D * distance2D + (hBs - hUt) * (hBs - hUt));
3840
3841 Angles sAngle(uMob->GetPosition(), sMob->GetPosition());
3842 Angles uAngle(sMob->GetPosition(), uMob->GetPosition());
3843
3844 Double2DVector sinCosA; // cached multiplications of sin and cos of the ZoA and AoA angles
3845 Double2DVector sinSinA; // cached multiplications of sines of the ZoA and AoA angles
3846 Double2DVector cosZoA; // cached cos of the ZoA angle
3847 Double2DVector sinCosD; // cached multiplications of sin and cos of the ZoD and AoD angles
3848 Double2DVector sinSinD; // cached multiplications of the cosines of the ZoA and AoA angles
3849 Double2DVector cosZoD; // cached cos of the ZoD angle
3850
3851 // contains part of the ray expression, cached as independent from the u- and s-indexes,
3852 // but calculate it for different polarization angles of s and u
3853 std::map<std::pair<uint8_t, uint8_t>, Complex2DVector> raysPreComp;
3854 for (size_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
3855 {
3856 for (size_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
3857 {
3858 raysPreComp[std::make_pair(polSa, polUa)] =
3859 Complex2DVector(channelParams->m_reducedClusterNumber, table3gpp->m_raysPerCluster);
3860 }
3861 }
3862
3863 // resize to appropriate dimensions
3864 sinCosA.resize(channelParams->m_reducedClusterNumber);
3865 sinSinA.resize(channelParams->m_reducedClusterNumber);
3866 cosZoA.resize(channelParams->m_reducedClusterNumber);
3867 sinCosD.resize(channelParams->m_reducedClusterNumber);
3868 sinSinD.resize(channelParams->m_reducedClusterNumber);
3869 cosZoD.resize(channelParams->m_reducedClusterNumber);
3870 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3871 {
3872 sinCosA[nIndex].resize(table3gpp->m_raysPerCluster);
3873 sinSinA[nIndex].resize(table3gpp->m_raysPerCluster);
3874 cosZoA[nIndex].resize(table3gpp->m_raysPerCluster);
3875 sinCosD[nIndex].resize(table3gpp->m_raysPerCluster);
3876 sinSinD[nIndex].resize(table3gpp->m_raysPerCluster);
3877 cosZoD[nIndex].resize(table3gpp->m_raysPerCluster);
3878 }
3879 // pre-compute the terms which are independent from uIndex and sIndex
3880 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3881 {
3882 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3883 {
3884 DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
3885 NS_ASSERT(4 <= initialPhase.size());
3886 double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
3887
3888 // cache the component of the "rays" terms which depend on the random angle of arrivals
3889 // and departures and initial phases only
3890 for (uint8_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
3891 {
3892 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
3893 Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
3894 channelParams->m_rayZoaRadian[nIndex][mIndex]),
3895 polUa);
3896 for (uint8_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
3897 {
3898 auto [txFieldPatternPhi, txFieldPatternTheta] =
3899 sAntenna->GetElementFieldPattern(
3900 Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
3901 channelParams->m_rayZodRadian[nIndex][mIndex]),
3902 polSa);
3903 raysPreComp[std::make_pair(polSa, polUa)](nIndex, mIndex) =
3904 std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
3905 rxFieldPatternTheta * txFieldPatternTheta +
3906 std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
3907 std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
3908 std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
3909 std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
3910 std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
3911 rxFieldPatternPhi * txFieldPatternPhi;
3912 }
3913 }
3914
3915 // cache the component of the "rxPhaseDiff" terms which depend on the random angle of
3916 // arrivals only
3917 double sinRayZoa = sin(rayZoaRadian[nIndex][mIndex]);
3918 double sinRayAoa = sin(rayAoaRadian[nIndex][mIndex]);
3919 double cosRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
3920 sinCosA[nIndex][mIndex] = sinRayZoa * cosRayAoa;
3921 sinSinA[nIndex][mIndex] = sinRayZoa * sinRayAoa;
3922 cosZoA[nIndex][mIndex] = cos(rayZoaRadian[nIndex][mIndex]);
3923
3924 // cache the component of the "txPhaseDiff" terms which depend on the random angle of
3925 // departure only
3926 double sinRayZod = sin(rayZodRadian[nIndex][mIndex]);
3927 double sinRayAod = sin(rayAodRadian[nIndex][mIndex]);
3928 double cosRayAod = cos(rayAodRadian[nIndex][mIndex]);
3929 sinCosD[nIndex][mIndex] = sinRayZod * cosRayAod;
3930 sinSinD[nIndex][mIndex] = sinRayZod * sinRayAod;
3931 cosZoD[nIndex][mIndex] = cos(rayZodRadian[nIndex][mIndex]);
3932 }
3933 }
3934
3935 // The following for loops computes the channel coefficients
3936 // Keeps track of how many sub-clusters have been added up to now
3937 uint8_t numSubClustersAdded = 0;
3938 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3939 {
3940 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
3941 {
3942 Vector uLoc = uAntenna->GetElementLocation(uIndex);
3943
3944 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
3945 {
3946 Vector sLoc = sAntenna->GetElementLocation(sIndex);
3947 // Compute the N-2 weakest cluster, assuming 0 slant angle and a
3948 // polarization slant angle configured in the array (7.5-22)
3949 if (nIndex != channelParams->m_cluster1st && nIndex != channelParams->m_cluster2nd)
3950 {
3951 std::complex<double> rays(0, 0);
3952 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3953 {
3954 // lambda_0 is accounted in the antenna spacing uLoc and sLoc.
3955 double rxPhaseDiff =
3956 2 * M_PI *
3957 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
3958 cosZoA[nIndex][mIndex] * uLoc.z);
3959
3960 double txPhaseDiff =
3961 2 * M_PI *
3962 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
3963 cosZoD[nIndex][mIndex] * sLoc.z);
3964 // NOTE Doppler is computed in the CalcBeamformingGain function and is
3965 // simplified to only account for the center angle of each cluster.
3966 rays += raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
3967 uAntenna->GetElemPol(uIndex))](nIndex,
3968 mIndex) *
3969 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
3970 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
3971 }
3972 rays *=
3973 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
3974 hUsn(uIndex, sIndex, nIndex) = rays;
3975 }
3976 else //(7.5-28)
3977 {
3978 std::complex<double> raysSub1(0, 0);
3979 std::complex<double> raysSub2(0, 0);
3980 std::complex<double> raysSub3(0, 0);
3981
3982 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3983 {
3984 // ZML:Just remind me that the angle offsets for the 3 subclusters were not
3985 // generated correctly.
3986 double rxPhaseDiff =
3987 2 * M_PI *
3988 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
3989 cosZoA[nIndex][mIndex] * uLoc.z);
3990
3991 double txPhaseDiff =
3992 2 * M_PI *
3993 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
3994 cosZoD[nIndex][mIndex] * sLoc.z);
3995
3996 std::complex<double> raySub =
3997 raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
3998 uAntenna->GetElemPol(uIndex))](nIndex,
3999 mIndex) *
4000 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
4001 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
4002
4003 switch (mIndex)
4004 {
4005 case 9:
4006 case 10:
4007 case 11:
4008 case 12:
4009 case 17:
4010 case 18:
4011 raysSub2 += raySub;
4012 break;
4013 case 13:
4014 case 14:
4015 case 15:
4016 case 16:
4017 raysSub3 += raySub;
4018 break;
4019 default: // case 1,2,3,4,5,6,7,8,19,20
4020 raysSub1 += raySub;
4021 break;
4022 }
4023 }
4024 raysSub1 *=
4025 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
4026 raysSub2 *=
4027 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
4028 raysSub3 *=
4029 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
4030 hUsn(uIndex, sIndex, nIndex) = raysSub1;
4031 hUsn(uIndex,
4032 sIndex,
4033 channelParams->m_reducedClusterNumber + numSubClustersAdded) = raysSub2;
4034 hUsn(uIndex,
4035 sIndex,
4036 channelParams->m_reducedClusterNumber + numSubClustersAdded + 1) =
4037 raysSub3;
4038 }
4039 }
4040 }
4041 if (nIndex == channelParams->m_cluster1st || nIndex == channelParams->m_cluster2nd)
4042 {
4043 numSubClustersAdded += 2;
4044 }
4045 }
4046
4047 if (channelParams->m_losCondition == ChannelCondition::LOS) //(7.5-29) && (7.5-30)
4048 {
4049 double lambda = 3.0e8 / m_frequency; // the wavelength of the carrier frequency
4050 std::complex<double> phaseDiffDueToDistance(cos(-2 * M_PI * distance3D / lambda),
4051 sin(-2 * M_PI * distance3D / lambda));
4052
4053 const double sinUAngleIncl = sin(uAngle.GetInclination());
4054 const double cosUAngleIncl = cos(uAngle.GetInclination());
4055 const double sinUAngleAz = sin(uAngle.GetAzimuth());
4056 const double cosUAngleAz = cos(uAngle.GetAzimuth());
4057 const double sinSAngleIncl = sin(sAngle.GetInclination());
4058 const double cosSAngleIncl = cos(sAngle.GetInclination());
4059 const double sinSAngleAz = sin(sAngle.GetAzimuth());
4060 const double cosSAngleAz = cos(sAngle.GetAzimuth());
4061
4062 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
4063 {
4064 Vector uLoc = uAntenna->GetElementLocation(uIndex);
4065 double rxPhaseDiff = 2 * M_PI *
4066 (sinUAngleIncl * cosUAngleAz * uLoc.x +
4067 sinUAngleIncl * sinUAngleAz * uLoc.y + cosUAngleIncl * uLoc.z);
4068
4069 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
4070 {
4071 Vector sLoc = sAntenna->GetElementLocation(sIndex);
4072 std::complex<double> ray(0, 0);
4073 double txPhaseDiff =
4074 2 * M_PI *
4075 (sinSAngleIncl * cosSAngleAz * sLoc.x + sinSAngleIncl * sinSAngleAz * sLoc.y +
4076 cosSAngleIncl * sLoc.z);
4077
4078 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
4079 Angles(uAngle.GetAzimuth(), uAngle.GetInclination()),
4080 uAntenna->GetElemPol(uIndex));
4081 auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
4082 Angles(sAngle.GetAzimuth(), sAngle.GetInclination()),
4083 sAntenna->GetElemPol(sIndex));
4084
4085 ray = (rxFieldPatternTheta * txFieldPatternTheta -
4086 rxFieldPatternPhi * txFieldPatternPhi) *
4087 phaseDiffDueToDistance *
4088 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
4089 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
4090
4091 double kLinear = pow(10, channelParams->m_K_factor / 10.0);
4092 // the LOS path should be attenuated if blockage is enabled.
4093 hUsn(uIndex, sIndex, 0) =
4094 sqrt(1.0 / (kLinear + 1)) * hUsn(uIndex, sIndex, 0) +
4095 sqrt(kLinear / (1 + kLinear)) * ray /
4096 pow(10,
4097 channelParams->m_attenuation_dB[0] / 10.0); //(7.5-30) for tau = tau1
4098 for (size_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
4099 {
4100 hUsn(uIndex, sIndex, nIndex) *=
4101 sqrt(1.0 / (kLinear + 1)); //(7.5-30) for tau = tau2...tauN
4102 }
4103 }
4104 }
4105 }
4106
4107 NS_LOG_DEBUG("Husn (sAntenna, uAntenna):" << sAntenna->GetId() << ", " << uAntenna->GetId());
4108 for (size_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
4109 {
4110 for (size_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
4111 {
4112 for (size_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
4113 {
4114 NS_LOG_DEBUG(" " << hUsn(rowIdx, colIdx, cIndex) << ",");
4115 }
4116 }
4117 }
4118
4119 NS_LOG_INFO("size of coefficient matrix (rows, columns, clusters) = ("
4120 << hUsn.GetNumRows() << ", " << hUsn.GetNumCols() << ", " << hUsn.GetNumPages()
4121 << ")");
4122 channelMatrix->m_channel = hUsn;
4123 return channelMatrix;
4124}
4125
4126std::pair<double, double>
4127ThreeGppChannelModel::WrapAngles(double azimuthRad, double inclinationRad)
4128{
4129 inclinationRad = WrapTo2Pi(inclinationRad);
4130 if (inclinationRad > M_PI)
4131 {
4132 // inclination must be in [0, M_PI]
4133 inclinationRad -= M_PI;
4134 azimuthRad += M_PI;
4135 }
4136
4137 azimuthRad = WrapTo2Pi(azimuthRad);
4138
4139 NS_ASSERT_MSG(0 <= inclinationRad && inclinationRad <= M_PI,
4140 "inclinationRad=" << inclinationRad << " not valid, should be in [0, pi]");
4141 NS_ASSERT_MSG(0 <= azimuthRad && azimuthRad <= 2 * M_PI,
4142 "azimuthRad=" << azimuthRad << " not valid, should be in [0, 2*pi]");
4143
4144 return std::make_pair(azimuthRad, inclinationRad);
4145}
4146
4150 const DoubleVector& clusterAOA,
4151 const DoubleVector& clusterZOA) const
4152{
4153 NS_LOG_FUNCTION(this);
4154
4155 auto clusterNum = clusterAOA.size();
4156
4157 // Initial power attenuation for all clusters to be 0 dB
4158 DoubleVector powerAttenuation(clusterNum, 0);
4159
4160 // step a: the number of non-self blocking blockers is stored in m_numNonSelfBlocking.
4161
4162 // step b:Generate the size and location of each blocker
4163 // generate self blocking (i.e., for blockage from the human body)
4164 // table 7.6.4.1-1 Self-blocking region parameters.
4165 // Defaults: landscape mode
4166 double phiSb = 40;
4167 double xSb = 160;
4168 double thetaSb = 110;
4169 double ySb = 75;
4170 if (m_portraitMode)
4171 {
4172 phiSb = 260;
4173 xSb = 120;
4174 thetaSb = 100;
4175 ySb = 80;
4176 }
4177
4178 // generate or update non-self blocking
4179 if (channelParams->m_nonSelfBlocking.empty()) // generate new blocking regions
4180 {
4181 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
4182 {
4183 // draw value from table 7.6.4.1-2 Blocking region parameters
4184 DoubleVector table;
4185 table.push_back(m_normalRv->GetValue()); // phi_k: store the normal RV that will be
4186 // mapped to uniform (0,360) later.
4187 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
4188 {
4189 table.push_back(m_uniformRv->GetValue(15, 45)); // x_k
4190 table.push_back(90); // Theta_k
4191 table.push_back(m_uniformRv->GetValue(5, 15)); // y_k
4192 table.push_back(2); // r
4193 }
4194 else
4195 {
4196 table.push_back(m_uniformRv->GetValue(5, 15)); // x_k
4197 table.push_back(90); // Theta_k
4198 table.push_back(5); // y_k
4199 table.push_back(10); // r
4200 }
4201 channelParams->m_nonSelfBlocking.push_back(table);
4202 }
4203 }
4204 else
4205 {
4206 double deltaX = sqrt(pow(channelParams->m_preLocUT.x - channelParams->m_locUT.x, 2) +
4207 pow(channelParams->m_preLocUT.y - channelParams->m_locUT.y, 2));
4208 // if deltaX and speed are both 0, the autocorrelation is 1, skip updating
4209 if (deltaX > 1e-6 || m_blockerSpeed > 1e-6)
4210 {
4211 double corrDis;
4212 // draw value from table 7.6.4.1-4: Spatial correlation distance for different
4213 // m_scenarios.
4214 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
4215 {
4216 // InH, correlation distance = 5;
4217 corrDis = 5;
4218 }
4219 else
4220 {
4221 if (channelParams->m_o2iCondition == ChannelCondition::O2I) // outdoor to indoor
4222 {
4223 corrDis = 5;
4224 }
4225 else // LOS or NLOS
4226 {
4227 corrDis = 10;
4228 }
4229 }
4230 double R;
4231 if (m_blockerSpeed > 1e-6) // speed not equal to 0
4232 {
4233 double corrT = corrDis / m_blockerSpeed;
4234 R = exp(-1 * (deltaX / corrDis +
4235 (Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()) /
4236 corrT));
4237 }
4238 else
4239 {
4240 R = exp(-1 * (deltaX / corrDis));
4241 }
4242
4243 NS_LOG_INFO("Distance change:"
4244 << deltaX << " Speed:" << m_blockerSpeed << " Time difference:"
4245 << Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()
4246 << " correlation:" << R);
4247
4248 // In order to generate correlated uniform random variables, we first generate
4249 // correlated normal random variables and map the normal RV to uniform RV. Notice the
4250 // correlation will change if the RV is transformed from normal to uniform. To
4251 // compensate the distortion, the correlation of the normal RV is computed such that the
4252 // uniform RV would have the desired correlation when transformed from normal RV.
4253
4254 // The following formula was obtained from MATLAB numerical simulation.
4255
4256 if (R * R * (-0.069) + R * 1.074 - 0.002 <
4257 1) // transform only when the correlation of normal RV is smaller than 1
4258 {
4259 R = R * R * (-0.069) + R * 1.074 - 0.002;
4260 }
4261 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
4262 {
4263 // Generate a new correlated normal RV with the following formula
4264 channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] =
4265 R * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] +
4266 sqrt(1 - R * R) * m_normalRv->GetValue();
4267 }
4268 }
4269 }
4270
4271 // step c: Determine the attenuation of each blocker due to blockers
4272 for (std::size_t cInd = 0; cInd < clusterNum; cInd++)
4273 {
4274 NS_ASSERT_MSG(clusterAOA[cInd] >= 0 && clusterAOA[cInd] <= 360,
4275 "the AOA should be the range of [0,360]");
4276 NS_ASSERT_MSG(clusterZOA[cInd] >= 0 && clusterZOA[cInd] <= 180,
4277 "the ZOA should be the range of [0,180]");
4278
4279 // check self blocking
4280 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiSb - xSb / 2.0 << ","
4281 << phiSb + xSb / 2.0 << "]");
4282 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaSb - ySb / 2.0 << ","
4283 << thetaSb + ySb / 2.0 << "]");
4284 if (std::abs(clusterAOA[cInd] - phiSb) < (xSb / 2.0) &&
4285 std::abs(clusterZOA[cInd] - thetaSb) < (ySb / 2.0))
4286 {
4287 powerAttenuation[cInd] += 30; // attenuate by 30 dB.
4288 NS_LOG_INFO("Cluster[" << +cInd
4289 << "] is blocked by self blocking region and reduce 30 dB power,"
4290 "the attenuation is ["
4291 << powerAttenuation[cInd] << " dB]");
4292 }
4293
4294 // check non-self blocking
4295 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
4296 {
4297 // The normal RV is transformed to uniform RV with the desired correlation.
4298 double phiK =
4299 (0.5 * erfc(-1 * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] / sqrt(2))) *
4300 360;
4301 while (phiK > 360)
4302 {
4303 phiK -= 360;
4304 }
4305
4306 while (phiK < 0)
4307 {
4308 phiK += 360;
4309 }
4310
4311 double xK = channelParams->m_nonSelfBlocking[blockInd][X_INDEX];
4312 double thetaK = channelParams->m_nonSelfBlocking[blockInd][THETA_INDEX];
4313 double yK = channelParams->m_nonSelfBlocking[blockInd][Y_INDEX];
4314
4315 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiK - xK << ","
4316 << phiK + xK << "]");
4317 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaK - yK << ","
4318 << thetaK + yK << "]");
4319
4320 if (std::abs(clusterAOA[cInd] - phiK) < (xK) &&
4321 std::abs(clusterZOA[cInd] - thetaK) < (yK))
4322 {
4323 double A1 = clusterAOA[cInd] - (phiK + xK / 2.0); //(7.6-24)
4324 double A2 = clusterAOA[cInd] - (phiK - xK / 2.0); //(7.6-25)
4325 double Z1 = clusterZOA[cInd] - (thetaK + yK / 2.0); //(7.6-26)
4326 double Z2 = clusterZOA[cInd] - (thetaK - yK / 2.0); //(7.6-27)
4327 int signA1;
4328 int signA2;
4329 int signZ1;
4330 int signZ2;
4331 // draw sign for the above parameters according to table 7.6.4.1-3 Description of
4332 // signs
4333 if (xK / 2.0 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
4334 {
4335 signA1 = -1;
4336 }
4337 else
4338 {
4339 signA1 = 1;
4340 }
4341 if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2.0)
4342 {
4343 signA2 = -1;
4344 }
4345 else
4346 {
4347 signA2 = 1;
4348 }
4349
4350 if (yK / 2.0 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
4351 {
4352 signZ1 = -1;
4353 }
4354 else
4355 {
4356 signZ1 = 1;
4357 }
4358 if (-1 * yK < clusterZOA[cInd] - thetaK &&
4359 clusterZOA[cInd] - thetaK <= -1 * yK / 2.0)
4360 {
4361 signZ2 = -1;
4362 }
4363 else
4364 {
4365 signZ2 = 1;
4366 }
4367 double lambda = 3e8 / m_frequency;
4368 double fA1 =
4369 atan(signA1 * M_PI / 2.0 *
4370 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
4371 (1.0 / cos(DegreesToRadians(A1)) - 1))) /
4372 M_PI; //(7.6-23)
4373 double fA2 =
4374 atan(signA2 * M_PI / 2.0 *
4375 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
4376 (1.0 / cos(DegreesToRadians(A2)) - 1))) /
4377 M_PI;
4378 double fZ1 =
4379 atan(signZ1 * M_PI / 2.0 *
4380 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
4381 (1.0 / cos(DegreesToRadians(Z1)) - 1))) /
4382 M_PI;
4383 double fZ2 =
4384 atan(signZ2 * M_PI / 2.0 *
4385 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
4386 (1.0 / cos(DegreesToRadians(Z2)) - 1))) /
4387 M_PI;
4388 double lDb = -20 * log10(1 - (fA1 + fA2) * (fZ1 + fZ2)); //(7.6-22)
4389 powerAttenuation[cInd] += lDb;
4390 NS_LOG_INFO("Cluster[" << +cInd << "] is blocked by no-self blocking, the loss is ["
4391 << lDb << "] dB");
4392 }
4393 }
4394 }
4395 return powerAttenuation;
4396}
4397
4398void
4399ThreeGppChannelModel::Shuffle(double* first, double* last) const
4400{
4401 for (auto i = (last - first) - 1; i > 0; --i)
4402 {
4403 std::swap(first[i], first[m_uniformRvShuffle->GetInteger(0, i)]);
4404 }
4405}
4406
4407int64_t
4409{
4410 NS_LOG_FUNCTION(this << stream);
4411 m_normalRv->SetStream(stream);
4412 m_uniformRv->SetStream(stream + 1);
4413 m_uniformRvShuffle->SetStream(stream + 2);
4414 m_uniformRvDoppler->SetStream(stream + 3);
4415 return 4;
4416}
4417
4418} // namespace ns3
Class holding the azimuth and inclination angles of spherical coordinates.
Definition: angles.h:118
double GetInclination() const
Getter for inclination angle.
Definition: angles.cc:247
double GetAzimuth() const
Getter for azimuth angle.
Definition: angles.cc:241
AttributeValue implementation for Boolean.
Definition: boolean.h:37
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Hold a signed integer type.
Definition: integer.h:45
This is an interface for a channel model that can be described by a channel matrix,...
std::vector< double > DoubleVector
Type definition for vectors of doubles.
ComplexMatrixArray Complex2DVector
Create an alias for 2D complex vectors.
std::vector< DoubleVector > Double2DVector
Type definition for matrices of doubles.
static uint64_t GetKey(uint32_t a, uint32_t b)
Generate a unique value for the pair of unsigned integer of 32 bits, where the order does not matter,...
A network Node.
Definition: node.h:57
uint32_t GetId() const
Definition: node.cc:117
AttributeValue implementation for Pointer.
Definition: pointer.h:48
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Hold variables of type string.
Definition: string.h:56
DoubleVector CalcAttenuationOfBlockage(const Ptr< ThreeGppChannelModel::ThreeGppChannelParams > channelParams, const DoubleVector &clusterAOA, const DoubleVector &clusterZOA) const
Applies the blockage model A described in 3GPP TR 38.901.
int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model.
bool AntennaSetupChanged(Ptr< const PhasedArrayModel > aAntenna, Ptr< const PhasedArrayModel > bAntenna, Ptr< const ChannelMatrix > channelMatrix)
Check if the channel matrix has to be updated due to changes in the number of antenna ports.
bool m_portraitMode
true if portrait mode, false if landscape
bool ChannelParamsNeedsUpdate(Ptr< const ThreeGppChannelParams > channelParams, Ptr< const ChannelCondition > channelCondition) const
Check if the channel params has to be updated.
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
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.
std::string GetScenario() const
Returns the propagation scenario.
virtual Ptr< ChannelMatrix > GetNewChannel(Ptr< const ThreeGppChannelParams > channelParams, Ptr< const ParamsTable > table3gpp, const Ptr< const MobilityModel > sMob, const Ptr< const MobilityModel > uMob, Ptr< const PhasedArrayModel > sAntenna, Ptr< const PhasedArrayModel > uAntenna) const
Compute the channel matrix between two nodes a and b, and their antenna arrays aAntenna and bAntenna ...
static const uint8_t PHI_INDEX
index of the PHI value in the m_nonSelfBlocking array
double m_frequency
the operating frequency
double m_vScatt
value used to compute the additional Doppler contribution for the delayed paths
Ptr< ChannelConditionModel > GetChannelConditionModel() const
Get the associated channel condition model.
Ptr< ChannelConditionModel > m_channelConditionModel
the channel condition model
std::string m_scenario
the 3GPP scenario
static const uint8_t R_INDEX
index of the R value in the m_nonSelfBlocking array
static TypeId GetTypeId()
Get the type ID.
void Shuffle(double *first, double *last) const
Shuffle the elements of a simple sequence container of type double.
Ptr< ThreeGppChannelParams > GenerateChannelParameters(const Ptr< const ChannelCondition > channelCondition, const Ptr< const ParamsTable > table3gpp, const Ptr< const MobilityModel > aMob, const Ptr< const MobilityModel > bMob) const
Prepare 3gpp channel parameters among the nodes a and b.
double GetFrequency() const
Returns the center frequency.
Time m_updatePeriod
the channel update period
static const uint8_t X_INDEX
index of the X value in the m_nonSelfBlocking array
Ptr< UniformRandomVariable > m_uniformRvShuffle
uniform random variable used to shuffle array in GetNewChannel
@ NS
nanosecond
Definition: nstime.h:119
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
AttributeValue implementation for Time.
Definition: nstime.h:1406
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetGroupName(std::string groupName)
Set the group name.
Definition: type-id.cc:940
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
double GetValue(double min, double max)
Get the next random value drawn from the distribution.
uint32_t GetInteger(uint32_t min, uint32_t max)
Get the next random value drawn from the distribution.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Definition: integer.h:46
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Definition: pointer.h:259
Ptr< const AttributeChecker > MakeStringChecker()
Definition: string.cc:30
Ptr< const AttributeAccessor > MakeStringAccessor(T1 a1)
Definition: string.h:57
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1407
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_UNCOND(msg)
Output the requested message unconditionally.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:305
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1331
Definition: first.py:1
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static const std::map< std::string, std::map< int, std::vector< float > > > m_NTNRuralLOS
The nested map containing the threegpp value tables for the NTN Rural LOS scenario.
Table3gppParams
The enumerator used for code clarity when performing parameter assignment in GetThreeGppTable.
static constexpr double DEG2RAD
Conversion factor: degrees to radians.
static const double sqrtC_NTN_Suburban_NLOS[6][6]
The square root matrix for NTN Suburban NLOS, which is generated using the Cholesky decomposition acc...
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 std::map< std::string, std::map< int, std::vector< float > > > m_NTNDenseUrbanLOS
The nested map containing the threegpp value tables for the NTN Dense Urban LOS scenario.
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 std::map< std::string, std::map< int, std::vector< float > > > m_NTNUrbanNLOS
The nested map containing the threegpp value tables for the NTN Urban NLOS scenario.
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 std::map< std::string, std::map< int, std::vector< float > > > m_NTNRuralNLOS
The nested map containing the threegpp value tables for the NTN Rural NLOS scenario.
static const double sqrtC_NTN_DenseUrban_NLOS[6][6]
The square root matrix for NTN Dense Urban NLOS, which is generated using the Cholesky decomposition ...
static const double sqrtC_RMa_NLOS[6][6]
The square root matrix for RMa NLOS, which is generated using the Cholesky decomposition according to...
static const double sqrtC_UMa_LOS[7][7]
The square root matrix for UMa LOS, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_UMi_NLOS[6][6]
The square root matrix for UMi NLOS, which is generated using the Cholesky decomposition according to...
static const std::map< std::string, std::map< int, std::vector< float > > > m_NTNDenseUrbanNLOS
The nested map containing the threegpp value tables for the NTN Dense Urban NLOS scenario.
static const double sqrtC_NTN_Rural_LOS[7][7]
The square root matrix for NTN Rural LOS, which is generated using the Cholesky decomposition accordi...
static const double sqrtC_NTN_Suburban_LOS[7][7]
The square root matrix for NTN Suburban LOS, which is generated using the Cholesky decomposition acco...
static const std::map< std::string, std::map< int, std::vector< float > > > m_NTNUrbanLOS
The nested map containing the threegpp value tables for the NTN Urban LOS scenario.
static const double sqrtC_RMa_LOS[7][7]
The square root matrix for RMa LOS, which is generated using the Cholesky decomposition according to ...
static const std::map< std::string, std::map< int, std::vector< float > > > m_NTNSuburbanNLOS
The nested map containing the threegpp value tables for the NTN Suburban NLOS scenario.
double DegreesToRadians(double degrees)
converts degrees to radians
Definition: angles.cc:39
static const double sqrtC_NTN_Urban_LOS[7][7]
The square root matrix for NTN Urban LOS, which is generated using the Cholesky decomposition accordi...
static const std::map< std::string, std::map< int, std::vector< float > > > m_NTNSuburbanLOS
The nested map containing the threegpp value tables for the NTN Suburban LOS scenario.
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 std::map< int, std::vector< std::vector< double > > > sqrtC_NTN_Rural_NLOS_Ka
The square root matrix for NTN Rural NLOS Ka Band, which is generated using the Cholesky decompositio...
static const std::map< int, std::vector< std::vector< double > > > sqrtC_NTN_Rural_NLOS_S
The square root matrix for NTN Rural NLOS S Band, which is generated using the Cholesky decomposition...
static const double sqrtC_UMa_NLOS[6][6]
The square root matrix for UMa NLOS, which is generated using the Cholesky decomposition according to...
double WrapTo2Pi(double a)
Wrap angle in [0, 2*M_PI)
Definition: angles.cc:117
static const double sqrtC_NTN_DenseUrban_LOS[7][7]
The square root matrix for NTN Dense Urban LOS, which is generated using the Cholesky decomposition a...
static const std::map< int, std::vector< std::vector< double > > > sqrtC_NTN_Urban_NLOS
The square root matrix for NTN Urban NLOS, which is generated using the Cholesky decomposition accord...
double RadiansToDegrees(double radians)
converts radians to degrees
Definition: angles.cc:45