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
AttributeValue implementation for Boolean.
Definition boolean.h:26
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
AttributeValue implementation for Time.
Definition nstime.h:1431
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 AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
Ptr< const AttributeChecker > MakeDoubleChecker()
Definition double.h:82
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition double.h:32
Ptr< const AttributeChecker > MakeIntegerChecker()
Definition integer.h:99
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition integer.h:35
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
Ptr< const AttributeChecker > MakeStringChecker()
Definition string.cc:19
Ptr< const AttributeAccessor > MakeStringAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition string.h:46
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1432
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1452
#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:1356
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...
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.
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...
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...
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...
static constexpr std::array< double, 20 > offSetAlpha
The ray offset angles within a cluster, given for rms angle spread normalized to 1.
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
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.