diff --git a/src/spectrum/model/three-gpp-channel-model.cc b/src/spectrum/model/three-gpp-channel-model.cc index ffe54cf84..86cf05c1d 100644 --- a/src/spectrum/model/three-gpp-channel-model.cc +++ b/src/spectrum/model/three-gpp-channel-model.cc @@ -1090,6 +1090,21 @@ ThreeGppChannelModel::ChannelMatrixNeedsUpdate(Ptr return channelParams->m_generatedTime > channelMatrix->m_generatedTime; } +bool +ThreeGppChannelModel::AntennaSetupChanged(Ptr aAntenna, + Ptr bAntenna, + Ptr channelMatrix) +{ + // This allows changing the number of antenna ports during execution, + // which is used by nr's initial association. + size_t sAntNumElems = aAntenna->GetNumElems(); + size_t uAntNumElems = bAntenna->GetNumElems(); + size_t chanNumRows = channelMatrix->m_channel.GetNumRows(); + size_t chanNumCols = channelMatrix->m_channel.GetNumCols(); + return ((uAntNumElems != chanNumRows) || (sAntNumElems != chanNumCols)) && + ((uAntNumElems != chanNumCols) || (sAntNumElems != chanNumRows)); +} + Ptr ThreeGppChannelModel::GetChannel(Ptr aMob, Ptr bMob, @@ -1162,6 +1177,7 @@ ThreeGppChannelModel::GetChannel(Ptr aMob, NS_LOG_DEBUG("channel matrix present in the map"); channelMatrix = m_channelMatrixMap[channelMatrixKey]; updateMatrix = ChannelMatrixNeedsUpdate(channelParams, channelMatrix); + updateMatrix |= AntennaSetupChanged(aAntenna, bAntenna, channelMatrix); } else { diff --git a/src/spectrum/model/three-gpp-channel-model.h b/src/spectrum/model/three-gpp-channel-model.h index fec908e59..33966b2ae 100644 --- a/src/spectrum/model/three-gpp-channel-model.h +++ b/src/spectrum/model/three-gpp-channel-model.h @@ -335,6 +335,18 @@ class ThreeGppChannelModel : public MatrixBasedChannelModel bool ChannelMatrixNeedsUpdate(Ptr channelParams, Ptr channelMatrix); + /** + * Check if the channel matrix has to be updated due to + * changes in the number of antenna ports + * \param aAntenna the antenna array of node a + * \param bAntenna the antenna array of node b + * \param channelMatrix channel matrix structure + * \return true if the channel matrix has to be updated, false otherwise + */ + bool AntennaSetupChanged(Ptr aAntenna, + Ptr bAntenna, + Ptr channelMatrix); + std::unordered_map> m_channelMatrixMap; //!< map containing the channel realizations per pair of //!< PhasedAntennaArray instances, the key of this map is reciprocal diff --git a/src/spectrum/test/three-gpp-channel-test-suite.cc b/src/spectrum/test/three-gpp-channel-test-suite.cc index f6a2b412a..4e31236fd 100644 --- a/src/spectrum/test/three-gpp-channel-test-suite.cc +++ b/src/spectrum/test/three-gpp-channel-test-suite.cc @@ -496,6 +496,234 @@ ThreeGppChannelMatrixUpdateTest::DoRun() Simulator::Destroy(); } +/** + * \ingroup spectrum-tests + * + * Test case for the ThreeGppChannelModel class. + * It checks if the channel realizations are correctly + * updated after a change in the number of antenna elements. + */ +class ThreeGppAntennaSetupChangedTest : public TestCase +{ + public: + /** + * Constructor + */ + ThreeGppAntennaSetupChangedTest(); + + /** + * Destructor + */ + ~ThreeGppAntennaSetupChangedTest() override; + + private: + /** + * Build the test scenario + */ + void DoRun() override; + + /** + * This method is used to schedule the channel matrix computation at different + * time instants and to check if it correctly updated + * \param channelModel the ThreeGppChannelModel object used to generate the channel matrix + * \param txMob the mobility model of the first node + * \param rxMob the mobility model of the second node + * \param txAntenna the antenna object associated to the first node + * \param rxAntenna the antenna object associated to the second node + * \param update whether if the channel matrix should be updated or not + */ + void DoGetChannel(Ptr channelModel, + Ptr txMob, + Ptr rxMob, + Ptr txAntenna, + Ptr rxAntenna, + bool update); + + Ptr + m_currentChannel; //!< used by DoGetChannel to store the current channel matrix + uint32_t m_txAntennaElements{4}; //!< number of rows and columns of tx antenna array + uint32_t m_rxAntennaElements{4}; //!< number of rows and columns of rx antenna array + uint32_t m_txPorts{1}; //!< number of horizontal and vertical ports of tx antenna array + uint32_t m_rxPorts{1}; //!< number of horizontal and vertical ports of rx antenna array +}; + +ThreeGppAntennaSetupChangedTest::ThreeGppAntennaSetupChangedTest() + : TestCase("Check if the channel realizations are correctly updated after antenna port changes " + "during the simulation") +{ +} + +ThreeGppAntennaSetupChangedTest::~ThreeGppAntennaSetupChangedTest() +{ +} + +void +ThreeGppAntennaSetupChangedTest::DoGetChannel(Ptr channelModel, + Ptr txMob, + Ptr rxMob, + Ptr txAntenna, + Ptr rxAntenna, + bool update) +{ + // retrieve the channel matrix + Ptr channelMatrix = + channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna); + + if (m_currentChannel) + { + // compare the old and the new channel matrices + NS_TEST_ASSERT_MSG_EQ((m_currentChannel->m_channel != channelMatrix->m_channel), + update, + Simulator::Now().GetMilliSeconds() + << " The channel matrix is not correctly updated"); + } + m_currentChannel = channelMatrix; +} + +void +ThreeGppAntennaSetupChangedTest::DoRun() +{ + // Build the scenario for the test + uint32_t updatePeriodMs = 100; // update period in ms + + // create the channel condition model + Ptr channelConditionModel = + CreateObject(); + + // create the ThreeGppChannelModel object used to generate the channel matrix + Ptr channelModel = CreateObject(); + channelModel->SetAttribute("Frequency", DoubleValue(60.0e9)); + channelModel->SetAttribute("Scenario", StringValue("UMa")); + channelModel->SetAttribute("ChannelConditionModel", PointerValue(channelConditionModel)); + channelModel->SetAttribute("UpdatePeriod", TimeValue(MilliSeconds(updatePeriodMs))); + + // create the tx and rx nodes + NodeContainer nodes; + nodes.Create(2); + + // create the tx and rx devices + Ptr txDev = CreateObject(); + Ptr rxDev = CreateObject(); + + // associate the nodes and the devices + nodes.Get(0)->AddDevice(txDev); + txDev->SetNode(nodes.Get(0)); + nodes.Get(1)->AddDevice(rxDev); + rxDev->SetNode(nodes.Get(1)); + + // create the tx and rx mobility models and set their positions + Ptr txMob = CreateObject(); + txMob->SetPosition(Vector(0.0, 0.0, 10.0)); + Ptr rxMob = CreateObject(); + rxMob->SetPosition(Vector(100.0, 0.0, 1.6)); + + // associate the nodes and the mobility models + nodes.Get(0)->AggregateObject(txMob); + nodes.Get(1)->AggregateObject(rxMob); + + // create the tx and rx antennas and set the their dimensions + Ptr txAntenna = CreateObjectWithAttributes( + "NumColumns", + UintegerValue(m_txAntennaElements), + "NumRows", + UintegerValue(m_txAntennaElements), + "AntennaElement", + PointerValue(CreateObject()), + "NumVerticalPorts", + UintegerValue(m_txPorts), + "NumHorizontalPorts", + UintegerValue(m_txPorts)); + + Ptr rxAntenna = CreateObjectWithAttributes( + "NumColumns", + UintegerValue(m_rxAntennaElements), + "NumRows", + UintegerValue(m_rxAntennaElements), + "AntennaElement", + PointerValue(CreateObject()), + "NumVerticalPorts", + UintegerValue(m_rxPorts), + "NumHorizontalPorts", + UintegerValue(m_rxPorts)); + + // check if the channel matrix is correctly updated + + // compute the channel matrix for the first time + Simulator::Schedule(MilliSeconds(1), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + true); + + // call GetChannel before the update period is exceeded, the channel matrix + // should not be updated + Simulator::Schedule(MilliSeconds(2), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + false); + + // after changing the number of antenna ports, the channel matrix + // should be recomputed + Simulator::Schedule(MilliSeconds(3), + [&txAntenna]() { txAntenna->SetNumRows(txAntenna->GetNumRows() + 1); }); + Simulator::Schedule(MilliSeconds(4), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + true); + + // after recomputing it once, the channel matrix should be cached + Simulator::Schedule(MilliSeconds(5), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + false); + + // after recomputing it once, the channel matrix should be cached + Simulator::Schedule(MilliSeconds(6), + [&rxAntenna]() { rxAntenna->SetNumRows(rxAntenna->GetNumRows() + 1); }); + Simulator::Schedule(MilliSeconds(7), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + true); + + // after recomputing it once, the channel matrix should be cached + Simulator::Schedule(MilliSeconds(8), + &ThreeGppAntennaSetupChangedTest::DoGetChannel, + this, + channelModel, + txMob, + rxMob, + txAntenna, + rxAntenna, + false); + + Simulator::Run(); + Simulator::Destroy(); +} + /** * \ingroup spectrum-tests * \brief A structure that holds the parameters for the function @@ -1239,6 +1467,7 @@ ThreeGppChannelTestSuite::ThreeGppChannelTestSuite() AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 2, 1, 1), TestCase::Duration::QUICK); AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 4, 2, 2), TestCase::Duration::QUICK); AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 2, 2, 2), TestCase::Duration::QUICK); + AddTestCase(new ThreeGppAntennaSetupChangedTest(), TestCase::Duration::QUICK); AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 4, 1, 1), TestCase::Duration::QUICK); AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 4, 2, 2),