1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
4  * University of Padova
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include "ns3/log.h"
21 #include "ns3/abort.h"
22 #include "ns3/test.h"
23 #include "ns3/config.h"
24 #include "ns3/double.h"
25 #include "ns3/channel-condition-model.h"
26 #include "ns3/constant-position-mobility-model.h"
27 #include "ns3/simulator.h"
28 #include "ns3/node-container.h"
29 
30 using namespace ns3;
31 
32 NS_LOG_COMPONENT_DEFINE ("ChannelConditionModelsTest");
33 
34 /**
35  * Test case for the 3GPP channel condition models. It determines the
36  * channel condition multiple times, estimates the LOS probability and
37  * compares it with the value given by the formulas in 3GPP TR 38.901,
38  * Table Table 7.4.2-1.
39  */
40 class ThreeGppChannelConditionModelTestCase : public TestCase
41 {
42 public:
43   /**
44    * Constructor
45    */
46   ThreeGppChannelConditionModelTestCase ();
47 
48   /**
49    * Destructor
50    */
51   virtual ~ThreeGppChannelConditionModelTestCase ();
52 
53 private:
54   /**
55    * Builds the simulation scenario and perform the tests
56    */
57   virtual void DoRun (void);
58 
59   /**
60    * Evaluates the channel condition between two nodes by calling the method
61    * GetChannelCondition on m_condModel. If the channel condition is LOS it
62    * increments m_numLos
63    * \param a the mobility model of the first node
64    * \param b the mobility model of the second node
65    */
66   void EvaluateChannelCondition (Ptr<MobilityModel> a, Ptr<MobilityModel> b);
67 
68   /**
69    * Struct containing the parameters for each test
70    */
71   typedef struct
72   {
73     Vector m_positionA; //!< the position of the first node
74     Vector m_positionB; //!< the position of the second node
75     double m_pLos;  //!< LOS probability
76     TypeId m_typeId; //!< the type ID of the channel condition model to be used
77   } TestVector;
78 
79   TestVectors<TestVector> m_testVectors; //!< array containing all the test vectors
80   Ptr<ThreeGppChannelConditionModel> m_condModel; //!< the channel condition model
81   uint64_t m_numLos; //!< the number of LOS occurrences
82   double m_tolerance; //!< tolerance
83 };
84 
ThreeGppChannelConditionModelTestCase()85 ThreeGppChannelConditionModelTestCase::ThreeGppChannelConditionModelTestCase ()
86   : TestCase ("Test case for the child classes of ThreeGppChannelConditionModel"),
87     m_testVectors (),
88     m_tolerance (2e-3)
89 {
90 }
91 
~ThreeGppChannelConditionModelTestCase()92 ThreeGppChannelConditionModelTestCase::~ThreeGppChannelConditionModelTestCase ()
93 {
94 }
95 
96 void
EvaluateChannelCondition(Ptr<MobilityModel> a,Ptr<MobilityModel> b)97 ThreeGppChannelConditionModelTestCase::EvaluateChannelCondition (Ptr<MobilityModel> a, Ptr<MobilityModel> b)
98 {
99   Ptr<ChannelCondition> cond = m_condModel->GetChannelCondition (a, b);
100   if (cond->GetLosCondition () == ChannelCondition::LosConditionValue::LOS)
101     {
102       m_numLos++;
103     }
104 }
105 
106 void
DoRun(void)107 ThreeGppChannelConditionModelTestCase::DoRun (void)
108 {
109   // create the test vector
110   TestVector testVector;
111 
112   // tests for the RMa scenario
113   testVector.m_positionA = Vector (0, 0, 35.0);
114   testVector.m_positionB = Vector (10, 0, 1.5);
115   testVector.m_pLos = 1;
116   testVector.m_typeId = ThreeGppRmaChannelConditionModel::GetTypeId ();
117   m_testVectors.Add (testVector);
118 
119   testVector.m_positionA = Vector (0, 0, 35.0);
120   testVector.m_positionB = Vector (100, 0, 1.5);
121   testVector.m_pLos = exp (-(100.0 - 10.0) / 1000.0);
122   testVector.m_typeId = ThreeGppRmaChannelConditionModel::GetTypeId ();
123   m_testVectors.Add (testVector);
124 
125   testVector.m_positionA = Vector (0, 0, 35.0);
126   testVector.m_positionB = Vector (1000, 0, 1.5);
127   testVector.m_pLos = exp (-(1000.0 - 10.0) / 1000.0);
128   testVector.m_typeId = ThreeGppRmaChannelConditionModel::GetTypeId ();
129   m_testVectors.Add (testVector);
130 
131   // tests for the UMa scenario
132   testVector.m_positionA = Vector (0, 0, 25.0);
133   testVector.m_positionB = Vector (18, 0, 1.5);
134   testVector.m_pLos = 1;
135   testVector.m_typeId = ThreeGppUmaChannelConditionModel::GetTypeId ();
136   m_testVectors.Add (testVector);
137 
138   testVector.m_positionA = Vector (0, 0, 25.0);
139   testVector.m_positionB = Vector (50, 0, 1.5);
140   testVector.m_pLos = (18.0 / 50.0 + exp (-50.0 / 63.0) * (1.0 - 18.0 / 50.0)) * (1.0 + 0);
141   testVector.m_typeId = ThreeGppUmaChannelConditionModel::GetTypeId ();
142   m_testVectors.Add (testVector);
143 
144   testVector.m_positionA = Vector (0, 0, 25.0);
145   testVector.m_positionB = Vector (50, 0, 15);
146   testVector.m_pLos = (18.0 / 50.0 + exp (-50.0 / 63.0) * (1.0 - 18.0 / 50.0)) * (1.0 + pow (2.0 / 10.0, 1.5) * 5.0 / 4.0 * pow (50.0 / 100.0, 3) * exp (-50.0 / 150.0));
147   testVector.m_typeId = ThreeGppUmaChannelConditionModel::GetTypeId ();
148   m_testVectors.Add (testVector);
149 
150   testVector.m_positionA = Vector (0, 0, 25.0);
151   testVector.m_positionB = Vector (100, 0, 1.5);
152   testVector.m_pLos = (18.0 / 100.0 + exp (-100.0 / 63.0) * (1.0 - 18.0 / 100.0)) * (1.0 + 0);
153   testVector.m_typeId = ThreeGppUmaChannelConditionModel::GetTypeId ();
154   m_testVectors.Add (testVector);
155 
156   testVector.m_positionA = Vector (0, 0, 25.0);
157   testVector.m_positionB = Vector (100, 0, 15);
158   testVector.m_pLos = (18.0 / 100.0 + exp (-100.0 / 63.0) * (1.0 - 18.0 / 100.0)) * (1.0 + pow (2.0 / 10.0, 1.5) * 5.0 / 4.0 * 1.0 * exp (-100.0 / 150.0));
159   testVector.m_typeId = ThreeGppUmaChannelConditionModel::GetTypeId ();
160   m_testVectors.Add (testVector);
161 
162   // tests for the UMi-Street Canyon scenario
163   testVector.m_positionA = Vector (0, 0, 10.0);
164   testVector.m_positionB = Vector (18, 0, 1.5);
165   testVector.m_pLos = 1;
166   testVector.m_typeId = ThreeGppUmiStreetCanyonChannelConditionModel::GetTypeId ();
167   m_testVectors.Add (testVector);
168 
169   testVector.m_positionA = Vector (0, 0, 10.0);
170   testVector.m_positionB = Vector (50, 0, 1.5);
171   testVector.m_pLos = (18.0 / 50.0 + exp (-50.0 / 36.0) * (1.0 - 18.0 / 50.0));
172   testVector.m_typeId = ThreeGppUmiStreetCanyonChannelConditionModel::GetTypeId ();
173   m_testVectors.Add (testVector);
174 
175   m_testVectors.Add (testVector);
176   testVector.m_positionA = Vector (0, 0, 10.0);
177   testVector.m_positionB = Vector (100, 0, 15);
178   testVector.m_pLos = (18.0 / 100.0 + exp (-100.0 / 36.0) * (1.0 - 18.0 / 100.0));
179   testVector.m_typeId = ThreeGppUmiStreetCanyonChannelConditionModel::GetTypeId ();
180   m_testVectors.Add (testVector);
181 
182   // tests for the Indoor Mixed Office scenario
183   testVector.m_positionA = Vector (0, 0, 2.0);
184   testVector.m_positionB = Vector (1.2, 0, 1.5);
185   testVector.m_pLos = 1;
186   testVector.m_typeId = ThreeGppIndoorMixedOfficeChannelConditionModel::GetTypeId ();
187   m_testVectors.Add (testVector);
188 
189   testVector.m_positionA = Vector (0, 0, 2.0);
190   testVector.m_positionB = Vector (5, 0, 1.5);
191   testVector.m_pLos = exp (-(5.0 - 1.2) / 4.7);
192   testVector.m_typeId = ThreeGppIndoorMixedOfficeChannelConditionModel::GetTypeId ();
193   m_testVectors.Add (testVector);
194 
195   testVector.m_positionA = Vector (0, 0, 2.0);
196   testVector.m_positionB = Vector (10, 0, 1.5);
197   testVector.m_pLos = exp (-(10.0 - 6.5) / 32.6) * 0.32;
198   testVector.m_typeId = ThreeGppIndoorMixedOfficeChannelConditionModel::GetTypeId ();
199   m_testVectors.Add (testVector);
200 
201   // tests for the Indoor Open Office scenario
202   testVector.m_positionA = Vector (0, 0, 3.0);
203   testVector.m_positionB = Vector (5, 0, 1.5);
204   testVector.m_pLos = 1;
205   testVector.m_typeId = ThreeGppIndoorOpenOfficeChannelConditionModel::GetTypeId ();
206   m_testVectors.Add (testVector);
207 
208   testVector.m_positionA = Vector (0, 0, 3.0);
209   testVector.m_positionB = Vector (30, 0, 1.5);
210   testVector.m_pLos = exp (-(30.0 - 5.0) / 70.8);
211   testVector.m_typeId = ThreeGppIndoorOpenOfficeChannelConditionModel::GetTypeId ();
212   m_testVectors.Add (testVector);
213 
214   testVector.m_positionA = Vector (0, 0, 3.0);
215   testVector.m_positionB = Vector (100, 0, 1.5);
216   testVector.m_pLos = exp (-(100.0 - 49.0) / 211.7) * 0.54;
217   testVector.m_typeId = ThreeGppIndoorOpenOfficeChannelConditionModel::GetTypeId ();
218   m_testVectors.Add (testVector);
219 
220   // create the factory for the channel condition models
221   ObjectFactory condModelFactory;
222 
223   // create the two nodes
224   NodeContainer nodes;
225   nodes.Create (2);
226 
227   // create the mobility models
228   Ptr<MobilityModel> a = CreateObject<ConstantPositionMobilityModel> ();
229   Ptr<MobilityModel> b = CreateObject<ConstantPositionMobilityModel> ();
230 
231   // aggregate the nodes and the mobility models
232   nodes.Get (0)->AggregateObject (a);
233   nodes.Get (1)->AggregateObject (b);
234 
235   // Get the channel condition multiple times and compute the LOS probability
236   uint32_t numberOfReps = 500000;
237   for (uint32_t i = 0; i < m_testVectors.GetN (); ++i)
238     {
239       testVector = m_testVectors.Get (i);
240 
241       // set the distance between the two nodes
242       a->SetPosition (testVector.m_positionA);
243       b->SetPosition (testVector.m_positionB);
244 
245       // create the channel condition model
246       condModelFactory.SetTypeId (testVector.m_typeId);
247       m_condModel = condModelFactory.Create<ThreeGppChannelConditionModel> ();
248       m_condModel->SetAttribute ("UpdatePeriod", TimeValue (MilliSeconds (9)));
249 
250       m_numLos = 0;
251       for (uint32_t j = 0; j < numberOfReps; j++)
252         {
253           Simulator::Schedule (MilliSeconds (10 * j), &ThreeGppChannelConditionModelTestCase::EvaluateChannelCondition, this, a, b);
254         }
255 
256       Simulator::Run ();
257       Simulator::Destroy ();
258 
259       double resultPlos = double (m_numLos) / double (numberOfReps);
260       NS_LOG_DEBUG (testVector.m_typeId << "  a pos " << testVector.m_positionA << " b pos " << testVector.m_positionB << " numLos " << m_numLos << " numberOfReps " << numberOfReps << " resultPlos " << resultPlos << " ref " << testVector.m_pLos);
261       NS_TEST_EXPECT_MSG_EQ_TOL (resultPlos, testVector.m_pLos, m_tolerance, "Got unexpected LOS probability");
262     }
263 }
264 
265 /**
266  * Test suite for the channel condition models
267  */
268 class ChannelConditionModelsTestSuite : public TestSuite
269 {
270 public:
271   ChannelConditionModelsTestSuite ();
272 };
273 
ChannelConditionModelsTestSuite()274 ChannelConditionModelsTestSuite::ChannelConditionModelsTestSuite ()
275   : TestSuite ("propagation-channel-condition-model", UNIT)
276 {
277   AddTestCase (new ThreeGppChannelConditionModelTestCase, TestCase::QUICK);
278 }
279 
280 static ChannelConditionModelsTestSuite ChannelConditionModelsTestSuite;
281