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 3GPP value tables for the NTN Dense Urban LOS scenario.
672 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
673 * the quantized elevation angle.
674 * The inner vector collects the table3gpp values.
675 */
676static const std::map<std::string, std::map<int, std::vector<float>>> NTNDenseUrbanLOS{
677 {"S",
678 {
679 {10, {-7.12, 0.8, -3.06, 0.48, 0.94, 0.7, 0.82, 0.03, -2.52, 0.5, 4.4,
680 3.3, 2.5, 24.4, 3.8, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
681 {20, {-7.28, 0.67, -2.68, 0.36, 0.87, 0.66, 0.5, 0.09, -2.29, 0.53, 9.0,
682 6.6, 2.5, 23.6, 4.7, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
683 {30, {-7.45, 0.68, -2.51, 0.38, 0.92, 0.68, 0.82, 0.05, -2.19, 0.58, 9.3,
684 6.1, 2.5, 23.2, 4.6, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
685 {40, {-7.73, 0.66, -2.4, 0.32, 0.79, 0.64, 1.23, 0.03, -2.24, 0.51, 7.9,
686 4.0, 2.5, 22.6, 4.9, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
687 {50, {-7.91, 0.62, -2.31, 0.33, 0.72, 0.63, 1.43, 0.06, -2.3, 0.46, 7.4,
688 3.0, 2.5, 21.8, 5.7, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
689 {60, {-8.14, 0.51, -2.2, 0.39, 0.6, 0.54, 1.56, 0.05, -2.48, 0.35, 7.0,
690 2.6, 2.5, 20.5, 6.9, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
691 {70, {-8.23, 0.45, -2.0, 0.4, 0.55, 0.52, 1.66, 0.05, -2.64, 0.31, 6.9,
692 2.2, 2.5, 19.3, 8.1, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
693 {80, {-8.28, 0.31, -1.64, 0.32, 0.71, 0.53, 1.73, 0.02, -2.68, 0.39, 6.5,
694 2.1, 2.5, 17.4, 10.3, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
695 {90, {-8.36, 0.08, -0.63, 0.53, 0.81, 0.62, 1.79, 0.01, -2.61, 0.28, 6.8,
696 1.9, 2.5, 12.3, 15.2, 3.0, 20.0, 3.9, 0.0, 11.0, 7.0, 3.0}},
697 }},
698 {"Ka",
699 {
700 {10, {-7.43, 0.9, -3.43, 0.54, 0.65, 0.82, 0.82, 0.05, -2.75, 0.55, 6.1,
701 2.6, 2.5, 24.7, 2.1, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
702 {20, {-7.62, 0.78, -3.06, 0.41, 0.53, 0.78, 0.47, 0.11, -2.64, 0.64, 13.7,
703 6.8, 2.5, 24.4, 2.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
704 {30, {-7.76, 0.8, -2.91, 0.42, 0.6, 0.83, 0.8, 0.05, -2.49, 0.69, 12.9,
705 6.0, 2.5, 24.4, 2.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
706 {40, {-8.02, 0.72, -2.81, 0.34, 0.43, 0.78, 1.23, 0.04, -2.51, 0.57, 10.3,
707 3.3, 2.5, 24.2, 2.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
708 {50, {-8.13, 0.61, -2.74, 0.34, 0.36, 0.77, 1.42, 0.1, -2.54, 0.5, 9.2,
709 2.2, 2.5, 23.9, 3.1, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
710 {60, {-8.3, 0.47, -2.72, 0.7, 0.16, 0.84, 1.56, 0.06, -2.71, 0.37, 8.4,
711 1.9, 2.5, 23.3, 3.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
712 {70, {-8.34, 0.39, -2.46, 0.4, 0.18, 0.64, 1.65, 0.07, -2.85, 0.31, 8.0,
713 1.5, 2.5, 22.6, 4.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
714 {80, {-8.39, 0.26, -2.3, 0.78, 0.24, 0.81, 1.73, 0.02, -3.01, 0.45, 7.4,
715 1.6, 2.5, 21.2, 6.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
716 {90, {-8.45, 0.01, -1.11, 0.51, 0.36, 0.65, 1.79, 0.01, -3.08, 0.27, 7.6,
717 1.3, 2.5, 17.6, 12.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
718 }},
719};
720
721/**
722 * The nested map containing the 3GPP value tables for the NTN Dense Urban NLOS scenario.
723 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
724 * the quantized elevation angle.
725 * The inner vector collects the table3gpp values.
726 */
727static const std::map<std::string, std::map<int, std::vector<float>>> NTNDenseUrbanNLOS{
728 {"S",
729 {
730 {10, {-6.84, 0.82, -2.08, 0.87, 1.0, 1.6, 1.0, 0.63, -2.08, 0.58,
731 2.3, 23.8, 4.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
732 {20, {-6.81, 0.61, -1.68, 0.73, 1.44, 0.87, 0.94, 0.65, -1.66, 0.5,
733 2.3, 21.9, 6.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
734 {30, {-6.94, 0.49, -1.46, 0.53, 1.54, 0.64, 1.15, 0.42, -1.48, 0.4,
735 2.3, 19.7, 8.1, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
736 {40, {-7.14, 0.49, -1.43, 0.5, 1.53, 0.56, 1.35, 0.28, -1.46, 0.37,
737 2.3, 18.1, 9.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
738 {50, {-7.34, 0.51, -1.44, 0.58, 1.48, 0.54, 1.44, 0.25, -1.53, 0.47,
739 2.3, 16.3, 11.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
740 {60, {-7.53, 0.47, -1.33, 0.49, 1.39, 0.68, 1.56, 0.16, -1.61, 0.43,
741 2.3, 14.0, 13.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
742 {70, {-7.67, 0.44, -1.31, 0.65, 1.42, 0.55, 1.64, 0.18, -1.77, 0.5,
743 2.3, 12.1, 14.9, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
744 {80, {-7.82, 0.42, -1.11, 0.69, 1.38, 0.6, 1.7, 0.09, -1.9, 0.42,
745 2.3, 8.7, 17.0, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
746 {90, {-7.84, 0.55, -0.11, 0.53, 1.23, 0.6, 1.7, 0.17, -1.99, 0.5,
747 2.3, 6.4, 12.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
748 }},
749 {"Ka",
750 {
751 {10, {-6.86, 0.81, -2.12, 0.94, 1.02, 1.44, 1.01, 0.56, -2.11, 0.59,
752 2.3, 23.7, 4.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
753 {20, {-6.84, 0.61, -1.74, 0.79, 1.44, 0.77, 0.96, 0.55, -1.69, 0.51,
754 2.3, 21.8, 6.3, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
755 {30, {-7.0, 0.56, -1.56, 0.66, 1.48, 0.7, 1.13, 0.43, -1.52, 0.46,
756 2.3, 19.6, 8.2, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
757 {40, {-7.21, 0.56, -1.54, 0.63, 1.46, 0.6, 1.3, 0.37, -1.51, 0.43,
758 2.3, 18.0, 9.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
759 {50, {-7.42, 0.57, -1.45, 0.56, 1.4, 0.59, 1.4, 0.32, -1.54, 0.45,
760 2.3, 16.3, 11.5, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
761 {60, {-7.86, 0.55, -1.64, 0.78, 0.97, 1.27, 1.41, 0.45, -1.84, 0.63,
762 2.3, 15.9, 12.4, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
763 {70, {-7.76, 0.47, -1.37, 0.56, 1.33, 0.56, 1.63, 0.17, -1.86, 0.51,
764 2.3, 12.3, 15.0, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
765 {80, {-8.07, 0.42, -1.29, 0.76, 1.12, 1.04, 1.68, 0.14, -2.16, 0.74,
766 2.3, 10.5, 15.7, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
767 {90, {-7.95, 0.59, -0.41, 0.59, 1.04, 0.63, 1.7, 0.17, -2.21, 0.61,
768 2.3, 10.5, 15.7, 4.0, 20.0, 3.9, 0.0, 15.0, 7.0, 3.0}},
769 }},
770};
771
772/**
773 * The nested map containing the 3GPP value tables for the NTN Urban LOS scenario.
774 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
775 * the quantized elevation angle.
776 * The inner vector collects the table3gpp values.
777 */
778static const std::map<std::string, std::map<int, std::vector<float>>> NTNUrbanLOS{
779 {"S",
780 {
781 {10, {-7.97, 1.0, -2.6, 0.79, 0.18, 0.74, -0.63, 2.6, -2.54, 2.62, 31.83,
782 13.84, 2.5, 8.0, 4.0, 4.0, 20.0, 3.9, 0.09, 12.55, 1.25, 3.0}},
783 {20, {-8.12, 0.83, -2.48, 0.8, 0.42, 0.9, -0.15, 3.31, -2.67, 2.96, 18.78,
784 13.78, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.09, 12.76, 3.23, 3.0}},
785 {30, {-8.21, 0.68, -2.44, 0.91, 0.41, 1.3, 0.54, 1.1, -2.03, 0.86, 10.49,
786 10.42, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.12, 14.36, 4.39, 3.0}},
787 {40, {-8.31, 0.48, -2.6, 1.02, 0.18, 1.69, 0.35, 1.59, -2.28, 1.19, 7.46,
788 8.01, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.16, 16.42, 5.72, 3.0}},
789 {50, {-8.37, 0.38, -2.71, 1.17, -0.07, 2.04, 0.27, 1.62, -2.48, 1.4, 6.52,
790 8.27, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.2, 17.13, 6.17, 3.0}},
791 {60, {-8.39, 0.24, -2.76, 1.17, -0.43, 2.54, 0.26, 0.97, -2.56, 0.85, 5.47,
792 7.26, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.28, 19.01, 7.36, 3.0}},
793 {70, {-8.38, 0.18, -2.78, 1.2, -0.64, 2.47, -0.12, 1.99, -2.96, 1.61, 4.54,
794 5.53, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.44, 19.31, 7.3, 3.0}},
795 {80, {-8.35, 0.13, -2.65, 1.45, -0.91, 2.69, -0.21, 1.82, -3.08, 1.49, 4.03,
796 4.49, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 0.9, 22.39, 7.7, 3.0}},
797 {90, {-8.34, 0.09, -2.27, 1.85, -0.54, 1.66, -0.07, 1.43, -3.0, 1.09, 3.68,
798 3.14, 2.5, 8.0, 4.0, 3.0, 20.0, 3.9, 2.87, 27.8, 9.25, 3.0}},
799 }},
800 {"Ka",
801 {
802 {10, {-8.52, 0.92, -3.18, 0.79, -0.4, 0.77, -0.67, 2.22, -2.61, 2.41, 40.18,
803 16.99, 2.5, 8.0, 4.0, 4.0, 20.0, 1.6, 0.09, 11.8, 1.14, 3.0}},
804 {20, {-8.59, 0.79, -3.05, 0.87, -0.15, 0.97, -0.34, 3.04, -2.82, 2.59, 23.62,
805 18.96, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.09, 11.6, 2.78, 3.0}},
806 {30, {-8.51, 0.65, -2.98, 1.04, -0.18, 1.58, 0.07, 1.33, -2.48, 1.02, 12.48,
807 14.23, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.11, 13.05, 3.87, 3.0}},
808 {40, {-8.49, 0.48, -3.11, 1.06, -0.31, 1.69, -0.08, 1.45, -2.76, 1.27, 8.56,
809 11.06, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.15, 14.56, 4.94, 3.0}},
810 {50, {-8.48, 0.46, -3.19, 1.12, -0.58, 2.13, -0.21, 1.62, -2.93, 1.38, 7.42,
811 11.21, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.18, 15.35, 5.41, 3.0}},
812 {60, {-8.44, 0.34, -3.25, 1.14, -0.9, 2.51, -0.25, 1.06, -3.05, 0.96, 5.97,
813 9.47, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.27, 16.97, 6.31, 3.0}},
814 {70, {-8.4, 0.27, -3.33, 1.25, -1.16, 2.47, -0.61, 1.88, -3.45, 1.51, 4.88,
815 7.24, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.42, 17.96, 6.66, 3.0}},
816 {80, {-8.37, 0.19, -3.22, 1.35, -1.48, 2.61, -0.79, 1.87, -3.66, 1.49, 4.22,
817 5.79, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 0.86, 20.68, 7.31, 3.0}},
818 {90, {-8.35, 0.14, -2.83, 1.62, -1.14, 1.7, -0.58, 1.19, -3.56, 0.89, 3.81,
819 4.25, 2.5, 8.0, 4.0, 3.0, 20.0, 1.6, 2.55, 25.08, 9.23, 3.0}},
820 }},
821};
822
823/**
824 * The nested map containing the 3GPP value tables for the NTN Urban NLOS scenario.
825 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
826 * the quantized elevation angle.
827 * The inner vector collects the table3gpp values.
828 */
829static const std::map<std::string, std::map<int, std::vector<float>>> NTNUrbanNLOS{
830 {"S",
831 {
832 {10, {-7.24, 1.26, -1.58, 0.89, 0.13, 2.99, -1.13, 2.66, -2.87, 2.76, 0.0,
833 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.08, 14.72, 1.57, 3.0}},
834 {20, {-7.7, 0.99, -1.67, 0.89, 0.19, 3.12, 0.49, 2.03, -2.68, 2.76, 0.0,
835 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.1, 14.62, 4.3, 3.0}},
836 {30, {-7.82, 0.86, -1.84, 1.3, 0.44, 2.69, 0.95, 1.54, -2.12, 1.54, 0.0,
837 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.14, 16.4, 6.64, 3.0}},
838 {40, {-8.04, 0.75, -2.02, 1.15, 0.48, 2.45, 1.15, 1.02, -2.27, 1.77, 0.0,
839 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.22, 17.86, 9.21, 3.0}},
840 {50, {-8.08, 0.77, -2.06, 1.23, 0.56, 2.17, 1.14, 1.61, -2.5, 2.36, 0.0,
841 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.31, 19.74, 10.32, 3.0}},
842 {60, {-8.1, 0.76, -1.99, 1.02, 0.55, 1.93, 1.13, 1.84, -2.47, 2.33, 0.0,
843 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.49, 19.73, 10.3, 3.0}},
844 {70, {-8.16, 0.73, -2.19, 1.78, 0.48, 1.72, 1.16, 1.81, -2.83, 2.84, 0.0,
845 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 0.97, 20.5, 10.2, 3.0}},
846 {80, {-8.03, 0.79, -1.88, 1.55, 0.53, 1.51, 1.28, 1.35, -2.82, 2.87, 0.0,
847 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 1.52, 26.16, 12.27, 3.0}},
848 {90, {-8.33, 0.7, -2.0, 1.4, 0.32, 1.2, 1.42, 0.6, -4.55, 4.27, 0.0,
849 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 5.36, 25.83, 12.75, 3.0}},
850 }},
851 {"Ka",
852 {
853 {10, {-7.24, 1.26, -1.58, 0.89, 0.13, 2.99, -1.13, 2.66, -2.87, 2.76, 0.0,
854 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.08, 14.72, 1.57, 3.0}},
855 {20, {-7.7, 0.99, -1.67, 0.89, 0.19, 3.12, 0.49, 2.03, -2.68, 2.76, 0.0,
856 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.1, 14.62, 4.3, 3.0}},
857 {30, {-7.82, 0.86, -1.84, 1.3, 0.44, 2.69, 0.95, 1.54, -2.12, 1.54, 0.0,
858 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.14, 16.4, 6.64, 3.0}},
859 {40, {-8.04, 0.75, -2.02, 1.15, 0.48, 2.45, 1.15, 1.02, -2.27, 1.77, 0.0,
860 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.22, 17.86, 9.21, 3.0}},
861 {50, {-8.08, 0.77, -2.06, 1.23, 0.56, 2.17, 1.14, 1.61, -2.5, 2.36, 0.0,
862 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.31, 19.74, 10.32, 3.0}},
863 {60, {-8.1, 0.76, -1.99, 1.02, 0.55, 1.93, 1.13, 1.84, -2.47, 2.33, 0.0,
864 0.0, 2.3, 7.0, 3.0, 3.0, 20.0, 1.6, 0.49, 19.73, 10.3, 3.0}},
865 {70, {-8.16, 0.73, -2.19, 1.78, 0.48, 1.72, 1.16, 1.81, -2.83, 2.84, 0.0,
866 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 0.97, 20.5, 10.2, 3.0}},
867 {80, {-8.03, 0.79, -1.88, 1.55, 0.53, 1.51, 1.28, 1.35, -2.82, 2.87, 0.0,
868 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 1.52, 26.16, 12.27, 3.0}},
869 {90, {-8.33, 0.7, -2.0, 1.4, 0.32, 1.2, 1.42, 0.6, -4.55, 4.27, 0.0,
870 0.0, 2.3, 7.0, 3.0, 2.0, 20.0, 1.6, 5.36, 25.83, 12.75, 3.0}},
871 }},
872};
873
874/**
875 * The nested map containing the 3GPP value tables for the NTN Suburban LOS scenario.
876 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
877 * the quantized elevation angle.
878 * The inner vector collects the table3gpp values.
879 */
880static const std::map<std::string, std::map<int, std::vector<float>>> NTNSuburbanLOS{
881 {"S",
882 {
883 {10, {-8.16, 0.99, -3.57, 1.62, 0.05, 1.84, -1.78, 0.62, -1.06, 0.96, 11.4,
884 6.26, 2.2, 21.3, 7.6, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
885 {20, {-8.56, 0.96, -3.8, 1.74, -0.38, 1.94, -1.84, 0.81, -1.21, 0.95, 19.45,
886 10.32, 3.36, 21.0, 8.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
887 {30, {-8.72, 0.79, -3.77, 1.72, -0.56, 1.75, -1.67, 0.57, -1.28, 0.49, 20.8,
888 16.34, 3.5, 21.2, 8.5, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
889 {40, {-8.71, 0.81, -3.57, 1.6, -0.59, 1.82, -1.59, 0.86, -1.32, 0.79, 21.2,
890 15.63, 2.81, 21.1, 8.4, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
891 {50, {-8.72, 1.12, -3.42, 1.49, -0.58, 1.87, -1.55, 1.05, -1.39, 0.97, 21.6,
892 14.22, 2.39, 20.7, 9.2, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
893 {60, {-8.66, 1.23, -3.27, 1.43, -0.55, 1.92, -1.51, 1.23, -1.36, 1.17, 19.75,
894 14.19, 2.73, 20.6, 9.8, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
895 {70, {-8.38, 0.55, -3.08, 1.36, -0.28, 1.16, -1.27, 0.54, -1.08, 0.62, 12.0,
896 5.7, 2.07, 20.3, 10.8, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
897 {80, {-8.34, 0.63, -2.75, 1.26, -0.17, 1.09, -1.28, 0.67, -1.31, 0.76, 12.85,
898 9.91, 2.04, 19.8, 12.2, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
899 {90, {-8.34, 0.63, -2.75, 1.26, -0.17, 1.09, -1.28, 0.67, -1.31, 0.76, 12.85,
900 9.91, 2.04, 19.1, 13.0, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
901 }},
902 {"Ka",
903 {
904 {10, {-8.07, 0.46, -3.55, 0.48, 0.89, 0.67, 0.63, 0.35, -3.37, 0.28, 8.9,
905 4.4, 2.5, 23.2, 5.0, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
906 {20, {-8.61, 0.45, -3.69, 0.41, 0.31, 0.78, 0.76, 0.3, -3.28, 0.27, 14.0,
907 4.6, 2.5, 23.6, 4.5, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
908 {30, {-8.72, 0.28, -3.59, 0.41, 0.02, 0.75, 1.11, 0.28, -3.04, 0.26, 11.3,
909 3.7, 2.5, 23.5, 4.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
910 {40, {-8.63, 0.17, -3.38, 0.35, -0.1, 0.65, 1.37, 0.23, -2.88, 0.21, 9.0,
911 3.5, 2.5, 23.4, 5.2, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
912 {50, {-8.54, 0.14, -3.23, 0.35, -0.19, 0.55, 1.53, 0.23, -2.83, 0.18, 7.5,
913 3.0, 2.5, 23.2, 5.7, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
914 {60, {-8.48, 0.15, -3.19, 0.43, -0.54, 0.96, 1.65, 0.17, -2.86, 0.17, 6.6,
915 2.6, 2.5, 23.3, 5.9, 3.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
916 {70, {-8.42, 0.09, -2.83, 0.33, -0.24, 0.43, 1.74, 0.11, -2.95, 0.1, 5.9,
917 1.7, 2.5, 23.4, 6.2, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
918 {80, {-8.39, 0.05, -2.66, 0.44, -0.52, 0.93, 1.82, 0.05, -3.21, 0.07, 5.5,
919 0.7, 2.5, 23.2, 7.0, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
920 {90, {-8.37, 0.02, -1.22, 0.31, -0.15, 0.44, 1.87, 0.02, -3.49, 0.24, 5.4,
921 0.3, 2.5, 23.1, 7.6, 2.0, 20.0, 1.6, 0.0, 11.0, 7.0, 3.0}},
922 }},
923};
924
925/**
926 * The nested map containing the 3GPP value tables for the NTN Suburban NLOS scenario.
927 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
928 * the quantized elevation angle.
929 * The inner vector collects the table3gpp values.
930 */
931static const std::map<std::string, std::map<int, std::vector<float>>> NTNSuburbanNLOS{
932 {"S",
933 {
934 {10, {-7.43, 0.5, -2.89, 0.41, 1.49, 0.4, 0.81, 0.36, -3.09, 0.32, 0.0,
935 0.0, 2.3, 22.5, 5.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
936 {20, {-7.63, 0.61, -2.76, 0.41, 1.24, 0.82, 1.06, 0.41, -2.93, 0.47, 0.0,
937 0.0, 2.3, 19.4, 8.5, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
938 {30, {-7.86, 0.56, -2.64, 0.41, 1.06, 0.71, 1.12, 0.4, -2.91, 0.46, 0.0,
939 0.0, 2.3, 15.5, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
940 {40, {-7.96, 0.58, -2.41, 0.52, 0.91, 0.55, 1.14, 0.39, -2.78, 0.54, 0.0,
941 0.0, 2.3, 13.9, 10.6, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
942 {50, {-7.98, 0.59, -2.42, 0.7, 0.98, 0.58, 1.29, 0.35, -2.7, 0.45, 0.0,
943 0.0, 2.3, 11.7, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
944 {60, {-8.45, 0.47, -2.53, 0.5, 0.49, 1.37, 1.38, 0.36, -3.03, 0.36, 0.0,
945 0.0, 2.3, 9.8, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
946 {70, {-8.21, 0.36, -2.35, 0.58, 0.73, 0.49, 1.36, 0.29, -2.9, 0.42, 0.0,
947 0.0, 2.3, 10.3, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
948 {80, {-8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
949 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
950 {90, {-8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
951 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
952 }},
953 {"Ka",
954 {
955 {10, {-7.43, 0.5, -2.89, 0.41, 1.49, 0.4, 0.81, 0.36, -3.09, 0.32, 0.0,
956 0.0, 2.3, 22.5, 5.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
957 {20, {-7.63, 0.61, -2.76, 0.41, 1.24, 0.82, 1.06, 0.41, -2.93, 0.47, 0.0,
958 0.0, 2.3, 19.4, 8.5, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
959 {30, {-7.86, 0.56, -2.64, 0.41, 1.06, 0.71, 1.12, 0.4, -2.91, 0.46, 0.0,
960 0.0, 2.3, 15.5, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
961 {40, {-7.96, 0.58, -2.41, 0.52, 0.91, 0.55, 1.14, 0.39, -2.78, 0.54, 0.0,
962 0.0, 2.3, 13.9, 10.6, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
963 {50, {-7.98, 0.59, -2.42, 0.7, 0.98, 0.58, 1.29, 0.35, -2.7, 0.45, 0.0,
964 0.0, 2.3, 11.7, 10.0, 4.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
965 {60, {-8.45, 0.47, -2.53, 0.5, 0.49, 1.37, 1.38, 0.36, -3.03, 0.36, 0.0,
966 0.0, 2.3, 9.8, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
967 {70, {-8.21, 0.36, -2.35, 0.58, 0.73, 0.49, 1.36, 0.29, -2.9, 0.42, 0.0,
968 0.0, 2.3, 10.3, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
969 {80, {-8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
970 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
971 {90, {-8.69, 0.29, -2.31, 0.73, -0.04, 1.48, 1.38, 0.2, -3.2, 0.3, 0.0,
972 0.0, 2.3, 15.6, 9.1, 3.0, 20.0, 1.6, 0.0, 15.0, 7.0, 3.0}},
973 }},
974};
975
976/**
977 * The nested map containing the 3GPP value tables for the NTN Rural LOS scenario.
978 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
979 * the quantized elevation angle.
980 * The inner vector collects the table3gpp values.
981 */
982static const std::map<std::string, std::map<int, std::vector<float>>> NTNRuralLOS{
983 {"S",
984 {
985 {10, {-9.55, 0.66, -3.42, 0.89, -9.45, 7.83, -4.2, 6.3, -6.03, 5.19, 24.72,
986 5.07, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.39, 10.81, 1.94, 3.0}},
987 {20, {-8.68, 0.44, -3.0, 0.63, -4.45, 6.86, -2.31, 5.04, -4.31, 4.18, 12.31,
988 5.75, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.31, 8.09, 1.83, 3.0}},
989 {30, {-8.46, 0.28, -2.86, 0.52, -2.39, 5.14, -0.28, 0.81, -2.57, 0.61, 8.05,
990 5.46, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.29, 13.7, 2.28, 3.0}},
991 {40, {-8.36, 0.19, -2.78, 0.45, -1.28, 3.44, -0.38, 1.16, -2.59, 0.79, 6.21,
992 5.23, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.37, 20.05, 2.93, 3.0}},
993 {50, {-8.29, 0.14, -2.7, 0.42, -0.99, 2.59, -0.38, 0.82, -2.59, 0.65, 5.04,
994 3.95, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.61, 24.51, 2.84, 3.0}},
995 {60, {-8.26, 0.1, -2.66, 0.41, -1.05, 2.42, -0.46, 0.67, -2.65, 0.52, 4.42,
996 3.75, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.9, 26.35, 3.17, 3.0}},
997 {70, {-8.22, 0.1, -2.53, 0.42, -0.9, 1.78, -0.49, 1.0, -2.69, 0.78, 3.92,
998 2.56, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 1.43, 31.84, 3.88, 3.0}},
999 {80, {-8.2, 0.05, -2.21, 0.5, -0.89, 1.65, -0.53, 1.18, -2.65, 1.01, 3.65,
1000 1.77, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 2.87, 36.62, 4.17, 3.0}},
1001 {90, {-8.19, 0.06, -1.78, 0.91, -0.81, 1.26, -0.46, 0.91, -2.65, 0.71, 3.59,
1002 1.77, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 5.48, 36.77, 4.29, 3.0}},
1003 }},
1004 {"Ka",
1005 {
1006 {10, {-9.68, 0.46, -4.03, 0.91, -9.74, 7.52, -5.85, 6.51, -7.45, 5.3, 25.43,
1007 7.04, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.36, 4.63, 0.75, 3.0}},
1008 {20, {-8.86, 0.29, -3.55, 0.7, -4.88, 6.67, -3.27, 5.36, -5.25, 4.42, 12.72,
1009 7.47, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.3, 6.83, 1.25, 3.0}},
1010 {30, {-8.59, 0.18, -3.45, 0.55, -2.6, 4.63, -0.88, 0.93, -3.16, 0.68, 8.4,
1011 7.18, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.25, 12.91, 1.93, 3.0}},
1012 {40, {-8.46, 0.19, -3.38, 0.52, -1.92, 3.45, -0.93, 0.96, -3.15, 0.73, 6.52,
1013 6.88, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.35, 18.9, 2.37, 3.0}},
1014 {50, {-8.36, 0.14, -3.33, 0.46, -1.56, 2.44, -0.99, 0.97, -3.2, 0.77, 5.24,
1015 5.28, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.53, 22.44, 2.66, 3.0}},
1016 {60, {-8.3, 0.15, -3.29, 0.43, -1.66, 2.38, -1.04, 0.83, -3.27, 0.61, 4.57,
1017 4.92, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 0.88, 25.69, 3.23, 3.0}},
1018 {70, {-8.26, 0.13, -3.24, 0.46, -1.59, 1.67, -1.17, 1.01, -3.42, 0.74, 4.02,
1019 3.4, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 1.39, 27.95, 3.71, 3.0}},
1020 {80, {-8.22, 0.03, -2.9, 0.44, -1.58, 1.44, -1.19, 1.01, -3.36, 0.79, 3.7,
1021 2.22, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 2.7, 31.45, 4.17, 3.0}},
1022 {90, {-8.21, 0.07, -2.5, 0.82, -1.51, 1.13, -1.13, 0.85, -3.35, 0.65, 3.62,
1023 2.28, 3.8, 12.0, 4.0, 2.0, 20.0, 0.0, 4.97, 28.01, 4.14, 3.0}},
1024 }},
1025};
1026
1027/**
1028 * The nested map containing the 3GPP value tables for the NTN Rural NLOS scenario
1029 * The outer key specifies the band (either "S" or "Ka"), while the inner key represents
1030 * the quantized elevation angle.
1031 * The inner vector collects the table3gpp values.
1032 */
1033static const std::map<std::string, std::map<int, std::vector<float>>> NTNRuralNLOS{
1034 {"S",
1035 {
1036 {10, {-9.01, 1.59, -2.9, 1.34, -3.33, 6.22, -0.88, 3.26, -4.92, 3.96, 0.0,
1037 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.03, 18.16, 2.32, 3.0}},
1038 {20, {-8.37, 0.95, -2.5, 1.18, -0.74, 4.22, -0.07, 3.29, -4.06, 4.07, 0.0,
1039 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.05, 26.82, 7.34, 3.0}},
1040 {30, {-8.05, 0.92, -2.12, 1.08, 0.08, 3.02, 0.75, 1.92, -2.33, 1.7, 0.0,
1041 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.07, 21.99, 8.28, 3.0}},
1042 {40, {-7.92, 0.92, -1.99, 1.06, 0.32, 2.45, 0.72, 1.92, -2.24, 2.01, 0.0,
1043 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.1, 22.86, 8.76, 3.0}},
1044 {50, {-7.92, 0.87, -1.9, 1.05, 0.53, 1.63, 0.95, 1.45, -2.24, 2.0, 0.0,
1045 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.15, 25.93, 9.68, 3.0}},
1046 {60, {-7.96, 0.87, -1.85, 1.06, 0.33, 2.08, 0.97, 1.62, -2.22, 1.82, 0.0,
1047 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.22, 27.79, 9.94, 3.0}},
1048 {70, {-7.91, 0.82, -1.69, 1.14, 0.55, 1.58, 1.1, 1.43, -2.19, 1.66, 0.0,
1049 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.5, 28.5, 8.9, 3.0}},
1050 {80, {-7.79, 0.86, -1.46, 1.16, 0.45, 2.01, 0.97, 1.88, -2.41, 2.58, 0.0,
1051 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 1.04, 37.53, 13.74, 3.0}},
1052 {90, {-7.74, 0.81, -1.32, 1.3, 0.4, 2.19, 1.35, 0.62, -2.45, 2.52, 0.0,
1053 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 2.11, 29.23, 12.16, 3.0}},
1054 }},
1055 {"Ka",
1056 {
1057 {10, {-9.13, 1.91, -2.9, 1.32, -3.4, 6.28, -1.19, 3.81, -5.47, 4.39, 0.0,
1058 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.03, 18.21, 2.13, 3.0}},
1059 {20, {-8.39, 0.94, -2.53, 1.18, -0.51, 3.75, -0.11, 3.33, -4.06, 4.04, 0.0,
1060 0.0, 1.7, 7.0, 3.0, 3.0, 20.0, 0.0, 0.05, 24.08, 6.52, 3.0}},
1061 {30, {-8.1, 0.92, -2.16, 1.08, 0.06, 2.95, 0.72, 1.93, -2.32, 1.54, 0.0,
1062 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.07, 22.06, 7.72, 3.0}},
1063 {40, {-7.96, 0.94, -2.04, 1.09, 0.2, 2.65, 0.69, 1.91, -2.19, 1.73, 0.0,
1064 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.09, 21.4, 8.45, 3.0}},
1065 {50, {-7.99, 0.89, -1.99, 1.08, 0.4, 1.85, 0.84, 1.7, -2.16, 1.5, 0.0,
1066 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.16, 24.26, 8.92, 3.0}},
1067 {60, {-8.05, 0.87, -1.95, 1.06, 0.32, 1.83, 0.99, 1.27, -2.24, 1.64, 0.0,
1068 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.22, 24.15, 8.76, 3.0}},
1069 {70, {-8.01, 0.82, -1.81, 1.17, 0.46, 1.57, 0.95, 1.86, -2.29, 1.66, 0.0,
1070 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.51, 25.99, 9.0, 3.0}},
1071 {80, {-8.05, 1.65, -1.56, 1.2, 0.33, 1.99, 0.92, 1.84, -2.65, 2.86, 0.0,
1072 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 0.89, 36.07, 13.6, 3.0}},
1073 {90, {-7.91, 0.76, -1.53, 1.27, 0.24, 2.18, 1.29, 0.59, -2.23, 1.12, 0.0,
1074 0.0, 1.7, 7.0, 3.0, 2.0, 20.0, 0.0, 1.68, 24.51, 10.56, 3.0}},
1075 }},
1076};
1077
1079{
1080 NS_LOG_FUNCTION(this);
1081 m_uniformRv = CreateObject<UniformRandomVariable>();
1082 m_uniformRvShuffle = CreateObject<UniformRandomVariable>();
1083 m_uniformRvDoppler = CreateObject<UniformRandomVariable>();
1084
1085 m_normalRv = CreateObject<NormalRandomVariable>();
1086 m_normalRv->SetAttribute("Mean", DoubleValue(0.0));
1087 m_normalRv->SetAttribute("Variance", DoubleValue(1.0));
1088}
1089
1091{
1092 NS_LOG_FUNCTION(this);
1093}
1094
1095void
1097{
1098 NS_LOG_FUNCTION(this);
1100 {
1101 m_channelConditionModel->Dispose();
1102 }
1103 m_channelMatrixMap.clear();
1104 m_channelParamsMap.clear();
1105 m_channelConditionModel = nullptr;
1106}
1107
1108TypeId
1110{
1111 static TypeId tid =
1112 TypeId("ns3::ThreeGppChannelModel")
1113 .SetGroupName("Spectrum")
1115 .AddConstructor<ThreeGppChannelModel>()
1116 .AddAttribute("Frequency",
1117 "The operating Frequency in Hz",
1118 DoubleValue(500.0e6),
1121 MakeDoubleChecker<double>())
1122 .AddAttribute(
1123 "Scenario",
1124 "The 3GPP scenario (RMa, UMa, UMi-StreetCanyon, InH-OfficeOpen, InH-OfficeMixed, "
1125 "NTN-DenseUrban, NTN-Urban, NTN-Suburban, NTN-Rural)",
1126 StringValue("UMa"),
1130 .AddAttribute("ChannelConditionModel",
1131 "Pointer to the channel condition model",
1132 PointerValue(),
1135 MakePointerChecker<ChannelConditionModel>())
1136 .AddAttribute("UpdatePeriod",
1137 "Specify the channel coherence time",
1141 // attributes for the blockage model
1142 .AddAttribute("Blockage",
1143 "Enable blockage model A (sec 7.6.4.1)",
1144 BooleanValue(false),
1147 .AddAttribute("NumNonselfBlocking",
1148 "number of non-self-blocking regions",
1149 IntegerValue(4),
1151 MakeIntegerChecker<uint16_t>())
1152 .AddAttribute("PortraitMode",
1153 "true for portrait mode, false for landscape mode",
1154 BooleanValue(true),
1157 .AddAttribute("BlockerSpeed",
1158 "The speed of moving blockers, the unit is m/s",
1159 DoubleValue(1),
1161 MakeDoubleChecker<double>())
1162 .AddAttribute("vScatt",
1163 "Maximum speed of the vehicle in the layout (see 3GPP TR 37.885 v15.3.0, "
1164 "Sec. 6.2.3)."
1165 "Used to compute the additional contribution for the Doppler of"
1166 "delayed (reflected) paths",
1167 DoubleValue(0.0),
1169 MakeDoubleChecker<double>(0.0))
1170
1171 ;
1172 return tid;
1173}
1174
1175void
1177{
1178 NS_LOG_FUNCTION(this);
1180}
1181
1184{
1185 NS_LOG_FUNCTION(this);
1187}
1188
1189void
1191{
1192 NS_LOG_FUNCTION(this);
1193 NS_ASSERT_MSG(f >= 500.0e6 && f <= 100.0e9,
1194 "Frequency should be between 0.5 and 100 GHz but is " << f);
1195 m_frequency = f;
1196}
1197
1198double
1200{
1201 NS_LOG_FUNCTION(this);
1202 return m_frequency;
1203}
1204
1205void
1206ThreeGppChannelModel::SetScenario(const std::string& scenario)
1207{
1208 NS_LOG_FUNCTION(this);
1209 NS_ASSERT_MSG(scenario == "RMa" || scenario == "UMa" || scenario == "UMi-StreetCanyon" ||
1210 scenario == "InH-OfficeOpen" || scenario == "InH-OfficeMixed" ||
1211 scenario == "V2V-Urban" || scenario == "V2V-Highway" ||
1212 scenario == "NTN-DenseUrban" || scenario == "NTN-Urban" ||
1213 scenario == "NTN-Suburban" || scenario == "NTN-Rural",
1214 "Unknown scenario, choose between: RMa, UMa, UMi-StreetCanyon, "
1215 "InH-OfficeOpen, InH-OfficeMixed, V2V-Urban, V2V-Highway, "
1216 "NTN-DenseUrban, NTN-Urban, NTN-Suburban or NTN-Rural");
1217 m_scenario = scenario;
1218}
1219
1220std::string
1222{
1223 NS_LOG_FUNCTION(this);
1224 return m_scenario;
1225}
1226
1229 const Ptr<const MobilityModel> bMob,
1230 Ptr<const ChannelCondition> channelCondition) const
1231{
1232 NS_LOG_FUNCTION(this);
1233
1234 // NOTE we assume hUT = min (height(a), height(b)) and
1235 // hBS = max (height (a), height (b))
1236 double hUT = std::min(aMob->GetPosition().z, bMob->GetPosition().z);
1237 double hBS = std::max(aMob->GetPosition().z, bMob->GetPosition().z);
1238
1239 double distance2D = sqrt(pow(aMob->GetPosition().x - bMob->GetPosition().x, 2) +
1240 pow(aMob->GetPosition().y - bMob->GetPosition().y, 2));
1241
1242 double fcGHz = m_frequency / 1.0e9;
1243 Ptr<ParamsTable> table3gpp = Create<ParamsTable>();
1244 // table3gpp includes the following parameters:
1245 // numOfCluster, raysPerCluster, uLgDS, sigLgDS, uLgASD, sigLgASD,
1246 // uLgASA, sigLgASA, uLgZSA, sigLgZSA, uLgZSD, sigLgZSD, offsetZOD,
1247 // cDS, cASD, cASA, cZSA, uK, sigK, rTau, uXpr, sigXpr, shadowingStd
1248
1249 bool los = channelCondition->IsLos();
1250 bool o2i = channelCondition->IsO2i();
1251
1252 // In NLOS case, parameter uK and sigK are not used and they are set to 0
1253 if (m_scenario == "RMa")
1254 {
1255 if (los && !o2i)
1256 {
1257 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
1258 // entry is N/A.
1259 table3gpp->m_numOfCluster = 11;
1260 table3gpp->m_raysPerCluster = 20;
1261 table3gpp->m_uLgDS = -7.49;
1262 table3gpp->m_sigLgDS = 0.55;
1263 table3gpp->m_uLgASD = 0.90;
1264 table3gpp->m_sigLgASD = 0.38;
1265 table3gpp->m_uLgASA = 1.52;
1266 table3gpp->m_sigLgASA = 0.24;
1267 table3gpp->m_uLgZSA = 0.47;
1268 table3gpp->m_sigLgZSA = 0.40;
1269 table3gpp->m_uLgZSD = 0.34;
1270 table3gpp->m_sigLgZSD =
1271 std::max(-1.0, -0.17 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.22);
1272 table3gpp->m_offsetZOD = 0;
1273 table3gpp->m_cDS = 3.91e-9;
1274 table3gpp->m_cASD = 2;
1275 table3gpp->m_cASA = 3;
1276 table3gpp->m_cZSA = 3;
1277 table3gpp->m_uK = 7;
1278 table3gpp->m_sigK = 4;
1279 table3gpp->m_rTau = 3.8;
1280 table3gpp->m_uXpr = 12;
1281 table3gpp->m_sigXpr = 4;
1282 table3gpp->m_perClusterShadowingStd = 3;
1283
1284 for (uint8_t row = 0; row < 7; row++)
1285 {
1286 for (uint8_t column = 0; column < 7; column++)
1287 {
1288 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_LOS[row][column];
1289 }
1290 }
1291 }
1292 else if (!los && !o2i)
1293 {
1294 table3gpp->m_numOfCluster = 10;
1295 table3gpp->m_raysPerCluster = 20;
1296 table3gpp->m_uLgDS = -7.43;
1297 table3gpp->m_sigLgDS = 0.48;
1298 table3gpp->m_uLgASD = 0.95;
1299 table3gpp->m_sigLgASD = 0.45;
1300 table3gpp->m_uLgASA = 1.52;
1301 table3gpp->m_sigLgASA = 0.13;
1302 table3gpp->m_uLgZSA = 0.58;
1303 table3gpp->m_sigLgZSA = 0.37;
1304 table3gpp->m_uLgZSD =
1305 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
1306 table3gpp->m_sigLgZSD = 0.30;
1307 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
1308 table3gpp->m_cDS = 3.91e-9;
1309 table3gpp->m_cASD = 2;
1310 table3gpp->m_cASA = 3;
1311 table3gpp->m_cZSA = 3;
1312 table3gpp->m_uK = 0;
1313 table3gpp->m_sigK = 0;
1314 table3gpp->m_rTau = 1.7;
1315 table3gpp->m_uXpr = 7;
1316 table3gpp->m_sigXpr = 3;
1317 table3gpp->m_perClusterShadowingStd = 3;
1318
1319 for (uint8_t row = 0; row < 6; row++)
1320 {
1321 for (uint8_t column = 0; column < 6; column++)
1322 {
1323 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_NLOS[row][column];
1324 }
1325 }
1326 }
1327 else // o2i
1328 {
1329 table3gpp->m_numOfCluster = 10;
1330 table3gpp->m_raysPerCluster = 20;
1331 table3gpp->m_uLgDS = -7.47;
1332 table3gpp->m_sigLgDS = 0.24;
1333 table3gpp->m_uLgASD = 0.67;
1334 table3gpp->m_sigLgASD = 0.18;
1335 table3gpp->m_uLgASA = 1.66;
1336 table3gpp->m_sigLgASA = 0.21;
1337 table3gpp->m_uLgZSA = 0.93;
1338 table3gpp->m_sigLgZSA = 0.22;
1339 table3gpp->m_uLgZSD =
1340 std::max(-1.0, -0.19 * (distance2D / 1000.0) - 0.01 * (hUT - 1.5) + 0.28);
1341 table3gpp->m_sigLgZSD = 0.30;
1342 table3gpp->m_offsetZOD = atan((35 - 3.5) / distance2D) - atan((35 - 1.5) / distance2D);
1343 table3gpp->m_cDS = 3.91e-9;
1344 table3gpp->m_cASD = 2;
1345 table3gpp->m_cASA = 3;
1346 table3gpp->m_cZSA = 3;
1347 table3gpp->m_uK = 0;
1348 table3gpp->m_sigK = 0;
1349 table3gpp->m_rTau = 1.7;
1350 table3gpp->m_uXpr = 7;
1351 table3gpp->m_sigXpr = 3;
1352 table3gpp->m_perClusterShadowingStd = 3;
1353
1354 for (uint8_t row = 0; row < 6; row++)
1355 {
1356 for (uint8_t column = 0; column < 6; column++)
1357 {
1358 table3gpp->m_sqrtC[row][column] = sqrtC_RMa_O2I[row][column];
1359 }
1360 }
1361 }
1362 }
1363 else if (m_scenario == "UMa")
1364 {
1365 if (los && !o2i)
1366 {
1367 table3gpp->m_numOfCluster = 12;
1368 table3gpp->m_raysPerCluster = 20;
1369 table3gpp->m_uLgDS = -6.955 - 0.0963 * log10(fcGHz);
1370 table3gpp->m_sigLgDS = 0.66;
1371 table3gpp->m_uLgASD = 1.06 + 0.1114 * log10(fcGHz);
1372 table3gpp->m_sigLgASD = 0.28;
1373 table3gpp->m_uLgASA = 1.81;
1374 table3gpp->m_sigLgASA = 0.20;
1375 table3gpp->m_uLgZSA = 0.95;
1376 table3gpp->m_sigLgZSA = 0.16;
1377 table3gpp->m_uLgZSD =
1378 std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.75);
1379 table3gpp->m_sigLgZSD = 0.40;
1380 table3gpp->m_offsetZOD = 0;
1381 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
1382 table3gpp->m_cASD = 5;
1383 table3gpp->m_cASA = 11;
1384 table3gpp->m_cZSA = 7;
1385 table3gpp->m_uK = 9;
1386 table3gpp->m_sigK = 3.5;
1387 table3gpp->m_rTau = 2.5;
1388 table3gpp->m_uXpr = 8;
1389 table3gpp->m_sigXpr = 4;
1390 table3gpp->m_perClusterShadowingStd = 3;
1391
1392 for (uint8_t row = 0; row < 7; row++)
1393 {
1394 for (uint8_t column = 0; column < 7; column++)
1395 {
1396 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_LOS[row][column];
1397 }
1398 }
1399 }
1400 else
1401 {
1402 double uLgZSD = std::max(-0.5, -2.1 * distance2D / 1000.0 - 0.01 * (hUT - 1.5) + 0.9);
1403
1404 double afc = 0.208 * log10(fcGHz) - 0.782;
1405 double bfc = 25;
1406 double cfc = -0.13 * log10(fcGHz) + 2.03;
1407 double efc = 7.66 * log10(fcGHz) - 5.96;
1408
1409 double offsetZOD = efc - std::pow(10, afc * log10(std::max(bfc, distance2D)) + cfc);
1410
1411 if (!los && !o2i)
1412 {
1413 table3gpp->m_numOfCluster = 20;
1414 table3gpp->m_raysPerCluster = 20;
1415 table3gpp->m_uLgDS = -6.28 - 0.204 * log10(fcGHz);
1416 table3gpp->m_sigLgDS = 0.39;
1417 table3gpp->m_uLgASD = 1.5 - 0.1144 * log10(fcGHz);
1418 table3gpp->m_sigLgASD = 0.28;
1419 table3gpp->m_uLgASA = 2.08 - 0.27 * log10(fcGHz);
1420 table3gpp->m_sigLgASA = 0.11;
1421 table3gpp->m_uLgZSA = -0.3236 * log10(fcGHz) + 1.512;
1422 table3gpp->m_sigLgZSA = 0.16;
1423 table3gpp->m_uLgZSD = uLgZSD;
1424 table3gpp->m_sigLgZSD = 0.49;
1425 table3gpp->m_offsetZOD = offsetZOD;
1426 table3gpp->m_cDS = std::max(0.25, -3.4084 * log10(fcGHz) + 6.5622) * 1e-9;
1427 table3gpp->m_cASD = 2;
1428 table3gpp->m_cASA = 15;
1429 table3gpp->m_cZSA = 7;
1430 table3gpp->m_uK = 0;
1431 table3gpp->m_sigK = 0;
1432 table3gpp->m_rTau = 2.3;
1433 table3gpp->m_uXpr = 7;
1434 table3gpp->m_sigXpr = 3;
1435 table3gpp->m_perClusterShadowingStd = 3;
1436
1437 for (uint8_t row = 0; row < 6; row++)
1438 {
1439 for (uint8_t column = 0; column < 6; column++)
1440 {
1441 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_NLOS[row][column];
1442 }
1443 }
1444 }
1445 else //(o2i)
1446 {
1447 table3gpp->m_numOfCluster = 12;
1448 table3gpp->m_raysPerCluster = 20;
1449 table3gpp->m_uLgDS = -6.62;
1450 table3gpp->m_sigLgDS = 0.32;
1451 table3gpp->m_uLgASD = 1.25;
1452 table3gpp->m_sigLgASD = 0.42;
1453 table3gpp->m_uLgASA = 1.76;
1454 table3gpp->m_sigLgASA = 0.16;
1455 table3gpp->m_uLgZSA = 1.01;
1456 table3gpp->m_sigLgZSA = 0.43;
1457 table3gpp->m_uLgZSD = uLgZSD;
1458 table3gpp->m_sigLgZSD = 0.49;
1459 table3gpp->m_offsetZOD = offsetZOD;
1460 table3gpp->m_cDS = 11e-9;
1461 table3gpp->m_cASD = 5;
1462 table3gpp->m_cASA = 8;
1463 table3gpp->m_cZSA = 3;
1464 table3gpp->m_uK = 0;
1465 table3gpp->m_sigK = 0;
1466 table3gpp->m_rTau = 2.2;
1467 table3gpp->m_uXpr = 9;
1468 table3gpp->m_sigXpr = 5;
1469 table3gpp->m_perClusterShadowingStd = 4;
1470
1471 for (uint8_t row = 0; row < 6; row++)
1472 {
1473 for (uint8_t column = 0; column < 6; column++)
1474 {
1475 table3gpp->m_sqrtC[row][column] = sqrtC_UMa_O2I[row][column];
1476 }
1477 }
1478 }
1479 }
1480 }
1481 else if (m_scenario == "UMi-StreetCanyon")
1482 {
1483 if (los && !o2i)
1484 {
1485 table3gpp->m_numOfCluster = 12;
1486 table3gpp->m_raysPerCluster = 20;
1487 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 7.14;
1488 table3gpp->m_sigLgDS = 0.38;
1489 table3gpp->m_uLgASD = -0.05 * log10(1 + fcGHz) + 1.21;
1490 table3gpp->m_sigLgASD = 0.41;
1491 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.73;
1492 table3gpp->m_sigLgASA = 0.014 * log10(1 + fcGHz) + 0.28;
1493 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
1494 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
1495 table3gpp->m_uLgZSD =
1496 std::max(-0.21, -14.8 * distance2D / 1000.0 + 0.01 * std::abs(hUT - hBS) + 0.83);
1497 table3gpp->m_sigLgZSD = 0.35;
1498 table3gpp->m_offsetZOD = 0;
1499 table3gpp->m_cDS = 5e-9;
1500 table3gpp->m_cASD = 3;
1501 table3gpp->m_cASA = 17;
1502 table3gpp->m_cZSA = 7;
1503 table3gpp->m_uK = 9;
1504 table3gpp->m_sigK = 5;
1505 table3gpp->m_rTau = 3;
1506 table3gpp->m_uXpr = 9;
1507 table3gpp->m_sigXpr = 3;
1508 table3gpp->m_perClusterShadowingStd = 3;
1509
1510 for (uint8_t row = 0; row < 7; row++)
1511 {
1512 for (uint8_t column = 0; column < 7; column++)
1513 {
1514 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1515 }
1516 }
1517 }
1518 else
1519 {
1520 double uLgZSD =
1521 std::max(-0.5, -3.1 * distance2D / 1000.0 + 0.01 * std::max(hUT - hBS, 0.0) + 0.2);
1522 double offsetZOD = -1 * std::pow(10, -1.5 * log10(std::max(10.0, distance2D)) + 3.3);
1523 if (!los && !o2i)
1524 {
1525 table3gpp->m_numOfCluster = 19;
1526 table3gpp->m_raysPerCluster = 20;
1527 table3gpp->m_uLgDS = -0.24 * log10(1 + fcGHz) - 6.83;
1528 table3gpp->m_sigLgDS = 0.16 * log10(1 + fcGHz) + 0.28;
1529 table3gpp->m_uLgASD = -0.23 * log10(1 + fcGHz) + 1.53;
1530 table3gpp->m_sigLgASD = 0.11 * log10(1 + fcGHz) + 0.33;
1531 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
1532 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
1533 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1534 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1535 table3gpp->m_uLgZSD = uLgZSD;
1536 table3gpp->m_sigLgZSD = 0.35;
1537 table3gpp->m_offsetZOD = offsetZOD;
1538 table3gpp->m_cDS = 11e-9;
1539 table3gpp->m_cASD = 10;
1540 table3gpp->m_cASA = 22;
1541 table3gpp->m_cZSA = 7;
1542 table3gpp->m_uK = 0;
1543 table3gpp->m_sigK = 0;
1544 table3gpp->m_rTau = 2.1;
1545 table3gpp->m_uXpr = 8;
1546 table3gpp->m_sigXpr = 3;
1547 table3gpp->m_perClusterShadowingStd = 3;
1548
1549 for (uint8_t row = 0; row < 6; row++)
1550 {
1551 for (uint8_t column = 0; column < 6; column++)
1552 {
1553 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
1554 }
1555 }
1556 }
1557 else //(o2i)
1558 {
1559 table3gpp->m_numOfCluster = 12;
1560 table3gpp->m_raysPerCluster = 20;
1561 table3gpp->m_uLgDS = -6.62;
1562 table3gpp->m_sigLgDS = 0.32;
1563 table3gpp->m_uLgASD = 1.25;
1564 table3gpp->m_sigLgASD = 0.42;
1565 table3gpp->m_uLgASA = 1.76;
1566 table3gpp->m_sigLgASA = 0.16;
1567 table3gpp->m_uLgZSA = 1.01;
1568 table3gpp->m_sigLgZSA = 0.43;
1569 table3gpp->m_uLgZSD = uLgZSD;
1570 table3gpp->m_sigLgZSD = 0.35;
1571 table3gpp->m_offsetZOD = offsetZOD;
1572 table3gpp->m_cDS = 11e-9;
1573 table3gpp->m_cASD = 5;
1574 table3gpp->m_cASA = 8;
1575 table3gpp->m_cZSA = 3;
1576 table3gpp->m_uK = 0;
1577 table3gpp->m_sigK = 0;
1578 table3gpp->m_rTau = 2.2;
1579 table3gpp->m_uXpr = 9;
1580 table3gpp->m_sigXpr = 5;
1581 table3gpp->m_perClusterShadowingStd = 4;
1582
1583 for (uint8_t row = 0; row < 6; row++)
1584 {
1585 for (uint8_t column = 0; column < 6; column++)
1586 {
1587 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_O2I[row][column];
1588 }
1589 }
1590 }
1591 }
1592 }
1593 else if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
1594 {
1595 NS_ASSERT_MSG(!o2i, "The indoor scenario does out support outdoor to indoor");
1596 if (los)
1597 {
1598 table3gpp->m_numOfCluster = 15;
1599 table3gpp->m_raysPerCluster = 20;
1600 table3gpp->m_uLgDS = -0.01 * log10(1 + fcGHz) - 7.692;
1601 table3gpp->m_sigLgDS = 0.18;
1602 table3gpp->m_uLgASD = 1.60;
1603 table3gpp->m_sigLgASD = 0.18;
1604 table3gpp->m_uLgASA = -0.19 * log10(1 + fcGHz) + 1.781;
1605 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.119;
1606 table3gpp->m_uLgZSA = -0.26 * log10(1 + fcGHz) + 1.44;
1607 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.264;
1608 table3gpp->m_uLgZSD = -1.43 * log10(1 + fcGHz) + 2.228;
1609 table3gpp->m_sigLgZSD = 0.13 * log10(1 + fcGHz) + 0.30;
1610 table3gpp->m_offsetZOD = 0;
1611 table3gpp->m_cDS = 3.91e-9;
1612 table3gpp->m_cASD = 5;
1613 table3gpp->m_cASA = 8;
1614 table3gpp->m_cZSA = 9;
1615 table3gpp->m_uK = 7;
1616 table3gpp->m_sigK = 4;
1617 table3gpp->m_rTau = 3.6;
1618 table3gpp->m_uXpr = 11;
1619 table3gpp->m_sigXpr = 4;
1620 table3gpp->m_perClusterShadowingStd = 6;
1621
1622 for (uint8_t row = 0; row < 7; row++)
1623 {
1624 for (uint8_t column = 0; column < 7; column++)
1625 {
1626 table3gpp->m_sqrtC[row][column] = sqrtC_office_LOS[row][column];
1627 }
1628 }
1629 }
1630 else
1631 {
1632 table3gpp->m_numOfCluster = 19;
1633 table3gpp->m_raysPerCluster = 20;
1634 table3gpp->m_uLgDS = -0.28 * log10(1 + fcGHz) - 7.173;
1635 table3gpp->m_sigLgDS = 0.1 * log10(1 + fcGHz) + 0.055;
1636 table3gpp->m_uLgASD = 1.62;
1637 table3gpp->m_sigLgASD = 0.25;
1638 table3gpp->m_uLgASA = -0.11 * log10(1 + fcGHz) + 1.863;
1639 table3gpp->m_sigLgASA = 0.12 * log10(1 + fcGHz) + 0.059;
1640 table3gpp->m_uLgZSA = -0.15 * log10(1 + fcGHz) + 1.387;
1641 table3gpp->m_sigLgZSA = -0.09 * log10(1 + fcGHz) + 0.746;
1642 table3gpp->m_uLgZSD = 1.08;
1643 table3gpp->m_sigLgZSD = 0.36;
1644 table3gpp->m_offsetZOD = 0;
1645 table3gpp->m_cDS = 3.91e-9;
1646 table3gpp->m_cASD = 5;
1647 table3gpp->m_cASA = 11;
1648 table3gpp->m_cZSA = 9;
1649 table3gpp->m_uK = 0;
1650 table3gpp->m_sigK = 0;
1651 table3gpp->m_rTau = 3;
1652 table3gpp->m_uXpr = 10;
1653 table3gpp->m_sigXpr = 4;
1654 table3gpp->m_perClusterShadowingStd = 3;
1655
1656 for (uint8_t row = 0; row < 6; row++)
1657 {
1658 for (uint8_t column = 0; column < 6; column++)
1659 {
1660 table3gpp->m_sqrtC[row][column] = sqrtC_office_NLOS[row][column];
1661 }
1662 }
1663 }
1664 }
1665 else if (m_scenario == "V2V-Urban")
1666 {
1667 if (channelCondition->IsLos())
1668 {
1669 // 3GPP mentioned that 3.91 ns should be used when the Cluster DS (cDS)
1670 // entry is N/A.
1671 table3gpp->m_numOfCluster = 12;
1672 table3gpp->m_raysPerCluster = 20;
1673 table3gpp->m_uLgDS = -0.2 * log10(1 + fcGHz) - 7.5;
1674 table3gpp->m_sigLgDS = 0.1;
1675 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.6;
1676 table3gpp->m_sigLgASD = 0.1;
1677 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.6;
1678 table3gpp->m_sigLgASA = 0.1;
1679 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
1680 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
1681 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
1682 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
1683 table3gpp->m_offsetZOD = 0;
1684 table3gpp->m_cDS = 5;
1685 table3gpp->m_cASD = 17;
1686 table3gpp->m_cASA = 17;
1687 table3gpp->m_cZSA = 7;
1688 table3gpp->m_uK = 3.48;
1689 table3gpp->m_sigK = 2;
1690 table3gpp->m_rTau = 3;
1691 table3gpp->m_uXpr = 9;
1692 table3gpp->m_sigXpr = 3;
1693 table3gpp->m_perClusterShadowingStd = 4;
1694
1695 for (uint8_t row = 0; row < 7; row++)
1696 {
1697 for (uint8_t column = 0; column < 7; column++)
1698 {
1699 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1700 }
1701 }
1702 }
1703 else if (channelCondition->IsNlos())
1704 {
1705 table3gpp->m_numOfCluster = 19;
1706 table3gpp->m_raysPerCluster = 20;
1707 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
1708 table3gpp->m_sigLgDS = 0.28;
1709 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
1710 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
1711 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
1712 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
1713 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1714 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1715 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
1716 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
1717 table3gpp->m_offsetZOD = 0;
1718 table3gpp->m_cDS = 11;
1719 table3gpp->m_cASD = 22;
1720 table3gpp->m_cASA = 22;
1721 table3gpp->m_cZSA = 7;
1722 table3gpp->m_uK = 0; // N/A
1723 table3gpp->m_sigK = 0; // N/A
1724 table3gpp->m_rTau = 2.1;
1725 table3gpp->m_uXpr = 8;
1726 table3gpp->m_sigXpr = 3;
1727 table3gpp->m_perClusterShadowingStd = 4;
1728
1729 for (uint8_t row = 0; row < 6; row++)
1730 {
1731 for (uint8_t column = 0; column < 6; column++)
1732 {
1733 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
1734 }
1735 }
1736 }
1737 else if (channelCondition->IsNlosv())
1738 {
1739 table3gpp->m_numOfCluster = 19;
1740 table3gpp->m_raysPerCluster = 20;
1741 table3gpp->m_uLgDS = -0.4 * log10(1 + fcGHz) - 7;
1742 table3gpp->m_sigLgDS = 0.1;
1743 table3gpp->m_uLgASD = -0.1 * log10(1 + fcGHz) + 1.7;
1744 table3gpp->m_sigLgASD = 0.1;
1745 table3gpp->m_uLgASA = -0.1 * log10(1 + fcGHz) + 1.7;
1746 table3gpp->m_sigLgASA = 0.1;
1747 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1748 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1749 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
1750 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
1751 table3gpp->m_offsetZOD = 0;
1752 table3gpp->m_cDS = 11;
1753 table3gpp->m_cASD = 22;
1754 table3gpp->m_cASA = 22;
1755 table3gpp->m_cZSA = 7;
1756 table3gpp->m_uK = 0;
1757 table3gpp->m_sigK = 4.5;
1758 table3gpp->m_rTau = 2.1;
1759 table3gpp->m_uXpr = 8;
1760 table3gpp->m_sigXpr = 3;
1761 table3gpp->m_perClusterShadowingStd = 4;
1762
1763 for (uint8_t row = 0; row < 6; row++)
1764 {
1765 for (uint8_t column = 0; column < 6; column++)
1766 {
1767 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1768 }
1769 }
1770 }
1771 else
1772 {
1773 NS_FATAL_ERROR("Unknown channel condition");
1774 }
1775 }
1776 else if (m_scenario == "V2V-Highway")
1777 {
1778 if (channelCondition->IsLos())
1779 {
1780 table3gpp->m_numOfCluster = 12;
1781 table3gpp->m_raysPerCluster = 20;
1782 table3gpp->m_uLgDS = -8.3;
1783 table3gpp->m_sigLgDS = 0.2;
1784 table3gpp->m_uLgASD = 1.4;
1785 table3gpp->m_sigLgASD = 0.1;
1786 table3gpp->m_uLgASA = 1.4;
1787 table3gpp->m_sigLgASA = 0.1;
1788 table3gpp->m_uLgZSA = -0.1 * log10(1 + fcGHz) + 0.73;
1789 table3gpp->m_sigLgZSA = -0.04 * log10(1 + fcGHz) + 0.34;
1790 table3gpp->m_uLgZSD = -0.1 * log10(1 + fcGHz) + 0.73;
1791 table3gpp->m_sigLgZSD = -0.04 * log10(1 + fcGHz) + 0.34;
1792 table3gpp->m_offsetZOD = 0;
1793 table3gpp->m_cDS = 5;
1794 table3gpp->m_cASD = 17;
1795 table3gpp->m_cASA = 17;
1796 table3gpp->m_cZSA = 7;
1797 table3gpp->m_uK = 9;
1798 table3gpp->m_sigK = 3.5;
1799 table3gpp->m_rTau = 3;
1800 table3gpp->m_uXpr = 9;
1801 table3gpp->m_sigXpr = 3;
1802 table3gpp->m_perClusterShadowingStd = 4;
1803
1804 for (uint8_t row = 0; row < 7; row++)
1805 {
1806 for (uint8_t column = 0; column < 7; column++)
1807 {
1808 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1809 }
1810 }
1811 }
1812 else if (channelCondition->IsNlosv())
1813 {
1814 table3gpp->m_numOfCluster = 19;
1815 table3gpp->m_raysPerCluster = 20;
1816 table3gpp->m_uLgDS = -8.3;
1817 table3gpp->m_sigLgDS = 0.3;
1818 table3gpp->m_uLgASD = 1.5;
1819 table3gpp->m_sigLgASD = 0.1;
1820 table3gpp->m_uLgASA = 1.5;
1821 table3gpp->m_sigLgASA = 0.1;
1822 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1823 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1824 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
1825 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
1826 table3gpp->m_offsetZOD = 0;
1827 table3gpp->m_cDS = 11;
1828 table3gpp->m_cASD = 22;
1829 table3gpp->m_cASA = 22;
1830 table3gpp->m_cZSA = 7;
1831 table3gpp->m_uK = 0;
1832 table3gpp->m_sigK = 4.5;
1833 table3gpp->m_rTau = 2.1;
1834 table3gpp->m_uXpr = 8.0;
1835 table3gpp->m_sigXpr = 3;
1836 table3gpp->m_perClusterShadowingStd = 4;
1837
1838 for (uint8_t row = 0; row < 6; row++)
1839 {
1840 for (uint8_t column = 0; column < 6; column++)
1841 {
1842 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_LOS[row][column];
1843 }
1844 }
1845 }
1846 else if (channelCondition->IsNlos())
1847 {
1849 "The fast fading parameters for the NLOS condition in the Highway scenario are not "
1850 "defined in TR 37.885, use the ones defined in TDoc R1-1803671 instead");
1851
1852 table3gpp->m_numOfCluster = 19;
1853 table3gpp->m_raysPerCluster = 20;
1854 table3gpp->m_uLgDS = -0.3 * log10(1 + fcGHz) - 7;
1855 table3gpp->m_sigLgDS = 0.28;
1856 table3gpp->m_uLgASD = -0.08 * log10(1 + fcGHz) + 1.81;
1857 table3gpp->m_sigLgASD = 0.05 * log10(1 + fcGHz) + 0.3;
1858 table3gpp->m_uLgASA = -0.08 * log10(1 + fcGHz) + 1.81;
1859 table3gpp->m_sigLgASA = 0.05 * log10(1 + fcGHz) + 0.3;
1860 table3gpp->m_uLgZSA = -0.04 * log10(1 + fcGHz) + 0.92;
1861 table3gpp->m_sigLgZSA = -0.07 * log10(1 + fcGHz) + 0.41;
1862 table3gpp->m_uLgZSD = -0.04 * log10(1 + fcGHz) + 0.92;
1863 table3gpp->m_sigLgZSD = -0.07 * log10(1 + fcGHz) + 0.41;
1864 table3gpp->m_offsetZOD = 0;
1865 table3gpp->m_cDS = 11;
1866 table3gpp->m_cASD = 22;
1867 table3gpp->m_cASA = 22;
1868 table3gpp->m_cZSA = 7;
1869 table3gpp->m_uK = 0; // N/A
1870 table3gpp->m_sigK = 0; // N/A
1871 table3gpp->m_rTau = 2.1;
1872 table3gpp->m_uXpr = 8;
1873 table3gpp->m_sigXpr = 3;
1874 table3gpp->m_perClusterShadowingStd = 4;
1875
1876 for (uint8_t row = 0; row < 6; row++)
1877 {
1878 for (uint8_t column = 0; column < 6; column++)
1879 {
1880 table3gpp->m_sqrtC[row][column] = sqrtC_UMi_NLOS[row][column];
1881 }
1882 }
1883 }
1884 else
1885 {
1886 NS_FATAL_ERROR("Unknown channel condition");
1887 }
1888 }
1889 else if (m_scenario.substr(0, 3) == "NTN")
1890 {
1891 std::string freqBand = (fcGHz < 13) ? "S" : "Ka";
1892
1893 double elevAngle = 0;
1894 bool isSatellite = false; // flag to indicate if one of the two nodes is a satellite
1895 // if so, parameters will be set accordingly to NOTE 8 of
1896 // Table 6.7.2 from 3GPP 38.811 V15.4.0 (2020-09)
1897
1898 Ptr<MobilityModel> aMobNonConst = ConstCast<MobilityModel>(aMob);
1899 Ptr<MobilityModel> bMobNonConst = ConstCast<MobilityModel>(bMob);
1900
1901 if (DynamicCast<GeocentricConstantPositionMobilityModel>(
1902 ConstCast<MobilityModel>(aMob)) && // Transform to NS_ASSERT
1903 DynamicCast<GeocentricConstantPositionMobilityModel>(
1904 ConstCast<MobilityModel>(bMob))) // check if aMob and bMob are of type
1905 // GeocentricConstantPositionMobilityModel
1906 {
1908 DynamicCast<GeocentricConstantPositionMobilityModel>(aMobNonConst);
1910 DynamicCast<GeocentricConstantPositionMobilityModel>(bMobNonConst);
1911
1912 if (aNTNMob->GetGeographicPosition().z <
1913 bNTNMob->GetGeographicPosition().z) // b is the HAPS/Satellite
1914 {
1915 elevAngle = aNTNMob->GetElevationAngle(bNTNMob);
1916 if (bNTNMob->GetGeographicPosition().z > 50000)
1917 {
1918 isSatellite = true;
1919 }
1920 }
1921 else // a is the HAPS/Satellite
1922 {
1923 elevAngle = bNTNMob->GetElevationAngle(aNTNMob);
1924 if (aNTNMob->GetGeographicPosition().z > 50000)
1925 {
1926 isSatellite = true;
1927 }
1928 }
1929 }
1930 else
1931 {
1932 NS_FATAL_ERROR("Mobility Models needs to be of type Geocentric for NTN scenarios");
1933 }
1934
1935 // Round the elevation angle into a two-digits integer between 10 and 90.
1936 int elevAngleQuantized = (elevAngle < 10) ? 10 : round(elevAngle / 10) * 10;
1937
1938 if (m_scenario == "NTN-DenseUrban")
1939 {
1940 if (channelCondition->IsLos())
1941 {
1942 table3gpp->m_uLgDS =
1943 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
1944 table3gpp->m_sigLgDS =
1945 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
1946
1947 // table3gpp->m_uLgASD=-1.79769e+308; //FOR SATELLITES
1948 table3gpp->m_uLgASD =
1949 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
1950 // table3gpp->m_sigLgASD=0; //FOR SATELLITES
1951 table3gpp->m_sigLgASD =
1952 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
1953
1954 table3gpp->m_uLgASA =
1955 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
1956 table3gpp->m_sigLgASA =
1957 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
1958 table3gpp->m_uLgZSA =
1959 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
1960 table3gpp->m_sigLgZSA =
1961 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
1962
1963 // table3gpp->m_uLgZSD=-1.79769e+308; //FOR SATELLITES
1964 table3gpp->m_uLgZSD =
1965 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
1966 // table3gpp->m_sigLgZSD= 0; //FOR SATELLITES
1967 table3gpp->m_sigLgZSD =
1968 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
1969
1970 table3gpp->m_uK =
1971 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
1972 table3gpp->m_sigK =
1973 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
1974 table3gpp->m_rTau =
1975 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
1976 table3gpp->m_uXpr =
1977 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
1978 table3gpp->m_sigXpr =
1979 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
1980 table3gpp->m_numOfCluster = NTNDenseUrbanLOS.at(freqBand).at(
1981 elevAngleQuantized)[Table3gppParams::numOfCluster];
1982 table3gpp->m_raysPerCluster = NTNDenseUrbanLOS.at(freqBand).at(
1983 elevAngleQuantized)[Table3gppParams::raysPerCluster];
1984 table3gpp->m_cDS =
1985 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
1986 table3gpp->m_cASD =
1987 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
1988 table3gpp->m_cASA =
1989 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
1990 table3gpp->m_cZSA =
1991 NTNDenseUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
1992 table3gpp->m_perClusterShadowingStd = NTNDenseUrbanLOS.at(freqBand).at(
1993 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
1994
1995 for (uint8_t row = 0; row < 7; row++)
1996 {
1997 for (uint8_t column = 0; column < 7; column++)
1998 {
1999 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_DenseUrban_LOS[row][column];
2000 }
2001 }
2002 }
2003 else if (channelCondition->IsNlos())
2004 {
2005 NS_LOG_UNCOND("Dense Urban NLOS");
2006 table3gpp->m_uLgDS =
2007 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2008 table3gpp->m_sigLgDS =
2009 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2010 table3gpp->m_uLgASD =
2011 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2012 table3gpp->m_sigLgASD = NTNDenseUrbanNLOS.at(freqBand).at(
2013 elevAngleQuantized)[Table3gppParams::sigLgASD];
2014 table3gpp->m_uLgASA =
2015 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2016 table3gpp->m_sigLgASA = NTNDenseUrbanNLOS.at(freqBand).at(
2017 elevAngleQuantized)[Table3gppParams::sigLgASA];
2018 table3gpp->m_uLgZSA =
2019 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2020 table3gpp->m_sigLgZSA = NTNDenseUrbanNLOS.at(freqBand).at(
2021 elevAngleQuantized)[Table3gppParams::sigLgZSA];
2022 table3gpp->m_uLgZSD =
2023 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2024 table3gpp->m_sigLgZSD = NTNDenseUrbanNLOS.at(freqBand).at(
2025 elevAngleQuantized)[Table3gppParams::sigLgZSD];
2026 table3gpp->m_rTau =
2027 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2028 table3gpp->m_uXpr =
2029 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2030 table3gpp->m_sigXpr =
2031 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2032 table3gpp->m_numOfCluster = NTNDenseUrbanNLOS.at(freqBand).at(
2033 elevAngleQuantized)[Table3gppParams::numOfCluster];
2034 table3gpp->m_raysPerCluster = NTNDenseUrbanNLOS.at(freqBand).at(
2035 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2036 table3gpp->m_cDS =
2037 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2038 table3gpp->m_cASD =
2039 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2040 table3gpp->m_cASA =
2041 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2042 table3gpp->m_cZSA =
2043 NTNDenseUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2044 table3gpp->m_perClusterShadowingStd = NTNDenseUrbanNLOS.at(freqBand).at(
2045 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2046
2047 for (uint8_t row = 0; row < 6; row++)
2048 {
2049 for (uint8_t column = 0; column < 6; column++)
2050 {
2051 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_DenseUrban_NLOS[row][column];
2052 }
2053 }
2054 }
2055 }
2056 else if (m_scenario == "NTN-Urban")
2057 {
2058 if (channelCondition->IsLos())
2059 {
2060 table3gpp->m_uLgDS =
2061 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2062 table3gpp->m_sigLgDS =
2063 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2064 table3gpp->m_uLgASD =
2065 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2066 table3gpp->m_sigLgASD =
2067 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2068 table3gpp->m_uLgASA =
2069 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2070 table3gpp->m_sigLgASA =
2071 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2072 table3gpp->m_uLgZSA =
2073 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2074 table3gpp->m_sigLgZSA =
2075 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2076 table3gpp->m_uLgZSD =
2077 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2078 table3gpp->m_sigLgZSD =
2079 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2080 table3gpp->m_uK =
2081 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2082 table3gpp->m_sigK =
2083 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2084 table3gpp->m_rTau =
2085 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2086 table3gpp->m_uXpr =
2087 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2088 table3gpp->m_sigXpr =
2089 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2090 table3gpp->m_numOfCluster =
2091 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2092 table3gpp->m_raysPerCluster = NTNUrbanLOS.at(freqBand).at(
2093 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2094 table3gpp->m_cDS =
2095 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2096 table3gpp->m_cASD =
2097 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2098 table3gpp->m_cASA =
2099 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2100 table3gpp->m_cZSA =
2101 NTNUrbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2102 table3gpp->m_perClusterShadowingStd = NTNUrbanLOS.at(freqBand).at(
2103 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2104
2105 for (uint8_t row = 0; row < 7; row++)
2106 {
2107 for (uint8_t column = 0; column < 7; column++)
2108 {
2109 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Urban_LOS[row][column];
2110 }
2111 }
2112 }
2113 else if (channelCondition->IsNlos())
2114 {
2115 table3gpp->m_uLgDS =
2116 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2117 table3gpp->m_sigLgDS =
2118 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2119 table3gpp->m_uLgASD =
2120 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2121 table3gpp->m_sigLgASD =
2122 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2123 table3gpp->m_uLgASA =
2124 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2125 table3gpp->m_sigLgASA =
2126 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2127 table3gpp->m_uLgZSA =
2128 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2129 table3gpp->m_sigLgZSA =
2130 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2131 table3gpp->m_uLgZSD =
2132 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2133 table3gpp->m_sigLgZSD =
2134 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2135 table3gpp->m_uK =
2136 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2137 table3gpp->m_sigK =
2138 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2139 table3gpp->m_rTau =
2140 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2141 table3gpp->m_uXpr =
2142 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2143 table3gpp->m_sigXpr =
2144 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2145 table3gpp->m_numOfCluster =
2146 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2147 table3gpp->m_raysPerCluster = NTNUrbanNLOS.at(freqBand).at(
2148 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2149 table3gpp->m_cDS =
2150 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2151 table3gpp->m_cASD =
2152 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2153 table3gpp->m_cASA =
2154 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2155 table3gpp->m_cZSA =
2156 NTNUrbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2157 table3gpp->m_perClusterShadowingStd = NTNUrbanNLOS.at(freqBand).at(
2158 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2159
2160 for (uint8_t row = 0; row < 6; row++)
2161 {
2162 for (uint8_t column = 0; column < 6; column++)
2163 {
2164 table3gpp->m_sqrtC[row][column] =
2165 sqrtC_NTN_Urban_NLOS.at(elevAngleQuantized)[row][column];
2166 }
2167 }
2168 }
2169 }
2170 else if (m_scenario == "NTN-Suburban")
2171 {
2172 if (channelCondition->IsLos())
2173 {
2174 table3gpp->m_uLgDS =
2175 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2176 table3gpp->m_sigLgDS =
2177 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2178 table3gpp->m_uLgASD =
2179 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2180 table3gpp->m_sigLgASD =
2181 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2182 table3gpp->m_uLgASA =
2183 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2184 table3gpp->m_sigLgASA =
2185 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2186 table3gpp->m_uLgZSA =
2187 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2188 table3gpp->m_sigLgZSA =
2189 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2190 table3gpp->m_uLgZSD =
2191 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2192 table3gpp->m_sigLgZSD =
2193 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2194 table3gpp->m_uK =
2195 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2196 table3gpp->m_sigK =
2197 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2198 table3gpp->m_rTau =
2199 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2200 table3gpp->m_uXpr =
2201 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2202 table3gpp->m_sigXpr =
2203 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2204 table3gpp->m_numOfCluster = NTNSuburbanLOS.at(freqBand).at(
2205 elevAngleQuantized)[Table3gppParams::numOfCluster];
2206 table3gpp->m_raysPerCluster = NTNSuburbanLOS.at(freqBand).at(
2207 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2208 table3gpp->m_cDS =
2209 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2210 table3gpp->m_cASD =
2211 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2212 table3gpp->m_cASA =
2213 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2214 table3gpp->m_cZSA =
2215 NTNSuburbanLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2216 table3gpp->m_perClusterShadowingStd = NTNSuburbanLOS.at(freqBand).at(
2217 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2218
2219 for (uint8_t row = 0; row < 7; row++)
2220 {
2221 for (uint8_t column = 0; column < 7; column++)
2222 {
2223 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Suburban_LOS[row][column];
2224 }
2225 }
2226 }
2227 else if (channelCondition->IsNlos())
2228 {
2229 table3gpp->m_uLgDS =
2230 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2231 table3gpp->m_sigLgDS =
2232 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2233 table3gpp->m_uLgASD =
2234 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2235 table3gpp->m_sigLgASD =
2236 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2237 table3gpp->m_uLgASA =
2238 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2239 table3gpp->m_sigLgASA =
2240 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2241 table3gpp->m_uLgZSA =
2242 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2243 table3gpp->m_sigLgZSA =
2244 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2245 table3gpp->m_uLgZSD =
2246 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2247 table3gpp->m_sigLgZSD =
2248 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2249 table3gpp->m_uK =
2250 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2251 table3gpp->m_sigK =
2252 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2253 table3gpp->m_rTau =
2254 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2255 table3gpp->m_uXpr =
2256 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2257 table3gpp->m_sigXpr =
2258 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2259 table3gpp->m_numOfCluster = NTNSuburbanNLOS.at(freqBand).at(
2260 elevAngleQuantized)[Table3gppParams::numOfCluster];
2261 table3gpp->m_raysPerCluster = NTNSuburbanNLOS.at(freqBand).at(
2262 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2263 table3gpp->m_cDS =
2264 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2265 table3gpp->m_cASD =
2266 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2267 table3gpp->m_cASA =
2268 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2269 table3gpp->m_cZSA =
2270 NTNSuburbanNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2271 table3gpp->m_perClusterShadowingStd = NTNSuburbanNLOS.at(freqBand).at(
2272 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2273
2274 for (uint8_t row = 0; row < 6; row++)
2275 {
2276 for (uint8_t column = 0; column < 6; column++)
2277 {
2278 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Suburban_NLOS[row][column];
2279 }
2280 }
2281 }
2282 }
2283 else if (m_scenario == "NTN-Rural")
2284 {
2285 if (channelCondition->IsLos())
2286 {
2287 table3gpp->m_uLgDS =
2288 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2289 table3gpp->m_sigLgDS =
2290 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2291 table3gpp->m_uLgASD =
2292 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2293 table3gpp->m_sigLgASD =
2294 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2295 table3gpp->m_uLgASA =
2296 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2297 table3gpp->m_sigLgASA =
2298 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2299 table3gpp->m_uLgZSA =
2300 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2301 table3gpp->m_sigLgZSA =
2302 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2303 table3gpp->m_uLgZSD =
2304 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2305 table3gpp->m_sigLgZSD =
2306 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2307 table3gpp->m_uK =
2308 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2309 table3gpp->m_sigK =
2310 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2311 table3gpp->m_rTau =
2312 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2313 table3gpp->m_uXpr =
2314 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2315 table3gpp->m_sigXpr =
2316 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2317 table3gpp->m_numOfCluster =
2318 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2319 table3gpp->m_raysPerCluster = NTNRuralLOS.at(freqBand).at(
2320 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2321 table3gpp->m_cDS =
2322 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2323 table3gpp->m_cASD =
2324 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2325 table3gpp->m_cASA =
2326 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2327 table3gpp->m_cZSA =
2328 NTNRuralLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2329 table3gpp->m_perClusterShadowingStd = NTNRuralLOS.at(freqBand).at(
2330 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2331
2332 for (uint8_t row = 0; row < 7; row++)
2333 {
2334 for (uint8_t column = 0; column < 7; column++)
2335 {
2336 table3gpp->m_sqrtC[row][column] = sqrtC_NTN_Rural_LOS[row][column];
2337 }
2338 }
2339 }
2340 else if (channelCondition->IsNlos())
2341 {
2342 table3gpp->m_uLgDS =
2343 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgDS];
2344 table3gpp->m_sigLgDS =
2345 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgDS];
2346 table3gpp->m_uLgASD =
2347 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASD];
2348 table3gpp->m_sigLgASD =
2349 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASD];
2350 table3gpp->m_uLgASA =
2351 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgASA];
2352 table3gpp->m_sigLgASA =
2353 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgASA];
2354 table3gpp->m_uLgZSA =
2355 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSA];
2356 table3gpp->m_sigLgZSA =
2357 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSA];
2358 table3gpp->m_uLgZSD =
2359 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uLgZSD];
2360 table3gpp->m_sigLgZSD =
2361 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigLgZSD];
2362 table3gpp->m_uK =
2363 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uK];
2364 table3gpp->m_sigK =
2365 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigK];
2366 table3gpp->m_rTau =
2367 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::rTau];
2368 table3gpp->m_uXpr =
2369 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::uXpr];
2370 table3gpp->m_sigXpr =
2371 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::sigXpr];
2372 table3gpp->m_numOfCluster =
2373 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::numOfCluster];
2374 table3gpp->m_raysPerCluster = NTNRuralNLOS.at(freqBand).at(
2375 elevAngleQuantized)[Table3gppParams::raysPerCluster];
2376 table3gpp->m_cDS =
2377 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cDS];
2378 table3gpp->m_cASD =
2379 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASD];
2380 table3gpp->m_cASA =
2381 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cASA];
2382 table3gpp->m_cZSA =
2383 NTNRuralNLOS.at(freqBand).at(elevAngleQuantized)[Table3gppParams::cZSA];
2384 table3gpp->m_perClusterShadowingStd = NTNRuralNLOS.at(freqBand).at(
2385 elevAngleQuantized)[Table3gppParams::perClusterShadowingStd];
2386
2387 if (freqBand == "S")
2388 {
2389 for (uint8_t row = 0; row < 6; row++)
2390 {
2391 for (uint8_t column = 0; column < 6; column++)
2392 {
2393 table3gpp->m_sqrtC[row][column] =
2394 sqrtC_NTN_Rural_NLOS_S.at(elevAngleQuantized)[row][column];
2395 }
2396 }
2397 }
2398 else if (freqBand == "Ka")
2399 {
2400 for (uint8_t row = 0; row < 6; row++)
2401 {
2402 for (uint8_t column = 0; column < 6; column++)
2403 {
2404 table3gpp->m_sqrtC[row][column] =
2405 sqrtC_NTN_Rural_NLOS_Ka.at(elevAngleQuantized)[row][column];
2406 }
2407 }
2408 }
2409 }
2410 }
2411 // Parameters that should be set to -inf are instead set to the minimum
2412 // value of double
2413 if (isSatellite)
2414 {
2415 table3gpp->m_uLgASD = std::numeric_limits<double>::min();
2416 table3gpp->m_sigLgASD = 0;
2417 table3gpp->m_uLgZSD = std::numeric_limits<double>::min();
2418 table3gpp->m_sigLgZSD = 0;
2419 }
2420 }
2421 else
2422 {
2423 NS_FATAL_ERROR("unknown scenarios");
2424 }
2425
2426 return table3gpp;
2427}
2428
2429bool
2431 Ptr<const ChannelCondition> channelCondition) const
2432{
2433 NS_LOG_FUNCTION(this);
2434
2435 bool update = false;
2436
2437 // if the channel condition is different the channel has to be updated
2438 if (!channelCondition->IsEqual(channelParams->m_losCondition, channelParams->m_o2iCondition))
2439 {
2440 NS_LOG_DEBUG("Update the channel condition");
2441 update = true;
2442 }
2443
2444 // if the coherence time is over the channel has to be updated
2445 if (!m_updatePeriod.IsZero() &&
2446 Simulator::Now() - channelParams->m_generatedTime > m_updatePeriod)
2447 {
2448 NS_LOG_DEBUG("Generation time " << channelParams->m_generatedTime.As(Time::NS) << " now "
2449 << Now().As(Time::NS));
2450 update = true;
2451 }
2452
2453 return update;
2454}
2455
2456bool
2458 Ptr<const ChannelMatrix> channelMatrix)
2459{
2460 return channelParams->m_generatedTime > channelMatrix->m_generatedTime;
2461}
2462
2463bool
2466 Ptr<const ChannelMatrix> channelMatrix)
2467{
2468 // This allows changing the number of antenna ports during execution,
2469 // which is used by nr's initial association.
2470 size_t sAntNumElems = aAntenna->GetNumElems();
2471 size_t uAntNumElems = bAntenna->GetNumElems();
2472 size_t chanNumRows = channelMatrix->m_channel.GetNumRows();
2473 size_t chanNumCols = channelMatrix->m_channel.GetNumCols();
2474 return ((uAntNumElems != chanNumRows) || (sAntNumElems != chanNumCols)) &&
2475 ((uAntNumElems != chanNumCols) || (sAntNumElems != chanNumRows));
2476}
2477
2483{
2484 NS_LOG_FUNCTION(this);
2485
2486 // Compute the channel params key. The key is reciprocal, i.e., key (a, b) = key (b, a)
2487 uint64_t channelParamsKey =
2488 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
2489 // Compute the channel matrix key. The key is reciprocal, i.e., key (a, b) = key (b, a)
2490 uint64_t channelMatrixKey = GetKey(aAntenna->GetId(), bAntenna->GetId());
2491
2492 // retrieve the channel condition
2493 Ptr<const ChannelCondition> condition =
2494 m_channelConditionModel->GetChannelCondition(aMob, bMob);
2495
2496 // Check if the channel is present in the map and return it, otherwise
2497 // generate a new channel
2498 bool updateParams = false;
2499 bool updateMatrix = false;
2500 bool notFoundParams = false;
2501 bool notFoundMatrix = false;
2502 Ptr<ChannelMatrix> channelMatrix;
2503 Ptr<ThreeGppChannelParams> channelParams;
2504
2505 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
2506 {
2507 channelParams = m_channelParamsMap[channelParamsKey];
2508 // check if it has to be updated
2509 updateParams = ChannelParamsNeedsUpdate(channelParams, condition);
2510 }
2511 else
2512 {
2513 NS_LOG_DEBUG("channel params not found");
2514 notFoundParams = true;
2515 }
2516
2517 // get the 3GPP parameters
2518 Ptr<const ParamsTable> table3gpp = GetThreeGppTable(aMob, bMob, condition);
2519
2520 if (notFoundParams || updateParams)
2521 {
2522 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
2523 // Step 5: Generate Delays.
2524 // Step 6: Generate cluster powers.
2525 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
2526 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
2527 // shuffle all the arrays to perform random coupling
2528 // Step 9: Generate the cross polarization power ratios
2529 // Step 10: Draw initial phases
2530 channelParams = GenerateChannelParameters(condition, table3gpp, aMob, bMob);
2531 // store or replace the channel parameters
2532 m_channelParamsMap[channelParamsKey] = channelParams;
2533 }
2534
2535 if (m_channelMatrixMap.find(channelMatrixKey) != m_channelMatrixMap.end())
2536 {
2537 // channel matrix present in the map
2538 NS_LOG_DEBUG("channel matrix present in the map");
2539 channelMatrix = m_channelMatrixMap[channelMatrixKey];
2540 updateMatrix = ChannelMatrixNeedsUpdate(channelParams, channelMatrix);
2541 updateMatrix |= AntennaSetupChanged(aAntenna, bAntenna, channelMatrix);
2542 }
2543 else
2544 {
2545 NS_LOG_DEBUG("channel matrix not found");
2546 notFoundMatrix = true;
2547 }
2548
2549 // If the channel is not present in the map or if it has to be updated
2550 // generate a new realization
2551 if (notFoundMatrix || updateMatrix)
2552 {
2553 // channel matrix not found or has to be updated, generate a new one
2554 channelMatrix = GetNewChannel(channelParams, table3gpp, aMob, bMob, aAntenna, bAntenna);
2555 channelMatrix->m_antennaPair =
2556 std::make_pair(aAntenna->GetId(),
2557 bAntenna->GetId()); // save antenna pair, with the exact order of s and u
2558 // antennas at the moment of the channel generation
2559
2560 // store or replace the channel matrix in the channel map
2561 m_channelMatrixMap[channelMatrixKey] = channelMatrix;
2562 }
2563
2564 return channelMatrix;
2565}
2566
2569{
2570 NS_LOG_FUNCTION(this);
2571
2572 // Compute the channel key. The key is reciprocal, i.e., key (a, b) = key (b, a)
2573 uint64_t channelParamsKey =
2574 GetKey(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
2575
2576 if (m_channelParamsMap.find(channelParamsKey) != m_channelParamsMap.end())
2577 {
2578 return m_channelParamsMap.find(channelParamsKey)->second;
2579 }
2580 else
2581 {
2582 NS_LOG_WARN("Channel params map not found. Returning a nullptr.");
2583 return nullptr;
2584 }
2585}
2586
2589 const Ptr<const ParamsTable> table3gpp,
2590 const Ptr<const MobilityModel> aMob,
2591 const Ptr<const MobilityModel> bMob) const
2592{
2593 NS_LOG_FUNCTION(this);
2594 // create a channel matrix instance
2595 Ptr<ThreeGppChannelParams> channelParams = Create<ThreeGppChannelParams>();
2596 channelParams->m_generatedTime = Simulator::Now();
2597 channelParams->m_nodeIds =
2598 std::make_pair(aMob->GetObject<Node>()->GetId(), bMob->GetObject<Node>()->GetId());
2599 channelParams->m_losCondition = channelCondition->GetLosCondition();
2600 channelParams->m_o2iCondition = channelCondition->GetO2iCondition();
2601
2602 // Step 4: Generate large scale parameters. All LSPS are uncorrelated.
2603 DoubleVector LSPsIndep;
2604 DoubleVector LSPs;
2605 uint8_t paramNum = 6;
2606 if (channelParams->m_losCondition == ChannelCondition::LOS)
2607 {
2608 paramNum = 7;
2609 }
2610
2611 // Generate paramNum independent LSPs.
2612 for (uint8_t iter = 0; iter < paramNum; iter++)
2613 {
2614 LSPsIndep.push_back(m_normalRv->GetValue());
2615 }
2616 for (uint8_t row = 0; row < paramNum; row++)
2617 {
2618 double temp = 0;
2619 for (uint8_t column = 0; column < paramNum; column++)
2620 {
2621 temp += table3gpp->m_sqrtC[row][column] * LSPsIndep[column];
2622 }
2623 LSPs.push_back(temp);
2624 }
2625
2626 // NOTE the shadowing is generated in the propagation loss model
2627 double DS;
2628 double ASD;
2629 double ASA;
2630 double ZSA;
2631 double ZSD;
2632 double kFactor = 0;
2633 if (channelParams->m_losCondition == ChannelCondition::LOS)
2634 {
2635 kFactor = LSPs[1] * table3gpp->m_sigK + table3gpp->m_uK;
2636 DS = pow(10, LSPs[2] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
2637 ASD = pow(10, LSPs[3] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
2638 ASA = pow(10, LSPs[4] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
2639 ZSD = pow(10, LSPs[5] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
2640 ZSA = pow(10, LSPs[6] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
2641 }
2642 else
2643 {
2644 DS = pow(10, LSPs[1] * table3gpp->m_sigLgDS + table3gpp->m_uLgDS);
2645 ASD = pow(10, LSPs[2] * table3gpp->m_sigLgASD + table3gpp->m_uLgASD);
2646 ASA = pow(10, LSPs[3] * table3gpp->m_sigLgASA + table3gpp->m_uLgASA);
2647 ZSD = pow(10, LSPs[4] * table3gpp->m_sigLgZSD + table3gpp->m_uLgZSD);
2648 ZSA = pow(10, LSPs[5] * table3gpp->m_sigLgZSA + table3gpp->m_uLgZSA);
2649 }
2650 ASD = std::min(ASD, 104.0);
2651 ASA = std::min(ASA, 104.0);
2652 ZSD = std::min(ZSD, 52.0);
2653 ZSA = std::min(ZSA, 52.0);
2654
2655 // save DS and K_factor parameters in the structure
2656 channelParams->m_DS = DS;
2657 channelParams->m_K_factor = kFactor;
2658
2659 NS_LOG_INFO("K-factor=" << kFactor << ", DS=" << DS << ", ASD=" << ASD << ", ASA=" << ASA
2660 << ", ZSD=" << ZSD << ", ZSA=" << ZSA);
2661
2662 // Step 5: Generate Delays.
2663 DoubleVector clusterDelay;
2664 double minTau = 100.0;
2665 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2666 {
2667 double tau = -1 * table3gpp->m_rTau * DS * log(m_uniformRv->GetValue(0, 1)); //(7.5-1)
2668 if (minTau > tau)
2669 {
2670 minTau = tau;
2671 }
2672 clusterDelay.push_back(tau);
2673 }
2674
2675 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2676 {
2677 clusterDelay[cIndex] -= minTau;
2678 }
2679 std::sort(clusterDelay.begin(), clusterDelay.end()); //(7.5-2)
2680
2681 /* since the scaled Los delays are not to be used in cluster power generation,
2682 * we will generate cluster power first and resume to compute Los cluster delay later.*/
2683
2684 // Step 6: Generate cluster powers.
2685 DoubleVector clusterPower;
2686 double powerSum = 0;
2687 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2688 {
2689 double power =
2690 exp(-1 * clusterDelay[cIndex] * (table3gpp->m_rTau - 1) / table3gpp->m_rTau / DS) *
2691 pow(10,
2692 -1 * m_normalRv->GetValue() * table3gpp->m_perClusterShadowingStd / 10.0); //(7.5-5)
2693 powerSum += power;
2694 clusterPower.push_back(power);
2695 }
2696 channelParams->m_clusterPower = clusterPower;
2697
2698 double powerMax = 0;
2699
2700 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2701 {
2702 channelParams->m_clusterPower[cIndex] =
2703 channelParams->m_clusterPower[cIndex] / powerSum; //(7.5-6)
2704 }
2705
2706 DoubleVector clusterPowerForAngles; // this power is only for equation (7.5-9) and (7.5-14), not
2707 // for (7.5-22)
2708 if (channelParams->m_losCondition == ChannelCondition::LOS)
2709 {
2710 double kLinear = pow(10, kFactor / 10.0);
2711
2712 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2713 {
2714 if (cIndex == 0)
2715 {
2716 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
2717 (1 + kLinear) +
2718 kLinear / (1 + kLinear)); //(7.5-8)
2719 }
2720 else
2721 {
2722 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex] /
2723 (1 + kLinear)); //(7.5-8)
2724 }
2725 if (powerMax < clusterPowerForAngles[cIndex])
2726 {
2727 powerMax = clusterPowerForAngles[cIndex];
2728 }
2729 }
2730 }
2731 else
2732 {
2733 for (uint8_t cIndex = 0; cIndex < table3gpp->m_numOfCluster; cIndex++)
2734 {
2735 clusterPowerForAngles.push_back(channelParams->m_clusterPower[cIndex]); //(7.5-6)
2736 if (powerMax < clusterPowerForAngles[cIndex])
2737 {
2738 powerMax = clusterPowerForAngles[cIndex];
2739 }
2740 }
2741 }
2742
2743 // remove clusters with less than -25 dB power compared to the maxim cluster power;
2744 // double thresh = pow(10, -2.5);
2745 double thresh = 0.0032;
2746 for (uint8_t cIndex = table3gpp->m_numOfCluster; cIndex > 0; cIndex--)
2747 {
2748 if (clusterPowerForAngles[cIndex - 1] < thresh * powerMax)
2749 {
2750 clusterPowerForAngles.erase(clusterPowerForAngles.begin() + cIndex - 1);
2751 channelParams->m_clusterPower.erase(channelParams->m_clusterPower.begin() + cIndex - 1);
2752 clusterDelay.erase(clusterDelay.begin() + cIndex - 1);
2753 }
2754 }
2755
2756 NS_ASSERT(channelParams->m_clusterPower.size() < UINT8_MAX);
2757 channelParams->m_reducedClusterNumber = channelParams->m_clusterPower.size();
2758 // Resume step 5 to compute the delay for LoS condition.
2759 if (channelParams->m_losCondition == ChannelCondition::LOS)
2760 {
2761 double cTau =
2762 0.7705 - 0.0433 * kFactor + 2e-4 * pow(kFactor, 2) + 17e-6 * pow(kFactor, 3); //(7.5-3)
2763 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
2764 {
2765 clusterDelay[cIndex] = clusterDelay[cIndex] / cTau; //(7.5-4)
2766 }
2767 }
2768
2769 // Step 7: Generate arrival and departure angles for both azimuth and elevation.
2770
2771 double cNlos;
2772 // According to table 7.5-6, only cluster number equals to 8, 10, 11, 12, 19 and 20 is valid.
2773 // Not sure why the other cases are in Table 7.5-2.
2774 // Added case 2 and 3 for the NTN according to table 6.7.2-1aa (28.811)
2775 switch (table3gpp->m_numOfCluster) // Table 7.5-2
2776 {
2777 case 2:
2778 cNlos = 0.501;
2779 break;
2780 case 3:
2781 cNlos = 0.680;
2782 break;
2783 case 4:
2784 cNlos = 0.779;
2785 break;
2786 case 5:
2787 cNlos = 0.860;
2788 break;
2789 case 8:
2790 cNlos = 1.018;
2791 break;
2792 case 10:
2793 cNlos = 1.090;
2794 break;
2795 case 11:
2796 cNlos = 1.123;
2797 break;
2798 case 12:
2799 cNlos = 1.146;
2800 break;
2801 case 14:
2802 cNlos = 1.190;
2803 break;
2804 case 15:
2805 cNlos = 1.221;
2806 break;
2807 case 16:
2808 cNlos = 1.226;
2809 break;
2810 case 19:
2811 cNlos = 1.273;
2812 break;
2813 case 20:
2814 cNlos = 1.289;
2815 break;
2816 default:
2817 NS_FATAL_ERROR("Invalid cluster number");
2818 }
2819
2820 double cPhi = cNlos;
2821
2822 if (channelParams->m_losCondition == ChannelCondition::LOS)
2823 {
2824 cPhi *= (1.1035 - 0.028 * kFactor - 2e-3 * pow(kFactor, 2) +
2825 1e-4 * pow(kFactor, 3)); //(7.5-10))
2826 }
2827
2828 // Added case 2, 3 and 4 for the NTN according to table 6.7.2-1ab (28.811)
2829 switch (table3gpp->m_numOfCluster) // Table 7.5-4
2830 {
2831 case 2:
2832 cNlos = 0.430;
2833 break;
2834 case 3:
2835 cNlos = 0.594;
2836 break;
2837 case 4:
2838 cNlos = 0.697;
2839 break;
2840 case 8:
2841 cNlos = 0.889;
2842 break;
2843 case 10:
2844 cNlos = 0.957;
2845 break;
2846 case 11:
2847 cNlos = 1.031;
2848 break;
2849 case 12:
2850 cNlos = 1.104;
2851 break;
2852 case 15:
2853 cNlos = 1.1088;
2854 break;
2855 case 19:
2856 cNlos = 1.184;
2857 break;
2858 case 20:
2859 cNlos = 1.178;
2860 break;
2861 default:
2862 NS_FATAL_ERROR("Invalid cluster number");
2863 }
2864
2865 double cTheta = cNlos;
2866 if (channelCondition->IsLos())
2867 {
2868 cTheta *= (1.3086 + 0.0339 * kFactor - 0.0077 * pow(kFactor, 2) +
2869 2e-4 * pow(kFactor, 3)); //(7.5-15)
2870 }
2871
2872 DoubleVector clusterAoa;
2873 DoubleVector clusterAod;
2874 DoubleVector clusterZoa;
2875 DoubleVector clusterZod;
2876 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
2877 {
2878 double logCalc = -1 * log(clusterPowerForAngles[cIndex] / powerMax);
2879 double angle = 2 * sqrt(logCalc) / 1.4 / cPhi; //(7.5-9)
2880 clusterAoa.push_back(ASA * angle);
2881 clusterAod.push_back(ASD * angle);
2882 angle = logCalc / cTheta; //(7.5-14)
2883 clusterZoa.push_back(ZSA * angle);
2884 clusterZod.push_back(ZSD * angle);
2885 }
2886
2887 Angles sAngle(bMob->GetPosition(), aMob->GetPosition());
2888 Angles uAngle(aMob->GetPosition(), bMob->GetPosition());
2889
2890 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
2891 {
2892 int Xn = 1;
2893 if (m_uniformRv->GetValue(0, 1) < 0.5)
2894 {
2895 Xn = -1;
2896 }
2897 clusterAoa[cIndex] = clusterAoa[cIndex] * Xn + (m_normalRv->GetValue() * ASA / 7.0) +
2898 RadiansToDegrees(uAngle.GetAzimuth()); //(7.5-11)
2899 clusterAod[cIndex] = clusterAod[cIndex] * Xn + (m_normalRv->GetValue() * ASD / 7.0) +
2900 RadiansToDegrees(sAngle.GetAzimuth());
2901 if (channelCondition->IsO2i())
2902 {
2903 clusterZoa[cIndex] =
2904 clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) + 90; //(7.5-16)
2905 }
2906 else
2907 {
2908 clusterZoa[cIndex] = clusterZoa[cIndex] * Xn + (m_normalRv->GetValue() * ZSA / 7.0) +
2909 RadiansToDegrees(uAngle.GetInclination()); //(7.5-16)
2910 }
2911 clusterZod[cIndex] = clusterZod[cIndex] * Xn + (m_normalRv->GetValue() * ZSD / 7.0) +
2913 table3gpp->m_offsetZOD; //(7.5-19)
2914 }
2915
2916 if (channelParams->m_losCondition == ChannelCondition::LOS)
2917 {
2918 // The 7.5-12 can be rewrite as Theta_n,ZOA = Theta_n,ZOA - (Theta_1,ZOA - Theta_LOS,ZOA) =
2919 // Theta_n,ZOA - diffZOA, Similar as AOD, ZSA and ZSD.
2920 double diffAoa = clusterAoa[0] - RadiansToDegrees(uAngle.GetAzimuth());
2921 double diffAod = clusterAod[0] - RadiansToDegrees(sAngle.GetAzimuth());
2922 double diffZsa = clusterZoa[0] - RadiansToDegrees(uAngle.GetInclination());
2923 double diffZsd = clusterZod[0] - RadiansToDegrees(sAngle.GetInclination());
2924
2925 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
2926 {
2927 clusterAoa[cIndex] -= diffAoa; //(7.5-12)
2928 clusterAod[cIndex] -= diffAod;
2929 clusterZoa[cIndex] -= diffZsa; //(7.5-17)
2930 clusterZod[cIndex] -= diffZsd;
2931 }
2932 }
2933
2934 double sizeTemp = clusterZoa.size();
2935 for (uint8_t ind = 0; ind < 4; ind++)
2936 {
2937 DoubleVector angleDegree;
2938 switch (ind)
2939 {
2940 case 0:
2941 angleDegree = clusterAoa;
2942 break;
2943 case 1:
2944 angleDegree = clusterZoa;
2945 break;
2946 case 2:
2947 angleDegree = clusterAod;
2948 break;
2949 case 3:
2950 angleDegree = clusterZod;
2951 break;
2952 default:
2953 NS_FATAL_ERROR("Programming Error");
2954 }
2955 for (uint8_t nIndex = 0; nIndex < sizeTemp; nIndex++)
2956 {
2957 while (angleDegree[nIndex] > 360)
2958 {
2959 angleDegree[nIndex] -= 360;
2960 }
2961
2962 while (angleDegree[nIndex] < 0)
2963 {
2964 angleDegree[nIndex] += 360;
2965 }
2966
2967 if (ind == 1 || ind == 3)
2968 {
2969 if (angleDegree[nIndex] > 180)
2970 {
2971 angleDegree[nIndex] = 360 - angleDegree[nIndex];
2972 }
2973 }
2974 }
2975 switch (ind)
2976 {
2977 case 0:
2978 clusterAoa = angleDegree;
2979 break;
2980 case 1:
2981 clusterZoa = angleDegree;
2982 break;
2983 case 2:
2984 clusterAod = angleDegree;
2985 break;
2986 case 3:
2987 clusterZod = angleDegree;
2988 break;
2989 default:
2990 NS_FATAL_ERROR("Programming Error");
2991 }
2992 }
2993
2994 DoubleVector attenuationDb;
2995 if (m_blockage)
2996 {
2997 attenuationDb = CalcAttenuationOfBlockage(channelParams, clusterAoa, clusterZoa);
2998 for (uint8_t cInd = 0; cInd < channelParams->m_reducedClusterNumber; cInd++)
2999 {
3000 channelParams->m_clusterPower[cInd] =
3001 channelParams->m_clusterPower[cInd] / pow(10, attenuationDb[cInd] / 10.0);
3002 }
3003 }
3004 else
3005 {
3006 attenuationDb.push_back(0);
3007 }
3008
3009 // store attenuation
3010 channelParams->m_attenuation_dB = attenuationDb;
3011
3012 // Step 8: Coupling of rays within a cluster for both azimuth and elevation
3013 // shuffle all the arrays to perform random coupling
3015 channelParams->m_reducedClusterNumber,
3016 DoubleVector(table3gpp->m_raysPerCluster,
3017 0)); // rayAoaRadian[n][m], where n is cluster index, m is ray index
3019 channelParams->m_reducedClusterNumber,
3020 DoubleVector(table3gpp->m_raysPerCluster,
3021 0)); // rayAodRadian[n][m], where n is cluster index, m is ray index
3023 channelParams->m_reducedClusterNumber,
3024 DoubleVector(table3gpp->m_raysPerCluster,
3025 0)); // rayZoaRadian[n][m], where n is cluster index, m is ray index
3027 channelParams->m_reducedClusterNumber,
3028 DoubleVector(table3gpp->m_raysPerCluster,
3029 0)); // rayZodRadian[n][m], where n is cluster index, m is ray index
3030
3031 const double pow10_uLgZSD = pow(10, table3gpp->m_uLgZSD);
3032 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
3033 {
3034 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
3035 {
3036 double tempAoa = clusterAoa[nInd] + table3gpp->m_cASA * offSetAlpha[mInd]; //(7.5-13)
3037 double tempZoa = clusterZoa[nInd] + table3gpp->m_cZSA * offSetAlpha[mInd]; //(7.5-18)
3038 std::tie(rayAoaRadian[nInd][mInd], rayZoaRadian[nInd][mInd]) =
3039 WrapAngles(DegreesToRadians(tempAoa), DegreesToRadians(tempZoa));
3040
3041 double tempAod = clusterAod[nInd] + table3gpp->m_cASD * offSetAlpha[mInd]; //(7.5-13)
3042 double tempZod = clusterZod[nInd] + 0.375 * pow10_uLgZSD * offSetAlpha[mInd]; //(7.5-20)
3043 std::tie(rayAodRadian[nInd][mInd], rayZodRadian[nInd][mInd]) =
3044 WrapAngles(DegreesToRadians(tempAod), DegreesToRadians(tempZod));
3045 }
3046 }
3047
3048 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3049 {
3050 Shuffle(&rayAodRadian[cIndex][0], &rayAodRadian[cIndex][table3gpp->m_raysPerCluster]);
3051 Shuffle(&rayAoaRadian[cIndex][0], &rayAoaRadian[cIndex][table3gpp->m_raysPerCluster]);
3052 Shuffle(&rayZodRadian[cIndex][0], &rayZodRadian[cIndex][table3gpp->m_raysPerCluster]);
3053 Shuffle(&rayZoaRadian[cIndex][0], &rayZoaRadian[cIndex][table3gpp->m_raysPerCluster]);
3054 }
3055
3056 // store values
3057 channelParams->m_rayAodRadian = rayAodRadian;
3058 channelParams->m_rayAoaRadian = rayAoaRadian;
3059 channelParams->m_rayZodRadian = rayZodRadian;
3060 channelParams->m_rayZoaRadian = rayZoaRadian;
3061
3062 // Step 9: Generate the cross polarization power ratios
3063 // Step 10: Draw initial phases
3064
3065 // vector containing the cross polarization power ratios, as defined by 7.5-21
3066 auto& crossPolarizationPowerRatios = channelParams->m_crossPolarizationPowerRatios;
3067 // rayAoaRadian[n][m], where n is cluster index, m is ray index
3068 auto& clusterPhase = channelParams->m_clusterPhase;
3069
3070 const double uXprLinear = pow(10, table3gpp->m_uXpr / 10.0); // convert to linear
3071 const double sigXprLinear = pow(10, table3gpp->m_sigXpr / 10.0); // convert to linear
3072
3073 // store the PHI values for all the possible combination of polarization
3074 clusterPhase.resize(channelParams->m_reducedClusterNumber);
3075 crossPolarizationPowerRatios.resize(channelParams->m_reducedClusterNumber);
3076 for (uint8_t nInd = 0; nInd < channelParams->m_reducedClusterNumber; nInd++)
3077 {
3078 clusterPhase[nInd].resize(table3gpp->m_raysPerCluster);
3079 crossPolarizationPowerRatios[nInd].resize(table3gpp->m_raysPerCluster);
3080 for (uint8_t mInd = 0; mInd < table3gpp->m_raysPerCluster; mInd++)
3081 {
3082 clusterPhase[nInd][mInd].resize(4);
3083 // used to store the XPR values
3084 crossPolarizationPowerRatios[nInd][mInd] =
3085 std::pow(10, (m_normalRv->GetValue() * sigXprLinear + uXprLinear) / 10.0);
3086 for (uint8_t pInd = 0; pInd < 4; pInd++)
3087 {
3088 // used to store the PHI values
3089 clusterPhase[nInd][mInd][pInd] = m_uniformRv->GetValue(-1 * M_PI, M_PI);
3090 }
3091 }
3092 }
3093
3094 uint8_t cluster1st = 0;
3095 uint8_t cluster2nd = 0; // first and second strongest cluster;
3096 double maxPower = 0;
3097 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3098 {
3099 if (maxPower < channelParams->m_clusterPower[cIndex])
3100 {
3101 maxPower = channelParams->m_clusterPower[cIndex];
3102 cluster1st = cIndex;
3103 }
3104 }
3105 channelParams->m_cluster1st = cluster1st;
3106 maxPower = 0;
3107 for (uint8_t cIndex = 0; cIndex < channelParams->m_reducedClusterNumber; cIndex++)
3108 {
3109 if (maxPower < channelParams->m_clusterPower[cIndex] && cluster1st != cIndex)
3110 {
3111 maxPower = channelParams->m_clusterPower[cIndex];
3112 cluster2nd = cIndex;
3113 }
3114 }
3115 channelParams->m_cluster2nd = cluster2nd;
3116
3117 NS_LOG_INFO("1st strongest cluster:" << +cluster1st
3118 << ", 2nd strongest cluster:" << +cluster2nd);
3119
3120 // store the delays and the angles for the subclusters
3121 if (cluster1st == cluster2nd)
3122 {
3123 clusterDelay.push_back(clusterDelay[cluster1st] + 1.28 * table3gpp->m_cDS);
3124 clusterDelay.push_back(clusterDelay[cluster1st] + 2.56 * table3gpp->m_cDS);
3125
3126 clusterAoa.push_back(clusterAoa[cluster1st]);
3127 clusterAoa.push_back(clusterAoa[cluster1st]);
3128
3129 clusterZoa.push_back(clusterZoa[cluster1st]);
3130 clusterZoa.push_back(clusterZoa[cluster1st]);
3131
3132 clusterAod.push_back(clusterAod[cluster1st]);
3133 clusterAod.push_back(clusterAod[cluster1st]);
3134
3135 clusterZod.push_back(clusterZod[cluster1st]);
3136 clusterZod.push_back(clusterZod[cluster1st]);
3137 }
3138 else
3139 {
3140 double min;
3141 double max;
3142 if (cluster1st < cluster2nd)
3143 {
3144 min = cluster1st;
3145 max = cluster2nd;
3146 }
3147 else
3148 {
3149 min = cluster2nd;
3150 max = cluster1st;
3151 }
3152 clusterDelay.push_back(clusterDelay[min] + 1.28 * table3gpp->m_cDS);
3153 clusterDelay.push_back(clusterDelay[min] + 2.56 * table3gpp->m_cDS);
3154 clusterDelay.push_back(clusterDelay[max] + 1.28 * table3gpp->m_cDS);
3155 clusterDelay.push_back(clusterDelay[max] + 2.56 * table3gpp->m_cDS);
3156
3157 clusterAoa.push_back(clusterAoa[min]);
3158 clusterAoa.push_back(clusterAoa[min]);
3159 clusterAoa.push_back(clusterAoa[max]);
3160 clusterAoa.push_back(clusterAoa[max]);
3161
3162 clusterZoa.push_back(clusterZoa[min]);
3163 clusterZoa.push_back(clusterZoa[min]);
3164 clusterZoa.push_back(clusterZoa[max]);
3165 clusterZoa.push_back(clusterZoa[max]);
3166
3167 clusterAod.push_back(clusterAod[min]);
3168 clusterAod.push_back(clusterAod[min]);
3169 clusterAod.push_back(clusterAod[max]);
3170 clusterAod.push_back(clusterAod[max]);
3171
3172 clusterZod.push_back(clusterZod[min]);
3173 clusterZod.push_back(clusterZod[min]);
3174 clusterZod.push_back(clusterZod[max]);
3175 clusterZod.push_back(clusterZod[max]);
3176 }
3177
3178 channelParams->m_delay = clusterDelay;
3179 channelParams->m_angle.clear();
3180 channelParams->m_angle.push_back(clusterAoa);
3181 channelParams->m_angle.push_back(clusterZoa);
3182 channelParams->m_angle.push_back(clusterAod);
3183 channelParams->m_angle.push_back(clusterZod);
3184
3185 // Precompute angles sincos
3186 channelParams->m_cachedAngleSincos.resize(channelParams->m_angle.size());
3187 for (size_t direction = 0; direction < channelParams->m_angle.size(); direction++)
3188 {
3189 channelParams->m_cachedAngleSincos[direction].resize(
3190 channelParams->m_angle[direction].size());
3191 for (size_t cluster = 0; cluster < channelParams->m_angle[direction].size(); cluster++)
3192 {
3193 channelParams->m_cachedAngleSincos[direction][cluster] = {
3194 sin(channelParams->m_angle[direction][cluster] * DEG2RAD),
3195 cos(channelParams->m_angle[direction][cluster] * DEG2RAD)};
3196 }
3197 }
3198
3199 // Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
3200 // These terms account for an additional Doppler contribution due to the
3201 // presence of moving objects in the surrounding environment, such as in
3202 // vehicular scenarios.
3203 // This contribution is applied only to the delayed (reflected) paths and
3204 // must be properly configured by setting the value of
3205 // m_vScatt, which is defined as "maximum speed of the vehicle in the
3206 // layout".
3207 // By default, m_vScatt is set to 0, so there is no additional Doppler
3208 // contribution.
3209
3210 DoubleVector dopplerTermAlpha;
3211 DoubleVector dopplerTermD;
3212
3213 // 2 or 4 is added to account for additional subrays for the 1st and 2nd clusters, if there is
3214 // only one cluster then would be added 2 more subrays (see creation of Husn channel matrix)
3215 uint8_t updatedClusterNumber = (channelParams->m_reducedClusterNumber == 1)
3216 ? channelParams->m_reducedClusterNumber + 2
3217 : channelParams->m_reducedClusterNumber + 4;
3218
3219 for (uint8_t cIndex = 0; cIndex < updatedClusterNumber; cIndex++)
3220 {
3221 double alpha = 0;
3222 double D = 0;
3223 if (cIndex != 0)
3224 {
3225 alpha = m_uniformRvDoppler->GetValue(-1, 1);
3227 }
3228 dopplerTermAlpha.push_back(alpha);
3229 dopplerTermD.push_back(D);
3230 }
3231 channelParams->m_alpha = dopplerTermAlpha;
3232 channelParams->m_D = dopplerTermD;
3233
3234 return channelParams;
3235}
3236
3239 Ptr<const ParamsTable> table3gpp,
3240 const Ptr<const MobilityModel> sMob,
3241 const Ptr<const MobilityModel> uMob,
3243 Ptr<const PhasedArrayModel> uAntenna) const
3244{
3245 NS_LOG_FUNCTION(this);
3246
3247 NS_ASSERT_MSG(m_frequency > 0.0, "Set the operating frequency first!");
3248
3249 // create a channel matrix instance
3250 Ptr<ChannelMatrix> channelMatrix = Create<ChannelMatrix>();
3251 channelMatrix->m_generatedTime = Simulator::Now();
3252 // save in which order is generated this matrix
3253 channelMatrix->m_nodeIds =
3254 std::make_pair(sMob->GetObject<Node>()->GetId(), uMob->GetObject<Node>()->GetId());
3255 // check if channelParams structure is generated in direction s-to-u or u-to-s
3256 bool isSameDirection = (channelParams->m_nodeIds == channelMatrix->m_nodeIds);
3257
3262
3263 // if channel params is generated in the same direction in which we
3264 // generate the channel matrix, angles and zenith od departure and arrival are ok,
3265 // just set them to corresponding variable that will be used for the generation
3266 // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
3267 if (isSameDirection)
3268 {
3269 rayAodRadian = channelParams->m_rayAodRadian;
3270 rayAoaRadian = channelParams->m_rayAoaRadian;
3271 rayZodRadian = channelParams->m_rayZodRadian;
3272 rayZoaRadian = channelParams->m_rayZoaRadian;
3273 }
3274 else
3275 {
3276 rayAodRadian = channelParams->m_rayAoaRadian;
3277 rayAoaRadian = channelParams->m_rayAodRadian;
3278 rayZodRadian = channelParams->m_rayZoaRadian;
3279 rayZoaRadian = channelParams->m_rayZodRadian;
3280 }
3281
3282 // Step 11: Generate channel coefficients for each cluster n and each receiver
3283 // and transmitter element pair u,s.
3284 // where n is cluster index, u and s are receive and transmit antenna element.
3285 size_t uSize = uAntenna->GetNumElems();
3286 size_t sSize = sAntenna->GetNumElems();
3287
3288 // NOTE: Since each of the strongest 2 clusters are divided into 3 sub-clusters,
3289 // the total cluster will generally be numReducedCLuster + 4.
3290 // However, it might be that m_cluster1st = m_cluster2nd. In this case the
3291 // total number of clusters will be numReducedCLuster + 2.
3292 uint16_t numOverallCluster = (channelParams->m_cluster1st != channelParams->m_cluster2nd)
3293 ? channelParams->m_reducedClusterNumber + 4
3294 : channelParams->m_reducedClusterNumber + 2;
3295 Complex3DVector hUsn(uSize, sSize, numOverallCluster); // channel coefficient hUsn (u, s, n);
3296 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPhase.size());
3297 NS_ASSERT(channelParams->m_reducedClusterNumber <= channelParams->m_clusterPower.size());
3298 NS_ASSERT(channelParams->m_reducedClusterNumber <=
3299 channelParams->m_crossPolarizationPowerRatios.size());
3300 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZoaRadian.size());
3301 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayZodRadian.size());
3302 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAoaRadian.size());
3303 NS_ASSERT(channelParams->m_reducedClusterNumber <= rayAodRadian.size());
3304 NS_ASSERT(table3gpp->m_raysPerCluster <= channelParams->m_clusterPhase[0].size());
3305 NS_ASSERT(table3gpp->m_raysPerCluster <=
3306 channelParams->m_crossPolarizationPowerRatios[0].size());
3307 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZoaRadian[0].size());
3308 NS_ASSERT(table3gpp->m_raysPerCluster <= rayZodRadian[0].size());
3309 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAoaRadian[0].size());
3310 NS_ASSERT(table3gpp->m_raysPerCluster <= rayAodRadian[0].size());
3311
3312 double x = sMob->GetPosition().x - uMob->GetPosition().x;
3313 double y = sMob->GetPosition().y - uMob->GetPosition().y;
3314 double distance2D = sqrt(x * x + y * y);
3315 // NOTE we assume hUT = min (height(a), height(b)) and
3316 // hBS = max (height (a), height (b))
3317 double hUt = std::min(sMob->GetPosition().z, uMob->GetPosition().z);
3318 double hBs = std::max(sMob->GetPosition().z, uMob->GetPosition().z);
3319 // compute the 3D distance using eq. 7.4-1
3320 double distance3D = std::sqrt(distance2D * distance2D + (hBs - hUt) * (hBs - hUt));
3321
3322 Angles sAngle(uMob->GetPosition(), sMob->GetPosition());
3323 Angles uAngle(sMob->GetPosition(), uMob->GetPosition());
3324
3325 Double2DVector sinCosA; // cached multiplications of sin and cos of the ZoA and AoA angles
3326 Double2DVector sinSinA; // cached multiplications of sines of the ZoA and AoA angles
3327 Double2DVector cosZoA; // cached cos of the ZoA angle
3328 Double2DVector sinCosD; // cached multiplications of sin and cos of the ZoD and AoD angles
3329 Double2DVector sinSinD; // cached multiplications of the cosines of the ZoA and AoA angles
3330 Double2DVector cosZoD; // cached cos of the ZoD angle
3331
3332 // contains part of the ray expression, cached as independent from the u- and s-indexes,
3333 // but calculate it for different polarization angles of s and u
3334 std::map<std::pair<uint8_t, uint8_t>, Complex2DVector> raysPreComp;
3335 for (size_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
3336 {
3337 for (size_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
3338 {
3339 raysPreComp[std::make_pair(polSa, polUa)] =
3340 Complex2DVector(channelParams->m_reducedClusterNumber, table3gpp->m_raysPerCluster);
3341 }
3342 }
3343
3344 // resize to appropriate dimensions
3345 sinCosA.resize(channelParams->m_reducedClusterNumber);
3346 sinSinA.resize(channelParams->m_reducedClusterNumber);
3347 cosZoA.resize(channelParams->m_reducedClusterNumber);
3348 sinCosD.resize(channelParams->m_reducedClusterNumber);
3349 sinSinD.resize(channelParams->m_reducedClusterNumber);
3350 cosZoD.resize(channelParams->m_reducedClusterNumber);
3351 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3352 {
3353 sinCosA[nIndex].resize(table3gpp->m_raysPerCluster);
3354 sinSinA[nIndex].resize(table3gpp->m_raysPerCluster);
3355 cosZoA[nIndex].resize(table3gpp->m_raysPerCluster);
3356 sinCosD[nIndex].resize(table3gpp->m_raysPerCluster);
3357 sinSinD[nIndex].resize(table3gpp->m_raysPerCluster);
3358 cosZoD[nIndex].resize(table3gpp->m_raysPerCluster);
3359 }
3360 // pre-compute the terms which are independent from uIndex and sIndex
3361 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3362 {
3363 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3364 {
3365 DoubleVector initialPhase = channelParams->m_clusterPhase[nIndex][mIndex];
3366 NS_ASSERT(4 <= initialPhase.size());
3367 double k = channelParams->m_crossPolarizationPowerRatios[nIndex][mIndex];
3368
3369 // cache the component of the "rays" terms which depend on the random angle of arrivals
3370 // and departures and initial phases only
3371 for (uint8_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
3372 {
3373 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
3374 Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
3375 channelParams->m_rayZoaRadian[nIndex][mIndex]),
3376 polUa);
3377 for (uint8_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
3378 {
3379 auto [txFieldPatternPhi, txFieldPatternTheta] =
3380 sAntenna->GetElementFieldPattern(
3381 Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
3382 channelParams->m_rayZodRadian[nIndex][mIndex]),
3383 polSa);
3384 raysPreComp[std::make_pair(polSa, polUa)](nIndex, mIndex) =
3385 std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
3386 rxFieldPatternTheta * txFieldPatternTheta +
3387 std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
3388 std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
3389 std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
3390 std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
3391 std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
3392 rxFieldPatternPhi * txFieldPatternPhi;
3393 }
3394 }
3395
3396 // cache the component of the "rxPhaseDiff" terms which depend on the random angle of
3397 // arrivals only
3398 double sinRayZoa = sin(rayZoaRadian[nIndex][mIndex]);
3399 double sinRayAoa = sin(rayAoaRadian[nIndex][mIndex]);
3400 double cosRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
3401 sinCosA[nIndex][mIndex] = sinRayZoa * cosRayAoa;
3402 sinSinA[nIndex][mIndex] = sinRayZoa * sinRayAoa;
3403 cosZoA[nIndex][mIndex] = cos(rayZoaRadian[nIndex][mIndex]);
3404
3405 // cache the component of the "txPhaseDiff" terms which depend on the random angle of
3406 // departure only
3407 double sinRayZod = sin(rayZodRadian[nIndex][mIndex]);
3408 double sinRayAod = sin(rayAodRadian[nIndex][mIndex]);
3409 double cosRayAod = cos(rayAodRadian[nIndex][mIndex]);
3410 sinCosD[nIndex][mIndex] = sinRayZod * cosRayAod;
3411 sinSinD[nIndex][mIndex] = sinRayZod * sinRayAod;
3412 cosZoD[nIndex][mIndex] = cos(rayZodRadian[nIndex][mIndex]);
3413 }
3414 }
3415
3416 // The following for loops computes the channel coefficients
3417 // Keeps track of how many sub-clusters have been added up to now
3418 uint8_t numSubClustersAdded = 0;
3419 for (uint8_t nIndex = 0; nIndex < channelParams->m_reducedClusterNumber; nIndex++)
3420 {
3421 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
3422 {
3423 Vector uLoc = uAntenna->GetElementLocation(uIndex);
3424
3425 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
3426 {
3427 Vector sLoc = sAntenna->GetElementLocation(sIndex);
3428 // Compute the N-2 weakest cluster, assuming 0 slant angle and a
3429 // polarization slant angle configured in the array (7.5-22)
3430 if (nIndex != channelParams->m_cluster1st && nIndex != channelParams->m_cluster2nd)
3431 {
3432 std::complex<double> rays(0, 0);
3433 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3434 {
3435 // lambda_0 is accounted in the antenna spacing uLoc and sLoc.
3436 double rxPhaseDiff =
3437 2 * M_PI *
3438 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
3439 cosZoA[nIndex][mIndex] * uLoc.z);
3440
3441 double txPhaseDiff =
3442 2 * M_PI *
3443 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
3444 cosZoD[nIndex][mIndex] * sLoc.z);
3445 // NOTE Doppler is computed in the CalcBeamformingGain function and is
3446 // simplified to only account for the center angle of each cluster.
3447 rays += raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
3448 uAntenna->GetElemPol(uIndex))](nIndex,
3449 mIndex) *
3450 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
3451 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
3452 }
3453 rays *=
3454 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
3455 hUsn(uIndex, sIndex, nIndex) = rays;
3456 }
3457 else //(7.5-28)
3458 {
3459 std::complex<double> raysSub1(0, 0);
3460 std::complex<double> raysSub2(0, 0);
3461 std::complex<double> raysSub3(0, 0);
3462
3463 for (uint8_t mIndex = 0; mIndex < table3gpp->m_raysPerCluster; mIndex++)
3464 {
3465 // ZML:Just remind me that the angle offsets for the 3 subclusters were not
3466 // generated correctly.
3467 double rxPhaseDiff =
3468 2 * M_PI *
3469 (sinCosA[nIndex][mIndex] * uLoc.x + sinSinA[nIndex][mIndex] * uLoc.y +
3470 cosZoA[nIndex][mIndex] * uLoc.z);
3471
3472 double txPhaseDiff =
3473 2 * M_PI *
3474 (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
3475 cosZoD[nIndex][mIndex] * sLoc.z);
3476
3477 std::complex<double> raySub =
3478 raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
3479 uAntenna->GetElemPol(uIndex))](nIndex,
3480 mIndex) *
3481 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
3482 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
3483
3484 switch (mIndex)
3485 {
3486 case 9:
3487 case 10:
3488 case 11:
3489 case 12:
3490 case 17:
3491 case 18:
3492 raysSub2 += raySub;
3493 break;
3494 case 13:
3495 case 14:
3496 case 15:
3497 case 16:
3498 raysSub3 += raySub;
3499 break;
3500 default: // case 1,2,3,4,5,6,7,8,19,20
3501 raysSub1 += raySub;
3502 break;
3503 }
3504 }
3505 raysSub1 *=
3506 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
3507 raysSub2 *=
3508 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
3509 raysSub3 *=
3510 sqrt(channelParams->m_clusterPower[nIndex] / table3gpp->m_raysPerCluster);
3511 hUsn(uIndex, sIndex, nIndex) = raysSub1;
3512 hUsn(uIndex,
3513 sIndex,
3514 channelParams->m_reducedClusterNumber + numSubClustersAdded) = raysSub2;
3515 hUsn(uIndex,
3516 sIndex,
3517 channelParams->m_reducedClusterNumber + numSubClustersAdded + 1) =
3518 raysSub3;
3519 }
3520 }
3521 }
3522 if (nIndex == channelParams->m_cluster1st || nIndex == channelParams->m_cluster2nd)
3523 {
3524 numSubClustersAdded += 2;
3525 }
3526 }
3527
3528 if (channelParams->m_losCondition == ChannelCondition::LOS) //(7.5-29) && (7.5-30)
3529 {
3530 double lambda = 3.0e8 / m_frequency; // the wavelength of the carrier frequency
3531 std::complex<double> phaseDiffDueToDistance(cos(-2 * M_PI * distance3D / lambda),
3532 sin(-2 * M_PI * distance3D / lambda));
3533
3534 const double sinUAngleIncl = sin(uAngle.GetInclination());
3535 const double cosUAngleIncl = cos(uAngle.GetInclination());
3536 const double sinUAngleAz = sin(uAngle.GetAzimuth());
3537 const double cosUAngleAz = cos(uAngle.GetAzimuth());
3538 const double sinSAngleIncl = sin(sAngle.GetInclination());
3539 const double cosSAngleIncl = cos(sAngle.GetInclination());
3540 const double sinSAngleAz = sin(sAngle.GetAzimuth());
3541 const double cosSAngleAz = cos(sAngle.GetAzimuth());
3542
3543 for (size_t uIndex = 0; uIndex < uSize; uIndex++)
3544 {
3545 Vector uLoc = uAntenna->GetElementLocation(uIndex);
3546 double rxPhaseDiff = 2 * M_PI *
3547 (sinUAngleIncl * cosUAngleAz * uLoc.x +
3548 sinUAngleIncl * sinUAngleAz * uLoc.y + cosUAngleIncl * uLoc.z);
3549
3550 for (size_t sIndex = 0; sIndex < sSize; sIndex++)
3551 {
3552 Vector sLoc = sAntenna->GetElementLocation(sIndex);
3553 std::complex<double> ray(0, 0);
3554 double txPhaseDiff =
3555 2 * M_PI *
3556 (sinSAngleIncl * cosSAngleAz * sLoc.x + sinSAngleIncl * sinSAngleAz * sLoc.y +
3557 cosSAngleIncl * sLoc.z);
3558
3559 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
3560 Angles(uAngle.GetAzimuth(), uAngle.GetInclination()),
3561 uAntenna->GetElemPol(uIndex));
3562 auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
3563 Angles(sAngle.GetAzimuth(), sAngle.GetInclination()),
3564 sAntenna->GetElemPol(sIndex));
3565
3566 ray = (rxFieldPatternTheta * txFieldPatternTheta -
3567 rxFieldPatternPhi * txFieldPatternPhi) *
3568 phaseDiffDueToDistance *
3569 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
3570 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
3571
3572 double kLinear = pow(10, channelParams->m_K_factor / 10.0);
3573 // the LOS path should be attenuated if blockage is enabled.
3574 hUsn(uIndex, sIndex, 0) =
3575 sqrt(1.0 / (kLinear + 1)) * hUsn(uIndex, sIndex, 0) +
3576 sqrt(kLinear / (1 + kLinear)) * ray /
3577 pow(10,
3578 channelParams->m_attenuation_dB[0] / 10.0); //(7.5-30) for tau = tau1
3579 for (size_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
3580 {
3581 hUsn(uIndex, sIndex, nIndex) *=
3582 sqrt(1.0 / (kLinear + 1)); //(7.5-30) for tau = tau2...tauN
3583 }
3584 }
3585 }
3586 }
3587
3588 NS_LOG_DEBUG("Husn (sAntenna, uAntenna):" << sAntenna->GetId() << ", " << uAntenna->GetId());
3589 for (size_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
3590 {
3591 for (size_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
3592 {
3593 for (size_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
3594 {
3595 NS_LOG_DEBUG(" " << hUsn(rowIdx, colIdx, cIndex) << ",");
3596 }
3597 }
3598 }
3599
3600 NS_LOG_INFO("size of coefficient matrix (rows, columns, clusters) = ("
3601 << hUsn.GetNumRows() << ", " << hUsn.GetNumCols() << ", " << hUsn.GetNumPages()
3602 << ")");
3603 channelMatrix->m_channel = hUsn;
3604 return channelMatrix;
3605}
3606
3607std::pair<double, double>
3608ThreeGppChannelModel::WrapAngles(double azimuthRad, double inclinationRad)
3609{
3610 inclinationRad = WrapTo2Pi(inclinationRad);
3611 if (inclinationRad > M_PI)
3612 {
3613 // inclination must be in [0, M_PI]
3614 inclinationRad -= M_PI;
3615 azimuthRad += M_PI;
3616 }
3617
3618 azimuthRad = WrapTo2Pi(azimuthRad);
3619
3620 NS_ASSERT_MSG(0 <= inclinationRad && inclinationRad <= M_PI,
3621 "inclinationRad=" << inclinationRad << " not valid, should be in [0, pi]");
3622 NS_ASSERT_MSG(0 <= azimuthRad && azimuthRad <= 2 * M_PI,
3623 "azimuthRad=" << azimuthRad << " not valid, should be in [0, 2*pi]");
3624
3625 return std::make_pair(azimuthRad, inclinationRad);
3626}
3627
3631 const DoubleVector& clusterAOA,
3632 const DoubleVector& clusterZOA) const
3633{
3634 NS_LOG_FUNCTION(this);
3635
3636 auto clusterNum = clusterAOA.size();
3637
3638 // Initial power attenuation for all clusters to be 0 dB
3639 DoubleVector powerAttenuation(clusterNum, 0);
3640
3641 // step a: the number of non-self blocking blockers is stored in m_numNonSelfBlocking.
3642
3643 // step b:Generate the size and location of each blocker
3644 // generate self blocking (i.e., for blockage from the human body)
3645 // table 7.6.4.1-1 Self-blocking region parameters.
3646 // Defaults: landscape mode
3647 double phiSb = 40;
3648 double xSb = 160;
3649 double thetaSb = 110;
3650 double ySb = 75;
3651 if (m_portraitMode)
3652 {
3653 phiSb = 260;
3654 xSb = 120;
3655 thetaSb = 100;
3656 ySb = 80;
3657 }
3658
3659 // generate or update non-self blocking
3660 if (channelParams->m_nonSelfBlocking.empty()) // generate new blocking regions
3661 {
3662 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
3663 {
3664 // draw value from table 7.6.4.1-2 Blocking region parameters
3665 DoubleVector table;
3666 table.push_back(m_normalRv->GetValue()); // phi_k: store the normal RV that will be
3667 // mapped to uniform (0,360) later.
3668 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
3669 {
3670 table.push_back(m_uniformRv->GetValue(15, 45)); // x_k
3671 table.push_back(90); // Theta_k
3672 table.push_back(m_uniformRv->GetValue(5, 15)); // y_k
3673 table.push_back(2); // r
3674 }
3675 else
3676 {
3677 table.push_back(m_uniformRv->GetValue(5, 15)); // x_k
3678 table.push_back(90); // Theta_k
3679 table.push_back(5); // y_k
3680 table.push_back(10); // r
3681 }
3682 channelParams->m_nonSelfBlocking.push_back(table);
3683 }
3684 }
3685 else
3686 {
3687 double deltaX = sqrt(pow(channelParams->m_preLocUT.x - channelParams->m_locUT.x, 2) +
3688 pow(channelParams->m_preLocUT.y - channelParams->m_locUT.y, 2));
3689 // if deltaX and speed are both 0, the autocorrelation is 1, skip updating
3690 if (deltaX > 1e-6 || m_blockerSpeed > 1e-6)
3691 {
3692 double corrDis;
3693 // draw value from table 7.6.4.1-4: Spatial correlation distance for different
3694 // m_scenarios.
3695 if (m_scenario == "InH-OfficeMixed" || m_scenario == "InH-OfficeOpen")
3696 {
3697 // InH, correlation distance = 5;
3698 corrDis = 5;
3699 }
3700 else
3701 {
3702 if (channelParams->m_o2iCondition == ChannelCondition::O2I) // outdoor to indoor
3703 {
3704 corrDis = 5;
3705 }
3706 else // LOS or NLOS
3707 {
3708 corrDis = 10;
3709 }
3710 }
3711 double R;
3712 if (m_blockerSpeed > 1e-6) // speed not equal to 0
3713 {
3714 double corrT = corrDis / m_blockerSpeed;
3715 R = exp(-1 * (deltaX / corrDis +
3716 (Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()) /
3717 corrT));
3718 }
3719 else
3720 {
3721 R = exp(-1 * (deltaX / corrDis));
3722 }
3723
3724 NS_LOG_INFO("Distance change:"
3725 << deltaX << " Speed:" << m_blockerSpeed << " Time difference:"
3726 << Now().GetSeconds() - channelParams->m_generatedTime.GetSeconds()
3727 << " correlation:" << R);
3728
3729 // In order to generate correlated uniform random variables, we first generate
3730 // correlated normal random variables and map the normal RV to uniform RV. Notice the
3731 // correlation will change if the RV is transformed from normal to uniform. To
3732 // compensate the distortion, the correlation of the normal RV is computed such that the
3733 // uniform RV would have the desired correlation when transformed from normal RV.
3734
3735 // The following formula was obtained from MATLAB numerical simulation.
3736
3737 if (R * R * (-0.069) + R * 1.074 - 0.002 <
3738 1) // transform only when the correlation of normal RV is smaller than 1
3739 {
3740 R = R * R * (-0.069) + R * 1.074 - 0.002;
3741 }
3742 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
3743 {
3744 // Generate a new correlated normal RV with the following formula
3745 channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] =
3746 R * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] +
3747 sqrt(1 - R * R) * m_normalRv->GetValue();
3748 }
3749 }
3750 }
3751
3752 // step c: Determine the attenuation of each blocker due to blockers
3753 for (std::size_t cInd = 0; cInd < clusterNum; cInd++)
3754 {
3755 NS_ASSERT_MSG(clusterAOA[cInd] >= 0 && clusterAOA[cInd] <= 360,
3756 "the AOA should be the range of [0,360]");
3757 NS_ASSERT_MSG(clusterZOA[cInd] >= 0 && clusterZOA[cInd] <= 180,
3758 "the ZOA should be the range of [0,180]");
3759
3760 // check self blocking
3761 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiSb - xSb / 2.0 << ","
3762 << phiSb + xSb / 2.0 << "]");
3763 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaSb - ySb / 2.0 << ","
3764 << thetaSb + ySb / 2.0 << "]");
3765 if (std::abs(clusterAOA[cInd] - phiSb) < (xSb / 2.0) &&
3766 std::abs(clusterZOA[cInd] - thetaSb) < (ySb / 2.0))
3767 {
3768 powerAttenuation[cInd] += 30; // attenuate by 30 dB.
3769 NS_LOG_INFO("Cluster[" << +cInd
3770 << "] is blocked by self blocking region and reduce 30 dB power,"
3771 "the attenuation is ["
3772 << powerAttenuation[cInd] << " dB]");
3773 }
3774
3775 // check non-self blocking
3776 for (uint16_t blockInd = 0; blockInd < m_numNonSelfBlocking; blockInd++)
3777 {
3778 // The normal RV is transformed to uniform RV with the desired correlation.
3779 double phiK =
3780 (0.5 * erfc(-1 * channelParams->m_nonSelfBlocking[blockInd][PHI_INDEX] / sqrt(2))) *
3781 360;
3782 while (phiK > 360)
3783 {
3784 phiK -= 360;
3785 }
3786
3787 while (phiK < 0)
3788 {
3789 phiK += 360;
3790 }
3791
3792 double xK = channelParams->m_nonSelfBlocking[blockInd][X_INDEX];
3793 double thetaK = channelParams->m_nonSelfBlocking[blockInd][THETA_INDEX];
3794 double yK = channelParams->m_nonSelfBlocking[blockInd][Y_INDEX];
3795
3796 NS_LOG_INFO("AOA=" << clusterAOA[cInd] << " Block Region[" << phiK - xK << ","
3797 << phiK + xK << "]");
3798 NS_LOG_INFO("ZOA=" << clusterZOA[cInd] << " Block Region[" << thetaK - yK << ","
3799 << thetaK + yK << "]");
3800
3801 if (std::abs(clusterAOA[cInd] - phiK) < (xK) &&
3802 std::abs(clusterZOA[cInd] - thetaK) < (yK))
3803 {
3804 double A1 = clusterAOA[cInd] - (phiK + xK / 2.0); //(7.6-24)
3805 double A2 = clusterAOA[cInd] - (phiK - xK / 2.0); //(7.6-25)
3806 double Z1 = clusterZOA[cInd] - (thetaK + yK / 2.0); //(7.6-26)
3807 double Z2 = clusterZOA[cInd] - (thetaK - yK / 2.0); //(7.6-27)
3808 int signA1;
3809 int signA2;
3810 int signZ1;
3811 int signZ2;
3812 // draw sign for the above parameters according to table 7.6.4.1-3 Description of
3813 // signs
3814 if (xK / 2.0 < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= xK)
3815 {
3816 signA1 = -1;
3817 }
3818 else
3819 {
3820 signA1 = 1;
3821 }
3822 if (-1 * xK < clusterAOA[cInd] - phiK && clusterAOA[cInd] - phiK <= -1 * xK / 2.0)
3823 {
3824 signA2 = -1;
3825 }
3826 else
3827 {
3828 signA2 = 1;
3829 }
3830
3831 if (yK / 2.0 < clusterZOA[cInd] - thetaK && clusterZOA[cInd] - thetaK <= yK)
3832 {
3833 signZ1 = -1;
3834 }
3835 else
3836 {
3837 signZ1 = 1;
3838 }
3839 if (-1 * yK < clusterZOA[cInd] - thetaK &&
3840 clusterZOA[cInd] - thetaK <= -1 * yK / 2.0)
3841 {
3842 signZ2 = -1;
3843 }
3844 else
3845 {
3846 signZ2 = 1;
3847 }
3848 double lambda = 3e8 / m_frequency;
3849 double fA1 =
3850 atan(signA1 * M_PI / 2.0 *
3851 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
3852 (1.0 / cos(DegreesToRadians(A1)) - 1))) /
3853 M_PI; //(7.6-23)
3854 double fA2 =
3855 atan(signA2 * M_PI / 2.0 *
3856 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
3857 (1.0 / cos(DegreesToRadians(A2)) - 1))) /
3858 M_PI;
3859 double fZ1 =
3860 atan(signZ1 * M_PI / 2.0 *
3861 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
3862 (1.0 / cos(DegreesToRadians(Z1)) - 1))) /
3863 M_PI;
3864 double fZ2 =
3865 atan(signZ2 * M_PI / 2.0 *
3866 sqrt(M_PI / lambda * channelParams->m_nonSelfBlocking[blockInd][R_INDEX] *
3867 (1.0 / cos(DegreesToRadians(Z2)) - 1))) /
3868 M_PI;
3869 double lDb = -20 * log10(1 - (fA1 + fA2) * (fZ1 + fZ2)); //(7.6-22)
3870 powerAttenuation[cInd] += lDb;
3871 NS_LOG_INFO("Cluster[" << +cInd << "] is blocked by no-self blocking, the loss is ["
3872 << lDb << "] dB");
3873 }
3874 }
3875 }
3876 return powerAttenuation;
3877}
3878
3879void
3880ThreeGppChannelModel::Shuffle(double* first, double* last) const
3881{
3882 for (auto i = (last - first) - 1; i > 0; --i)
3883 {
3884 std::swap(first[i], first[m_uniformRvShuffle->GetInteger(0, i)]);
3885 }
3886}
3887
3888int64_t
3890{
3891 NS_LOG_FUNCTION(this << stream);
3892 m_normalRv->SetStream(stream);
3893 m_uniformRv->SetStream(stream + 1);
3894 m_uniformRvShuffle->SetStream(stream + 2);
3895 m_uniformRvDoppler->SetStream(stream + 3);
3896 return 4;
3897}
3898
3899} // 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
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
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 > MakePointerAccessor(T1 a1)
Definition: pointer.h:259
#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.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Definition: integer.h:46
Table3gppParams
The enumerator used for code clarity when performing parameter assignment in GetThreeGppTable.
static const std::map< std::string, std::map< int, std::vector< float > > > NTNSuburbanNLOS
The nested map containing the 3GPP value tables for the NTN Suburban NLOS scenario.
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 std::map< std::string, std::map< int, std::vector< float > > > NTNSuburbanLOS
The nested map containing the 3GPP value tables for the NTN Suburban LOS scenario.
static const double offSetAlpha[20]
The ray offset angles within a cluster, given for rms angle spread normalized to 1.
static const std::map< std::string, std::map< int, std::vector< float > > > NTNUrbanNLOS
The nested map containing the 3GPP value tables for the NTN Urban NLOS scenario.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1407
static const double sqrtC_RMa_O2I[6][6]
The square root matrix for RMa O2I, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_UMi_LOS[7][7]
The square root matrix for UMi LOS, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_office_LOS[7][7]
The square root matrix for Indoor-Office LOS, which is generated using the Cholesky decomposition acc...
static const double sqrtC_UMa_O2I[6][6]
The square root matrix for UMa O2I, which is generated using the Cholesky decomposition according to ...
static const double sqrtC_NTN_DenseUrban_NLOS[6][6]
The square root matrix for NTN Dense Urban NLOS, which is generated using the Cholesky decomposition ...
static const std::map< std::string, std::map< int, std::vector< float > > > NTNRuralLOS
The nested map containing the 3GPP value tables for the NTN Rural LOS scenario.
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 > > > NTNDenseUrbanLOS
The nested map containing the 3GPP value tables for the NTN Dense Urban LOS 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 double sqrtC_RMa_LOS[7][7]
The square root matrix for RMa LOS, which is generated using the Cholesky decomposition according to ...
double DegreesToRadians(double degrees)
converts degrees to radians
Definition: angles.cc:39
static const double sqrtC_NTN_Urban_LOS[7][7]
The square root matrix for NTN Urban LOS, which is generated using the Cholesky decomposition accordi...
Ptr< const AttributeChecker > MakeStringChecker()
Definition: string.cc:30
static const std::map< std::string, std::map< int, std::vector< float > > > NTNDenseUrbanNLOS
The nested map containing the 3GPP value tables for the NTN Dense Urban NLOS scenario.
Ptr< const AttributeAccessor > MakeStringAccessor(T1 a1)
Definition: string.h:57
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
static const std::map< std::string, std::map< int, std::vector< float > > > NTNRuralNLOS
The nested map containing the 3GPP value tables for the NTN Rural NLOS scenario The outer key specifi...
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
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427
static const std::map< std::string, std::map< int, std::vector< float > > > NTNUrbanLOS
The nested map containing the 3GPP value tables for the NTN Urban LOS scenario.