From 4435ace673134836e87f86e9cf029d65cb56c057 Mon Sep 17 00:00:00 2001 From: Nicola Baldo Date: Fri, 1 Mar 2013 20:53:54 +0100 Subject: [PATCH] added support for measurements in LteUeRrc --- src/lte/model/lte-common.cc | 44 +++ src/lte/model/lte-common.h | 70 +++- src/lte/model/lte-rrc-sap.h | 15 +- src/lte/model/lte-ue-rrc.cc | 627 +++++++++++++++++++++++++++++++++++- src/lte/model/lte-ue-rrc.h | 44 +++ 5 files changed, 790 insertions(+), 10 deletions(-) diff --git a/src/lte/model/lte-common.cc b/src/lte/model/lte-common.cc index be66e58f8..6f3020ce8 100644 --- a/src/lte/model/lte-common.cc +++ b/src/lte/model/lte-common.cc @@ -198,5 +198,49 @@ TransmissionModesLayers::TxMode2LayerNum (uint8_t txMode) } +double +EutranMeasurementMapping::RsrpRange2Dbm (uint8_t range) +{ + // 3GPP TS 36.133 section 9.1.4 RSRP Measurement Report Mapping + NS_ASSERT_MSG (range <= 97, "value " << range << " is out of range"); + return (double) range - 141.0; +} + +uint8_t +EutranMeasurementMapping::Dbm2RsrpRange (double dbm) +{ + // 3GPP TS 36.133 section 9.1.4 RSRP Measurement Report Mapping + double range = std::min( std::max (std::floor(dbm + 141), 0.0), 97.0); + return (uint8_t) range; +} + +double +EutranMeasurementMapping::RsrqRange2Db (uint8_t range) +{ + // 3GPP TS 36.133 section 9.1.7 RSRQ Measurement Report Mapping + NS_ASSERT_MSG (range <= 34, "value " << range << " is out of range"); + return ((double) range - 40.0)*0.5; +} + +uint8_t +EutranMeasurementMapping::Db2RsrqRange (double db) +{ + // 3GPP TS 36.133 section 9.1.7 RSRQ Measurement Report Mapping + double range = std::min (std::max (std::floor (db*2 + 40), 0.0), 34.0); + return (uint8_t) range; +} + +double +EutranMeasurementMapping::QuantizeRsrp (double v) +{ + return RsrpRange2Dbm (Dbm2RsrpRange (v)); +} + +double +EutranMeasurementMapping::QuantizeRsrq (double v) +{ + return RsrqRange2Db (Db2RsrqRange (v)); +} + }; // namespace ns3 diff --git a/src/lte/model/lte-common.h b/src/lte/model/lte-common.h index 1d49a59ad..b6aff232f 100644 --- a/src/lte/model/lte-common.h +++ b/src/lte/model/lte-common.h @@ -22,7 +22,7 @@ #define LTE_COMMON_H #include "ns3/uinteger.h" -#include +#include // see 36.213 section 8 #define UL_PUSCH_TTIS_DELAY 4 @@ -148,6 +148,74 @@ struct PhyReceptionStatParameters }; +/** + * Implements the E-UTRA measurement mappings defined in 3GPP TS + * 36.133 section 9.1 E-UTRAN measurements + * + */ +class EutranMeasurementMapping +{ +public: + /** + * converts an RSRP range to dBm as per + * 3GPP TS 36.133 section 9.1.4 RSRP Measurement Report Mapping + * + * \param range the RSRP range value + * + * \return the corresponding RSRP value in dBm + */ + static double RsrpRange2Dbm (uint8_t range); + + /** + * convert an RSRP value in dBm to the corresponding range as per + * 3GPP TS 36.133 section 9.1.4 RSRP Measurement Report Mapping + * + * \param dbm the RSRP value in dBm + * + * \return the corresponding range + */ + static uint8_t Dbm2RsrpRange (double dbm); + + /** + * converts an RSRQ range to dB as per + * 3GPP TS 36.133 section 9.1.7 RSRQ Measurement Report Mapping + * + * \param range the RSRQ range value + * + * \return the corresponding RSRQ value in dB + */ + static double RsrqRange2Db (uint8_t range); + + /** + * convert an RSRQ value in dB to the corresponding range as per + * 3GPP TS 36.133 section 9.1.7 RSRQ Measurement Report Mapping + * + * \param db the RSRQ value in dB + * + * \return the corresponding range + */ + static uint8_t Db2RsrqRange (double db); + + /** + * Quantize an RSRP value according to the measurement mapping of TS 36.133 + * + * \param v RSRP value in dBm + * + * \return the quantized RSRP value in dBm + */ + static double QuantizeRsrp (double v); + + /** + * Quantize an RSRQ value according to the measurement mapping of TS 36.133 + * + * \param v RSRQ value in dB + * + * \return the quantized RSRQ value in dB + */ + static double QuantizeRsrq(double v); + +}; + }; // namespace ns3 diff --git a/src/lte/model/lte-rrc-sap.h b/src/lte/model/lte-rrc-sap.h index 2c53376cc..9d8b4eede 100644 --- a/src/lte/model/lte-rrc-sap.h +++ b/src/lte/model/lte-rrc-sap.h @@ -232,7 +232,7 @@ public: enum {eventA1,eventA2,eventA3,eventA4,eventA5} eventId; ThresholdEutra threshold1; // used for A1, A2, A4, A5 ThresholdEutra threshold2; // used for A5 - bool reportOnLeave; // used for A3 + bool reportOnLeave; int8_t a3Offset; // used for A3 uint8_t hysteresis; uint16_t timeToTrigger; @@ -492,6 +492,7 @@ public: { MeasResults measResults; }; + }; @@ -518,7 +519,7 @@ public: virtual void SendRrcConnectionReconfigurationCompleted (RrcConnectionReconfigurationCompleted msg) = 0; virtual void SendRrcConnectionReestablishmentRequest (RrcConnectionReestablishmentRequest msg) = 0; virtual void SendRrcConnectionReestablishmentComplete (RrcConnectionReestablishmentComplete msg) = 0; - + virtual void SendMeasurementReport (MeasurementReport msg) = 0; }; @@ -641,7 +642,8 @@ public: virtual void SendRrcConnectionReconfigurationCompleted (RrcConnectionReconfigurationCompleted msg); virtual void SendRrcConnectionReestablishmentRequest (RrcConnectionReestablishmentRequest msg); virtual void SendRrcConnectionReestablishmentComplete (RrcConnectionReestablishmentComplete msg); - + virtual void SendMeasurementReport (MeasurementReport msg); + private: MemberLteUeRrcSapUser (); C* m_owner; @@ -700,6 +702,13 @@ MemberLteUeRrcSapUser::SendRrcConnectionReestablishmentComplete (RrcConnectio m_owner->DoSendRrcConnectionReestablishmentComplete (msg); } +template +void +MemberLteUeRrcSapUser::SendMeasurementReport (MeasurementReport msg) +{ + m_owner->DoSendMeasurementReport (msg); +} + /** * Template for the implementation of the LteUeRrcSapProvider as a member * of an owner class of type C to which all methods are forwarded diff --git a/src/lte/model/lte-ue-rrc.cc b/src/lte/model/lte-ue-rrc.cc index f1f8bc079..6d4c701cb 100644 --- a/src/lte/model/lte-ue-rrc.cc +++ b/src/lte/model/lte-ue-rrc.cc @@ -38,6 +38,8 @@ #include "lte-as-sap.h" #include "lte-enb-net-device.h" +#include + NS_LOG_COMPONENT_DEFINE ("LteUeRrc"); namespace ns3 { @@ -539,7 +541,7 @@ LteUeRrc::DoRecvMasterInformationBlock (LteRrcSap::MasterInformationBlock msg) m_receivedMib = true; if (m_state == IDLE_WAIT_SYSTEM_INFO && m_receivedMib && m_receivedSib2) { - SwitchToState (IDLE_CAMPED_NORMALLY); + SwitchToState (IDLE_CAMPED_NORMALLY); } } @@ -547,12 +549,246 @@ void LteUeRrc::DoReportUeMeasurements (LteUeCphySapUser::UeMeasurementsParameters params) { NS_LOG_FUNCTION (this); - for (uint16_t i = 0; i < params.m_ueMeasurementsList.size (); i++) - { - - NS_LOG_DEBUG (this << " CellId " << params.m_ueMeasurementsList.at (i).m_cellId << " RSRP " << params.m_ueMeasurementsList.at (i).m_rsrp << " RSRQ " << params.m_ueMeasurementsList.at (i).m_rsrq); + + for (std::vector ::iterator newMeasIt = params.m_ueMeasurementsList.begin (); newMeasIt != params.m_ueMeasurementsList.end (); ++newMeasIt) + { + NS_LOG_LOGIC (this << " CellId " << newMeasIt->m_cellId << " RSRP " << newMeasIt->m_rsrp << " RSRQ " << newMeasIt->m_rsrq); + // 3GPP TS 36.331 section 5.5.3.2 Layer 3 filtering + std::map::iterator storedMeasIt = m_storedMeasValues.find (newMeasIt->m_cellId); + if (storedMeasIt == m_storedMeasValues.end ()) + { + // first value is unfiltered + MeasValues v; + v.rsrp = newMeasIt->m_rsrp; + v.rsrq = newMeasIt->m_rsrq; + std::pair val (newMeasIt->m_cellId, v); + std::pair::iterator, bool> + ret = m_storedMeasValues.insert (val); + NS_ASSERT_MSG (ret.second == true, "element already existed"); + storedMeasIt = ret.first; + } + else + { + // F_n = (1-a) F_{n-1} + a M_n + storedMeasIt->second.rsrp = (1 - m_varMeasConfig.aRsrp) * storedMeasIt->second.rsrp + + m_varMeasConfig.aRsrp * newMeasIt->m_rsrp; + storedMeasIt->second.rsrq = (1 - m_varMeasConfig.aRsrq) * storedMeasIt->second.rsrq + + m_varMeasConfig.aRsrq * newMeasIt->m_rsrq; + } + storedMeasIt->second.timestamp = Simulator::Now (); } + + + // 3GPP TS 36.331 section 5.5.4.1 Measurement report triggering - General + for (std::map::iterator measIdIt + = m_varMeasConfig.measIdList.begin (); + measIdIt != m_varMeasConfig.measIdList.end (); + ++measIdIt) + { + NS_ASSERT (measIdIt->first == measIdIt->second.measId); + uint8_t measId = measIdIt->first; + NS_LOG_LOGIC (this << " considering measId " << (uint32_t) measId); + + std::map::iterator + reportConfigIt = m_varMeasConfig.reportConfigList.find (measIdIt->second.reportConfigId); + NS_ASSERT (reportConfigIt != m_varMeasConfig.reportConfigList.end ()); + LteRrcSap::ReportConfigEutra& reportConfigEutra = reportConfigIt->second.reportConfigEutra; + + std::map::iterator + measObjectIt = m_varMeasConfig.measObjectList.find (measIdIt->second.measObjectId); + NS_ASSERT (measObjectIt != m_varMeasConfig.measObjectList.end ()); + LteRrcSap::MeasObjectEutra& measObjectEutra = measObjectIt->second.measObjectEutra; + + std::map::iterator + measReportIt = m_varMeasReportList.find (measId); + + // we don't check the purpose field, as it is only included for + // triggerType == periodical, which is not supported + NS_ASSERT_MSG (reportConfigEutra.triggerType + == LteRrcSap::ReportConfigEutra::event, + "only triggerType == event is supported"); + // only EUTRA is supported, no need to check for it + + bool eventEntryCondApplicable = false; + bool eventLeavingCondApplicable = false; + + std::list concernedCellsEntry; + std::list concernedCellsLeaving; + + switch (reportConfigEutra.eventId) + { + case LteRrcSap::ReportConfigEutra::eventA2: + { + double ms; // Ms, the measurement for serving cell + double thresh; // Tresh, the threshold parameter for this event + double hys = (double) reportConfigEutra.hysteresis * 0.5; // Hys, the hysteresis parameter for this event. See 36.331 section 6.3.5 for the conversion. + switch (reportConfigEutra.triggerQuantity) + { + case LteRrcSap::ReportConfigEutra::rsrp: + ms = m_storedMeasValues[m_cellId].rsrp; + //ms = EutranMeasurementMapping::QuantizeRsrp (m_storedMeasValues[m_cellId].rsrp); + NS_ASSERT (reportConfigEutra.threshold1.choice + == LteRrcSap::ThresholdEutra::thresholdRsrp); + thresh = EutranMeasurementMapping::RsrpRange2Dbm (reportConfigEutra.threshold1.range); + break; + case LteRrcSap::ReportConfigEutra::rsrq: + ms = m_storedMeasValues[m_cellId].rsrq; + //ms = EutranMeasurementMapping::QuantizeRsrq (m_storedMeasValues[m_cellId].rsrq); + NS_ASSERT (reportConfigEutra.threshold1.choice + == LteRrcSap::ThresholdEutra::thresholdRsrq); + thresh = EutranMeasurementMapping::RsrqRange2Db (reportConfigEutra.threshold1.range); + break; + default: + NS_FATAL_ERROR ("unsupported triggerQuantity"); + break; + } + // Inequality A2-1 (Entering condition) : Ms + Hys < Thresh + bool entryCond = ms + hys < thresh; + if (entryCond == true + && (measReportIt == m_varMeasReportList.end () + || (measReportIt != m_varMeasReportList.end () + && (measReportIt->second.cellsTriggeredList.find (m_cellId) + == measReportIt->second.cellsTriggeredList.end ())))) + { + concernedCellsEntry.push_back (m_cellId); + eventEntryCondApplicable = true; + } + // Inequality A2-2 (Leaving condition) : Ms − Hys > Thresh + bool leavingCond = ms - hys > thresh; + if (leavingCond + && measReportIt != m_varMeasReportList.end () + && (measReportIt->second.cellsTriggeredList.find (m_cellId) + != measReportIt->second.cellsTriggeredList.end ())) + { + concernedCellsLeaving.push_back (m_cellId); + eventLeavingCondApplicable = true; + } + NS_LOG_LOGIC ("event A2: serving cell " << m_cellId << " ms=" << ms << " thresh=" << thresh << " entryCond=" << entryCond << " leavingCond=" << leavingCond); + } + break; + + case LteRrcSap::ReportConfigEutra::eventA4: + { + for (std::map::iterator storedMeasIt = m_storedMeasValues.begin (); + storedMeasIt != m_storedMeasValues.end (); + ++storedMeasIt) + { + uint16_t cellId = storedMeasIt->first; + if (cellId != m_cellId) + { + double mn; // Mn, the measurement for neighboring cell + double thresh; // Tresh, the threshold parameter for this event + double hys = (double) reportConfigEutra.hysteresis * 0.5; // Hys, the hysteresis parameter for this event. See 36.331 section 6.3.5 for the conversion. + switch (reportConfigEutra.triggerQuantity) + { + case LteRrcSap::ReportConfigEutra::rsrp: + mn = storedMeasIt->second.rsrp; + //mn = EutranMeasurementMapping::QuantizeRsrp (storedMeasIt->second.rsrp); + NS_ASSERT (reportConfigEutra.threshold1.choice + == LteRrcSap::ThresholdEutra::thresholdRsrp); + thresh = EutranMeasurementMapping::RsrpRange2Dbm (reportConfigEutra.threshold1.range); + break; + case LteRrcSap::ReportConfigEutra::rsrq: + mn = storedMeasIt->second.rsrq; + //mn = EutranMeasurementMapping::QuantizeRsrq (storedMeasIt->second.rsrq); + NS_ASSERT (reportConfigEutra.threshold1.choice + == LteRrcSap::ThresholdEutra::thresholdRsrq); + thresh = EutranMeasurementMapping::RsrqRange2Db (reportConfigEutra.threshold1.range); + break; + default: + NS_FATAL_ERROR ("unsupported triggerQuantity"); + break; + } + // Inequality A4-1 (Entering condition) : Mn + Ofn + Ocn − Hys > Thresh + bool entryCond = mn + measObjectEutra.offsetFreq + 0.0 - hys > thresh; + if (entryCond == true + && (measReportIt == m_varMeasReportList.end () + || (measReportIt != m_varMeasReportList.end () + && (measReportIt->second.cellsTriggeredList.find (cellId) + == measReportIt->second.cellsTriggeredList.end ())))) + { + concernedCellsEntry.push_back (cellId); + eventEntryCondApplicable = true; + } + // Inequality A4-2 (Leaving condition) : Mn + Ofn + Ocn + Hys < Thresh + bool leavingCond = mn + measObjectEutra.offsetFreq + 0.0 + hys < thresh; + if (leavingCond + && measReportIt != m_varMeasReportList.end () + && (measReportIt->second.cellsTriggeredList.find (cellId) + != measReportIt->second.cellsTriggeredList.end ())) + { + concernedCellsLeaving.push_back (cellId); + eventLeavingCondApplicable = true; + } + + NS_LOG_LOGIC ("event A4: neighbor cell " << cellId << " mn=" << mn << " thresh=" << thresh << " entryCond=" << entryCond << " leavingCond=" << leavingCond); + } + } + } + break; + + default: + NS_FATAL_ERROR ("unsupported eventId " << reportConfigEutra.eventId); + break; + + } // switch (event type) + + NS_LOG_LOGIC ("eventEntryCondApplicable=" << eventEntryCondApplicable + << " eventLeavingCondApplicable=" << eventLeavingCondApplicable); + + bool initiateUeMeasurementReportingProcedure = false; + + if (eventEntryCondApplicable) + { + if (measReportIt == m_varMeasReportList.end ()) + { + VarMeasReport r; + r.measId = measId; + std::pair val (measId, r); + std::pair::iterator, bool> + ret = m_varMeasReportList.insert (val); + NS_ASSERT_MSG (ret.second == true, "element already existed"); + measReportIt = ret.first; + } + for (std::list::iterator it = concernedCellsEntry.begin (); + it != concernedCellsEntry.end (); + ++it) + { + measReportIt->second.cellsTriggeredList.insert (*it); + } + measReportIt->second.numberOfReportsSent = 0; + initiateUeMeasurementReportingProcedure = true; + } + + if (eventLeavingCondApplicable) + { + NS_ASSERT (measReportIt != m_varMeasReportList.end ()); + for (std::list::iterator it = concernedCellsLeaving.begin (); + it != concernedCellsLeaving.end (); + ++it) + { + measReportIt->second.cellsTriggeredList.erase (*it); + } + if (reportConfigEutra.reportOnLeave) + { + initiateUeMeasurementReportingProcedure = true; + } + } + + if (initiateUeMeasurementReportingProcedure) + { + SendMeasurementReport (measId); + } + + if ((measReportIt != m_varMeasReportList.end ()) + && (measReportIt->second.cellsTriggeredList.empty ())) + { + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + + } // for each measId in m_varMeasConfig.measIdList } @@ -664,7 +900,12 @@ LteUeRrc::DoRecvRrcConnectionReconfiguration (LteRrcSap::RrcConnectionReconfigur m_srb1 = 0; // new instance will be be created within ApplyRadioResourceConfigDedicated m_drbMap.clear (); // dispose all DRBs - ApplyRadioResourceConfigDedicated (msg.radioResourceConfigDedicated); + ApplyRadioResourceConfigDedicated (msg.radioResourceConfigDedicated); + + if (msg.haveMeasConfig) + { + ApplyMeasConfig (msg.measConfig); + } // RRC connection reconfiguration completed will be sent // after handover is complete } @@ -675,6 +916,10 @@ LteUeRrc::DoRecvRrcConnectionReconfiguration (LteRrcSap::RrcConnectionReconfigur { ApplyRadioResourceConfigDedicated (msg.radioResourceConfigDedicated); } + if (msg.haveMeasConfig) + { + ApplyMeasConfig (msg.measConfig); + } LteRrcSap::RrcConnectionReconfigurationCompleted msg2; msg2.rrcTransactionIdentifier = msg.rrcTransactionIdentifier; m_rrcSapUser->SendRrcConnectionReconfigurationCompleted (msg2); @@ -912,6 +1157,376 @@ LteUeRrc::ApplyRadioResourceConfigDedicated (LteRrcSap::RadioResourceConfigDedic } +void +LteUeRrc::ApplyMeasConfig (LteRrcSap::MeasConfig mc) +{ + NS_LOG_FUNCTION (this); + + // perform the actions specified in 3GPP TS 36.331 section 5.5.2.1 + + // 3GPP TS 36.331 section 5.5.2.4 Measurement object removal + for (std::list::iterator it = mc.measObjectToRemoveList.begin (); + it != mc.measObjectToRemoveList.end (); + ++it) + { + uint8_t measObjectId = *it; + NS_LOG_LOGIC (this << " deleting measObjectId " << (uint32_t) measObjectId); + m_varMeasConfig.measObjectList.erase (measObjectId); + std::map::iterator measIdIt = m_varMeasConfig.measIdList.begin (); + while (measIdIt != m_varMeasConfig.measIdList.end ()) + { + if (measIdIt->second.measObjectId == measObjectId) + { + uint8_t measId = measIdIt->second.measId; + NS_ASSERT (measId == measIdIt->first); + NS_LOG_LOGIC (this << " deleting measId " << (uint32_t) measId << " because referring to measObjectId " << (uint32_t) measObjectId); + // note: postfix operator preserves iterator validity + m_varMeasConfig.measIdList.erase (measIdIt++); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId " << (uint32_t) measId << " because referring to measObjectId " << (uint32_t) measObjectId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + else + { + ++measIdIt; + } + } + + } + + // 3GPP TS 36.331 section 5.5.2.5 Measurement object addition/ modification + for (std::list::iterator it = mc.measObjectToAddModList.begin (); + it != mc.measObjectToAddModList.end (); + ++it) + { + // simplifying assumptions + NS_ASSERT_MSG (it->measObjectEutra.cellsToRemoveList.empty (), "cellsToRemoveList not supported"); + NS_ASSERT_MSG (it->measObjectEutra.cellsToAddModList.empty (), "cellsToAddModList not supported"); + NS_ASSERT_MSG (it->measObjectEutra.cellsToRemoveList.empty (), "blackCellsToRemoveList not supported"); + NS_ASSERT_MSG (it->measObjectEutra.blackCellsToAddModList.empty (), "blackCellsToAddModList not supported"); + NS_ASSERT_MSG (it->measObjectEutra.haveCellForWhichToReportCGI == false , "cellForWhichToReportCGI is not supported"); + + + uint8_t measObjectId = it->measObjectId; + std::map::iterator measObjectIt = m_varMeasConfig.measObjectList.find (measObjectId); + if (measObjectIt != m_varMeasConfig.measObjectList.end ()) + { + NS_LOG_LOGIC ("measObjectId " << (uint32_t) measObjectId << " exists, updating entry"); + measObjectIt->second = *it; + for (std::map::iterator measIdIt + = m_varMeasConfig.measIdList.begin (); + measIdIt != m_varMeasConfig.measIdList.end (); + ++measIdIt) + { + if (measIdIt->second.measObjectId == measObjectId) + { + uint8_t measId = measIdIt->second.measId; + NS_LOG_LOGIC (this << " found measId " << (uint32_t) measId << " referring to measObjectId " << (uint32_t) measObjectId); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId " << (uint32_t) measId << " because referring to measObjectId " << (uint32_t) measObjectId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + } + } + else + { + NS_LOG_LOGIC ("measObjectId " << (uint32_t) measObjectId << " is new, adding entry"); + m_varMeasConfig.measObjectList[measObjectId] = *it; + } + } + + // 3GPP TS 36.331 section 5.5.2.6 Reporting configuration removal + for (std::list::iterator it = mc.reportConfigToRemoveList.begin (); + it != mc.reportConfigToRemoveList.end (); + ++it) + { + uint8_t reportConfigId = *it; + NS_LOG_LOGIC (this << " deleting reportConfigId " << (uint32_t) reportConfigId); + m_varMeasConfig.reportConfigList.erase (reportConfigId); + std::map::iterator measIdIt = m_varMeasConfig.measIdList.begin (); + while (measIdIt != m_varMeasConfig.measIdList.end ()) + { + if (measIdIt->second.reportConfigId == reportConfigId) + { + uint8_t measId = measIdIt->second.measId; + NS_ASSERT (measId == measIdIt->first); + NS_LOG_LOGIC (this << " deleting measId " << (uint32_t) measId << " because referring to reportConfigId " << (uint32_t) reportConfigId); + // note: postfix operator preserves iterator validity + m_varMeasConfig.measIdList.erase (measIdIt++); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId" << (uint32_t) measId << " because referring to reportConfigId " << (uint32_t) reportConfigId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + else + { + ++measIdIt; + } + } + + } + + // 3GPP TS 36.331 section 5.5.2.7 Reporting configuration addition/ modification + for (std::list::iterator it = mc.reportConfigToAddModList.begin (); + it != mc.reportConfigToAddModList.end (); + ++it) + { + // simplifying assumptions + NS_ASSERT_MSG (it->reportConfigEutra.triggerType == LteRrcSap::ReportConfigEutra::event, + "only trigger type EVENT is supported"); + NS_ASSERT_MSG (it->reportConfigEutra.eventId == LteRrcSap::ReportConfigEutra::eventA2 + || it->reportConfigEutra.eventId == LteRrcSap::ReportConfigEutra::eventA4, + "only events A2 and A4 are supported"); + NS_ASSERT_MSG (it->reportConfigEutra.timeToTrigger == 0, "timeToTrigger > 0 is not supported"); + + uint8_t reportConfigId = it->reportConfigId; + std::map::iterator reportConfigIt = m_varMeasConfig.reportConfigList.find (reportConfigId); + if (reportConfigIt != m_varMeasConfig.reportConfigList.end ()) + { + NS_LOG_LOGIC ("reportConfigId " << (uint32_t) reportConfigId << " exists, updating entry"); + m_varMeasConfig.reportConfigList[reportConfigId] = *it; + for (std::map::iterator measIdIt + = m_varMeasConfig.measIdList.begin (); + measIdIt != m_varMeasConfig.measIdList.end (); + ++measIdIt) + { + if (measIdIt->second.reportConfigId == reportConfigId) + { + uint8_t measId = measIdIt->second.measId; + NS_LOG_LOGIC (this << " found measId " << (uint32_t) measId << " referring to reportConfigId " << (uint32_t) reportConfigId); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId " << (uint32_t) measId << " because referring to reportConfigId " << (uint32_t) reportConfigId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + } + } + else + { + NS_LOG_LOGIC ("reportConfigId " << (uint32_t) reportConfigId << " is new, adding entry"); + m_varMeasConfig.reportConfigList[reportConfigId] = *it; + } + } + + + // 3GPP TS 36.331 section 5.5.2.8 Quantity configuration + if (mc.haveQuantityConfig) + { + NS_LOG_LOGIC (this << " setting quantityConfig"); + m_varMeasConfig.quantityConfig = mc.quantityConfig; + std::map::iterator measIdIt = m_varMeasConfig.measIdList.begin (); + while (measIdIt != m_varMeasConfig.measIdList.end ()) + { + uint8_t measId = measIdIt->second.measId; + NS_ASSERT (measId == measIdIt->first); + NS_LOG_LOGIC (this << " deleting measId " << (uint32_t) measId); + // note: postfix operator preserves iterator validity + m_varMeasConfig.measIdList.erase (measIdIt++); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId" << (uint32_t) measId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + // we calculate here the coefficient a used for Layer 3 filtering, see 3GPP TS 36.331 section 5.5.3.2 + m_varMeasConfig.aRsrp = std::pow (0.5, mc.quantityConfig.filterCoefficientRSRP/4.0); + m_varMeasConfig.aRsrq = std::pow (0.5, mc.quantityConfig.filterCoefficientRSRQ/4.0); + NS_LOG_LOGIC (this << " new filter coefficients: aRsrp=" << m_varMeasConfig.aRsrp << ", aRsrq=" << m_varMeasConfig.aRsrq); + + + } + + + // 3GPP TS 36.331 section 5.5.2.2 Measurement identity removal + for (std::list::iterator it = mc.measIdToRemoveList.begin (); + it != mc.measIdToRemoveList.end (); + ++it) + { + uint8_t measId = *it; + NS_LOG_LOGIC (this << " deleting measId " << (uint32_t) measId); + m_varMeasConfig.measIdList.erase (measId); + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt != m_varMeasReportList.end ()) + { + NS_LOG_LOGIC (this << " deleting existing report for measId" << (uint32_t) measId); + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + } + + // 3GPP TS 36.331 section 5.5.2.3 Measurement identity addition/ modification + for (std::list::iterator it = mc.measIdToAddModList.begin (); + it != mc.measIdToAddModList.end (); + ++it) + { + NS_LOG_LOGIC (this << " measId " << (uint32_t) it->measId << " (measObjectId=" + << (uint32_t) it->measObjectId << ", reportConfigId=" << (uint32_t) it->reportConfigId << ")"); + NS_ASSERT (m_varMeasConfig.measObjectList.find (it->measObjectId) + != m_varMeasConfig.measObjectList.end ()); + NS_ASSERT (m_varMeasConfig.reportConfigList.find (it->reportConfigId) + != m_varMeasConfig.reportConfigList.end ()); + m_varMeasConfig.measIdList[it->measId] = *it; // side effect: create new entry if not exists + std::map::iterator measReportIt = m_varMeasReportList.find (it->measId); + if (measReportIt != m_varMeasReportList.end ()) + { + measReportIt->second.periodicReportTimer.Cancel (); + m_varMeasReportList.erase (measReportIt); + } + NS_ASSERT (m_varMeasConfig.reportConfigList.find (it->reportConfigId) + ->second.reportConfigEutra.triggerType != LteRrcSap::ReportConfigEutra::periodical); + + } + + if (mc.haveMeasGapConfig) + { + NS_FATAL_ERROR ("measurement gaps are currently not supported"); + } + + if (mc.haveSmeasure) + { + NS_FATAL_ERROR ("s-measure is currently not supported"); + } + + if (mc.haveSpeedStatePars) + { + NS_FATAL_ERROR ("SpeedStatePars are currently not supported"); + } +} + +void +LteUeRrc::SendMeasurementReport (uint8_t measId) +{ + NS_LOG_FUNCTION (this << (uint32_t) measId); + // 3GPP TS 36.331 section 5.5.5 Measurement reporting + + std::map::iterator + measIdIt = m_varMeasConfig.measIdList.find (measId); + NS_ASSERT (measIdIt != m_varMeasConfig.measIdList.end ()); + + std::map::iterator + reportConfigIt = m_varMeasConfig.reportConfigList.find (measIdIt->second.reportConfigId); + NS_ASSERT (reportConfigIt != m_varMeasConfig.reportConfigList.end ()); + LteRrcSap::ReportConfigEutra& reportConfigEutra = reportConfigIt->second.reportConfigEutra; + + LteRrcSap::MeasurementReport measurementReport; + LteRrcSap::MeasResults& measResults = measurementReport.measResults; + measResults.measId = measId; + + std::map::iterator servingMeasIt = m_storedMeasValues.find (m_cellId); + NS_ASSERT (servingMeasIt != m_storedMeasValues.end ()); + measResults.rsrpResult = EutranMeasurementMapping::Dbm2RsrpRange (servingMeasIt->second.rsrp); + measResults.rsrqResult = EutranMeasurementMapping::Db2RsrqRange (servingMeasIt->second.rsrq); + NS_LOG_INFO (this << " reporting serving cell " + "RSRP " << (uint32_t) measResults.rsrpResult << " (" << servingMeasIt->second.rsrp << " dBm) " + "RSRQ " << (uint32_t) measResults.rsrqResult << " (" << servingMeasIt->second.rsrq << " dB)"); + measResults.haveMeasResultNeighCells = false; + std::map::iterator measReportIt = m_varMeasReportList.find (measId); + if (measReportIt == m_varMeasReportList.end ()) + { + NS_LOG_ERROR ("no entry found in m_varMeasReportList for measId " << (uint32_t) measId); + } + else + { + if (!(measReportIt->second.cellsTriggeredList.empty ())) + { + measResults.haveMeasResultNeighCells = true; + + std::multimap sortedNeighCells; + for (std::set::iterator cellsTriggeredIt = measReportIt->second.cellsTriggeredList.begin (); + cellsTriggeredIt != measReportIt->second.cellsTriggeredList.end (); + ++cellsTriggeredIt) + { + uint16_t cellId = *cellsTriggeredIt; + if (cellId != m_cellId) + { + std::map::iterator neighborMeasIt = m_storedMeasValues.find (cellId); + double triggerValue; + switch (reportConfigEutra.triggerQuantity) + { + case LteRrcSap::ReportConfigEutra::rsrp: + triggerValue = neighborMeasIt->second.rsrp; + break; + case LteRrcSap::ReportConfigEutra::rsrq: + triggerValue = neighborMeasIt->second.rsrq; + break; + default: + NS_FATAL_ERROR ("unsupported triggerQuantity"); + break; + } + sortedNeighCells.insert (std::pair (triggerValue, cellId)); + } + } + + std::multimap::reverse_iterator sortedNeighCellsIt; + uint32_t count; + for (sortedNeighCellsIt = sortedNeighCells.rbegin (), count = 0; + sortedNeighCellsIt != sortedNeighCells.rend () && count < reportConfigEutra.maxReportCells; + ++sortedNeighCellsIt, ++count) + { + uint16_t cellId = sortedNeighCellsIt->second; + std::map::iterator neighborMeasIt = m_storedMeasValues.find (cellId); + NS_ASSERT (neighborMeasIt != m_storedMeasValues.end ()); + LteRrcSap::MeasResultEutra measResultEutra; + measResultEutra.physCellId = cellId; + measResultEutra.haveCgiInfo = false; + measResultEutra.haveRsrpResult = true; + measResultEutra.rsrpResult = EutranMeasurementMapping::Dbm2RsrpRange (neighborMeasIt->second.rsrp); + measResultEutra.haveRsrqResult = true; + measResultEutra.rsrqResult = EutranMeasurementMapping::Db2RsrqRange (neighborMeasIt->second.rsrq); + NS_LOG_INFO (this << " reporting neighbor cell " << (uint32_t) measResultEutra.physCellId + << " RSRP " << (uint32_t) measResultEutra.rsrpResult + << " (" << neighborMeasIt->second.rsrp << " dBm) " + << " RSRQ " << (uint32_t) measResultEutra.rsrqResult + << " (" << neighborMeasIt->second.rsrq << " dB)"); + } + } + else + { + NS_LOG_WARN (this << " cellsTriggeredList is empty"); + } + measReportIt->second.numberOfReportsSent++; + measReportIt->second.periodicReportTimer.Cancel (); + + // the current LteRrcSap implementation is broken in that it does not allow for infinite values + // which is probably the most reasonable setting. So we just assume infinite reportAmount + // if (measReportIt->numberOfReportsSent < reportConfigEutra.reportAmount) + uint32_t intervalMs; + switch (reportConfigEutra.reportInterval) + { + case LteRrcSap::ReportConfigEutra::ms480: + intervalMs = 480; + break; + + default: + NS_FATAL_ERROR ("unsupported reportInterval"); + break; + } + measReportIt->second.periodicReportTimer + = Simulator::Schedule (MilliSeconds (intervalMs), + &LteUeRrc::SendMeasurementReport, + this, + measId); + + m_rrcSapUser->SendMeasurementReport (measurementReport); + } +} + void LteUeRrc::StartConnection () { diff --git a/src/lte/model/lte-ue-rrc.h b/src/lte/model/lte-ue-rrc.h index ebb8670f1..16ef8dc75 100644 --- a/src/lte/model/lte-ue-rrc.h +++ b/src/lte/model/lte-ue-rrc.h @@ -31,6 +31,7 @@ #include #include +#include namespace ns3 { @@ -259,6 +260,8 @@ private: // internal methods void ApplyRadioResourceConfigDedicated (LteRrcSap::RadioResourceConfigDedicated rrcd); + void ApplyMeasConfig (LteRrcSap::MeasConfig mc); + void SendMeasurementReport (uint8_t measId); void StartConnection (); void LeaveConnectedMode (); void DisposeOldSrb1 (); @@ -320,6 +323,47 @@ private: bool m_receivedMib; /**< true if MIB was received for the current cell */ bool m_receivedSib2; /**< true if SIB2 was received for the current cell */ + + /** + * Includes the accumulated configuration of the measurements to be + * performed by the UE, see TS 36.331 section 7.1. Also note that some + * optional variables in the specs are omitted. + * + */ + struct VarMeasConfig + { + std::map measIdList; + std::map measObjectList; + std::map reportConfigList; + LteRrcSap::QuantityConfig quantityConfig; + double aRsrp; + double aRsrq; + }; + + struct VarMeasReport + { + uint8_t measId; + std::set cellsTriggeredList; // note: only EUTRA is + // supported + uint32_t numberOfReportsSent; + EventId periodicReportTimer; + }; + + VarMeasConfig m_varMeasConfig; + // measId + std::map m_varMeasReportList; + + struct MeasValues + { + double rsrp; + double rsrq; + Time timestamp; + }; + + /////////cellId + std::map m_storedMeasValues; + + };