diff --git a/src/lte/doc/source/lte-design.rst b/src/lte/doc/source/lte-design.rst index 3686d2340..8fd0da307 100644 --- a/src/lte/doc/source/lte-design.rst +++ b/src/lte/doc/source/lte-design.rst @@ -568,12 +568,26 @@ from the index reported in [R1-081483]_. The alternative model is based on the physical error model developed for this simulator and explained in the following subsections. This scheme is able to adapt the MCS selection to the actual PHY layer performance according to the specific CQI report. According to their definition, a CQI index is assigned when a single PDSCH TB with the modulation coding scheme and code rate correspondent to that CQI index in table 7.2.3-1 of [TS36213]_ can be received with an error probability less than 0.1. In case of wideband CQIs, the reference TB includes all the RBGs available in order to have a reference based on the whole available resources; while, for subband CQIs, the reference TB is sized as the RBGs. +.. only:: latex + + .. raw:: latex + + \clearpage + Round Robin (RR) Scheduler -------------------------- The Round Robin (RR) scheduler is probably the simplest scheduler found in the literature. It works by dividing the -available resources among the active flows, i.e., those logical channels which have a non-empty RLC queue. If the number of RBGs is greater than the number of active flows, all the flows can be allocated in the same subframe. Otherwise, if the number of active flows is greater than the number of RBGs, not all the flows can be scheduled in a given subframe; then, in the next subframe the allocation will start from the last flow that was not allocated. The MCS to be adopted for each user is done according to the received wideband CQIs. +available resources among the active flows, i.e., those logical channels which have a non-empty RLC queue. If the number of RBGs is greater than the number of active flows, all the flows can be allocated in the same subframe. Otherwise, if the number of active flows is greater than the number of RBGs, not all the flows can be scheduled in a given subframe; then, in the next subframe the allocation will start from the last flow that was not allocated. The MCS to be adopted for each user is done according to the received wideband CQIs. +For what concern the HARQ, RR implements the non adaptive version, which implies that in allocating the retransmission RR uses a similar allocation configuration of the original block, which means maintaining the same RBGs and MCS. UEs that are allocated for HARQ retransmissions are not considered for the transmission of new data in case they have a transmission opportunity in the same TTI. + + +.. only:: latex + + .. raw:: latex + + \clearpage Proportional Fair (PF) Scheduler -------------------------------- @@ -643,6 +657,7 @@ where :math:`|\cdot|` indicates the cardinality of the set; finally, \right)}{\tau} +*HARQ PF specifications: to be defined*. Transport Blocks ---------------- @@ -1550,9 +1565,6 @@ Therefore the PHY layer implements the MIMO model as the gain perceived by the r - - - .. only:: latex .. raw:: latex @@ -1563,9 +1575,23 @@ Therefore the PHY layer implements the MIMO model as the gain perceived by the r HARQ Model ---------- -The HARQ scheme implemented is based on a incremental redundancy (IR) solutions combined with multiple stop-and-wait processes for enabling a continuous data flow. The core of the HARQ algorithm has been implemented within the respective schedulers class (i.e., ``RrFfMacScheduler`` and ``PfFfMacScheduler``), while the decodification part of the HARQ has been implemented in the ``LteSpectrumPhy`` class. +The HARQ scheme implemented is based on a incremental redundancy (IR) solutions combined with multiple stop-and-wait processes for enabling a continuous data flow. In detail, the solution adopted is the *soft combining hybrid IR Full incremental redundancy* (also called IR Type II), which implies that the retransmissions include only new information. The resource allocation algorithm of the HARQ has been implemented within the respective schedulers class (i.e., ``RrFfMacScheduler`` and ``PfFfMacScheduler``, refer to their respective sections for more info), while the decodification part of the HARQ has been implemented in the ``LteSpectrumPhy`` class which will be detailed in this section. -At the MAC layer, the HARQ entity residing in the scheduler is in charge of controlling the 8 HARQ processes for generating new packets and managing the retransmissions both for the DL and the UL. The scheduler collects the HARQ feedbacks from eNB and UE PHY layers (respectively UL and DL connection) by means of the FF API ``SchedUlTriggerReq`` and ``SchedUlTriggerReq`` in a FIFO buffer for maintaining the order of arrival. According to the HARQ feedbacks and the RLC buffers status, the scheduler generates a set of DCIs including both retransmissions of HARQ blocks received erroneous and new transmissions giving priority to the former. In allocating the retransmission we adopt this assumption, the scheduler uses a similar allocation configuration of the original block, which means maintaining the same number of RBGs and the same MCS. In case of the RGBs used for the original transmission are available they will be reused, otherwise the closest in frequency are selected in order to maintain similar channel conditions. It is to be noted that, the choice of maintaining in the retransmissions the same MCS of the original block is mandatory, otherwise the PHY would not be able of estimating the error probability of aggregated retransmissions. According to the standard, the UL retransmissions are synchronous and therefore are allocated 7 ms after the original transmission. While for the DL, they are asynchronous ans therefore can be allocated in a more flexible way starting from 7 ms. In detail, they receive highest priority for being transmitted after 7 ms; however, due to resource constraints they might be delayed a bit more. The HARQ processes behavior is depicted in Figure:ref:`fig-harq-processes-scheme`. +According to the standard, the UL retransmissions are synchronous and therefore are allocated 7 ms after the original transmission. On the other hand, for the DL, they are asynchronous ans therefore can be allocated in a more flexible way starting from 7 ms and it is a matter of the specific scheduler implementation. The HARQ processes behavior is depicted in Figure:ref:`fig-harq-processes-scheme`. + +At the MAC layer, the HARQ entity residing in the scheduler is in charge of controlling the 8 HARQ processes for generating new packets and managing the retransmissions both for the DL and the UL. The scheduler collects the HARQ feedback from eNB and UE PHY layers (respectively UL and DL connection) by means of the FF API primitives ``SchedUlTriggerReq`` and ``SchedUlTriggerReq``, and stores them in a FIFO buffer for maintaining the order of arrival. According to the HARQ feedback and the RLC buffers status, the scheduler generates a set of DCIs including both retransmissions of HARQ blocks received erroneous and new transmissions, in general, giving priority to the former. On this matter, the scheduler has to take into consideration one constraint when allocating the resource for HARQ retransmissions, it must use the same modulation order of the first transmission attempt (i.e., QPSK for MCS :math:`\in [0..9]`, 16QAM for MCS :math:`\in [10..16]` and 64QAM for MCS :math:`\in [17..28]`). This restriction comes from the specification of the rate matcher in the 3GPP standard [TS36212]_, where for generating the different TBs of the redundancy version the algorithm fixes the modulation order. + + +The PHY Error Model model has been extended for considering IR HARQ according to [wimaxEmd]_, where the parameters for the AWGN curves mapping for MIESM mapping in case of retransmissions are given by: + +.. math:: + + R_{eff} = \frac{X}{\sum\limits_{i=1}^q C_i} + + M_{I eff} = \frac{\sum\limits_{i=1}^q C_i M_i}{\sum\limits_{i=1}^q C_i} + +where :math:`X` is the number of original information bits, :math:`C_i` are number of coded bits, :math:`M_i` are the mutual informations per HARQ block received on the total number of :math:`q` retransmissions. Therefore, in order to be able to return the error probability with the error model implemented in the simulator evaluates the :math:`R_{eff}` and the :math:`MI_{I eff}` and return the value of error probability of the ECR of the same modulation with closest rate respect to the :math:`R_{eff}`. In order to consider the effect of HARQ retransmissions a new sets of curves have been integrated respect to the standard one used for the original MCS, especially for covering the cases when the most conservative MCS of a modulation is used. On this matter the curves for 1, 2 and 3 retransmissions have been evaluated for MCS 0, 10 and 17. +It is to be noted that, the first tranmission has been assumed as containing all the information bits to be coded; therefore :math:`X` is equal to the size of the first TB sent of a an HARQ process. .. _fig-harq-processes-scheme: @@ -1577,7 +1603,8 @@ At the MAC layer, the HARQ entity residing in the scheduler is in charge of cont -At the PHY layer the HARQ is involved in the evaluation of the error distribution process by controlling the information received per process bases and combining it with previous blocks, when necessary in retransmitted ones, by means of the MIESM mutual information scheme presented before. This part of HARQ devoted to manage the decodification of the HARQ blocks has been implemented in the ``LteSpectrumPhy`` class, where it has been also included the messaging algorithm in charge of communicating to the HARQ in the scheduler the result of the decodifications. These messages are encapsulated in the ``dlInfoListElement`` for DL and ``ulInfoListElement`` for UL and sent through the PUCCH and the PHICH respectively in an ideal error free way according to the assumptions in their implementation. A scketch of the iteration bewteen HARQ and LTE protocol stack in represented in Figure:ref:`fig-harq-architecture`. +This part of HARQ devoted to manage the decodification of the HARQ blocks has been implemented in the ``LteSpectrumPhy`` class, where it has been also included the messaging algorithm in charge of communicating to the HARQ in the scheduler the result of the decodifications. These messages are encapsulated in the ``dlInfoListElement`` for DL and ``ulInfoListElement`` for UL and sent through the PUCCH and the PHICH respectively in an ideal error free way according to the assumptions in their implementation. A sketch of the iteration between HARQ and LTE protocol stack in represented in Figure:ref:`fig-harq-architecture`. + .. _fig-harq-architecture: @@ -1597,7 +1624,6 @@ At the PHY layer the HARQ is involved in the evaluation of the error distributio - ----------------------- Channel and Propagation ----------------------- diff --git a/src/lte/helper/lte-helper.cc b/src/lte/helper/lte-helper.cc index 30be4ba05..949448baa 100644 --- a/src/lte/helper/lte-helper.cc +++ b/src/lte/helper/lte-helper.cc @@ -308,6 +308,11 @@ LteHelper::InstallSingleEnbDevice (Ptr n) Ptr phy = CreateObject (dlPhy, ulPhy); + Ptr harq = Create (); + dlPhy->SetHarqPhyModule (harq); + ulPhy->SetHarqPhyModule (harq); + phy->SetHarqPhyModule (harq); + Ptr pCtrl = Create (phy->GetObject ()); ulPhy->AddCtrlSinrChunkProcessor (pCtrl); // for evaluating SRS UL-CQI @@ -374,6 +379,7 @@ LteHelper::InstallSingleEnbDevice (Ptr n) n->AddDevice (dev); ulPhy->SetLtePhyRxDataEndOkCallback (MakeCallback (&LteEnbPhy::PhyPduReceived, phy)); ulPhy->SetLtePhyRxCtrlEndOkCallback (MakeCallback (&LteEnbPhy::ReceiveLteControlMessageList, phy)); + ulPhy->SetLtePhyUlHarqFeedbackCallback (MakeCallback (&LteEnbPhy::ReceiveLteUlHarqFeedback, phy)); rrc->SetForwardUpCallback (MakeCallback (&LteEnbNetDevice::Receive, dev)); NS_LOG_LOGIC ("set the propagation model frequencies"); @@ -426,6 +432,11 @@ LteHelper::InstallSingleUeDevice (Ptr n) Ptr phy = CreateObject (dlPhy, ulPhy); + Ptr harq = Create (); + dlPhy->SetHarqPhyModule (harq); + ulPhy->SetHarqPhyModule (harq); + phy->SetHarqPhyModule (harq); + Ptr pCtrl = Create (phy->GetObject (), dlPhy); dlPhy->AddCtrlSinrChunkProcessor (pCtrl); @@ -474,6 +485,7 @@ LteHelper::InstallSingleUeDevice (Ptr n) n->AddDevice (dev); dlPhy->SetLtePhyRxDataEndOkCallback (MakeCallback (&LteUePhy::PhyPduReceived, phy)); dlPhy->SetLtePhyRxCtrlEndOkCallback (MakeCallback (&LteUePhy::ReceiveLteControlMessageList, phy)); + dlPhy->SetLtePhyDlHarqFeedbackCallback (MakeCallback (&LteUePhy::ReceiveLteDlHarqFeedback, phy)); nas->SetForwardUpCallback (MakeCallback (&LteUeNetDevice::Receive, dev)); diff --git a/src/lte/model/lte-common.h b/src/lte/model/lte-common.h index a32daf7fa..1e298924d 100644 --- a/src/lte/model/lte-common.h +++ b/src/lte/model/lte-common.h @@ -27,6 +27,8 @@ // see 36.213 section 8 #define UL_PUSCH_TTIS_DELAY 4 +#define HARQ_PERIOD 7 + namespace ns3 { diff --git a/src/lte/model/lte-control-messages.cc b/src/lte/model/lte-control-messages.cc index 9b53e1a10..502e2aab5 100644 --- a/src/lte/model/lte-control-messages.cc +++ b/src/lte/model/lte-control-messages.cc @@ -176,5 +176,34 @@ BsrLteControlMessage::GetBsr (void) } + +// --------------------------------------------------------------------------- + + +DlHarqFeedbackLteControlMessage::DlHarqFeedbackLteControlMessage (void) +{ + SetMessageType (LteControlMessage::DL_HARQ); +} + + +DlHarqFeedbackLteControlMessage::~DlHarqFeedbackLteControlMessage (void) +{ + +} + +void +DlHarqFeedbackLteControlMessage::SetDlHarqFeedback (DlInfoListElement_s m) +{ + m_dlInfoListElement = m; +} + + +DlInfoListElement_s +DlHarqFeedbackLteControlMessage::GetDlHarqFeedback (void) +{ + return m_dlInfoListElement; +} + + } // namespace ns3 diff --git a/src/lte/model/lte-control-messages.h b/src/lte/model/lte-control-messages.h index 71628a17d..137de356b 100644 --- a/src/lte/model/lte-control-messages.h +++ b/src/lte/model/lte-control-messages.h @@ -48,7 +48,8 @@ public: { DL_DCI, UL_DCI, // Downlink/Uplink Data Control Indicator DL_CQI, UL_CQI, // Downlink/Uplink Channel Quality Indicator - BSR // Buffer Status Report + BSR, // Buffer Status Report + DL_HARQ // UL HARQ feedback }; LteControlMessage (void); @@ -77,7 +78,7 @@ private: -// ---------------------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------- #ifndef DL_DCI_LTE_CONTROL_MESSAGES_H @@ -120,7 +121,7 @@ private: #endif /* DL_DCI_LTE_CONTROL_MESSAGES_H */ -// ---------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------- #ifndef UL_DCI_LTE_CONTROL_MESSAGES_H @@ -164,7 +165,7 @@ private: -// ---------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------- @@ -210,7 +211,7 @@ private: #endif /* DLCQI_LTE_CONTROL_MESSAGES_H */ -// ---------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------- #ifndef BSR_LTE_CONTROL_MESSAGES_H #define BSR_LTE_CONTROL_MESSAGES_H @@ -253,5 +254,49 @@ private: }; } // namespace ns3 -#endif /* LTE_CONTROL_MESSAGES_H */ +#endif /* BSR_LTE_CONTROL_MESSAGES_H */ + + +// --------------------------------------------------------------------------- + +#ifndef DL_HARQ_LTE_CONTROL_MESSAGES_H +#define DL_HARQ_LTE_CONTROL_MESSAGES_H + +#include +#include + +namespace ns3 { + +/** + * \ingroup lte + * The downlink DlHarqFeedbackLteControlMessage defines the specific + * messages for transmitting the DL HARQ feedback through PUCCH + */ +class DlHarqFeedbackLteControlMessage : public LteControlMessage +{ +public: + DlHarqFeedbackLteControlMessage (void); + virtual ~DlHarqFeedbackLteControlMessage (void); + + /** + * \brief add a DL HARQ feedback record into the message. + * \param DlInfoListElement_s the dl HARQ feedback + */ + void SetDlHarqFeedback (DlInfoListElement_s m); + + /** + * \brief Get DL HARQ informations + * \return DL HARQ message + */ + DlInfoListElement_s GetDlHarqFeedback (void); + + +private: + DlInfoListElement_s m_dlInfoListElement; + + +}; +} // namespace ns3 + +#endif /* DL_HARQ_LTE_CONTROL_MESSAGES_H */ diff --git a/src/lte/model/lte-enb-mac.cc b/src/lte/model/lte-enb-mac.cc index cec09967e..2d2bd666e 100644 --- a/src/lte/model/lte-enb-mac.cc +++ b/src/lte/model/lte-enb-mac.cc @@ -234,6 +234,8 @@ public: virtual void SubframeIndication (uint32_t frameNo, uint32_t subframeNo); virtual void ReceiveLteControlMessage (Ptr msg); virtual void UlCqiReport (FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi); + virtual void UlInfoListElementHarqFeeback (UlInfoListElement_s params); + virtual void DlInfoListElementHarqFeeback (DlInfoListElement_s params); private: LteEnbMac* m_mac; @@ -268,6 +270,18 @@ EnbMacMemberLteEnbPhySapUser::UlCqiReport (FfMacSchedSapProvider::SchedUlCqiInfo m_mac->DoUlCqiReport (ulcqi); } +void +EnbMacMemberLteEnbPhySapUser::UlInfoListElementHarqFeeback (UlInfoListElement_s params) +{ + m_mac->DoUlInfoListElementHarqFeeback (params); +} + +void +EnbMacMemberLteEnbPhySapUser::DlInfoListElementHarqFeeback (DlInfoListElement_s params) +{ + m_mac->DoDlInfoListElementHarqFeeback (params); +} + // ////////////////////////////////////// // generic LteEnbMac methods @@ -312,6 +326,12 @@ void LteEnbMac::DoDispose () { NS_LOG_FUNCTION (this); + m_dlCqiReceived.clear (); + m_ulCqiReceived.clear (); + m_ulCeReceived.clear (); + m_dlInfoListReceived.clear (); + m_ulInfoListReceived.clear (); +m_miDlHarqProcessesPackets.clear (); delete m_macSapProvider; delete m_cmacSapProvider; delete m_schedSapUser; @@ -412,7 +432,6 @@ LteEnbMac::DoSubframeIndication (uint32_t frameNo, uint32_t subframeNo) m_schedSapProvider->SchedDlCqiInfoReq (dlcqiInfoReq); } - // Get downlink transmission opportunities uint32_t dlSchedFrameNo = m_frameNo; uint32_t dlSchedSubframeNo = m_subframeNo; @@ -426,9 +445,18 @@ LteEnbMac::DoSubframeIndication (uint32_t frameNo, uint32_t subframeNo) { dlSchedSubframeNo = dlSchedSubframeNo + m_macChTtiDelay; } - FfMacSchedSapProvider::SchedDlTriggerReqParameters params; // to be filled - params.m_sfnSf = ((0x3FF & dlSchedFrameNo) << 4) | (0xF & dlSchedSubframeNo); - m_schedSapProvider->SchedDlTriggerReq (params); + FfMacSchedSapProvider::SchedDlTriggerReqParameters dlparams; + dlparams.m_sfnSf = ((0x3FF & dlSchedFrameNo) << 4) | (0xF & dlSchedSubframeNo); + + // Forward DL HARQ feebacks collected during last TTI + if (m_dlInfoListReceived.size () > 0) + { + dlparams.m_dlInfoList = m_dlInfoListReceived; + // empty local buffer + m_dlInfoListReceived.clear (); + } + + m_schedSapProvider->SchedDlTriggerReq (dlparams); // --- UPLINK --- @@ -475,26 +503,34 @@ LteEnbMac::DoSubframeIndication (uint32_t frameNo, uint32_t subframeNo) FfMacSchedSapProvider::SchedUlTriggerReqParameters ulparams; ulparams.m_sfnSf = ((0x3FF & ulSchedFrameNo) << 4) | (0xF & ulSchedSubframeNo); - std::map ::iterator it; - for (it = m_ulInfoListElements.begin (); it != m_ulInfoListElements.end (); it++) + // Forward DL HARQ feebacks collected during last TTI + if (m_ulInfoListReceived.size () > 0) { - ulparams.m_ulInfoList.push_back ((*it).second); + ulparams.m_ulInfoList = m_ulInfoListReceived; + // empty local buffer + m_ulInfoListReceived.clear (); } + +// std::map ::iterator it; +// for (it = m_ulInfoListElements.begin (); it != m_ulInfoListElements.end (); it++) +// { +// ulparams.m_ulInfoList.push_back ((*it).second); +// } m_schedSapProvider->SchedUlTriggerReq (ulparams); // reset UL info - for (it = m_ulInfoListElements.begin (); it != m_ulInfoListElements.end (); it++) - { - for (uint16_t i = 0; i < (*it).second.m_ulReception.size (); i++) - { - (*it).second.m_ulReception.at (i) = 0; - } - (*it).second.m_receptionStatus = UlInfoListElement_s::Ok; - (*it).second.m_tpc = 0; - } +// for (it = m_ulInfoListElements.begin (); it != m_ulInfoListElements.end (); it++) +// { +// for (uint16_t i = 0; i < (*it).second.m_ulReception.size (); i++) +// { +// (*it).second.m_ulReception.at (i) = 0; +// } +// (*it).second.m_receptionStatus = UlInfoListElement_s::Ok; +// (*it).second.m_tpc = 0; +// } } void @@ -511,6 +547,11 @@ LteEnbMac::DoReceiveLteControlMessage (Ptr msg) Ptr bsr = DynamicCast (msg); ReceiveBsrMessage (bsr->GetBsr ()); } + else if (msg->GetMessageType () == LteControlMessage::DL_HARQ) + { + Ptr dlharq = DynamicCast (msg); + DoDlInfoListElementHarqFeeback (dlharq->GetDlHarqFeedback ()); + } else { NS_LOG_LOGIC (this << " LteControlMessage not recognized"); @@ -525,7 +566,6 @@ LteEnbMac::DoUlCqiReport (FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulc { NS_LOG_DEBUG (this << " eNB rxed an PUSCH UL-CQI"); } - // TODO store UL-CQI to send them to scheduler m_ulCqiReceived.push_back (ulcqi); } @@ -561,33 +601,33 @@ LteEnbMac::DoReceivePhyPdu (Ptr p) // store info of the packet received - std::map ::iterator it; +// std::map ::iterator it; // u_int rnti = tag.GetRnti (); // u_int lcid = tag.GetLcid (); - it = m_ulInfoListElements.find (tag.GetRnti ()); - if (it == m_ulInfoListElements.end ()) - { - // new RNTI - UlInfoListElement_s ulinfonew; - ulinfonew.m_rnti = tag.GetRnti (); - // always allocate full size of ulReception vector, initializing all elements to 0 - ulinfonew.m_ulReception.assign (MAX_LC_LIST+1, 0); - // set the element for the current LCID - ulinfonew.m_ulReception.at (tag.GetLcid ()) = p->GetSize (); - ulinfonew.m_receptionStatus = UlInfoListElement_s::Ok; - ulinfonew.m_tpc = 0; // Tx power control not implemented at this stage - m_ulInfoListElements.insert (std::pair (tag.GetRnti (), ulinfonew)); - - } - else - { - // existing RNTI: we just set the value for the current - // LCID. Note that the corresponding element had already been - // allocated previously. - NS_ASSERT_MSG ((*it).second.m_ulReception.at (tag.GetLcid ()) == 0, "would overwrite previously written ulReception element"); - (*it).second.m_ulReception.at (tag.GetLcid ()) = p->GetSize (); - (*it).second.m_receptionStatus = UlInfoListElement_s::Ok; - } +// it = m_ulInfoListElements.find (tag.GetRnti ()); +// if (it == m_ulInfoListElements.end ()) +// { +// // new RNTI +// UlInfoListElement_s ulinfonew; +// ulinfonew.m_rnti = tag.GetRnti (); +// // always allocate full size of ulReception vector, initializing all elements to 0 +// ulinfonew.m_ulReception.assign (MAX_LC_LIST+1, 0); +// // set the element for the current LCID +// ulinfonew.m_ulReception.at (tag.GetLcid ()) = p->GetSize (); +// ulinfonew.m_receptionStatus = UlInfoListElement_s::Ok; +// ulinfonew.m_tpc = 0; // Tx power control not implemented at this stage +// m_ulInfoListElements.insert (std::pair (tag.GetRnti (), ulinfonew)); +// +// } +// else +// { +// // existing RNTI: we just set the value for the current +// // LCID. Note that the corresponding element had already been +// // allocated previously. +// NS_ASSERT_MSG ((*it).second.m_ulReception.at (tag.GetLcid ()) == 0, "would overwrite previously written ulReception element"); +// (*it).second.m_ulReception.at (tag.GetLcid ()) = p->GetSize (); +// (*it).second.m_receptionStatus = UlInfoListElement_s::Ok; +// } @@ -629,6 +669,16 @@ LteEnbMac::DoAddUe (uint16_t rnti) params.m_rnti = rnti; params.m_transmissionMode = 0; // set to default value (SISO) for avoiding random initialization (valgrind error) m_cschedSapProvider->CschedUeConfigReq (params); + + // Create DL trasmission HARQ buffers + std::vector < Ptr > dlHarqLayer0pkt; + dlHarqLayer0pkt.resize (8); + std::vector < Ptr > dlHarqLayer1pkt; + dlHarqLayer1pkt.resize (8); + DlHarqProcessesBuffer_t buf; + buf.push_back (dlHarqLayer0pkt); + buf.push_back (dlHarqLayer1pkt); + m_miDlHarqProcessesPackets.insert (std::pair (rnti, buf)); } void @@ -703,9 +753,12 @@ LteEnbMac::DoTransmitPdu (LteMacSapProvider::TransmitPduParameters params) NS_LOG_FUNCTION (this); LteRadioBearerTag tag (params.rnti, params.lcid, params.layer); params.pdu->AddPacketTag (tag); -// Ptr pb = CreateObject (); -// pb->AddPacket (params.pdu); - + // Store pkt in HARQ buffer + std::map ::iterator it = m_miDlHarqProcessesPackets.find (params.rnti); + NS_ASSERT (it!=m_miDlHarqProcessesPackets.end ()); + NS_LOG_DEBUG (this << " LAYER " <<(uint16_t)tag.GetLayer () << " HARQ ID " << (uint16_t)params.harqProcessId); +// NS_ASSERT ((*it).second.at (params.layer).at (params.harqProcessId) == 0); + (*it).second.at (params.layer).at (params.harqProcessId) = params.pdu;//->Copy (); m_enbPhySapProvider->SendMacPdu (params.pdu); } @@ -746,12 +799,30 @@ LteEnbMac::DoSchedDlConfigInd (FfMacSchedSapUser::SchedDlConfigIndParameters ind { for (uint16_t k = 0; k < ind.m_buildDataList.at (i).m_rlcPduList.at (j).size (); k++) { - LteFlowId_t flow (ind.m_buildDataList.at (i).m_rnti, - ind.m_buildDataList.at (i).m_rlcPduList.at (j).at (k).m_logicalChannelIdentity); - it = m_rlcAttached.find (flow); - NS_ASSERT_MSG (it != m_rlcAttached.end (), "rnti=" << flow.m_rnti << " lcid=" << (uint32_t) flow.m_lcId); - NS_LOG_DEBUG (this << " rnti= " << flow.m_rnti << " lcid= " << (uint32_t) flow.m_lcId << " layer= " << k); - (*it).second->NotifyTxOpportunity (ind.m_buildDataList.at (i).m_rlcPduList.at (j).at (k).m_size, k); +// NS_ASSERT_MSG (ind.m_buildDataList.at (i).m_dci.m_ndi.size ()<=1, " NOT MIMO, layer " << k); +// NS_ASSERT_MSG (ind.m_buildDataList.size ()>i, " I " << i); +// NS_ASSERT_MSG (ind.m_buildDataList.at (i).m_dci.m_ndi.size ()>k, " k " << ind.m_buildDataList.at (i).m_rlcPduList.at (j).size ()); + if (ind.m_buildDataList.at (i).m_dci.m_ndi.at (k) == 1) + { + // New Data -> retrieve it from RLC + LteFlowId_t flow (ind.m_buildDataList.at (i).m_rnti, + ind.m_buildDataList.at (i).m_rlcPduList.at (j).at (k).m_logicalChannelIdentity); + it = m_rlcAttached.find (flow); + NS_ASSERT_MSG (it != m_rlcAttached.end (), "rnti=" << flow.m_rnti << " lcid=" << (uint32_t) flow.m_lcId); + NS_LOG_DEBUG (this << " rnti= " << flow.m_rnti << " lcid= " << (uint32_t) flow.m_lcId << " layer= " << k); + (*it).second->NotifyTxOpportunity (ind.m_buildDataList.at (i).m_rlcPduList.at (j).at (k).m_size, k, ind.m_buildDataList.at (i).m_dci.m_harqProcess); + } + else + { + if (ind.m_buildDataList.at (i).m_dci.m_tbsSize.at (k)>0) + { + // HARQ retransmission -> retrieve TB from HARQ buffer + std::map ::iterator it = m_miDlHarqProcessesPackets.find (ind.m_buildDataList.at (i).m_rnti); + NS_ASSERT(it!=m_miDlHarqProcessesPackets.end()); + Ptr pkt = (*it).second.at (k).at ( ind.m_buildDataList.at (i).m_dci.m_harqProcess)->Copy (); + m_enbPhySapProvider->SendMacPdu (pkt); + } + } } } // send the relative DCI @@ -884,5 +955,39 @@ LteEnbMac::DoCschedCellConfigUpdateInd (FfMacCschedSapUser::CschedCellConfigUpda NS_LOG_FUNCTION (this); } +void +LteEnbMac::DoUlInfoListElementHarqFeeback (UlInfoListElement_s params) +{ + NS_LOG_FUNCTION (this); + m_ulInfoListReceived.push_back (params); +} + +void +LteEnbMac::DoDlInfoListElementHarqFeeback (DlInfoListElement_s params) +{ + NS_LOG_FUNCTION (this); + // Update HARQ buffer + std::map ::iterator it = m_miDlHarqProcessesPackets.find (params.m_rnti); + NS_ASSERT (it!=m_miDlHarqProcessesPackets.end ()); + for (uint8_t layer = 0; layer < params.m_harqStatus.size (); layer++) + { + if (params.m_harqStatus.at (layer)==DlInfoListElement_s::ACK) + { + // discard buffer + (*it).second.at (layer).at (params.m_harqProcessId) = 0; + NS_LOG_DEBUG (this << " HARQ-ACK UE " << params.m_rnti << " harqId " << (uint16_t)params.m_harqProcessId << " layer " << (uint16_t)layer); + } + else if (params.m_harqStatus.at (layer)==DlInfoListElement_s::NACK) + { + NS_LOG_DEBUG (this << " HARQ-NACK UE " << params.m_rnti << " harqId " << (uint16_t)params.m_harqProcessId << " layer " << (uint16_t)layer); + } + else + { + NS_FATAL_ERROR (" HARQ functionality not implemented"); + } + } + m_dlInfoListReceived.push_back (params); +} + } // namespace ns3 diff --git a/src/lte/model/lte-enb-mac.h b/src/lte/model/lte-enb-mac.h index 4759ad39f..57bafd2a6 100644 --- a/src/lte/model/lte-enb-mac.h +++ b/src/lte/model/lte-enb-mac.h @@ -33,6 +33,7 @@ #include #include "ns3/traced-value.h" #include "ns3/trace-source-accessor.h" +#include namespace ns3 { @@ -40,7 +41,7 @@ class DlCqiLteControlMessage; class UlCqiLteControlMessage; class PdcchMapLteControlMessage; - +typedef std::vector > > DlHarqProcessesBuffer_t; /** * This class implements the MAC layer of the eNodeB device @@ -184,18 +185,23 @@ public: void DoReceivePhyPdu (Ptr p); private: -private: + void DoUlInfoListElementHarqFeeback (UlInfoListElement_s params); + void DoDlInfoListElementHarqFeeback (DlInfoListElement_s params); std::map m_rlcAttached; std::vector m_dlCqiReceived; // DL-CQI received std::vector m_ulCqiReceived; // UL-CQI received std::vector m_ulCeReceived; // CE received (BSR up to now) + std::vector m_dlInfoListReceived; // DL HARQ feedback received + + std::vector m_ulInfoListReceived; // UL HARQ feedback received + /* * Map of UE's info element (see 4.3.12 of FF MAC Scheduler API) */ - std::map m_ulInfoListElements; +// std::map m_ulInfoListElements; @@ -229,6 +235,9 @@ private: TracedCallback m_ulScheduling; uint8_t m_macChTtiDelay; // delay of MAC, PHY and channel in terms of TTIs + + + std::map m_miDlHarqProcessesPackets; // Packet under trasmission of the DL HARQ process }; diff --git a/src/lte/model/lte-enb-phy-sap.h b/src/lte/model/lte-enb-phy-sap.h index 38195f2c9..a764ae3d1 100644 --- a/src/lte/model/lte-enb-phy-sap.h +++ b/src/lte/model/lte-enb-phy-sap.h @@ -103,6 +103,21 @@ public: */ virtual void UlCqiReport (FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi) = 0; + /** + * Notify the HARQ on the UL tranmission status + * + * \param params + */ + virtual void UlInfoListElementHarqFeeback (UlInfoListElement_s params) = 0; + + + /** + * Notify the HARQ on the DL tranmission status + * + * \param params + */ + virtual void DlInfoListElementHarqFeeback (DlInfoListElement_s params) = 0; + }; diff --git a/src/lte/model/lte-enb-phy.cc b/src/lte/model/lte-enb-phy.cc index f12083d28..46d4ae2a1 100644 --- a/src/lte/model/lte-enb-phy.cc +++ b/src/lte/model/lte-enb-phy.cc @@ -155,6 +155,9 @@ LteEnbPhy::LteEnbPhy (Ptr dlPhy, Ptr ulPhy) { m_enbPhySapProvider = new EnbMemberLteEnbPhySapProvider (this); m_enbCphySapProvider = new MemberLteEnbCphySapProvider (this); + m_harqPhyModule = Create (); + m_downlinkSpectrumPhy->SetHarqPhyModule (m_harqPhyModule); + m_uplinkSpectrumPhy->SetHarqPhyModule (m_harqPhyModule); Simulator::ScheduleNow (&LteEnbPhy::StartFrame, this); } @@ -450,7 +453,7 @@ LteEnbPhy::StartSubFrame (void) m_currentSrsOffset = (m_currentSrsOffset + 1) % m_srsPeriodicity; } NS_LOG_INFO ("-----sub frame " << m_nrSubFrames << "-----"); - + m_harqPhyModule->SubframeIndication (m_nrFrames, m_nrSubFrames); // update info on TB to be received std::list uldcilist = DequeueUlDci (); @@ -475,7 +478,15 @@ LteEnbPhy::StartSubFrame (void) { rbMap.push_back (i); } - m_uplinkSpectrumPhy->AddExpectedTb ((*dciIt).GetDci ().m_rnti, (*dciIt).GetDci ().m_tbSize, (*dciIt).GetDci ().m_mcs, rbMap, 0 /* always SISO*/); + m_uplinkSpectrumPhy->AddExpectedTb ((*dciIt).GetDci ().m_rnti, (*dciIt).GetDci ().m_tbSize, (*dciIt).GetDci ().m_mcs, rbMap, 0 /* always SISO*/, 0 /* no HARQ proc id in UL*/, 0.0 /* MI TBD */, false /* UL*/); + if ((*dciIt).GetDci ().m_ndi==1) + { + NS_LOG_DEBUG (this << " RNTI " << (*dciIt).GetDci ().m_rnti << " NEW TB"); + } + else + { + NS_LOG_DEBUG (this << " RNTI " << (*dciIt).GetDci ().m_rnti << " HARQ RETX"); + } m_ulRntiRxed.push_back ((*dciIt).GetDci ().m_rnti); } } @@ -772,4 +783,18 @@ LteEnbPhy::DoSetSrsConfigurationIndex (uint16_t rnti, uint16_t srcCi) } +void +LteEnbPhy::SetHarqPhyModule (Ptr harq) +{ + m_harqPhyModule = harq; +} + + +void +LteEnbPhy::ReceiveLteUlHarqFeedback (UlInfoListElement_s mes) +{ + NS_LOG_FUNCTION (this); + m_enbPhySapUser->UlInfoListElementHarqFeeback (mes); +} + }; diff --git a/src/lte/model/lte-enb-phy.h b/src/lte/model/lte-enb-phy.h index 5dbedc813..103928ad4 100644 --- a/src/lte/model/lte-enb-phy.h +++ b/src/lte/model/lte-enb-phy.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -230,6 +231,13 @@ public: virtual void GenerateCtrlCqiReport (const SpectrumValue& sinr); virtual void GenerateDataCqiReport (const SpectrumValue& sinr); + /** + * \brief PhySpectrum generated a new UL HARQ feedback + */ + virtual void ReceiveLteUlHarqFeedback (UlInfoListElement_s mes); + + void SetHarqPhyModule (Ptr harq); + private: @@ -274,6 +282,8 @@ private: std::map m_srsCounter; std::vector m_srsUeOffset; uint16_t m_currentSrsOffset; + + Ptr m_harqPhyModule; }; diff --git a/src/lte/model/lte-enb-rrc.cc b/src/lte/model/lte-enb-rrc.cc index bdb6caf04..05f495280 100644 --- a/src/lte/model/lte-enb-rrc.cc +++ b/src/lte/model/lte-enb-rrc.cc @@ -414,9 +414,11 @@ LteEnbRrc::DoRecvConnectionRequest (uint64_t imsi) ueRrc->DoRecvConnectionSetup (ueConfig); // configure MAC (and scheduler) - FfMacCschedSapProvider::CschedUeConfigReqParameters req; - req.m_rnti = rnti; - req.m_transmissionMode = (*it).second->GetTransmissionMode (); + LteEnbCmacSapProvider::UeConfig params; + params.m_rnti = rnti; + params.m_transmissionMode = (*it).second->GetTransmissionMode (); + m_cmacSapProvider->UeUpdateConfigurationReq (params); + // configure PHY m_cphySapProvider->SetTransmissionMode (rnti, (*it).second->GetTransmissionMode ()); @@ -745,6 +747,7 @@ LteEnbRrc::CreateUeInfo () m_lastAllocatedRnti = rnti; Ptr ueInfo = CreateObject (); ueInfo->SetSrsConfigurationIndex (GetNewSrsConfigurationIndex ()); + ueInfo->SetTransmissionMode (m_defaultTransmissionMode); m_ueMap.insert (std::pair > (rnti, ueInfo)); NS_LOG_DEBUG (this << " New UE RNTI " << rnti << " cellId " << m_cellId << " srs CI " << ueInfo->GetSrsConfigurationIndex ()); return rnti; diff --git a/src/lte/model/lte-harq-phy.cc b/src/lte/model/lte-harq-phy.cc new file mode 100644 index 000000000..b24c00b99 --- /dev/null +++ b/src/lte/model/lte-harq-phy.cc @@ -0,0 +1,203 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo + */ + + +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("LteHarqPhy"); + +namespace ns3 { + +//NS_OBJECT_ENSURE_REGISTERED (LteHarqPhy); + + +LteHarqPhy::LteHarqPhy () +{ + // Create DL Decodification HARQ buffers + std::vector dlHarqLayer0; + dlHarqLayer0.resize (8); + std::vector dlHarqLayer1; + dlHarqLayer1.resize (8); + m_miDlHarqProcessesInfoMap.push_back (dlHarqLayer0); + m_miDlHarqProcessesInfoMap.push_back (dlHarqLayer1); +} + + +LteHarqPhy::~LteHarqPhy () +{ + m_miDlHarqProcessesInfoMap.clear (); + m_miUlHarqProcessesInfoMap.clear (); +} + + +void +LteHarqPhy::SubframeIndication (uint32_t frameNo, uint32_t subframeNo) +{ + NS_LOG_FUNCTION (this); + + // left shift UL HARQ buffers + std::map >:: iterator it; + for (it = m_miUlHarqProcessesInfoMap.begin (); it != m_miUlHarqProcessesInfoMap.end (); it++) + { + (*it).second.erase ((*it).second.begin ()); + HarqProcessInfoList_t h; + (*it).second.push_back (h); + } + +} + + +double +LteHarqPhy::GetAccumulatedMiDl (uint8_t harqProcId, uint8_t layer) +{ + NS_LOG_FUNCTION (this << (uint32_t)harqProcId << (uint16_t)layer); + HarqProcessInfoList_t list = m_miDlHarqProcessesInfoMap.at (layer).at (harqProcId); + double mi = 0.0; + for (uint8_t i = 0; i < list.size (); i++) + { + mi += list.at (i).m_mi; + } + return (mi); +} + +HarqProcessInfoList_t +LteHarqPhy::GetHarqProcessInfoDl (uint8_t harqProcId, uint8_t layer) +{ + NS_LOG_FUNCTION (this << (uint32_t)harqProcId << (uint16_t)layer); + return (m_miDlHarqProcessesInfoMap.at (layer).at (harqProcId)); +} + + +double +LteHarqPhy::GetAccumulatedMiUl (uint16_t rnti) +{ + NS_LOG_FUNCTION (this << rnti); + + std::map >::iterator it; + it = m_miUlHarqProcessesInfoMap.find (rnti); + NS_ASSERT_MSG (it!=m_miUlHarqProcessesInfoMap.end (), " Does not find MI for RNTI"); + HarqProcessInfoList_t list = (*it).second.at (0); + double mi = 0.0; + for (uint8_t i = 0; i < list.size (); i++) + { + mi += list.at (i).m_mi; + } + return (mi); +} + +HarqProcessInfoList_t +LteHarqPhy::GetHarqProcessInfoUl (uint16_t rnti, uint8_t harqProcId) +{ + NS_LOG_FUNCTION (this << rnti << (uint16_t)harqProcId); + std::map >::iterator it; + it = m_miUlHarqProcessesInfoMap.find (rnti); + if (it==m_miUlHarqProcessesInfoMap.end ()) + { + // new entry + std::vector harqList; + harqList.resize (8); + m_miUlHarqProcessesInfoMap.insert (std::pair > (rnti, harqList)); + return (harqList.at (harqProcId)); + } + else + { + return ((*it).second.at (harqProcId)); + } +} + + + +void +LteHarqPhy::UpdateDlHarqProcessStatus (uint8_t id, uint8_t layer, double mi, uint16_t infoBits, uint16_t codeBits) +{ + NS_LOG_FUNCTION (this << (uint16_t) id << mi); + HarqProcessInfoElement_t el; + el.m_mi = mi; + el.m_infoBits = infoBits; + el.m_codeBits = codeBits; + m_miDlHarqProcessesInfoMap.at (layer).at (id).push_back (el); +} + + +void +LteHarqPhy::ResetDlHarqProcessStatus (uint8_t id) +{ + NS_LOG_FUNCTION (this << (uint16_t) id); + for (uint8_t i = 0; i < m_miDlHarqProcessesInfoMap.size (); i++) + { + m_miDlHarqProcessesInfoMap.at (i).at (id).clear (); + } + +} + + +void +LteHarqPhy::UpdateUlHarqProcessStatus (uint16_t rnti, double mi, uint16_t infoBits, uint16_t codeBits) +{ + NS_LOG_FUNCTION (this << rnti << mi); + std::map >::iterator it; + it = m_miUlHarqProcessesInfoMap.find (rnti); + if (it==m_miUlHarqProcessesInfoMap.end ()) + { + // new entry + std::vector harqList; + harqList.resize (8); + HarqProcessInfoElement_t el; + el.m_mi = mi; + el.m_infoBits = infoBits; + el.m_codeBits = codeBits; + harqList.at (7).push_back (el); + m_miUlHarqProcessesInfoMap.insert (std::pair > (rnti, harqList)); + } + else + { + HarqProcessInfoElement_t el; + el.m_mi = mi; + el.m_infoBits = infoBits; + el.m_codeBits = codeBits; + (*it).second.at (7).push_back (el); + } +} + +void +LteHarqPhy::ResetUlHarqProcessStatus (uint16_t rnti, uint8_t id) +{ + NS_LOG_FUNCTION (this << rnti << (uint16_t)id); + std::map >::iterator it; + it = m_miUlHarqProcessesInfoMap.find (rnti); + if (it==m_miUlHarqProcessesInfoMap.end ()) + { + // new entry + std::vector harqList; + harqList.resize (8); + m_miUlHarqProcessesInfoMap.insert (std::pair > (rnti, harqList)); + } + else + { + (*it).second.at (id).clear (); + } +} + + + + +} // end namespace diff --git a/src/lte/model/lte-harq-phy.h b/src/lte/model/lte-harq-phy.h new file mode 100644 index 000000000..f121a833f --- /dev/null +++ b/src/lte/model/lte-harq-phy.h @@ -0,0 +1,143 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo + */ + + +#ifndef LTE_HARQ_PHY_MODULE_H +#define LTE_HARQ_PHY_MODULE_H + + + +#include +#include +#include +#include +#include +#include + + +namespace ns3 { + + + +struct HarqProcessInfoElement_t +{ + double m_mi; + uint8_t m_rv; + uint16_t m_infoBits; + uint16_t m_codeBits; +}; + +typedef std::vector HarqProcessInfoList_t; + +/** + * \ingroup lte + * \brief The LteHarqPhy class implements the HARQ functionalities related to PHY layer + *(i.e., decodification buffers for incremental redundancy managment) + * +*/ +class LteHarqPhy : public SimpleRefCount +{ +public: + LteHarqPhy (); + ~LteHarqPhy (); + + void SubframeIndication (uint32_t frameNo, uint32_t subframeNo); + + /** + * \brief Return the cumulated MI of the HARQ procId in case of retranmissions + * for DL (asynchronous) + * \param harqProcId the HARQ proc id + * \param layer layer no. (for MIMO spatial multiplexing) + * \return the MI accumulated + */ + double GetAccumulatedMiDl (uint8_t harqProcId, uint8_t layer); + + /** + * \brief Return the info of the HARQ procId in case of retranmissions + * for DL (asynchronous) + * \param harqProcId the HARQ proc id + * \param layer layer no. (for MIMO spatail multiplexing) + * \return the vector of the info related to HARQ proc Id + */ + HarqProcessInfoList_t GetHarqProcessInfoDl (uint8_t harqProcId, uint8_t layer); + + /** + * \brief Return the cumulated MI of the HARQ procId in case of retranmissions + * for UL (synchronous) + * \return the MI accumulated + */ + double GetAccumulatedMiUl (uint16_t rnti); + + /** + * \brief Return the info of the HARQ procId in case of retranmissions + * for UL (asynchronous) + * \param harqProcId the HARQ proc id + * \param layer layer no. (for MIMO spatail multiplexing) + * \return the vector of the info related to HARQ proc Id + */ + HarqProcessInfoList_t GetHarqProcessInfoUl (uint16_t rnti, uint8_t harqProcId); + + /** + * \brief Update the Info associated to the decodification of an HARQ process + * for DL (asynchronous) + * \param id the HARQ proc id + * \param layer layer no. (for MIMO spatail multiplexing) + * \param mi the new MI + */ + void UpdateDlHarqProcessStatus (uint8_t id, uint8_t layer, double mi, uint16_t infoBits, uint16_t codeBits); + + /** + * \brief Reset the info associated to the decodification of an HARQ process + * for DL (asynchronous) + * \param id the HARQ proc id + */ + void ResetDlHarqProcessStatus(uint8_t id); + + /** + * \brief Update the MI value associated to the decodification of an HARQ process + * for DL (asynchronous) + * \param rnti the RNTI of the transmitter + * \param mi the new MI + */ + void UpdateUlHarqProcessStatus (uint16_t rnti, double mi, uint16_t infoBits, uint16_t codeBits); + + /** + * \brief Reset the info associated to the decodification of an HARQ process + * for DL (asynchronous) + * \param id the HARQ proc id + */ + void ResetUlHarqProcessStatus(uint16_t rnti, uint8_t id); + + + + + +private: + + std::vector > m_miDlHarqProcessesInfoMap; + std::map > m_miUlHarqProcessesInfoMap; + + +}; + + +} + +#endif /* LTE_HARQ_PHY_MODULE_H */ diff --git a/src/lte/model/lte-mac-sap.h b/src/lte/model/lte-mac-sap.h index b77b37339..e06791f98 100644 --- a/src/lte/model/lte-mac-sap.h +++ b/src/lte/model/lte-mac-sap.h @@ -48,6 +48,7 @@ public: uint16_t rnti; /**< the C-RNTI identifying the UE */ uint8_t lcid; /**< the logical channel id corresponding to the sending RLC instance */ uint8_t layer; /**< the layer value that was passed by the MAC in the call to NotifyTxOpportunity that generated this PDU */ + uint8_t harqProcessId; /**< the HARQ process id that was passed by the MAC in the call to NotifyTxOpportunity that generated this PDU */ }; /** @@ -101,7 +102,7 @@ public: * \param bytes the number of bytes to transmit * \param layer the layer of transmission (MIMO) */ - virtual void NotifyTxOpportunity (uint32_t bytes, uint8_t layer) = 0; + virtual void NotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) = 0; /** * Called by the MAC to notify the RLC that an HARQ process related diff --git a/src/lte/model/lte-mi-error-model.cc b/src/lte/model/lte-mi-error-model.cc index 70f63239b..ba3b4b612 100644 --- a/src/lte/model/lte-mi-error-model.cc +++ b/src/lte/model/lte-mi-error-model.cc @@ -492,6 +492,126 @@ LteMiErrorModel::GetTbError (const SpectrumValue& sinr, const std::vector& return errorRate; } + +TbStats_t +LteMiErrorModel::GetTbDecodificationStats (const SpectrumValue& sinr, const std::vector& map, uint16_t size, uint8_t mcs, double mi) +{ + NS_LOG_FUNCTION (sinr << &map << (uint32_t) size << (uint32_t) mcs); + + double MI = Mib(sinr, map, mcs) + mi; + // estimate CB size (according to sec 5.1.2 of TS 36.212) + uint16_t Z = 6144; // max size of a codeblock (including CRC) + uint32_t B = size * 8; +// B = 1234; + uint32_t L = 0; + uint32_t C = 0; // no. of codeblocks + uint32_t Cplus = 0; // no. of codeblocks with size K+ + uint32_t Kplus = 0; // no. of codeblocks with size K+ + uint32_t Cminus = 0; // no. of codeblocks with size K+ + uint32_t Kminus = 0; // no. of codeblocks with size K+ + uint32_t B1 = 0; + uint32_t deltaK = 0; + if (B <= Z) + { + // only one codeblock + L = 0; + C = 1; + B1 = B; + } + else + { + L = 24; + C = ceil ((double)B / ((double)(Z-L))); + B1 = B + C * L; + } + // first segmentation: K+ = minimum K in table such that C * K >= B1 +// uint i = 0; +// while (B1 > cbSizeTable[i] * C) +// { +// // NS_LOG_INFO (" K+ " << cbSizeTable[i] << " means " << cbSizeTable[i] * C); +// i++; +// } +// uint16_t KplusId = i; +// Kplus = cbSizeTable[i]; + + // implement a modified binary search + int min = 0; + int max = 187; + int mid = 0; + do + { + mid = (min+max) / 2; + if (B1 > cbSizeTable[mid]*C) + { + if (B1 < cbSizeTable[mid+1]*C) + { + break; + } + else + { + min = mid + 1; + } + } + else + { + if (B1 > cbSizeTable[mid-1]*C) + { + break; + } + else + { + max = mid - 1; + } + } + } while ((cbSizeTable[mid]*C != B1) && (min < max)); + // adjust binary search to the largest integer value of K containing B1 + if (B1 > cbSizeTable[mid]*C) + { + mid ++; + } + + uint16_t KplusId = mid; + Kplus = cbSizeTable[mid]; + + + if (C==1) + { + Cplus = 1; + Cminus = 0; + Kminus = 0; + } + else + { + // second segmentation size: K- = maximum K in table such that K < K+ + Kminus = cbSizeTable[KplusId-1 > 0 ? KplusId-1 : 0]; + deltaK = Kplus - Kminus; + Cminus = floor ((((double) C * Kplus) - (double)B1) / (double)deltaK); + Cplus = C - Cminus; + } + NS_LOG_INFO ("--------------------LteMiErrorModel: TB size of " << B << " needs of " << B1 << " bits reparted in " << C << " CBs as "<< Cplus << " block(s) of " << Kplus << " and " << Cminus << " of " << Kminus); + + double errorRate = 1.0; + if (C!=1) + { + double cbler = MappingMiBler (MI, mcs, Kplus); + errorRate *= pow (1.0 - cbler, Cplus); + cbler = MappingMiBler (MI, mcs, Kminus); + errorRate *= pow (1.0 - cbler, Cminus); + errorRate = 1.0 - errorRate; + } + else + { + errorRate = MappingMiBler (MI, mcs, Kplus); + } + + NS_LOG_LOGIC (" Error rate " << errorRate); + TbStats_t ret; + ret.error = errorRate; + ret.mi = MI; + return ret; +} + + double LteMiErrorModel::GetPcfichPdcchError (const SpectrumValue& sinr) { @@ -578,6 +698,148 @@ LteMiErrorModel::GetPcfichPdcchError (const SpectrumValue& sinr) return (errorRate); } + + + +TbStats_t +LteMiErrorModel::GetTbDecodificationStats (const SpectrumValue& sinr, const std::vector& map, uint16_t size, uint8_t mcs, HarqProcessInfoList_t miHistory) +{ + NS_LOG_FUNCTION (sinr << &map << (uint32_t) size << (uint32_t) mcs); + + double tbMi = Mib(sinr, map, mcs); + double MI = 0.0; + double Reff = 0.0; + if (miHistory.size ()>0) + { + // evaluate R_eff and MI_eff + uint16_t codeBitsSum = 0; + double miSum = 0.0; + for (uint16_t i = 0; i < miHistory.size (); i++) + { + codeBitsSum += miHistory.at (i).m_codeBits; + miSum += (miHistory.at (i).m_mi*miHistory.at (i).m_codeBits); + } + Reff = miHistory.at (0).m_infoBits / codeBitsSum; // information bits are the size of the first TB + MI = miSum / (double)codeBitsSum; + } + else + { + MI = tbMi; + } + NS_LOG_DEBUG (" MI " << MI << " Reff " << Reff); + // estimate CB size (according to sec 5.1.2 of TS 36.212) + uint16_t Z = 6144; // max size of a codeblock (including CRC) + uint32_t B = size * 8; +// B = 1234; + uint32_t L = 0; + uint32_t C = 0; // no. of codeblocks + uint32_t Cplus = 0; // no. of codeblocks with size K+ + uint32_t Kplus = 0; // no. of codeblocks with size K+ + uint32_t Cminus = 0; // no. of codeblocks with size K+ + uint32_t Kminus = 0; // no. of codeblocks with size K+ + uint32_t B1 = 0; + uint32_t deltaK = 0; + if (B <= Z) + { + // only one codeblock + L = 0; + C = 1; + B1 = B; + } + else + { + L = 24; + C = ceil ((double)B / ((double)(Z-L))); + B1 = B + C * L; + } + // first segmentation: K+ = minimum K in table such that C * K >= B1 +// uint i = 0; +// while (B1 > cbSizeTable[i] * C) +// { +// // NS_LOG_INFO (" K+ " << cbSizeTable[i] << " means " << cbSizeTable[i] * C); +// i++; +// } +// uint16_t KplusId = i; +// Kplus = cbSizeTable[i]; + + // implement a modified binary search + int min = 0; + int max = 187; + int mid = 0; + do + { + mid = (min+max) / 2; + if (B1 > cbSizeTable[mid]*C) + { + if (B1 < cbSizeTable[mid+1]*C) + { + break; + } + else + { + min = mid + 1; + } + } + else + { + if (B1 > cbSizeTable[mid-1]*C) + { + break; + } + else + { + max = mid - 1; + } + } + } while ((cbSizeTable[mid]*C != B1) && (min < max)); + // adjust binary search to the largest integer value of K containing B1 + if (B1 > cbSizeTable[mid]*C) + { + mid ++; + } + + uint16_t KplusId = mid; + Kplus = cbSizeTable[mid]; + + + if (C==1) + { + Cplus = 1; + Cminus = 0; + Kminus = 0; + } + else + { + // second segmentation size: K- = maximum K in table such that K < K+ + Kminus = cbSizeTable[KplusId-1 > 0 ? KplusId-1 : 0]; + deltaK = Kplus - Kminus; + Cminus = floor ((((double) C * Kplus) - (double)B1) / (double)deltaK); + Cplus = C - Cminus; + } + NS_LOG_INFO ("--------------------LteMiErrorModel: TB size of " << B << " needs of " << B1 << " bits reparted in " << C << " CBs as "<< Cplus << " block(s) of " << Kplus << " and " << Cminus << " of " << Kminus); + + double errorRate = 1.0; + if (C!=1) + { + double cbler = MappingMiBler (MI, mcs, Kplus); + errorRate *= pow (1.0 - cbler, Cplus); + cbler = MappingMiBler (MI, mcs, Kminus); + errorRate *= pow (1.0 - cbler, Cminus); + errorRate = 1.0 - errorRate; + } + else + { + errorRate = MappingMiBler (MI, mcs, Kplus); + } + + NS_LOG_LOGIC (" Error rate " << errorRate); + TbStats_t ret; + ret.error = errorRate; + ret.mi = tbMi; + return ret; +} + + } // namespace ns3 diff --git a/src/lte/model/lte-mi-error-model.h b/src/lte/model/lte-mi-error-model.h index ec127db2d..ce3fe3409 100644 --- a/src/lte/model/lte-mi-error-model.h +++ b/src/lte/model/lte-mi-error-model.h @@ -38,6 +38,7 @@ #include #include #include +#include @@ -48,6 +49,12 @@ namespace ns3 { const uint16_t MI_MAP_QPSK_SIZE = 766; const uint16_t MI_MAP_16QAM_SIZE = 843; const uint16_t MI_MAP_64QAM_SIZE = 725; + +struct TbStats_t +{ + double error; + double mi; +}; @@ -85,6 +92,19 @@ public: * \return the TB error rate */ static double GetTbError (const SpectrumValue& sinr, const std::vector& map, uint16_t size, uint8_t mcs); + + /** + * \brief run the error-model algorithm for the specified TB + * \param sinr the perceived sinrs in the whole bandwidth + * \param map the actives RBs for the TB + * \param size the size in bytes of the TB + * \param mcs the MCS of the TB + * \param cumulatedMi MI of past transmissions (in case of retx) + * \return the TB error rate + */ + static TbStats_t GetTbDecodificationStats (const SpectrumValue& sinr, const std::vector& map, uint16_t size, uint8_t mcs, double cumulatedMi); + + static TbStats_t GetTbDecodificationStats (const SpectrumValue& sinr, const std::vector& map, uint16_t size, uint8_t mcs, HarqProcessInfoList_t miHistory); /** * \brief run the error-model algorithm for the specified PCFICH+PDCCH channels diff --git a/src/lte/model/lte-radio-bearer-tag.cc b/src/lte/model/lte-radio-bearer-tag.cc index bebf7e58a..e19efd4c2 100644 --- a/src/lte/model/lte-radio-bearer-tag.cc +++ b/src/lte/model/lte-radio-bearer-tag.cc @@ -91,7 +91,7 @@ LteRadioBearerTag::SetLayer (uint8_t layer) uint32_t LteRadioBearerTag::GetSerializedSize (void) const { - return 3; + return 4; } void @@ -99,6 +99,7 @@ LteRadioBearerTag::Serialize (TagBuffer i) const { i.WriteU16 (m_rnti); i.WriteU8 (m_lcid); + i.WriteU8 (m_layer); } void @@ -106,6 +107,7 @@ LteRadioBearerTag::Deserialize (TagBuffer i) { m_rnti = (uint16_t) i.ReadU16 (); m_lcid = (uint8_t) i.ReadU8 (); + m_layer = (uint8_t) i.ReadU8 (); } uint16_t @@ -129,7 +131,7 @@ LteRadioBearerTag::GetLayer () const void LteRadioBearerTag::Print (std::ostream &os) const { - os << "rnti=" << m_rnti << ", lcid=" << (uint16_t) m_lcid; + os << "rnti=" << m_rnti << ", lcid=" << (uint16_t) m_lcid << ", layer=" << (uint16_t)m_layer; } } // namespace ns3 diff --git a/src/lte/model/lte-rlc-am.cc b/src/lte/model/lte-rlc-am.cc index ae1b696b4..389d1f4b9 100644 --- a/src/lte/model/lte-rlc-am.cc +++ b/src/lte/model/lte-rlc-am.cc @@ -162,7 +162,7 @@ LteRlcAm::DoTransmitPdcpPdu (Ptr p) */ void -LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) +LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) { NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid << bytes); @@ -190,6 +190,8 @@ LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) params.pdu = packet; params.rnti = m_rnti; params.lcid = m_lcid; + params.layer = layer; + params.harqProcessId = harqId; m_macSapProvider->TransmitPdu (params); return; @@ -211,6 +213,8 @@ LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) params.pdu = packet; params.rnti = m_rnti; params.lcid = m_lcid; + params.layer = layer; + params.harqProcessId = harqId; m_macSapProvider->TransmitPdu (params); return; @@ -539,6 +543,7 @@ LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) params.rnti = m_rnti; params.lcid = m_lcid; params.layer = layer; + params.harqProcessId = harqId; m_macSapProvider->TransmitPdu (params); } diff --git a/src/lte/model/lte-rlc-am.h b/src/lte/model/lte-rlc-am.h index 1686cf867..b04e7b295 100644 --- a/src/lte/model/lte-rlc-am.h +++ b/src/lte/model/lte-rlc-am.h @@ -48,7 +48,7 @@ public: /** * MAC SAP */ - virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer); + virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId); virtual void DoNotifyHarqDeliveryFailure (); virtual void DoReceivePdu (Ptr p); diff --git a/src/lte/model/lte-rlc-um.cc b/src/lte/model/lte-rlc-um.cc index 383d002aa..879cec23a 100644 --- a/src/lte/model/lte-rlc-um.cc +++ b/src/lte/model/lte-rlc-um.cc @@ -115,7 +115,7 @@ LteRlcUm::DoTransmitPdcpPdu (Ptr p) */ void -LteRlcUm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) +LteRlcUm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) { NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid << bytes); @@ -372,6 +372,7 @@ LteRlcUm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) params.rnti = m_rnti; params.lcid = m_lcid; params.layer = layer; + params.harqProcessId = harqId; m_macSapProvider->TransmitPdu (params); diff --git a/src/lte/model/lte-rlc-um.h b/src/lte/model/lte-rlc-um.h index ca0fca6b1..d5165a20e 100644 --- a/src/lte/model/lte-rlc-um.h +++ b/src/lte/model/lte-rlc-um.h @@ -47,7 +47,7 @@ public: /** * MAC SAP */ - virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer); + virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId); virtual void DoNotifyHarqDeliveryFailure (); virtual void DoReceivePdu (Ptr p); diff --git a/src/lte/model/lte-rlc.cc b/src/lte/model/lte-rlc.cc index a499fa137..1d14cb480 100644 --- a/src/lte/model/lte-rlc.cc +++ b/src/lte/model/lte-rlc.cc @@ -42,7 +42,7 @@ public: LteRlcSpecificLteMacSapUser (LteRlc* rlc); // Interface implemented from LteMacSapUser - virtual void NotifyTxOpportunity (uint32_t bytes, uint8_t layer); + virtual void NotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId); virtual void NotifyHarqDeliveryFailure (); virtual void ReceivePdu (Ptr p); @@ -61,9 +61,9 @@ LteRlcSpecificLteMacSapUser::LteRlcSpecificLteMacSapUser () } void -LteRlcSpecificLteMacSapUser::NotifyTxOpportunity (uint32_t bytes, uint8_t layer) +LteRlcSpecificLteMacSapUser::NotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) { - m_rlc->DoNotifyTxOpportunity (bytes, layer); + m_rlc->DoNotifyTxOpportunity (bytes, layer, harqId); } void @@ -210,7 +210,7 @@ LteRlcSm::DoReceivePdu (Ptr p) } void -LteRlcSm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) +LteRlcSm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) { NS_LOG_FUNCTION (this << bytes); LteMacSapProvider::TransmitPduParameters params; @@ -218,6 +218,7 @@ LteRlcSm::DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) params.rnti = m_rnti; params.lcid = m_lcid; params.layer = layer; + params.harqProcessId = harqId; // RLC Performance evaluation RlcTag tag (Simulator::Now()); diff --git a/src/lte/model/lte-rlc.h b/src/lte/model/lte-rlc.h index 2e0c0d49e..77993256c 100644 --- a/src/lte/model/lte-rlc.h +++ b/src/lte/model/lte-rlc.h @@ -111,7 +111,7 @@ protected: LteRlcSapProvider* m_rlcSapProvider; // Interface forwarded by LteMacSapUser - virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer) = 0; + virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId) = 0; virtual void DoNotifyHarqDeliveryFailure () = 0; virtual void DoReceivePdu (Ptr p) = 0; @@ -150,7 +150,7 @@ public: static TypeId GetTypeId (void); virtual void DoTransmitPdcpPdu (Ptr p); - virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer); + virtual void DoNotifyTxOpportunity (uint32_t bytes, uint8_t layer, uint8_t harqId); virtual void DoNotifyHarqDeliveryFailure (); virtual void DoReceivePdu (Ptr p); diff --git a/src/lte/model/lte-spectrum-phy.cc b/src/lte/model/lte-spectrum-phy.cc index 2a5d07756..aa1071f35 100644 --- a/src/lte/model/lte-spectrum-phy.cc +++ b/src/lte/model/lte-spectrum-phy.cc @@ -52,6 +52,38 @@ static const Time UL_SRS_DURATION = NanoSeconds (71429 -1); // = 0.001 / 14 * 3 (ctrl fixed to 3 symbols) -1ns as margin to avoid overlapping simulator events static const Time DL_CTRL_DURATION = NanoSeconds (214286 -1); +double EffectiveCodingRate[29] = { + 0.08, + 0.1, + 0.11, + 0.15, + 0.19, + 0.24, + 0.3, + 0.37, + 0.44, + 0.51, + 0.3, + 0.33, + 0.37, + 0.42, + 0.48, + 0.54, + 0.6, + 0.43, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.89, + 0.92 +}; + @@ -81,7 +113,9 @@ NS_OBJECT_ENSURE_REGISTERED (LteSpectrumPhy); LteSpectrumPhy::LteSpectrumPhy () : m_state (IDLE), - m_transmissionMode (0) + m_transmissionMode (0), + m_layersNum (1), + errors (0) { NS_LOG_FUNCTION (this); m_random = CreateObject (); @@ -119,6 +153,8 @@ void LteSpectrumPhy::DoDispose () m_ltePhyRxDataEndOkCallback = MakeNullCallback< void, Ptr > (); m_ltePhyRxCtrlEndOkCallback = MakeNullCallback< void, std::list > > (); m_ltePhyRxCtrlEndErrorCallback = MakeNullCallback< void > (); + m_ltePhyDlHarqFeedbackCallback = MakeNullCallback< void, DlInfoListElement_s > (); + m_ltePhyUlHarqFeedbackCallback = MakeNullCallback< void, UlInfoListElement_s > (); SpectrumPhy::DoDispose (); } @@ -285,6 +321,20 @@ LteSpectrumPhy::SetLtePhyRxCtrlEndErrorCallback (LtePhyRxCtrlEndErrorCallback c) m_ltePhyRxCtrlEndErrorCallback = c; } +void +LteSpectrumPhy::SetLtePhyDlHarqFeedbackCallback (LtePhyDlHarqFeedbackCallback c) +{ + NS_LOG_FUNCTION (this); + m_ltePhyDlHarqFeedbackCallback = c; +} + +void +LteSpectrumPhy::SetLtePhyUlHarqFeedbackCallback (LtePhyUlHarqFeedbackCallback c) +{ + NS_LOG_FUNCTION (this); + m_ltePhyUlHarqFeedbackCallback = c; +} + Ptr LteSpectrumPhy::GetRxAntenna () @@ -314,6 +364,14 @@ LteSpectrumPhy::ChangeState (State newState) } +void +LteSpectrumPhy::SetHarqPhyModule (Ptr harq) +{ + m_harqPhyModule = harq; +} + + + bool LteSpectrumPhy::StartTxDataFrame (Ptr pb, std::list > ctrlMsgList, Time duration) @@ -717,22 +775,23 @@ LteSpectrumPhy::UpdateSinrPerceived (const SpectrumValue& sinr) void -LteSpectrumPhy::AddExpectedTb (uint16_t rnti, uint16_t size, uint8_t mcs, std::vector map, uint8_t layer) +LteSpectrumPhy::AddExpectedTb (uint16_t rnti, uint16_t size, uint8_t mcs, std::vector map, uint8_t layer, uint8_t harqId, double miCumulated, bool downlink) { - NS_LOG_LOGIC (this << " rnti: " << rnti << " size " << size << " mcs " << (uint16_t)mcs << " layer " << (uint8_t)layer); + NS_LOG_FUNCTION (this << " rnti: " << rnti << " size " << size << " mcs " << (uint16_t)mcs << " layer " << (uint16_t)layer << " MI " << miCumulated); TbId_t tbId; tbId.m_rnti = rnti; tbId.m_layer = layer; expectedTbs_t::iterator it; it = m_expectedTbs.find (tbId); if (it != m_expectedTbs.end ()) - { - // migth be a TB of an unreceived packet (due to high progpalosses) - m_expectedTbs.erase (it); - } + { + // migth be a TB of an unreceived packet (due to high progpalosses) + m_expectedTbs.erase (it); + } // insert new entry - tbInfo_t tbInfo = {size, mcs, map, false}; - m_expectedTbs.insert (std::pair (tbId,tbInfo )); + std::vector rv; + tbInfo_t tbInfo = {size, mcs, map, harqId, miCumulated, downlink, false}; + m_expectedTbs.insert (std::pair (tbId,tbInfo)); } @@ -760,9 +819,33 @@ LteSpectrumPhy::EndRxData () { if (m_dataErrorModelEnabled) { - double errorRate = LteMiErrorModel::GetTbError (m_sinrPerceived, (*itTb).second.rbBitmap, (*itTb).second.size, (*itTb).second.mcs); - (*itTb).second.corrupt = m_random->GetValue () > errorRate ? false : true; - NS_LOG_DEBUG (this << "RNTI " << (*itTb).first.m_rnti << " size " << (*itTb).second.size << " mcs " << (uint32_t)(*itTb).second.mcs << " bitmap " << (*itTb).second.rbBitmap.size () << " layer " << (uint16_t)(*itTb).first.m_layer << " ErrorRate " << errorRate << " corrupted " << (*itTb).second.corrupt); +// double errorRate = LteMiErrorModel::GetTbError (m_sinrPerceived, (*itTb).second.rbBitmap, (*itTb).second.size, (*itTb).second.mcs); + // retrieve HARQ info + HarqProcessInfoList_t harqInfoList; + uint16_t ulHarqId = 0; // TODO 0 means current HARQ porc under evaluation + if ((*itTb).second.downlink) + { + harqInfoList = m_harqPhyModule->GetHarqProcessInfoDl ((*itTb).second.harqProcessId, (*itTb).first.m_layer); + } + else + { + harqInfoList = m_harqPhyModule->GetHarqProcessInfoUl ((*itTb).first.m_rnti, ulHarqId); + } +// TbStats_t tbStats = LteMiErrorModel::GetTbDecodificationStats (m_sinrPerceived, (*itTb).second.rbBitmap, (*itTb).second.size, (*itTb).second.mcs, (*itTb).second.mi); + TbStats_t tbStats = LteMiErrorModel::GetTbDecodificationStats (m_sinrPerceived, (*itTb).second.rbBitmap, (*itTb).second.size, (*itTb).second.mcs, harqInfoList); + (*itTb).second.mi = tbStats.mi; + (*itTb).second.corrupt = m_random->GetValue () > tbStats.error ? false : true; + // DEBUG: force error for testing HARQ + if ((*itTb).second.downlink) + { +// if (((*itTb).second.harqProcessId == 0)&&(Simulator::Now ().GetNanoSeconds ()<=20000000)) + if ((errors<1) && ( ((*itTb).first.m_rnti==1)||((*itTb).first.m_rnti==3)) ) + { + (*itTb).second.corrupt = true; + errors++; + } + } + NS_LOG_DEBUG (this << "RNTI " << (*itTb).first.m_rnti << " size " << (*itTb).second.size << " mcs " << (uint32_t)(*itTb).second.mcs << " bitmap " << (*itTb).second.rbBitmap.size () << " layer " << (uint16_t)(*itTb).first.m_layer << " ErrorRate " << tbStats.error << " corrupted " << (*itTb).second.corrupt); } // for (uint16_t i = 0; i < (*itTb).second.rbBitmap.size (); i++) @@ -771,6 +854,7 @@ LteSpectrumPhy::EndRxData () // } itTb++; } + std::map harqDlInfoMap; for (std::list >::const_iterator i = m_rxPacketBurstList.begin (); i != m_rxPacketBurstList.end (); ++i) { @@ -783,7 +867,7 @@ LteSpectrumPhy::EndRxData () tbId.m_rnti = tag.GetRnti (); tbId.m_layer = tag.GetLayer (); itTb = m_expectedTbs.find (tbId); - NS_LOG_INFO (this << " Packet of " << tbId.m_rnti << " layer " << (uint8_t) tbId.m_layer); + NS_LOG_INFO (this << " Packet of " << tbId.m_rnti << " layer " << (uint16_t) tag.GetLayer ()); if (itTb!=m_expectedTbs.end ()) { if (!(*itTb).second.corrupt) @@ -800,10 +884,84 @@ LteSpectrumPhy::EndRxData () // TB received with errors m_phyRxEndErrorTrace (*j); } + + // send HARQ feedback + if (!(*itTb).second.downlink) + { + UlInfoListElement_s harqUlInfo; + harqUlInfo.m_rnti = tbId.m_rnti; + if ((*itTb).second.corrupt) + { + harqUlInfo.m_receptionStatus = UlInfoListElement_s::NotOk; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " send UL-HARQ-NACK"); + m_harqPhyModule->UpdateUlHarqProcessStatus (tbId.m_rnti, (*itTb).second.mi, (*itTb).second.size, EffectiveCodingRate [(*itTb).second.mcs] * (*itTb).second.size); + } + else + { + harqUlInfo.m_receptionStatus = UlInfoListElement_s::Ok; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " send UL-HARQ-ACK"); + m_harqPhyModule->ResetUlHarqProcessStatus (tbId.m_rnti, (*itTb).second.harqProcessId); + } + if (!m_ltePhyUlHarqFeedbackCallback.IsNull ()) + { + m_ltePhyUlHarqFeedbackCallback (harqUlInfo); + } + } + else + { + std::map ::iterator itHarq = harqDlInfoMap.find (tbId.m_rnti); + if (itHarq==harqDlInfoMap.end ()) + { + DlInfoListElement_s harqDlInfo; + harqDlInfo.m_harqStatus.resize (m_layersNum, DlInfoListElement_s::NACK); + harqDlInfo.m_rnti = tbId.m_rnti; + harqDlInfo.m_harqProcessId = (*itTb).second.harqProcessId; + if ((*itTb).second.corrupt) + { + harqDlInfo.m_harqStatus.at (tbId.m_layer) = DlInfoListElement_s::NACK; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " harqId " << (uint16_t)(*itTb).second.harqProcessId << " layer " <<(uint16_t)tbId.m_layer << " send DL-HARQ-NACK"); + m_harqPhyModule->UpdateDlHarqProcessStatus ((*itTb).second.harqProcessId, tbId.m_layer, (*itTb).second.mi, (*itTb).second.size, EffectiveCodingRate [(*itTb).second.mcs] * (*itTb).second.size); + } + else + { + + harqDlInfo.m_harqStatus.at (tbId.m_layer) = DlInfoListElement_s::ACK; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " harqId " << (uint16_t)(*itTb).second.harqProcessId << " layer " <<(uint16_t)tbId.m_layer << " size " << (*itTb).second.size << " send DL-HARQ-ACK"); + m_harqPhyModule->ResetDlHarqProcessStatus ((*itTb).second.harqProcessId); + } + harqDlInfoMap.insert (std::pair (tbId.m_rnti, harqDlInfo)); + } + else + { + if ((*itTb).second.corrupt) + { + (*itHarq).second.m_harqStatus.at (tbId.m_layer) = DlInfoListElement_s::NACK; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " harqId " << (uint16_t)(*itTb).second.harqProcessId << " layer " <<(uint16_t)tbId.m_layer << " size " << (*itHarq).second.m_harqStatus.size () << " send DL-HARQ-NACK"); + m_harqPhyModule->UpdateDlHarqProcessStatus ((*itTb).second.harqProcessId, tbId.m_layer, (*itTb).second.mi, (*itTb).second.size, EffectiveCodingRate [(*itTb).second.mcs] * (*itTb).second.size); + } + else + { + NS_ASSERT_MSG (tbId.m_layer < (*itHarq).second.m_harqStatus.size (), " layer " << (uint16_t)tbId.m_layer); + (*itHarq).second.m_harqStatus.at (tbId.m_layer) = DlInfoListElement_s::ACK; + NS_LOG_DEBUG (this << " RNTI " << tbId.m_rnti << " harqId " << (uint16_t)(*itTb).second.harqProcessId << " layer " << (uint16_t)tbId.m_layer << " size " << (*itHarq).second.m_harqStatus.size () << " send DL-HARQ-ACK"); + m_harqPhyModule->ResetDlHarqProcessStatus ((*itTb).second.harqProcessId); + } + } + } // end if ((*itTb).second.downlink) HARQ } } } - + + // send DL HARQ feedback to LtePhy + std::map ::iterator itHarq; + for (itHarq = harqDlInfoMap.begin (); itHarq != harqDlInfoMap.end (); itHarq++) + { + if (!m_ltePhyDlHarqFeedbackCallback.IsNull ()) + { + m_ltePhyDlHarqFeedbackCallback ((*itHarq).second); + } + } + // forward control messages of this frame to LtePhy if (!m_rxControlMessageList.empty ()) { if (!m_ltePhyRxCtrlEndOkCallback.IsNull ()) @@ -851,6 +1009,7 @@ LteSpectrumPhy::EndRxDlCtrl () { if (!m_ltePhyRxCtrlEndOkCallback.IsNull ()) { + NS_LOG_DEBUG (this << " PCFICH-PDCCH Rxed OK"); m_ltePhyRxCtrlEndOkCallback (m_rxControlMessageList); } } @@ -858,6 +1017,7 @@ LteSpectrumPhy::EndRxDlCtrl () { if (!m_ltePhyRxCtrlEndErrorCallback.IsNull ()) { + NS_LOG_DEBUG (this << " PCFICH-PDCCH Error"); m_ltePhyRxCtrlEndErrorCallback (); } } @@ -899,6 +1059,7 @@ LteSpectrumPhy::SetTransmissionMode (uint8_t txMode) NS_LOG_FUNCTION (this << (uint16_t) txMode); NS_ASSERT_MSG (txMode < m_txModeGain.size (), "TransmissionMode not available: 1.." << m_txModeGain.size ()); m_transmissionMode = txMode; + m_layersNum = TransmissionModesLayers::TxMode2LayerNum (txMode); } diff --git a/src/lte/model/lte-spectrum-phy.h b/src/lte/model/lte-spectrum-phy.h index 08eb09dbc..9fd94ec32 100644 --- a/src/lte/model/lte-spectrum-phy.h +++ b/src/lte/model/lte-spectrum-phy.h @@ -39,6 +39,8 @@ #include "ns3/random-variable-stream.h" #include #include +#include +#include namespace ns3 { @@ -61,6 +63,9 @@ struct tbInfo_t uint16_t size; uint8_t mcs; std::vector rbBitmap; + uint8_t harqProcessId; + double mi; + bool downlink; bool corrupt; }; @@ -113,6 +118,18 @@ typedef Callback< void, std::list > > LtePhyRxCtrlEndOkCa */ typedef Callback< void > LtePhyRxCtrlEndErrorCallback; +/** +* This method is used by the LteSpectrumPhy to notify the PHY about +* the status of a certain DL HARQ process +*/ +typedef Callback< void, DlInfoListElement_s > LtePhyDlHarqFeedbackCallback; + +/** +* This method is used by the LteSpectrumPhy to notify the PHY about +* the status of a certain UL HARQ process +*/ +typedef Callback< void, UlInfoListElement_s > LtePhyUlHarqFeedbackCallback; + /** @@ -154,6 +171,8 @@ public: void StartRxData (Ptr params); void StartRxCtrl (Ptr params); + void SetHarqPhyModule (Ptr harq); + /** * set the Power Spectral Density of outgoing signals in W/Hz. * @@ -253,6 +272,22 @@ public: */ void SetLtePhyRxCtrlEndErrorCallback (LtePhyRxCtrlEndErrorCallback c); + /** + * set the callback for the DL HARQ feedback as part of the + * interconnections betweenthe LteSpectrumPhy and the PHY + * + * @param c the callback + */ + void SetLtePhyDlHarqFeedbackCallback (LtePhyDlHarqFeedbackCallback c); + + /** + * set the callback for the UL HARQ feedback as part of the + * interconnections betweenthe LteSpectrumPhy and the PHY + * + * @param c the callback + */ + void SetLtePhyUlHarqFeedbackCallback (LtePhyUlHarqFeedbackCallback c); + /** * \brief Set the state of the phy layer * \param newState the state @@ -290,9 +325,13 @@ public: * \param mcs the MCS of the TB * \param map the map of RB(s) used * \param layer the layer (in case of MIMO tx) + * \param harqId the id of the HARQ process (valid only for DL) + * \param miCumulated the MI cumulated (in case of HARQ retx) + * \param downlink true when the TB is for DL */ - void AddExpectedTb (uint16_t rnti, uint16_t size, uint8_t mcs, std::vector map, uint8_t layer); - + void AddExpectedTb (uint16_t rnti, uint16_t size, uint8_t mcs, std::vector map, uint8_t layer, uint8_t harqId, double miCumulated, bool downlink); + + /** * * @@ -375,8 +414,15 @@ private: bool m_ctrlErrorModelEnabled; // when true (default) the phy error model is enabled for DL ctrl frame uint8_t m_transmissionMode; // for UEs: store the transmission mode + uint8_t m_layersNum; std::vector m_txModeGain; // duplicate value of LteUePhy - + + Ptr m_harqPhyModule; + LtePhyDlHarqFeedbackCallback m_ltePhyDlHarqFeedbackCallback; + LtePhyUlHarqFeedbackCallback m_ltePhyUlHarqFeedbackCallback; + + uint16_t errors; // DEBUG + }; diff --git a/src/lte/model/lte-ue-mac.cc b/src/lte/model/lte-ue-mac.cc index 93e0dc563..0f2e8a7f0 100644 --- a/src/lte/model/lte-ue-mac.cc +++ b/src/lte/model/lte-ue-mac.cc @@ -179,10 +179,12 @@ LteUeMac::GetTypeId (void) LteUeMac::LteUeMac () : m_bsrPeriodicity (MilliSeconds (1)), // ideal behavior m_bsrLast (MilliSeconds (0)), - m_freshUlBsr (false) + m_freshUlBsr (false), + m_harqProcessId (0) { NS_LOG_FUNCTION (this); + m_miUlHarqProcessesPacket.resize (HARQ_PERIOD); m_macSapProvider = new UeMemberLteMacSapProvider (this); m_cmacSapProvider = new UeMemberLteUeCmacSapProvider (this); m_uePhySapUser = new UeMemberLteUePhySapUser (this); @@ -198,6 +200,7 @@ void LteUeMac::DoDispose () { NS_LOG_FUNCTION (this); + m_miUlHarqProcessesPacket.clear (); delete m_macSapProvider; delete m_cmacSapProvider; delete m_uePhySapUser; @@ -244,11 +247,9 @@ LteUeMac::DoTransmitPdu (LteMacSapProvider::TransmitPduParameters params) NS_ASSERT_MSG (m_rnti == params.rnti, "RNTI mismatch between RLC and MAC"); LteRadioBearerTag tag (params.rnti, params.lcid, 0 /* UE works in SISO mode*/); params.pdu->AddPacketTag (tag); -// Ptr pb = CreateObject (); -// pb->AddPacket (params.pdu); + // store pdu in HARQ buffer + m_miUlHarqProcessesPacket.at (m_harqProcessId) = params.pdu;//->Copy (); m_uePhySapProvider->SendMacPdu (params.pdu); - // Uplink not implemented yet, so we wait can wait for the PHY SAP - // to be defined before we implement the transmission method. } void @@ -354,39 +355,51 @@ LteUeMac::DoReceiveLteControlMessage (Ptr msg) { Ptr msg2 = DynamicCast (msg); UlDciListElement_s dci = msg2->GetDci (); - std::map ::iterator itBsr; - NS_ASSERT_MSG (m_ulBsrReceived.size () <=4, " Too many LCs (max is 4)"); - uint16_t activeLcs = 0; - for (itBsr = m_ulBsrReceived.begin (); itBsr != m_ulBsrReceived.end (); itBsr++) + if (dci.m_ndi==1) { - if ((*itBsr).second > 0) + // New transmission -> retrieve data from RLC + std::map ::iterator itBsr; + NS_ASSERT_MSG (m_ulBsrReceived.size () <=4, " Too many LCs (max is 4)"); + uint16_t activeLcs = 0; + for (itBsr = m_ulBsrReceived.begin (); itBsr != m_ulBsrReceived.end (); itBsr++) { - activeLcs++; - } - } - if (activeLcs == 0) - { - NS_LOG_ERROR (this << " No active flows for this UL-DCI"); - return; - } - std::map ::iterator it; - NS_LOG_FUNCTION (this << " UE: UL-CQI notified TxOpportunity of " << dci.m_tbSize); - for (it = m_macSapUserMap.begin (); it!=m_macSapUserMap.end (); it++) - { - itBsr = m_ulBsrReceived.find ((*it).first); - if (itBsr!=m_ulBsrReceived.end ()) - { - NS_LOG_FUNCTION (this << "\t" << dci.m_tbSize / m_macSapUserMap.size () << " bytes to LC " << (uint16_t)(*it).first << " queue " << (*itBsr).second); - (*it).second->NotifyTxOpportunity (dci.m_tbSize / activeLcs, 0); - if ((*itBsr).second >= static_cast (dci.m_tbSize / activeLcs)) + if ((*itBsr).second > 0) { - (*itBsr).second -= dci.m_tbSize / activeLcs; - } - else - { - (*itBsr).second = 0; + activeLcs++; } } + if (activeLcs == 0) + { + NS_LOG_ERROR (this << " No active flows for this UL-DCI"); + return; + } + std::map ::iterator it; + NS_LOG_FUNCTION (this << " UE: UL-CQI notified TxOpportunity of " << dci.m_tbSize); + for (it = m_macSapUserMap.begin (); it!=m_macSapUserMap.end (); it++) + { + itBsr = m_ulBsrReceived.find ((*it).first); + if (itBsr!=m_ulBsrReceived.end ()) + { + NS_LOG_FUNCTION (this << "\t" << dci.m_tbSize / m_macSapUserMap.size () << " bytes to LC " << (uint16_t)(*it).first << " queue " << (*itBsr).second); + (*it).second->NotifyTxOpportunity (dci.m_tbSize / activeLcs, 0, 0); // layer and HARQ ID are not used in UL + if ((*itBsr).second >= static_cast (dci.m_tbSize / activeLcs)) + { + (*itBsr).second -= dci.m_tbSize / activeLcs; + } + else + { + (*itBsr).second = 0; + } + } + } + } + else + { + // HARQ retransmission -> retrieve data from HARQ buffer + NS_LOG_DEBUG (this << " UE MAC RETX HARQ " << (uint16_t)m_harqProcessId); + Ptr pkt = m_miUlHarqProcessesPacket.at (m_harqProcessId); + m_uePhySapProvider->SendMacPdu (pkt); + } } @@ -406,6 +419,7 @@ LteUeMac::DoSubframeIndication (uint32_t frameNo, uint32_t subframeNo) SendReportBufferStatus (); m_bsrLast = Simulator::Now (); m_freshUlBsr = false; + m_harqProcessId = (m_harqProcessId + 1) % HARQ_PERIOD; } } diff --git a/src/lte/model/lte-ue-mac.h b/src/lte/model/lte-ue-mac.h index 6c6fb7c1c..8fcafa150 100644 --- a/src/lte/model/lte-ue-mac.h +++ b/src/lte/model/lte-ue-mac.h @@ -30,7 +30,8 @@ #include #include #include - +#include +#include namespace ns3 { @@ -107,6 +108,9 @@ private: bool m_freshUlBsr; // true when a BSR has been received in the last TTI + uint8_t m_harqProcessId; + std::vector < Ptr > m_miUlHarqProcessesPacket; // Packets under trasmission of the UL HARQ processes + uint16_t m_rnti; diff --git a/src/lte/model/lte-ue-phy.cc b/src/lte/model/lte-ue-phy.cc index d8e2378bd..a37dd8a32 100644 --- a/src/lte/model/lte-ue-phy.cc +++ b/src/lte/model/lte-ue-phy.cc @@ -539,7 +539,7 @@ LteUePhy::ReceiveLteControlMessageList (std::list > msgLi for (int k = 0; k < GetRbgSize (); k++) { dlRb.push_back ((i * GetRbgSize ()) + k); - //NS_LOG_DEBUG(this << "DL-DCI allocated PRB " << (i*GetRbgSize()) + k); +// NS_LOG_DEBUG(this << " RNTI " << m_rnti << " RBG " << i << " DL-DCI allocated PRB " << (i*GetRbgSize()) + k); } } mask = (mask << 1); @@ -549,7 +549,13 @@ LteUePhy::ReceiveLteControlMessageList (std::list > msgLi NS_LOG_DEBUG (this << " UE " << m_rnti << " DL-DCI " << dci.m_rnti << " bitmap " << dci.m_rbBitmap); for (uint8_t i = 0; i < dci.m_tbsSize.size (); i++) { - m_downlinkSpectrumPhy->AddExpectedTb (dci.m_rnti, dci.m_tbsSize.at (i), dci.m_mcs.at (i), dlRb, i); + double miCumulated = 0.0; + if (dci.m_ndi.at (i)!=0) + { + // TODO : retrieve MI info on retransmissions + miCumulated = 0.0; + } + m_downlinkSpectrumPhy->AddExpectedTb (dci.m_rnti, dci.m_tbsSize.at (i), dci.m_mcs.at (i), dlRb, i, dci.m_harqProcess, miCumulated, true /* DL */); } SetSubChannelsForReception (dlRb); @@ -633,11 +639,11 @@ LteUePhy::SubframeIndication (uint32_t frameNo, uint32_t subframeNo) std::list > ctrlMsg = GetControlMessages (); // send packets in queue + NS_LOG_LOGIC (this << " UE - start TX PUSCH + PUCCH"); // send the current burts of packets Ptr pb = GetPacketBurst (); if (pb) { - NS_LOG_LOGIC (this << " UE - start TX PUSCH + PUCCH"); m_uplinkSpectrumPhy->StartTxDataFrame (pb, ctrlMsg, UL_DATA_DURATION); } else @@ -835,4 +841,22 @@ LteUePhy::UpdateNoisePsd () } +void +LteUePhy::ReceiveLteDlHarqFeedback (DlInfoListElement_s m) +{ + NS_LOG_FUNCTION (this); + // generate feedback to eNB and send it through ideal PUCCH + Ptr msg = Create (); + msg->SetDlHarqFeedback (m); + SetControlMessages (msg); +} + +void +LteUePhy::SetHarqPhyModule (Ptr harq) +{ + m_harqPhyModule = harq; +} + + + } // namespace ns3 diff --git a/src/lte/model/lte-ue-phy.h b/src/lte/model/lte-ue-phy.h index cc36abfd1..72d4895e2 100644 --- a/src/lte/model/lte-ue-phy.h +++ b/src/lte/model/lte-ue-phy.h @@ -39,6 +39,7 @@ namespace ns3 { class PacketBurst; class LteNetDevice; class LteEnbPhy; +class LteHarqPhy; /** * \ingroup lte @@ -196,7 +197,17 @@ public: */ void SendSrs (); - + /** + * \brief PhySpectrum generated a new DL HARQ feedback + */ + virtual void ReceiveLteDlHarqFeedback (DlInfoListElement_s mes); + + /** + * \brief Set the HARQ PHY module + */ + void SetHarqPhyModule (Ptr harq); + + private: @@ -254,6 +265,8 @@ private: uint16_t m_srsPeriodicity; uint16_t m_srsCounter; + Ptr m_harqPhyModule; + }; diff --git a/src/lte/model/pf-ff-mac-scheduler.cc b/src/lte/model/pf-ff-mac-scheduler.cc index d1c37388e..458bbf120 100644 --- a/src/lte/model/pf-ff-mac-scheduler.cc +++ b/src/lte/model/pf-ff-mac-scheduler.cc @@ -29,6 +29,7 @@ #include #include #include +#include NS_LOG_COMPONENT_DEFINE ("PfFfMacScheduler"); @@ -247,6 +248,11 @@ PfFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&PfFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) + .AddAttribute ("HarqEnabled", + "Activate/Deactivate the HARQ [by default is active].", + BooleanValue (true), + MakeBooleanAccessor (&PfFfMacScheduler::m_harqOn), + MakeBooleanChecker ()) ; return tid; } @@ -564,7 +570,6 @@ PfFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched newEl.m_rnti = (*itMap).first; // create the DlDciListElement_s DlDciListElement_s newDci; - std::vector newRlcPduLe; newDci.m_rnti = (*itMap).first; uint16_t lcActives = LcActivePerFlow ((*itMap).first); @@ -649,6 +654,7 @@ PfFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) { + std::vector newRlcPduLe; for (uint8_t j = 0; j < nLayer; j++) { RlcPduListElement_s newRlcEl; @@ -658,21 +664,24 @@ PfFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched newRlcPduLe.push_back (newRlcEl); UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); } + newEl.m_rlcPduList.push_back (newRlcPduLe); } if ((*itBufReq).first.m_rnti > (*itMap).first) { break; } } - newDci.m_ndi.push_back (1); // TBD (new data indicator) - newDci.m_rv.push_back (0); // TBD (redundancy version) + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + } + newDci.m_harqProcess = 0; // NO HARQ newEl.m_dci = newDci; // ...more parameters -> ingored in this version - newEl.m_rlcPduList.push_back (newRlcPduLe); ret.m_buildDataList.push_back (newEl); - // update UE stats std::map ::iterator it; it = m_flowStatsDl.find ((*itMap).first); diff --git a/src/lte/model/pf-ff-mac-scheduler.h b/src/lte/model/pf-ff-mac-scheduler.h index a3e7d989d..921030ef1 100644 --- a/src/lte/model/pf-ff-mac-scheduler.h +++ b/src/lte/model/pf-ff-mac-scheduler.h @@ -218,6 +218,13 @@ private: uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid std::map m_uesTxMode; // txMode of the UEs + + // HARQ attributes + /** + * m_harqOn when false inhibit te HARQ mechanisms (by default active) + */ + bool m_harqOn; + }; } // namespace ns3 diff --git a/src/lte/model/rr-ff-mac-scheduler.cc b/src/lte/model/rr-ff-mac-scheduler.cc index 5a6402cfd..8430f922d 100644 --- a/src/lte/model/rr-ff-mac-scheduler.cc +++ b/src/lte/model/rr-ff-mac-scheduler.cc @@ -24,12 +24,14 @@ #include #include +#include #include #include #include #include #include +#include NS_LOG_COMPONENT_DEFINE ("RrFfMacScheduler"); @@ -234,6 +236,12 @@ void RrFfMacScheduler::DoDispose () { NS_LOG_FUNCTION (this); + m_dlHarqProcessesDciBuffer.clear (); + m_dlHarqProcessesRlcPduListBuffer.clear (); + m_dlInfoListBuffered.clear (); + m_ulHarqCurrentProcessId.clear (); + m_ulHarqProcessesStatus.clear (); + m_ulHarqProcessesDciBuffer.clear (); delete m_cschedSapProvider; delete m_schedSapProvider; } @@ -249,6 +257,11 @@ RrFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&RrFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) + .AddAttribute ("HarqEnabled", + "Activate/Deactivate the HARQ [by default is active].", + BooleanValue (true), + MakeBooleanAccessor (&RrFfMacScheduler::m_harqOn), + MakeBooleanChecker ()) ; return tid; } @@ -299,6 +312,26 @@ RrFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::Csch if (it==m_uesTxMode.end ()) { m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + // generate HARQ buffers + m_dlHarqCurrentProcessId.insert (std::pair (params.m_rnti, 0)); + DlHarqProcessesStatus_t dlHarqPrcStatus; + dlHarqPrcStatus.resize (8,0); + m_dlHarqProcessesStatus.insert (std::pair (params.m_rnti, dlHarqPrcStatus)); + DlHarqProcessesDciBuffer_t dlHarqdci; + dlHarqdci.resize (8); + m_dlHarqProcessesDciBuffer.insert (std::pair (params.m_rnti, dlHarqdci)); + DlHarqRlcPduListBuffer_t dlHarqRlcPdu; + dlHarqRlcPdu.resize (2); + dlHarqRlcPdu.at (0).resize (8); + dlHarqRlcPdu.at (1).resize (8); + m_dlHarqProcessesRlcPduListBuffer.insert (std::pair (params.m_rnti, dlHarqRlcPdu)); + m_ulHarqCurrentProcessId.insert (std::pair (params.m_rnti, 0)); + UlHarqProcessesStatus_t ulHarqPrcStatus; + ulHarqPrcStatus.resize (8,0); + m_ulHarqProcessesStatus.insert (std::pair (params.m_rnti, ulHarqPrcStatus)); + UlHarqProcessesDciBuffer_t ulHarqdci; + ulHarqdci.resize (8); + m_ulHarqProcessesDciBuffer.insert (std::pair (params.m_rnti, ulHarqdci)); } else { @@ -402,6 +435,41 @@ RrFfMacScheduler::SortRlcBufferReq (FfMacSchedSapProvider::SchedDlRlcBufferReqPa } +uint8_t +RrFfMacScheduler::UpdateHarqProcessId (uint16_t rnti) +{ + NS_LOG_FUNCTION (this << rnti); + + std::map ::iterator it = m_dlHarqCurrentProcessId.find (rnti); + if (it==m_dlHarqCurrentProcessId.end ()) + { + NS_FATAL_ERROR ("No Process Id found for this RNTI " << rnti); + } + std::map ::iterator itStat = m_dlHarqProcessesStatus.find (rnti); + if (itStat==m_dlHarqProcessesStatus.end ()) + { + NS_FATAL_ERROR ("No Process Id Statusfound for this RNTI " << rnti); + } + uint8_t i = (*it).second; + do + { + i = (i + 1) % HARQ_PROC_NUM; +// NS_LOG_DEBUG (this << " check i " << (uint16_t)i << " stat " << (uint16_t)(*itStat).second.at (i)); + } while ( ((*itStat).second.at (i)!=0)&&(i!=(*it).second)); + if ((*itStat).second.at (i)==0) + { + (*it).second = i; + (*itStat).second.at (i) = 1; + } + else + { + return (9); // return a not valid harq proc id + } + + return ((*it).second); +} + + void RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) { @@ -409,6 +477,255 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched // API generated by RLC for triggering the scheduling of a DL subframe RefreshDlCqiMaps (); + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + + // Generate RBGs map + std::vector rbgMap; + uint16_t rbgAllocatedNum = 0; + std::set rntiAllocated; + rbgMap.resize (m_cschedCellConfig.m_dlBandwidth/rbgSize, false); + + // Process DL HARQ feedback + // retrieve past HARQ retx buffered + if (m_dlInfoListBuffered.size ()>0) + { + if (params.m_dlInfoList.size ()>0) + { + NS_LOG_DEBUG (this << " RECEIVED DL-HARQ"); + m_dlInfoListBuffered.insert (m_dlInfoListBuffered.end (), params.m_dlInfoList.begin (), params.m_dlInfoList.end ()); + } + } + else + { + if (params.m_dlInfoList.size ()>0) + { + m_dlInfoListBuffered = params.m_dlInfoList; + } + } + if (m_harqOn == false) + { + // Ignore HARQ feedbacks + m_dlInfoListBuffered.clear (); + NS_LOG_DEBUG (this << " HARQ OFF"); + } + std::vector dlInfoListUntxed; + for (uint8_t i = 0; i < m_dlInfoListBuffered.size (); i++) + { + uint8_t nLayers = m_dlInfoListBuffered.at (i).m_harqStatus.size (); + std::vector retx; + NS_LOG_DEBUG (this << " Processing DLHARQ-FEEDBACK"); + if (nLayers == 1) + { + retx.push_back (m_dlInfoListBuffered.at (i).m_harqStatus.at (0) == DlInfoListElement_s::NACK); + retx.push_back (false); + } + else + { + retx.push_back (m_dlInfoListBuffered.at (i).m_harqStatus.at (0) == DlInfoListElement_s::NACK); + retx.push_back (m_dlInfoListBuffered.at (i).m_harqStatus.at (1) == DlInfoListElement_s::NACK); + } + if (retx.at (0) || retx.at (1)) + { + // retrieve HARQ process information + uint16_t rnti = m_dlInfoListBuffered.at (i).m_rnti; + uint8_t harqId = m_dlInfoListBuffered.at (i).m_harqProcessId; + NS_LOG_DEBUG (this << " HARQ retx RNTI " << rnti << " harqId " << (uint16_t)harqId); + std::map ::iterator itHarq = m_dlHarqProcessesDciBuffer.find (rnti); + if (itHarq==m_dlHarqProcessesDciBuffer.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << rnti); + } + + DlDciListElement_s dci = (*itHarq).second.at (harqId); + int rv = 0; + if (dci.m_rv.size ()==1) + { + rv = dci.m_rv.at (0); + } + else + { + rv = (dci.m_rv.at (0) > dci.m_rv.at (1) ? dci.m_rv.at (0) : dci.m_rv.at (1)); + } + + if (rv == 3) + { + // maximum number of retx reached -> drop process + NS_LOG_DEBUG ("Max number of retransmissions reached -> drop process"); + std::map ::iterator it = m_dlHarqProcessesStatus.find (rnti); + if (it==m_dlHarqProcessesStatus.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << m_dlInfoListBuffered.at (i).m_rnti); + } + (*it).second.at (harqId) = 0; + std::map ::iterator itRlcPdu = m_dlHarqProcessesRlcPduListBuffer.find (rnti); + if (itRlcPdu==m_dlHarqProcessesRlcPduListBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RlcPdcList in HARQ buffer for RNTI " << m_dlInfoListBuffered.at (i).m_rnti); + } + for (uint16_t k = 0; k < (*itRlcPdu).second.size (); k++) + { + (*itRlcPdu).second.at (k).at (harqId).clear (); + } + continue; + } + // check the feasibility of retransmitting on the same RBGs + // translate the DCI to Spectrum framework + std::vector dciRbg; + uint32_t mask = 0x1; + NS_LOG_DEBUG ("Original RBGs " << dci.m_rbBitmap << " rnti " << dci.m_rnti); + for (int j = 0; j < 32; j++) + { + if (((dci.m_rbBitmap & mask) >> j) == 1) + { + dciRbg.push_back (j); + NS_LOG_DEBUG ("\t"< rbgMapCopy = rbgMap; + while ((j < dciRbg.size ())&&(startRbg!=rbgId)) + { + if (rbgMapCopy.at (rbgId) == false) + { + rbgMapCopy.at (rbgId) = true; + dciRbg.at (j) = rbgId; + j++; + } + rbgId++; + } + if (j == dciRbg.size ()) + { + // find new RBGs -> update DCI map + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < dciRbg.size (); k++) + { + rbgMask = rbgMask + (0x1 << dciRbg.at (k)); + // NS_LOG_DEBUG (this << " Allocated PRB " << (*itMap).second.at (k)); + rbgAllocatedNum++; + } + dci.m_rbBitmap = rbgMask; + rbgMap = rbgMapCopy; + NS_LOG_DEBUG (this << " Move retx in RBGs " << dciRbg.size ()); + } + else + { + // HARQ retx cannot be performed on this TTI -> store it + dlInfoListUntxed.push_back (params.m_dlInfoList.at (i)); + NS_LOG_DEBUG (this << " No resource for this retx -> buffer it"); + } + } + // retrieve RLC PDU list for retx TBsize and update DCI + BuildDataListElement_s newEl; + std::map ::iterator itRlcPdu = m_dlHarqProcessesRlcPduListBuffer.find (rnti); + if (itRlcPdu==m_dlHarqProcessesRlcPduListBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RlcPdcList in HARQ buffer for RNTI " << rnti); + } + for (uint8_t j = 0; j < nLayers; j++) + { + if (retx.at (j)) + { + if (j >= dci.m_ndi.size ()) + { + // for avoiding errors in MIMO transient phases + dci.m_ndi.push_back (0); + dci.m_rv.push_back (0); + dci.m_mcs.push_back (0); + dci.m_tbsSize.push_back (0); + NS_LOG_DEBUG (this << " layer " << (uint16_t)j << " no txed (MIMO transition)"); + + } + else + { + dci.m_ndi.at (j) = 0; + dci.m_rv.at (j) ++; + (*itHarq).second.at (harqId).m_rv.at (j)++; + NS_LOG_DEBUG (this << " layer " << (uint16_t)j << " RV " << (uint16_t)dci.m_rv.at (j)); + newEl.m_rlcPduList.push_back ((*itRlcPdu).second.at (j).at (dci.m_harqProcess)); + } +// NS_ASSERT_MSG ((*itRlcPdu).second.size () >dci.m_harqProcess, " size " << (*itRlcPdu).second.size ()); +// NS_ASSERT ((*itRlcPdu).second.at (j).size () > dci.m_harqProcess); +// newEl.m_rlcPduList.push_back ((*itRlcPdu).second.at (j).at (dci.m_harqProcess)); +// NS_LOG_DEBUG (this << " size 1 " << (*itRlcPdu).second.size () << " size 2 " << (*itRlcPdu).second.at (j).size ()); +// NS_ASSERT_MSG ((*itRlcPdu).second.at (j).at (dci.m_harqProcess).size () <= 1, " MIMO ERRROR 1"); +// for (uint16_t k = 0; k < (*itRlcPdu).second.at (j).at (dci.m_harqProcess).size (); k++) +// { +// NS_LOG_DEBUG (this << " k " << k); +// } + } + else + { + // empty TB of layer j + dci.m_ndi.at (j) = 0; + dci.m_rv.at (j) = 0; + dci.m_mcs.at (j) = 0; + dci.m_tbsSize.at (j) = 0; + NS_LOG_DEBUG (this << " layer " << (uint16_t)j << " no retx"); + } + } + newEl.m_rnti = rnti; + newEl.m_dci = dci; + ret.m_buildDataList.push_back (newEl); + rntiAllocated.insert (rnti); + } + else + { + // update HARQ process status + NS_LOG_DEBUG (this << " RR HARQ ACK UE " << m_dlInfoListBuffered.at (i).m_rnti); + std::map ::iterator it = m_dlHarqProcessesStatus.find (m_dlInfoListBuffered.at (i).m_rnti); + if (it==m_dlHarqProcessesStatus.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << m_dlInfoListBuffered.at (i).m_rnti); + } + (*it).second.at (m_dlInfoListBuffered.at (i).m_harqProcessId) = 0; + std::map ::iterator itRlcPdu = m_dlHarqProcessesRlcPduListBuffer.find (m_dlInfoListBuffered.at (i).m_rnti); + if (itRlcPdu==m_dlHarqProcessesRlcPduListBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RlcPdcList in HARQ buffer for RNTI " << m_dlInfoListBuffered.at (i).m_rnti); + } + for (uint16_t k = 0; k < (*itRlcPdu).second.size (); k++) + { + (*itRlcPdu).second.at (k).at (m_dlInfoListBuffered.at (i).m_harqProcessId).clear (); + } + } + } + m_dlInfoListBuffered.clear (); + m_dlInfoListBuffered = dlInfoListUntxed; // Get the actual active flows (queue!=0) std::list::iterator it; @@ -421,9 +738,11 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched { // NS_LOG_INFO (this << " User " << (*it).m_rnti << " LC " << (uint16_t)(*it).m_logicalChannelIdentity); // remove old entries of this UE-LC - if ( ((*it).m_rlcTransmissionQueueSize > 0) + std::set ::iterator itRnti = rntiAllocated.find ((*it).m_rnti); + if ( (((*it).m_rlcTransmissionQueueSize > 0) || ((*it).m_rlcRetransmissionQueueSize > 0) - || ((*it).m_rlcStatusPduSize > 0) ) + || ((*it).m_rlcStatusPduSize > 0)) + && (itRnti == rntiAllocated.end ()) ) // UE must not be allocated for HARQ retx { std::map ::iterator itCqi = m_p10CqiRxed.find ((*it).m_rnti); uint8_t cqi = 0; @@ -456,23 +775,24 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched if (nflows == 0) { - return; + if (ret.m_buildDataList.size ()>0) + { + m_schedSapUser->SchedDlConfigInd (ret); + } + return; } // Divide the resource equally among the active users according to // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) - int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); - int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; - int rbgPerTb = rbgNum / nTbs; + + int rbgPerTb = (rbgNum - rbgAllocatedNum) / nTbs; if (rbgPerTb == 0) { rbgPerTb = 1; // at least 1 rbg per TB (till available resource) } int rbgAllocated = 0; - FfMacSchedSapUser::SchedDlConfigIndParameters ret; - // round robin assignment to all UE-LC registered starting from the subsequent of the one - // served last scheduling trigger - //NS_LOG_DEBUG (this << " next to be served " << m_nextRntiDl << " nflows " << nflows); + // round robin assignment to all UEs registered starting from the subsequent of the one + // served last scheduling trigger event if (m_nextRntiDl != 0) { for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) @@ -497,9 +817,10 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched do { itLcRnti = lcActivesPerRnti.find ((*it).m_rnti); - if (itLcRnti == lcActivesPerRnti.end ()) + std::set ::iterator itRnti = rntiAllocated.find ((*it).m_rnti); + if ((itLcRnti == lcActivesPerRnti.end ())||(itRnti!=rntiAllocated.end ())) { - // skip this entry + // skip this entry (no active queue or yet allocated for HARQ) it++; if (it == m_rlcBufferReq.end ()) { @@ -514,6 +835,7 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).m_rnti); } int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + NS_LOG_DEBUG (this << " NLAYERS " << nLayer); int lcNum = (*itLcRnti).second; // create new BuildDataListElement_s for this RNTI BuildDataListElement_s newEl; @@ -521,6 +843,26 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched // create the DlDciListElement_s DlDciListElement_s newDci; newDci.m_rnti = (*it).m_rnti; + if (m_harqOn == true) + { + newDci.m_harqProcess = UpdateHarqProcessId ((*it).m_rnti); + if (newDci.m_harqProcess>8) + { + // not valid HARQ ID -> no more HARQ proc available, stop allocation for this user + it++; + if (it == m_rlcBufferReq.end ()) + { + // restart from the first + it = m_rlcBufferReq.begin (); + } + NS_LOG_DEBUG (this << " All HARQ process busy, drop UE allocation"); + continue; + } + } + else + { + newDci.m_harqProcess = 0; + } newDci.m_resAlloc = 0; newDci.m_rbBitmap = 0; std::map ::iterator itCqi = m_p10CqiRxed.find (newEl.m_rnti); @@ -535,15 +877,11 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched newDci.m_mcs.push_back ( m_amc->GetMcsFromCqi ((*itCqi).second) ); } } - // group the LCs of this RNTI - std::vector newRlcPduLe; -// int totRbg = lcNum * rbgPerFlow; -// totRbg = rbgNum / nTbs; int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (0), rbgPerTb * rbgSize) / 8); - NS_LOG_DEBUG (this << " DL - Allocate user " << newEl.m_rnti << " LCs " << (uint16_t)(*itLcRnti).second << " bytes " << tbSize << " PRBs " << rbgAllocated * rbgSize << "..." << (rbgAllocated* rbgSize) + (rbgPerTb * rbgSize) - 1 << " mcs " << (uint16_t) newDci.m_mcs.at (0) << " layers " << nLayer); uint16_t rlcPduSize = tbSize / lcNum; for (int i = 0; i < lcNum ; i++) { + std::vector newRlcPduLe; for (uint8_t j = 0; j < nLayer; j++) { RlcPduListElement_s newRlcEl; @@ -552,35 +890,61 @@ RrFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sched newRlcEl.m_size = rlcPduSize; UpdateDlRlcBufferInfo ((*it).m_rnti, newRlcEl.m_logicalChannelIdentity, rlcPduSize); newRlcPduLe.push_back (newRlcEl); + // store RLC PDU list for HARQ + std::map ::iterator itRlcPdu = m_dlHarqProcessesRlcPduListBuffer.find ((*it).m_rnti); + if (itRlcPdu==m_dlHarqProcessesRlcPduListBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RlcPdcList in HARQ buffer for RNTI " << (*it).m_rnti); + } +// NS_ASSERT_MSG ((*itRlcPdu).second.at (j).at (newDci.m_harqProcess).size () <= 1, " MIMO ERR 4" <<(*itRlcPdu).second.at (j).at (newDci.m_harqProcess).size () ); + (*itRlcPdu).second.at (j).at (newDci.m_harqProcess).push_back (newRlcEl); +// NS_ASSERT_MSG ((*itRlcPdu).second.at (j).at (newDci.m_harqProcess).size () <= 1, " MIMO ERR 3" <<(*itRlcPdu).second.at (j).at (newDci.m_harqProcess).size () ); + } - it++; - if (it == m_rlcBufferReq.end ()) - { - // restart from the first - it = m_rlcBufferReq.begin (); - } + newEl.m_rlcPduList.push_back (newRlcPduLe); + it++; + if (it == m_rlcBufferReq.end ()) + { + // restart from the first + it = m_rlcBufferReq.begin (); + } } uint32_t rbgMask = 0; - for (int i = 0; i < rbgPerTb; i++) + uint16_t i = 0; + NS_LOG_DEBUG (this << " DL - Allocate user " << newEl.m_rnti << " LCs " << (uint16_t)(*itLcRnti).second << " bytes " << tbSize << " mcs " << (uint16_t) newDci.m_mcs.at (0) << " harqId " << (uint16_t)newDci.m_harqProcess << " layers " << nLayer); + NS_LOG_DEBUG ("RBG:"); + while (i < rbgPerTb) { - rbgMask = rbgMask + (0x1 << rbgAllocated); - rbgAllocated++; + if (rbgMap.at (rbgAllocated)==false) + { + rbgMask = rbgMask + (0x1 << rbgAllocated); + NS_LOG_DEBUG ("\t " << rbgAllocated); + i++; + rbgMap.at (rbgAllocated) = true; + } + rbgAllocated++; } newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) for (int i = 0; i < nLayer; i++) { newDci.m_tbsSize.push_back (tbSize); - newDci.m_ndi.push_back (1); // TBD (new data indicator) - newDci.m_rv.push_back (0); // TBD (redundancy version) + newDci.m_ndi.push_back (1); + newDci.m_rv.push_back (0); } newEl.m_dci = newDci; + if (m_harqOn == true) + { + // store DCI for HARQ + std::map ::iterator itDci = m_dlHarqProcessesDciBuffer.find (newEl.m_rnti); + if (itDci==m_dlHarqProcessesDciBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RNTI entry in DCI HARQ buffer for RNTI " << (*it).m_rnti); + } + (*itDci).second.at (newDci.m_harqProcess) = newDci; + } // ...more parameters -> ignored in this version - - - - newEl.m_rlcPduList.push_back (newRlcPduLe); ret.m_buildDataList.push_back (newEl); if (rbgAllocated == rbgNum) { @@ -657,14 +1021,93 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched RefreshUlCqiMaps (); + + // Generate RBs map + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbMap; + uint16_t rbAllocatedNum = 0; + std::set rntiAllocated; + std::vector rbgAllocationMap; + rbgAllocationMap.resize (m_cschedCellConfig.m_ulBandwidth, 0); + + rbMap.resize (m_cschedCellConfig.m_ulBandwidth, false); + +// Process UL HARQ feedback +// update UL HARQ proc id + std::map ::iterator itProcId; + for (itProcId = m_ulHarqCurrentProcessId.begin (); itProcId!= m_ulHarqCurrentProcessId.end (); itProcId++) + { + (*itProcId).second = ((*itProcId).second + 1) % HARQ_PROC_NUM; + } + for (uint8_t i = 0; i < params.m_ulInfoList.size (); i++) + { + if (params.m_ulInfoList.at (i).m_receptionStatus==UlInfoListElement_s::NotOk) + { + // retx correspondent block: retrieve the UL-DCI + uint16_t rnti = params.m_ulInfoList.at (i).m_rnti; + uint8_t harqId = (uint8_t)((*itProcId).second - HARQ_PERIOD) % HARQ_PROC_NUM; + NS_LOG_DEBUG (this << " UL-HARQ retx RNTI " << rnti << " harqId " << (uint16_t)harqId); + std::map ::iterator itHarq = m_ulHarqProcessesDciBuffer.find (rnti); + if (itHarq==m_ulHarqProcessesDciBuffer.end ()) + { + NS_FATAL_ERROR ("No info find in UL-HARQ buffer for UE " << rnti); + } + itProcId = m_ulHarqCurrentProcessId.find (rnti); + if (itProcId==m_ulHarqCurrentProcessId.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << rnti); + } + UlDciListElement_s dci = (*itHarq).second.at (harqId); + std::map ::iterator itStat = m_ulHarqProcessesStatus.find (rnti); + if (itStat==m_ulHarqProcessesStatus.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << rnti); + } + if ((*itStat).second.at (harqId)>3) + { + NS_LOG_DEBUG ("Max number of retransmissions reached (UL)-> drop process"); + continue; + } + bool free = true; + for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++) + { + if (rbMap.at (j)==true) + { + free = false; + NS_LOG_DEBUG (this << " BUSY " << j); + } + } + if (free) + { + // retx on the same RBs + for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++) + { + rbMap.at (j) = true; + rbgAllocationMap.at (j) = dci.m_rnti; + NS_LOG_DEBUG ("\t" << j); + rbAllocatedNum++; + } + NS_LOG_DEBUG (this << " Send retx in the same RBGs " << (uint16_t)dci.m_rbStart << " to " << dci.m_rbStart + dci.m_rbLen); + } + else + { + NS_FATAL_ERROR ("Cannot allocare retx for UE " << rnti); + } + dci.m_ndi = 0; + ret.m_dciList.push_back (dci); + rntiAllocated.insert (dci.m_rnti); + + } + } std::map ::iterator it; int nflows = 0; for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) { - // remove old entries of this UE-LC - if ((*it).second > 0) + std::set ::iterator itRnti = rntiAllocated.find ((*it).first); + // select UEs with queues not empty and not yet allocated for HARQ + if (((*it).second > 0)&&(itRnti == rntiAllocated.end ())) { nflows++; } @@ -676,16 +1119,15 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched } - // Divide the resource equally among the active users starting from the subsequent one served last scheduling trigger - int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + // Divide the remaining resources equally among the active users starting from the subsequent one served last scheduling trigger + //uint16_t rbPerFlow = (m_cschedCellConfig.m_ulBandwidth - rbAllocatedNum) / nflows; + uint16_t rbPerFlow = (m_cschedCellConfig.m_ulBandwidth) / (nflows + rntiAllocated.size ()); if (rbPerFlow == 0) { rbPerFlow = 1; // at least 1 rbg per flow (till available resource) } - int rbAllocated = 0; - - FfMacSchedSapUser::SchedUlConfigIndParameters ret; - std::vector rbgAllocationMap; + uint16_t rbAllocated = 0; + if (m_nextRntiUl != 0) { for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) @@ -705,8 +1147,21 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched it = m_ceBsrRxed.begin (); m_nextRntiUl = (*it).first; } + NS_LOG_DEBUG (this << " rbPerFlow " << rbPerFlow); do { + std::set ::iterator itRnti = rntiAllocated.find ((*it).first); + if (itRnti!=rntiAllocated.end ()) + { + // UE already allocated for UL-HARQ -> skip it + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; + } if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) { // limit to physical resources last resource assignment @@ -715,8 +1170,47 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched UlDciListElement_s uldci; uldci.m_rnti = (*it).first; - uldci.m_rbStart = rbAllocated; uldci.m_rbLen = rbPerFlow; + bool allocated = false; + while ((!allocated)&&(rbAllocated0) + { + m_schedSapUser->SchedUlConfigInd (ret); + } + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + return; + } std::map >::iterator itCqi = m_ueCqi.find ((*it).first); int cqi = 0; if (itCqi == m_ueCqi.end ()) @@ -757,16 +1251,8 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched // NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } - - rbAllocated += rbPerFlow; - // store info on allocation for managing ul-cqi interpretation - for (int i = 0; i < rbPerFlow; i++) - { - rbgAllocationMap.push_back ((*it).first); - } - uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); // MCS 0 -> UL-AMC TBD - NS_LOG_DEBUG (this << " UL - UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); uldci.m_ndi = 1; uldci.m_cceIndex = 0; @@ -781,6 +1267,21 @@ RrFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sched uldci.m_freqHopping = 0; uldci.m_pdcchPowerOffset = 0; // not used ret.m_dciList.push_back (uldci); + // store DCI for HARQ_PERIOD + itProcId = m_ulHarqCurrentProcessId.find (uldci.m_rnti); + if (itProcId==m_ulHarqCurrentProcessId.end ()) + { + NS_FATAL_ERROR ("No info find in HARQ buffer for UE " << uldci.m_rnti); + } + uint8_t harqId = (*itProcId).second; + std::map ::iterator itDci = m_ulHarqProcessesDciBuffer.find (uldci.m_rnti); + if (itDci==m_ulHarqProcessesDciBuffer.end ()) + { + NS_FATAL_ERROR ("Unable to find RNTI entry in UL DCI HARQ buffer for RNTI " << uldci.m_rnti); + } + (*itDci).second.at (harqId) = uldci; + NS_LOG_DEBUG (this << " UL Allocation - UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " harqId " << (uint16_t)harqId); + it++; if (it == m_ceBsrRxed.end ()) { diff --git a/src/lte/model/rr-ff-mac-scheduler.h b/src/lte/model/rr-ff-mac-scheduler.h index 612ce528a..a1d5db2e5 100644 --- a/src/lte/model/rr-ff-mac-scheduler.h +++ b/src/lte/model/rr-ff-mac-scheduler.h @@ -30,10 +30,21 @@ #include #include +#define HARQ_PROC_NUM 8 namespace ns3 { +typedef std::vector < uint8_t > DlHarqProcessesStatus_t; +typedef std::vector < DlDciListElement_s > DlHarqProcessesDciBuffer_t; +typedef std::vector < std::vector > RlcPduList_t; // vector of the LCs and layers per UE +typedef std::vector < RlcPduList_t > DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE + +typedef std::vector < UlDciListElement_s > UlHarqProcessesDciBuffer_t; +typedef std::vector < uint8_t > UlHarqProcessesStatus_t; + + + /** * \ingroup ff-api @@ -129,6 +140,14 @@ private: void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + + /** + * \brief Update and return a new process Id for the RNTI specified + * + * \param rnti the RNTI of the UE to be updated + * \return the process id value + */ + uint8_t UpdateHarqProcessId (uint16_t rnti); Ptr m_amc; @@ -185,6 +204,27 @@ private: std::map m_uesTxMode; // txMode of the UEs + + // HARQ attributes + /** + * m_harqOn when false inhibit te HARQ mechanisms (by default active) + */ + bool m_harqOn; + std::map m_dlHarqCurrentProcessId; + //HARQ status + // 0: process Id available + // x>0: process Id equal to `x` trasmission count + std::map m_dlHarqProcessesStatus; + std::map m_dlHarqProcessesDciBuffer; + std::map m_dlHarqProcessesRlcPduListBuffer; + std::vector m_dlInfoListBuffered; // HARQ retx buffered + + std::map m_ulHarqCurrentProcessId; + //HARQ status + // 0: process Id available + // x>0: process Id equal to `x` trasmission count + std::map m_ulHarqProcessesStatus; + std::map m_ulHarqProcessesDciBuffer; }; } // namespace ns3 diff --git a/src/lte/test/lte-test-entities.cc b/src/lte/test/lte-test-entities.cc index 5a8209b6b..a6069e737 100644 --- a/src/lte/test/lte-test-entities.cc +++ b/src/lte/test/lte-test-entities.cc @@ -450,7 +450,7 @@ void LteTestMac::SendTxOpportunity (Time time, uint32_t bytes) { NS_LOG_FUNCTION (this << time << bytes); - Simulator::Schedule (time, &LteMacSapUser::NotifyTxOpportunity, m_macSapUser, bytes, 0); + Simulator::Schedule (time, &LteMacSapUser::NotifyTxOpportunity, m_macSapUser, bytes, 0, 0); if (m_txOpportunityMode == RANDOM_MODE) { if (m_txOppTime != Seconds (0)) @@ -572,17 +572,17 @@ LteTestMac::DoReportBufferStatus (LteMacSapProvider::ReportBufferStatusParameter if (params.statusPduSize) { Simulator::Schedule (Seconds (0.1), &LteMacSapUser::NotifyTxOpportunity, - m_macSapUser, params.statusPduSize + 2, 0); + m_macSapUser, params.statusPduSize + 2, 0, 0); } else if (params.txQueueSize) { Simulator::Schedule (Seconds (0.1), &LteMacSapUser::NotifyTxOpportunity, - m_macSapUser, params.txQueueSize + 2, 0); + m_macSapUser, params.txQueueSize + 2, 0, 0); } else if (params.retxQueueSize) { Simulator::Schedule (Seconds (0.1), &LteMacSapUser::NotifyTxOpportunity, - m_macSapUser, params.retxQueueSize + 2, 0); + m_macSapUser, params.retxQueueSize + 2, 0, 0); } } } diff --git a/src/lte/test/lte-test-mimo.cc b/src/lte/test/lte-test-mimo.cc index 5a713f292..80972f354 100644 --- a/src/lte/test/lte-test-mimo.cc +++ b/src/lte/test/lte-test-mimo.cc @@ -83,15 +83,15 @@ LenaTestMimoSuite::LenaTestMimoSuite () static LenaTestMimoSuite lenaTestMimoSuite; std::string -LenaMimoTestCase::BuildNameString (uint16_t dist) +LenaMimoTestCase::BuildNameString (uint16_t dist, std::string schedulerType) { std::ostringstream oss; - oss << " UE distance " << dist << " m"; + oss << " UE distance " << dist << " m" << " Scheduler " << schedulerType; return oss.str (); } LenaMimoTestCase::LenaMimoTestCase (uint16_t dist, std::vector estThrDl, std::string schedulerType) - : TestCase (BuildNameString (dist)), + : TestCase (BuildNameString (dist, schedulerType)), m_dist (dist), m_estThrDl (estThrDl), m_schedulerType (schedulerType) @@ -105,7 +105,7 @@ LenaMimoTestCase::~LenaMimoTestCase () void LenaMimoTestCase::DoRun (void) { -// Config::SetDefault ("ns3::LteSpectrumPhy::PemEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); @@ -115,6 +115,10 @@ LenaMimoTestCase::DoRun (void) Ptr lteHelper = CreateObject (); + Config::SetDefault ("ns3::RrFfMacScheduler::HarqEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::PfFfMacScheduler::HarqEnabled", BooleanValue (false)); + +// lteHelper->SetSchedulerAttribute ("HarqEnabled", BooleanValue (false)); lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::HybridBuildingsPropagationLossModel")); @@ -122,7 +126,7 @@ LenaMimoTestCase::DoRun (void) lteHelper->SetPathlossModelAttribute ("ShadowSigmaIndoor", DoubleValue (0.0)); lteHelper->SetPathlossModelAttribute ("ShadowSigmaExtWalls", DoubleValue (0.0)); -// lteHelper->EnableLogComponents (); + lteHelper->EnableLogComponents (); // Create Nodes: eNodeB and UE NodeContainer enbNodes; @@ -188,7 +192,6 @@ LenaMimoTestCase::DoRun (void) { NS_FATAL_ERROR ("No RR Scheduler available"); } - Simulator::Schedule (Seconds (0.2), &RrFfMacScheduler::TransmissionModeConfigurationUpdate, rrsched, rnti, 1); Simulator::Schedule (Seconds (0.3), &RrFfMacScheduler::TransmissionModeConfigurationUpdate, rrsched, rnti, 2); } diff --git a/src/lte/test/lte-test-mimo.h b/src/lte/test/lte-test-mimo.h index bdfebc7fd..91c43dee1 100644 --- a/src/lte/test/lte-test-mimo.h +++ b/src/lte/test/lte-test-mimo.h @@ -42,7 +42,7 @@ private: void GetRlcBufferSample (Ptr rlcStats, uint64_t imsi, uint8_t rnti); - static std::string BuildNameString (uint16_t dist); + static std::string BuildNameString (uint16_t dist, std::string schedulerType); uint16_t m_nUser; uint16_t m_nLc; uint16_t m_dist; diff --git a/src/lte/test/lte-test-pf-ff-mac-scheduler.cc b/src/lte/test/lte-test-pf-ff-mac-scheduler.cc index 04fbb71f4..f4354dfdd 100644 --- a/src/lte/test/lte-test-pf-ff-mac-scheduler.cc +++ b/src/lte/test/lte-test-pf-ff-mac-scheduler.cc @@ -213,7 +213,7 @@ LenaPfFfMacSchedulerTestCase1::DoRun (void) Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); LogComponentDisableAll (LOG_LEVEL_ALL); // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); - // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); // LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); // diff --git a/src/lte/test/lte-test-phy-error-model.cc b/src/lte/test/lte-test-phy-error-model.cc index f3e8e5e93..eac67478c 100644 --- a/src/lte/test/lte-test-phy-error-model.cc +++ b/src/lte/test/lte-test-phy-error-model.cc @@ -122,7 +122,8 @@ LenaDataPhyErrorModelTestCase::DoRun (void) Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (ber)); Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); - Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (true)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (true)); + Config::SetDefault ("ns3::RrFfMacScheduler::HarqEnabled", BooleanValue (false)); // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); @@ -163,7 +164,7 @@ LenaDataPhyErrorModelTestCase::DoRun (void) // LogComponentEnable ("LteMiErrorModel", LOG_LEVEL_ALL); // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); // - LogComponentDisableAll (LOG_LEVEL_ALL); +// LogComponentDisableAll (LOG_LEVEL_ALL); LogComponentEnable ("LenaTestPhyErrorModel", LOG_LEVEL_ALL); @@ -306,7 +307,8 @@ LenaDlCtrlPhyErrorModelTestCase::DoRun (void) Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (ber)); Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (true)); - Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::RrFfMacScheduler::HarqEnabled", BooleanValue (false)); // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); @@ -341,13 +343,13 @@ LenaDlCtrlPhyErrorModelTestCase::DoRun (void) // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); // LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); // LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); - // LogComponentEnable ("RrFfMacScheduler", LOG_LEVEL_ALL); +// LogComponentEnable ("RrFfMacScheduler", LOG_LEVEL_ALL); // LogComponentEnable ("LenaHelper", LOG_LEVEL_ALL); // LogComponentEnable ("BuildingsPropagationLossModel", LOG_LEVEL_ALL); // LogComponentEnable ("LteMiErrorModel", LOG_LEVEL_ALL); // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); // - LogComponentDisableAll (LOG_LEVEL_ALL); +// LogComponentDisableAll (LOG_LEVEL_ALL); LogComponentEnable ("LenaTestPhyErrorModel", LOG_LEVEL_ALL); diff --git a/src/lte/wscript b/src/lte/wscript index d3cb6577c..0ed7a0f92 100644 --- a/src/lte/wscript +++ b/src/lte/wscript @@ -74,6 +74,7 @@ def build(bld): 'model/epc-enb-s1-sap.cc', 'model/lte-as-sap.cc', 'model/epc-ue-nas.cc', + 'model/lte-harq-phy.cc', ] module_test = bld.create_ns3_module_test_library('lte') @@ -181,6 +182,7 @@ def build(bld): 'model/epc-enb-s1-sap.h', 'model/lte-as-sap.h', 'model/epc-ue-nas.h', + 'model/lte-harq-phy.h', ] if (bld.env['ENABLE_EXAMPLES']):