A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
buildings-penetration-loss-pathloss-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include "ns3/abort.h"
8#include "ns3/boolean.h"
9#include "ns3/building.h"
10#include "ns3/buildings-channel-condition-model.h"
11#include "ns3/channel-condition-model.h"
12#include "ns3/config.h"
13#include "ns3/constant-position-mobility-model.h"
14#include "ns3/constant-velocity-mobility-model.h"
15#include "ns3/double.h"
16#include "ns3/log.h"
17#include "ns3/mobility-building-info.h"
18#include "ns3/mobility-helper.h"
19#include "ns3/simulator.h"
20#include "ns3/test.h"
21#include "ns3/three-gpp-propagation-loss-model.h"
22
23#include <numeric>
24
25using namespace ns3;
26
27NS_LOG_COMPONENT_DEFINE("BuildingsPenetrationLossesTest");
28
30
31/**
32 * @ingroup propagation-tests
33 *
34 * Test case for the 3GPP channel building and vehicular O2I penetration losses.
35 * It considers pre-determined scenarios and based on the outdoor/indoor
36 * condition (O2O/O2I) checks whether the calculated received power
37 * is aligned with the calculation in 3GPP TR 38.901.
38 *
39 * For O2O condition the calculation is done according to Table 7.4.1-1
40 * and we check if the calculated received power is equal to the expected
41 * value. O2O condition is also tested when unblocked (LOS),
42 * or blocked (NLOS) by a building.
43
44 * For the O2I condition with frequencies equal or above 6GHz,
45 * the calculation is done considering the building penetration losses
46 * based on the material of the building walls, as specified
47 * in Table 7.4.3-1 and Table 7.4.3-2.
48 * For the O2I condition with frequencies below 6GHz, the calculation
49 * is done considering the backward compatibility model in Table 7.4.3-3.
50 * In both cases, we check if the calculated received power is within
51 * the expected normal distribution.
52 *
53 * All cases are tested with one node at 0 or 30 km/h, representing pedestrian or vehicles.
54 * In case a vehicle is part of a channel link, its O2I losses are applied to the channel,
55 * following procedure in 3GPP TR 38901 Section 7.4.3.2.
56 */
57
59{
60 public:
61 /**
62 * Constructor
63 */
65
66 /**
67 * Destructor
68 */
70
71 private:
72 /**
73 * Builds the simulation scenario and perform the tests
74 */
75 void DoRun() override;
76
77 /**
78 * Struct containing the parameters for each test
79 */
81 {
82 Vector m_positionA; //!< the position of the first node
83 Vector m_positionB; //!< the position of the second node
84 double m_ueVelocity; //!< velocity in km/h
85 double m_frequency; //!< carrier frequency in Hz
86 TypeId m_propModel; //!< the type ID of the propagation loss model to be used
87 O2ILH m_o2iLossType; //!< type of O2I loss (low/high)
88 double m_expectedMean; //!< expected mean in dBm
89 double m_expectedStddev; //!< expected deviation in dBm
90 };
91
92 std::vector<TestVector> m_testVectors; //!< array containing all the test vectors
93 Ptr<ThreeGppPropagationLossModel> m_propModel; //!< the propagation loss model
94};
95
97 : TestCase("Test case for BuildingsPenetrationLosses"),
99{
100}
101
105
106void
108{
112 // create the test vector
113 // clang-format off
114 m_testVectors = {
115 // PosA, PosB, B velocity, frequency, propagation model, O2I loss type, channel condition, mean and standard deviation of expected gaussian
116 // 6GHz, pedestrian, Low O2I loss
117 {{0, 0, 35.0}, { 10, 0, 1.5}, 0, 6e9, RMa, O2ILH::LOW, -93.0, 4.0}, // RMa O2I
118 {{0, 0, 35.0}, { 100, 0, 1.5}, 0, 6e9, RMa, O2ILH::LOW, -112.0, 4.0}, // RMa O2I
119 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, RMa, O2ILH::LOW, -97.0, 1.0}, // RMa O2O unblocked
120 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, RMa, O2ILH::LOW, -110.0, 1.0}, // RMa O2O blocked
121 {{0, 0, 25.0}, { 10, 0, 1.5}, 0, 6e9, UMa, O2ILH::LOW, -101.0, 5.0}, // UMa O2I
122 {{0, 0, 25.0}, { 100, 0, 1.5}, 0, 6e9, UMa, O2ILH::LOW, -125.0, 5.0}, // UMa O2I
123 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, UMa, O2ILH::LOW, -96.0, 1.0}, // UMa O2O unblocked
124 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, UMa, O2ILH::LOW, -117.0, 1.0}, // UMa O2O blocked
125 {{0, 0, 10.0}, { 10, 0, 1.5}, 0, 6e9, UMi, O2ILH::LOW, -96.0, 5.0}, // UMi O2I
126 {{0, 0, 10.0}, { 100, 0, 1.5}, 0, 6e9, UMi, O2ILH::LOW, -127.0, 5.0}, // UMi O2I
127 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, UMi, O2ILH::LOW, -99.0, 1.0}, // UMi O2O unblocked
128 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, UMi, O2ILH::LOW, -119.0, 1.0}, // UMi O2O blocked
129 // 6GHz, pedestrian, High O2I loss
130 {{0, 0, 35.0}, { 10, 0, 1.5}, 0, 6e9, RMa, O2ILH::HIGH, -93.0, 4.0}, // RMa O2I
131 {{0, 0, 35.0}, { 100, 0, 1.5}, 0, 6e9, RMa, O2ILH::HIGH, -112.0, 4.0}, // RMa O2I
132 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, RMa, O2ILH::HIGH, -97.0, 1.0}, // RMa O2O unblocked
133 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, RMa, O2ILH::HIGH, -110.0, 1.0}, // RMa O2O blocked
134 {{0, 0, 25.0}, { 10, 0, 1.5}, 0, 6e9, UMa, O2ILH::HIGH, -101.0, 5.0}, // UMa O2I
135 {{0, 0, 25.0}, { 100, 0, 1.5}, 0, 6e9, UMa, O2ILH::HIGH, -125.0, 5.0}, // UMa O2I
136 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, UMa, O2ILH::HIGH, -96.0, 1.0}, // UMa O2O unblocked
137 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, UMa, O2ILH::HIGH, -117.0, 1.0}, // UMa O2O blocked
138 {{0, 0, 10.0}, { 10, 0, 1.5}, 0, 6e9, UMi, O2ILH::HIGH, -95.0, 5.0}, // UMi O2I
139 {{0, 0, 10.0}, { 100, 0, 1.5}, 0, 6e9, UMi, O2ILH::HIGH, -127.0, 5.0}, // UMi O2I
140 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 6e9, UMi, O2ILH::HIGH, -99.0, 1.0}, // UMi O2O unblocked
141 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 6e9, UMi, O2ILH::HIGH, -119.0, 1.0}, // UMi O2O blocked
142 // 6GHz, vehicles, High O2I loss
143 {{0, 0, 35.0}, { 10, 0, 1.5}, 30, 6e9, RMa, O2ILH::HIGH, -94.0, 4.0}, // RMa O2I
144 {{0, 0, 35.0}, { 100, 0, 1.5}, 30, 6e9, RMa, O2ILH::HIGH, -112.0, 4.0}, // RMa O2I
145 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 6e9, RMa, O2ILH::HIGH, -106.0, 1.0}, // RMa O2O unblocked
146 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 6e9, RMa, O2ILH::HIGH, -119.0, 1.0}, // RMa O2O blocked
147 {{0, 0, 25.0}, { 10, 0, 1.5}, 30, 6e9, UMa, O2ILH::HIGH, -101.0, 5.0}, // UMa O2I
148 {{0, 0, 25.0}, { 100, 0, 1.5}, 30, 6e9, UMa, O2ILH::HIGH, -125.0, 5.0}, // UMa O2I
149 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 6e9, UMa, O2ILH::HIGH, -105.0, 5.0}, // UMa O2O unblocked
150 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 6e9, UMa, O2ILH::HIGH, -126.0, 5.0}, // UMa O2O blocked
151 {{0, 0, 10.0}, { 10, 0, 1.5}, 30, 6e9, UMi, O2ILH::HIGH, -95.0, 5.0}, // UMi O2I
152 {{0, 0, 10.0}, { 100, 0, 1.5}, 30, 6e9, UMi, O2ILH::HIGH, -127.0, 5.0}, // UMi O2I
153 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 6e9, UMi, O2ILH::HIGH, -108.0, 5.0}, // UMi O2O unblocked
154 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 6e9, UMi, O2ILH::HIGH, -128.0, 5.0}, // UMi O2O blocked
155 // Sub-6GHz, pedestrian, legacy O2I
156 {{0, 0, 35.0}, { 10, 0, 1.5}, 0, 5e9, RMa, O2ILH::HIGH, -97.0, 1.0}, // RMa O2I
157 {{0, 0, 35.0}, { 100, 0, 1.5}, 0, 5e9, RMa, O2ILH::HIGH, -115.0, 1.0}, // RMa O2I
158 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 5e9, RMa, O2ILH::HIGH, -96.0, 1.0}, // RMa O2O unblocked
159 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 5e9, RMa, O2ILH::HIGH, -108.0, 1.0}, // RMa O2O blocked
160 {{0, 0, 25.0}, { 10, 0, 1.5}, 0, 5e9, UMa, O2ILH::HIGH, -108.0, 3.0}, // UMa O2I
161 {{0, 0, 25.0}, { 100, 0, 1.5}, 0, 5e9, UMa, O2ILH::HIGH, -132.0, 3.0}, // UMa O2I
162 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 5e9, UMa, O2ILH::HIGH, -94.0, 1.0}, // UMa O2O unblocked
163 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 5e9, UMa, O2ILH::HIGH, -117.0, 1.0}, // UMa O2O blocked
164 {{0, 0, 10.0}, { 10, 0, 1.5}, 0, 5e9, UMi, O2ILH::HIGH, -103.0, 3.0}, // UMi O2I
165 {{0, 0, 10.0}, { 100, 0, 1.5}, 0, 5e9, UMi, O2ILH::HIGH, -134.0, 3.0}, // UMi O2I
166 {{0, 0, 10.0}, { 50, 15, 1.5}, 0, 5e9, UMi, O2ILH::HIGH, -98.0, 1.0}, // UMi O2O unblocked
167 {{0, 0, 10.0}, {1000, 0, 1.5}, 0, 5e9, UMi, O2ILH::HIGH, -119.0, 1.0}, // UMi O2O blocked
168 // Sub-6GHz, vehicles, legacy O2I
169 {{0, 0, 35.0}, { 10, 0, 1.5}, 30, 5e9, RMa, O2ILH::HIGH, -97.0, 1.0}, // RMa O2I
170 {{0, 0, 35.0}, { 100, 0, 1.5}, 30, 5e9, RMa, O2ILH::HIGH, -115.0, 1.0}, // RMa O2I
171 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 5e9, RMa, O2ILH::HIGH, -104.0, 1.0}, // RMa O2O unblocked
172 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 5e9, RMa, O2ILH::HIGH, -117.0, 1.0}, // RMa O2O blocked
173 {{0, 0, 25.0}, { 10, 0, 1.5}, 30, 5e9, UMa, O2ILH::HIGH, -108.0, 1.0}, // UMa O2I
174 {{0, 0, 25.0}, { 100, 0, 1.5}, 30, 5e9, UMa, O2ILH::HIGH, -132.0, 1.0}, // UMa O2I
175 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 5e9, UMa, O2ILH::HIGH, -104.0, 1.0}, // UMa O2O unblocked
176 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 5e9, UMa, O2ILH::HIGH, -126.0, 1.0}, // UMa O2O blocked
177 {{0, 0, 10.0}, { 10, 0, 1.5}, 30, 5e9, UMi, O2ILH::HIGH, -102.0, 1.0}, // UMi O2I
178 {{0, 0, 10.0}, { 100, 0, 1.5}, 30, 5e9, UMi, O2ILH::HIGH, -134.0, 1.0}, // UMi O2I
179 {{0, 0, 10.0}, { 50, 15, 1.5}, 30, 5e9, UMi, O2ILH::HIGH, -106.0, 1.0}, // UMi O2O unblocked
180 {{0, 0, 10.0}, {1000, 0, 1.5}, 30, 5e9, UMi, O2ILH::HIGH, -128.0, 1.0}, // UMi O2O blocked
181 };
182 // clang-format on
183
184 for (std::size_t i = 0; i < m_testVectors.size(); i++)
185 {
186 // create the factory for the propagation loss model
187 ObjectFactory propModelFactory;
188
189 auto building = CreateObject<Building>();
190 building->SetExtWallsType(Building::ExtWallsType_t::ConcreteWithWindows);
191 building->SetNRoomsX(1);
192 building->SetNRoomsY(1);
193 building->SetNFloors(2);
194 building->SetBoundaries(Box(0.0, 100.0, 0.0, 10.0, 0.0, 5.0));
195
196 // create the two nodes
198 nodes.Create(2);
199
200 // create the mobility models
204
205 // aggregate the nodes and the mobility models
206 nodes.Get(0)->AggregateObject(a);
207 nodes.Get(1)->AggregateObject(b);
208
211 a->AggregateObject(buildingInfoA); // operation usually done by BuildingsHelper::Install
212 buildingInfoA->MakeConsistent(a);
213
214 b->AggregateObject(buildingInfoB); // operation usually done by BuildingsHelper::Install
215 buildingInfoB->MakeConsistent(b);
216
218
219 // Configure test case setup
220 const auto& testVector = m_testVectors.at(i);
221 a->SetPosition(testVector.m_positionA);
222 b->SetPosition(testVector.m_positionB);
223 bcv->SetVelocity({testVector.m_ueVelocity, 0, 0});
224 bool isAIndoor = buildingInfoA->IsIndoor();
225 bool isBIndoor = buildingInfoB->IsIndoor();
226
227 Ptr<ChannelCondition> cond = condModel->GetChannelCondition(a, b);
228 cond->SetO2iLowHighCondition(testVector.m_o2iLossType);
229 if (!isAIndoor && !isBIndoor) // a and b are outdoor
230 {
231 cond->SetO2iCondition(ChannelCondition::O2iConditionValue::O2O);
232 }
233 else
234 {
235 cond->SetO2iCondition(ChannelCondition::O2iConditionValue::O2I);
236 }
237
238 propModelFactory.SetTypeId(testVector.m_propModel);
239 m_propModel = propModelFactory.Create<ThreeGppPropagationLossModel>();
240 m_propModel->SetAttribute("Frequency", DoubleValue(testVector.m_frequency));
241 m_propModel->SetAttribute("ShadowingEnabled", BooleanValue(false));
242 m_propModel->SetChannelConditionModel(condModel);
243 m_propModel->AssignStreams(0);
244
245 std::vector<double> samples(10000);
246 for (auto& sample : samples)
247 {
248 // Compute total loss
249 sample = m_propModel->CalcRxPower(0.0, a, b);
250 // Trigger another random variable usage
251 m_propModel->ClearO2iLossCacheMap();
252 }
253
254 auto getMeanAndStddev = [](const std::vector<double>& vec) {
255 // Calculate the mean
256 double sum = std::accumulate(vec.begin(), vec.end(), 0.0);
257 double mean = sum / vec.size();
258
259 // Calculate variance and standard deviation
260 double sq_sum = std::inner_product(vec.begin(), vec.end(), vec.begin(), 0.0);
261 double stddev = std::sqrt((sq_sum / vec.size()) - mean * mean);
262 return std::make_pair(mean, stddev);
263 };
264
265 auto chi2 = [](std::vector<double>& vecTested,
266 std::vector<double>& vecReference) -> const double {
267 std::sort(vecTested.begin(), vecTested.end());
268 std::sort(vecReference.begin(), vecReference.end());
269 double chi2 = 0.0;
270 for (std::size_t i = 0; i < vecTested.size(); ++i)
271 {
272 if (vecReference[i] == 0)
273 {
274 throw std::domain_error("Expected value at index " + std::to_string(i) +
275 " is zero");
276 }
277 double diff = vecTested[i] - vecReference[i];
278 chi2 += (diff * diff) / vecReference[i];
279 }
280 chi2 /= vecTested.size();
281 NS_ABORT_MSG_IF(std::isnan(chi2), "Chi-squared is NaN");
282 return chi2;
283 };
285 n->SetStdDev(testVector.m_expectedStddev);
286 n->SetAttribute("Mean", DoubleValue(testVector.m_expectedMean));
287 std::vector<double> reference(10000);
288 std::transform(reference.begin(), reference.end(), reference.begin(), [n](auto& a) {
289 return a = n->GetValue();
290 });
291 auto chi = chi2(samples, reference);
292 auto [mean, stddev] = getMeanAndStddev(samples);
293 NS_TEST_ASSERT_MSG_LT_OR_EQ(std::abs(chi),
294 0.5,
295 "Test vector "
296 << i << ", expected mean=" << testVector.m_expectedMean
297 << " and stddev=" << testVector.m_expectedStddev
298 << ", got mean=" << mean << " and stddev=" << stddev
299 << ". Chi-squared is higher than threshold: " << chi);
300 m_propModel = nullptr;
302 }
303}
304
305/**
306 * @ingroup propagation-tests
307 *
308 * Test suite for the buildings penetration losses
309 */
315
321
322/// Static variable for test initialization
static BuildingsPenetrationLossesTestSuite g_buildingsPenetrationLossesTestSuite
Static variable for test initialization.
ChannelCondition::O2iLowHighConditionValue O2ILH
Test case for the 3GPP channel building and vehicular O2I penetration losses.
Ptr< ThreeGppPropagationLossModel > m_propModel
the propagation loss model
void DoRun() override
Builds the simulation scenario and perform the tests.
std::vector< TestVector > m_testVectors
array containing all the test vectors
Test suite for the buildings penetration losses.
AttributeValue implementation for Boolean.
Definition boolean.h:26
a 3d box
Definition box.h:24
@ ConcreteWithWindows
Definition building.h:58
O2iLowHighConditionValue
Possible values for Low-High Penetration Loss condition.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
keep track of a set of node pointers.
Instantiate subclasses of ns3::Object.
Ptr< Object > Create() const
Create an Object instance of the configured TypeId.
void SetTypeId(TypeId tid)
Set the TypeId of the Objects to be created by this factory.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:67
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:293
@ QUICK
Fast test.
Definition test.h:1054
TestCase(const TestCase &)=delete
Type
Type of test.
Definition test.h:1257
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition test.cc:491
Base class for the 3GPP propagation models.
a unique identifier for an interface.
Definition type-id.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
#define NS_TEST_ASSERT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report and abort if not.
Definition test.h:739
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:585
TypeId m_propModel
the type ID of the propagation loss model to be used