1 /* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2019 SIGNET Lab, Department of Information Engineering,
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include "ns3/log.h"
20 #include "ns3/abort.h"
21 #include "ns3/test.h"
22 #include "ns3/config.h"
23 #include "ns3/double.h"
24 #include "ns3/uinteger.h"
25 #include "ns3/string.h"
26 #include "ns3/angles.h"
27 #include "ns3/pointer.h"
28 #include "ns3/node-container.h"
29 #include "ns3/constant-position-mobility-model.h"
30 #include "ns3/uniform-planar-array.h"
31 #include "ns3/isotropic-antenna-model.h"
32 #include "ns3/three-gpp-channel-model.h"
33 #include "ns3/simple-net-device.h"
34 #include "ns3/simulator.h"
35 #include "ns3/channel-condition-model.h"
36 #include "ns3/three-gpp-spectrum-propagation-loss-model.h"
37 #include "ns3/wifi-spectrum-value-helper.h"
38 
39 using namespace ns3;
40 
41 NS_LOG_COMPONENT_DEFINE ("ThreeGppChannelTestSuite");
42 
43 /**
44  * Test case for the ThreeGppChannelModel class.
45  * 1) check if the channel matrix has the correct dimensions
46  * 2) check if the channel matrix is correctly normalized
47  */
48 class ThreeGppChannelMatrixComputationTest : public TestCase
49 {
50 public:
51   /**
52    * Constructor
53    */
54   ThreeGppChannelMatrixComputationTest ();
55 
56   /**
57    * Destructor
58    */
59   virtual ~ThreeGppChannelMatrixComputationTest ();
60 
61 private:
62   /**
63    * Build the test scenario
64    */
65   virtual void DoRun (void);
66 
67   /**
68    * Compute the Frobenius norm of the channel matrix and stores it in m_normVector
69    * \param channelModel the ThreeGppChannelModel object used to generate the channel matrix
70    * \param txMob the mobility model of the first node
71    * \param rxMob the mobility model of the second node
72    * \param txAntenna the antenna object associated to the first node
73    * \param rxAntenna the antenna object associated to the second node
74    */
75   void DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna);
76 
77   std::vector<double> m_normVector; //!< each element is the norm of a channel realization
78 };
79 
ThreeGppChannelMatrixComputationTest()80 ThreeGppChannelMatrixComputationTest::ThreeGppChannelMatrixComputationTest ()
81   : TestCase ("Check the dimensions and the norm of the channel matrix")
82 {
83 }
84 
~ThreeGppChannelMatrixComputationTest()85 ThreeGppChannelMatrixComputationTest::~ThreeGppChannelMatrixComputationTest ()
86 {
87 }
88 
89 void
DoComputeNorm(Ptr<ThreeGppChannelModel> channelModel,Ptr<MobilityModel> txMob,Ptr<MobilityModel> rxMob,Ptr<PhasedArrayModel> txAntenna,Ptr<PhasedArrayModel> rxAntenna)90 ThreeGppChannelMatrixComputationTest::DoComputeNorm (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna)
91 {
92   uint64_t txAntennaElements = txAntenna->GetNumberOfElements ();
93   uint64_t rxAntennaElements = rxAntenna->GetNumberOfElements ();
94 
95   Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
96 
97   double channelNorm = 0;
98   uint8_t numTotClusters = channelMatrix->m_channel.at (0).at (0).size ();
99   for (uint8_t cIndex = 0; cIndex < numTotClusters; cIndex++)
100   {
101     double clusterNorm = 0;
102     for (uint64_t sIndex = 0; sIndex < txAntennaElements; sIndex++)
103     {
104       for (uint32_t uIndex = 0; uIndex < rxAntennaElements; uIndex++)
105       {
106         clusterNorm += std::pow (std::abs (channelMatrix->m_channel.at (uIndex).at (sIndex).at (cIndex)), 2);
107       }
108     }
109     channelNorm += clusterNorm;
110   }
111   m_normVector.push_back (channelNorm);
112 }
113 
114 void
DoRun(void)115 ThreeGppChannelMatrixComputationTest::DoRun (void)
116 {
117   // Build the scenario for the test
118   uint8_t txAntennaElements[] {2, 2}; // tx antenna dimensions
119   uint8_t rxAntennaElements[] {2, 2}; // rx antenna dimensions
120   uint32_t updatePeriodMs = 100; // update period in ms
121 
122   // create the channel condition model
123   Ptr<ChannelConditionModel> channelConditionModel = CreateObject<NeverLosChannelConditionModel> ();
124 
125   // create the ThreeGppChannelModel object used to generate the channel matrix
126   Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel> ();
127   channelModel->SetAttribute ("Frequency", DoubleValue (60.0e9));
128   channelModel->SetAttribute ("Scenario", StringValue ("RMa"));
129   channelModel->SetAttribute ("ChannelConditionModel", PointerValue (channelConditionModel));
130   channelModel->SetAttribute ("UpdatePeriod", TimeValue (MilliSeconds (updatePeriodMs-1)));
131 
132   // create the tx and rx nodes
133   NodeContainer nodes;
134   nodes.Create (2);
135 
136   // create the tx and rx devices
137   Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
138   Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
139 
140   // associate the nodes and the devices
141   nodes.Get (0)->AddDevice (txDev);
142   txDev->SetNode (nodes.Get (0));
143   nodes.Get (1)->AddDevice (rxDev);
144   rxDev->SetNode (nodes.Get (1));
145 
146   // create the tx and rx mobility models and set their positions
147   Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
148   txMob->SetPosition (Vector (0.0,0.0,10.0));
149   Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
150   rxMob->SetPosition (Vector (100.0,0.0,10.0));
151 
152   // associate the nodes and the mobility models
153   nodes.Get (0)->AggregateObject (txMob);
154   nodes.Get (1)->AggregateObject (rxMob);
155 
156   // create the tx and rx antennas and set the their dimensions
157   Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
158                                                                                     "NumRows", UintegerValue (txAntennaElements [1]),
159                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
160   Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
161                                                                                     "NumRows", UintegerValue (rxAntennaElements [1]),
162                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
163 
164   // generate the channel matrix
165   Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
166 
167   // check the channel matrix dimensions
168   NS_TEST_ASSERT_MSG_EQ (channelMatrix->m_channel.at (0).size (), txAntennaElements [0] * txAntennaElements [1], "The second dimension of H should be equal to the number of tx antenna elements");
169   NS_TEST_ASSERT_MSG_EQ (channelMatrix->m_channel.size (), rxAntennaElements [0] * rxAntennaElements [1], "The first dimension of H should be equal to the number of rx antenna elements");
170 
171   // test if the channel matrix is correctly generated
172   uint16_t numIt = 1000;
173   for (uint16_t i = 0; i < numIt; i++)
174   {
175     Simulator::Schedule (MilliSeconds (updatePeriodMs * i), &ThreeGppChannelMatrixComputationTest::DoComputeNorm, this, channelModel, txMob, rxMob, txAntenna, rxAntenna);
176   }
177 
178   Simulator::Run ();
179 
180   // compute the sample mean
181   double sampleMean = 0;
182   for (auto i : m_normVector)
183   {
184     sampleMean += i;
185   }
186   sampleMean /= numIt;
187 
188   // compute the sample standard deviation
189   double sampleStd = 0;
190   for (auto i : m_normVector)
191   {
192     sampleStd += ((i - sampleMean) * (i - sampleMean));
193   }
194   sampleStd = std::sqrt (sampleStd / (numIt - 1));
195 
196   // perform the one sample t-test with a significance level of 0.05 to test
197   // the hypothesis "E [|H|^2] = M*N, where |H| indicates the Frobenius norm of
198   // H, M is the number of transmit antenna elements, and N is the number of
199   // the receive antenna elements"
200   double t = (sampleMean - txAntennaElements [0] * txAntennaElements [1] * rxAntennaElements [0] * rxAntennaElements [1]) / (sampleMean / std::sqrt (numIt));
201 
202   // Using a significance level of 0.05, we reject the null hypothesis if |t| is
203   // greater than the critical value from a t-distribution with df = numIt-1
204   NS_TEST_ASSERT_MSG_EQ_TOL (std::abs (t), 0, 1.65, "We reject the hypothesis E[|H|^2] = M*N with a significance level of 0.05");
205 
206   Simulator::Destroy ();
207 }
208 
209 /**
210  * Test case for the ThreeGppChannelModel class.
211  * It checks if the channel realizations are correctly updated during the
212  * simulation.
213  */
214 class ThreeGppChannelMatrixUpdateTest : public TestCase
215 {
216 public:
217   /**
218    * Constructor
219    */
220   ThreeGppChannelMatrixUpdateTest ();
221 
222   /**
223    * Destructor
224    */
225   virtual ~ThreeGppChannelMatrixUpdateTest ();
226 
227 private:
228   /**
229    * Build the test scenario
230    */
231   virtual void DoRun (void);
232 
233   /**
234    * This method is used to schedule the channel matrix computation at different
235    * time instants and to check if it correctly updated
236    * \param channelModel the ThreeGppChannelModel object used to generate the channel matrix
237    * \param txMob the mobility model of the first node
238    * \param rxMob the mobility model of the second node
239    * \param txAntenna the antenna object associated to the first node
240    * \param rxAntenna the antenna object associated to the second node
241    * \param update whether if the channel matrix should be updated or not
242    */
243   void DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna, bool update);
244 
245   Ptr<const ThreeGppChannelModel::ChannelMatrix> m_currentChannel; //!< used by DoGetChannel to store the current channel matrix
246 };
247 
ThreeGppChannelMatrixUpdateTest()248 ThreeGppChannelMatrixUpdateTest::ThreeGppChannelMatrixUpdateTest ()
249   : TestCase ("Check if the channel realizations are correctly updated during the simulation")
250 {
251 }
252 
~ThreeGppChannelMatrixUpdateTest()253 ThreeGppChannelMatrixUpdateTest::~ThreeGppChannelMatrixUpdateTest ()
254 {
255 }
256 
257 void
DoGetChannel(Ptr<ThreeGppChannelModel> channelModel,Ptr<MobilityModel> txMob,Ptr<MobilityModel> rxMob,Ptr<PhasedArrayModel> txAntenna,Ptr<PhasedArrayModel> rxAntenna,bool update)258 ThreeGppChannelMatrixUpdateTest::DoGetChannel (Ptr<ThreeGppChannelModel> channelModel, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<PhasedArrayModel> txAntenna, Ptr<PhasedArrayModel> rxAntenna, bool update)
259 {
260   // retrieve the channel matrix
261   Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix = channelModel->GetChannel (txMob, rxMob, txAntenna, rxAntenna);
262 
263   if (m_currentChannel == 0)
264   {
265     // this is the first time we compute the channel matrix, we initialize
266     // m_currentChannel
267     m_currentChannel = channelMatrix;
268   }
269   else
270   {
271     // compare the old and the new channel matrices
272     NS_TEST_ASSERT_MSG_EQ ((m_currentChannel != channelMatrix),  update, Simulator::Now ().GetMilliSeconds () << " The channel matrix is not correctly updated");
273   }
274 }
275 
276 void
DoRun(void)277 ThreeGppChannelMatrixUpdateTest::DoRun (void)
278 {
279   // Build the scenario for the test
280 
281   uint8_t txAntennaElements[] {2, 2}; // tx antenna dimensions
282   uint8_t rxAntennaElements[] {4, 4}; // rx antenna dimensions
283   uint32_t updatePeriodMs = 100; // update period in ms
284 
285   // create the channel condition model
286   Ptr<ChannelConditionModel> channelConditionModel = CreateObject<AlwaysLosChannelConditionModel> ();
287 
288   // create the ThreeGppChannelModel object used to generate the channel matrix
289   Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel> ();
290   channelModel->SetAttribute ("Frequency", DoubleValue (60.0e9));
291   channelModel->SetAttribute ("Scenario", StringValue ("UMa"));
292   channelModel->SetAttribute ("ChannelConditionModel", PointerValue (channelConditionModel));
293   channelModel->SetAttribute ("UpdatePeriod", TimeValue (MilliSeconds (updatePeriodMs)));
294 
295   // create the tx and rx nodes
296   NodeContainer nodes;
297   nodes.Create (2);
298 
299   // create the tx and rx devices
300   Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
301   Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
302 
303   // associate the nodes and the devices
304   nodes.Get (0)->AddDevice (txDev);
305   txDev->SetNode (nodes.Get (0));
306   nodes.Get (1)->AddDevice (rxDev);
307   rxDev->SetNode (nodes.Get (1));
308 
309   // create the tx and rx mobility models and set their positions
310   Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
311   txMob->SetPosition (Vector (0.0,0.0,10.0));
312   Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
313   rxMob->SetPosition (Vector (100.0,0.0,1.6));
314 
315   // associate the nodes and the mobility models
316   nodes.Get (0)->AggregateObject (txMob);
317   nodes.Get (1)->AggregateObject (rxMob);
318 
319   // create the tx and rx antennas and set the their dimensions
320   Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
321                                                                                     "NumRows", UintegerValue (txAntennaElements [1]),
322                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
323   Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
324                                                                                     "NumRows", UintegerValue (rxAntennaElements [1]),
325                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
326 
327   // check if the channel matrix is correctly updated
328 
329   // compute the channel matrix for the first time
330   uint32_t firstTimeMs = 1; // time instant at which the channel matrix is generated for the first time
331   Simulator::Schedule (MilliSeconds (firstTimeMs), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
332                        this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
333 
334   // call GetChannel before the update period is exceeded, the channel matrix
335   // should not be updated
336   Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs / 2), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
337                        this, channelModel, txMob, rxMob, txAntenna, rxAntenna, false);
338 
339   // call GetChannel when the update period is exceeded, the channel matrix
340   // should be recomputed
341   Simulator::Schedule (MilliSeconds (firstTimeMs + updatePeriodMs + 1), &ThreeGppChannelMatrixUpdateTest::DoGetChannel,
342                        this, channelModel, txMob, rxMob, txAntenna, rxAntenna, true);
343 
344   Simulator::Run ();
345   Simulator::Destroy ();
346 }
347 
348 /**
349  * Test case for the ThreeGppSpectrumPropagationLossModelTest class.
350  * 1) checks if the long term components for the direct and the reverse link
351  *    are the same
352  * 2) checks if the long term component is updated when changing the beamforming
353  *    vectors
354  * 3) checks if the long term is updated when changing the channel matrix
355  */
356 class ThreeGppSpectrumPropagationLossModelTest : public TestCase
357 {
358 public:
359   /**
360    * Constructor
361    */
362   ThreeGppSpectrumPropagationLossModelTest ();
363 
364   /**
365    * Destructor
366    */
367   virtual ~ThreeGppSpectrumPropagationLossModelTest ();
368 
369 private:
370   /**
371    * Build the test scenario
372    */
373   virtual void DoRun (void);
374 
375   /**
376    * Points the beam of thisDevice towards otherDevice
377    * \param thisDevice the device to configure
378    * \param thisAntenna the antenna object associated to thisDevice
379    * \param otherDevice the device to communicate with
380    * \param otherAntenna the antenna object associated to otherDevice
381    */
382   void DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<PhasedArrayModel> otherAntenna);
383 
384   /**
385    * Test of the long term component is correctly updated when the channel
386    * matrix is recomputed
387    * \param lossModel the ThreeGppSpectrumPropagationLossModel object used to
388    *        compute the rx PSD
389    * \param txPsd the PSD of the transmitted signal
390    * \param txMob the mobility model of the tx device
391    * \param rxMob the mobility model of the rx device
392    * \param rxPsdOld the previously received PSD
393    */
394   void CheckLongTermUpdate (Ptr<ThreeGppSpectrumPropagationLossModel> lossModel, Ptr<SpectrumValue> txPsd, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<SpectrumValue> rxPsdOld);
395 
396   /**
397    * Checks if two PSDs are equal
398    * \param first the first PSD
399    * \param second the second PSD
400    * \return true if first and second are equal, false otherwise
401    */
402   static bool ArePsdEqual (Ptr<SpectrumValue> first, Ptr<SpectrumValue> second);
403 };
404 
ThreeGppSpectrumPropagationLossModelTest()405 ThreeGppSpectrumPropagationLossModelTest::ThreeGppSpectrumPropagationLossModelTest ()
406   : TestCase ("Test case for the ThreeGppSpectrumPropagationLossModel class")
407 {
408 }
409 
~ThreeGppSpectrumPropagationLossModelTest()410 ThreeGppSpectrumPropagationLossModelTest::~ThreeGppSpectrumPropagationLossModelTest ()
411 {
412 }
413 
414 void
DoBeamforming(Ptr<NetDevice> thisDevice,Ptr<PhasedArrayModel> thisAntenna,Ptr<NetDevice> otherDevice,Ptr<PhasedArrayModel> otherAntenna)415 ThreeGppSpectrumPropagationLossModelTest::DoBeamforming (Ptr<NetDevice> thisDevice, Ptr<PhasedArrayModel> thisAntenna, Ptr<NetDevice> otherDevice, Ptr<PhasedArrayModel> otherAntenna)
416 {
417   Vector aPos = thisDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
418   Vector bPos = otherDevice->GetNode ()->GetObject<MobilityModel> ()->GetPosition ();
419 
420   // compute the azimuth and the elevation angles
421   Angles completeAngle (bPos,aPos);
422 
423   PhasedArrayModel::ComplexVector antennaWeights = thisAntenna->GetBeamformingVector (completeAngle);
424   thisAntenna->SetBeamformingVector (antennaWeights);
425 }
426 
427 bool
ArePsdEqual(Ptr<SpectrumValue> first,Ptr<SpectrumValue> second)428 ThreeGppSpectrumPropagationLossModelTest::ArePsdEqual (Ptr<SpectrumValue> first, Ptr<SpectrumValue> second)
429 {
430   bool ret = true;
431   for (uint8_t i = 0; i < first->GetSpectrumModel ()->GetNumBands (); i++)
432   {
433     if ((*first) [i] != (*second) [i])
434     {
435       ret = false;
436       continue;
437     }
438   }
439   return ret;
440 }
441 
442 void
CheckLongTermUpdate(Ptr<ThreeGppSpectrumPropagationLossModel> lossModel,Ptr<SpectrumValue> txPsd,Ptr<MobilityModel> txMob,Ptr<MobilityModel> rxMob,Ptr<SpectrumValue> rxPsdOld)443 ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate (Ptr<ThreeGppSpectrumPropagationLossModel> lossModel, Ptr<SpectrumValue> txPsd, Ptr<MobilityModel> txMob, Ptr<MobilityModel> rxMob, Ptr<SpectrumValue> rxPsdOld)
444 {
445   Ptr<SpectrumValue> rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, txMob, rxMob);
446   NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew),  false, "The long term is not updated when the channel matrix is recomputed");
447 }
448 
449 void
DoRun()450 ThreeGppSpectrumPropagationLossModelTest::DoRun ()
451 {
452   // Build the scenario for the test
453   Config::SetDefault ("ns3::ThreeGppChannelModel::UpdatePeriod", TimeValue (MilliSeconds (100)));
454 
455   uint8_t txAntennaElements[] {4, 4}; // tx antenna dimensions
456   uint8_t rxAntennaElements[] {4, 4}; // rx antenna dimensions
457 
458   // create the ChannelConditionModel object to be used to retrieve the
459   // channel condition
460   Ptr<ChannelConditionModel> condModel = CreateObject<AlwaysLosChannelConditionModel> ();
461 
462   // create the ThreeGppSpectrumPropagationLossModel object, set frequency,
463   // scenario and channel condition model to be used
464   Ptr<ThreeGppSpectrumPropagationLossModel> lossModel = CreateObject<ThreeGppSpectrumPropagationLossModel> ();
465   lossModel->SetChannelModelAttribute ("Frequency", DoubleValue(2.4e9));
466   lossModel->SetChannelModelAttribute ("Scenario", StringValue("UMa"));
467   lossModel->SetChannelModelAttribute ("ChannelConditionModel", PointerValue (condModel));  // create the ThreeGppChannelModel object used to generate the channel matrix
468 
469   // create the tx and rx nodes
470   NodeContainer nodes;
471   nodes.Create (2);
472 
473   // create the tx and rx devices
474   Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice> ();
475   Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice> ();
476 
477   // associate the nodes and the devices
478   nodes.Get (0)->AddDevice (txDev);
479   txDev->SetNode (nodes.Get (0));
480   nodes.Get (1)->AddDevice (rxDev);
481   rxDev->SetNode (nodes.Get (1));
482 
483   // create the tx and rx mobility models and set their positions
484   Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel> ();
485   txMob->SetPosition (Vector (0.0,0.0,10.0));
486   Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel> ();
487   rxMob->SetPosition (Vector (15.0,0.0,10.0)); // in this position the channel condition is always LOS
488 
489   // associate the nodes and the mobility models
490   nodes.Get (0)->AggregateObject (txMob);
491   nodes.Get (1)->AggregateObject (rxMob);
492 
493   // create the tx and rx antennas and set the their dimensions
494   Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (txAntennaElements [0]),
495                                                                                     "NumRows", UintegerValue (txAntennaElements [1]),
496                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
497   Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray> ("NumColumns", UintegerValue (rxAntennaElements [0]),
498                                                                                     "NumRows", UintegerValue (rxAntennaElements [1]),
499                                                                                     "AntennaElement", PointerValue(CreateObject<IsotropicAntennaModel> ()));
500 
501   // initialize ThreeGppSpectrumPropagationLossModel
502   lossModel->AddDevice (txDev, txAntenna);
503   lossModel->AddDevice (rxDev, rxAntenna);
504 
505   // set the beamforming vectors
506   DoBeamforming (txDev, txAntenna, rxDev, rxAntenna);
507   DoBeamforming (rxDev, rxAntenna, txDev, txAntenna);
508 
509   // create the tx psd
510   WifiSpectrumValue5MhzFactory sf;
511   double txPower = 0.1; // Watts
512   uint32_t channelNumber = 1;
513   Ptr<SpectrumValue> txPsd =  sf.CreateTxPowerSpectralDensity (txPower, channelNumber);
514 
515   // compute the rx psd
516   Ptr<SpectrumValue> rxPsdOld = lossModel->DoCalcRxPowerSpectralDensity (txPsd, txMob, rxMob);
517 
518   // 1) check that the rx PSD is equal for both the direct and the reverse channel
519   Ptr<SpectrumValue> rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, rxMob, txMob);
520   NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew),  true, "The long term for the direct and the reverse channel are different");
521 
522   // 2) check if the long term is updated when changing the BF vector
523   // change the position of the rx device and recompute the beamforming vectors
524   rxMob->SetPosition (Vector (10.0, 5.0, 10.0));
525   PhasedArrayModel::ComplexVector txBfVector = txAntenna->GetBeamformingVector ();
526   txBfVector [0] = std::complex<double> (0.0, 0.0);
527   txAntenna->SetBeamformingVector (txBfVector);
528 
529   rxPsdNew = lossModel->DoCalcRxPowerSpectralDensity (txPsd, rxMob, txMob);
530   NS_TEST_ASSERT_MSG_EQ (ArePsdEqual (rxPsdOld, rxPsdNew),  false, "Changing the BF vectors the rx PSD does not change");
531 
532   // update rxPsdOld
533   rxPsdOld = rxPsdNew;
534 
535   // 3) check if the long term is updated when the channel matrix is recomputed
536   Simulator::Schedule (MilliSeconds (101), &ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate,
537                        this, lossModel, txPsd, txMob, rxMob, rxPsdOld);
538 
539   Simulator::Run ();
540   Simulator::Destroy ();
541 }
542 
543 /**
544  * \ingroup spectrum
545  *
546  * Test suite for the ThreeGppChannelModel class
547  */
548 class ThreeGppChannelTestSuite : public TestSuite
549 {
550 public:
551   /**
552    * Constructor
553    */
554   ThreeGppChannelTestSuite ();
555 };
556 
ThreeGppChannelTestSuite()557 ThreeGppChannelTestSuite::ThreeGppChannelTestSuite ()
558   : TestSuite ("three-gpp-channel", UNIT)
559 {
560   AddTestCase (new ThreeGppChannelMatrixComputationTest, TestCase::QUICK);
561   AddTestCase (new ThreeGppChannelMatrixUpdateTest, TestCase::QUICK);
562   AddTestCase (new ThreeGppSpectrumPropagationLossModelTest, TestCase::QUICK);
563 }
564 
565 static ThreeGppChannelTestSuite myTestSuite;
566