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