tentative RLC AM NACK implementation https://groups.google.com/d/msg/ns-3-users/CEfmMX3IRBw/tX1X1yY_tgoJ
--- a/src/lte/model/lte-rlc-am-header.cc Thu Oct 10 14:39:23 2013 +0200
+++ b/src/lte/model/lte-rlc-am-header.cc Fri Oct 11 11:35:18 2013 +0200
@@ -26,6 +26,7 @@
namespace ns3 {
+
NS_OBJECT_ENSURE_REGISTERED (LteRlcAmHeader);
LteRlcAmHeader::LteRlcAmHeader ()
@@ -214,6 +215,38 @@
m_ackSn = ackSn;
}
+void
+LteRlcAmHeader::PushNack (int nack)
+{
+ m_my_nackSNs.push_back (nack);
+
+ if (m_my_nackSNs.size () % 2 == 0)
+ {
+ m_headerLength++;
+ }
+ else
+ {
+ m_headerLength+=2;
+ }
+}
+
+
+int
+LteRlcAmHeader::PopNack (void)
+{
+ if ( m_my_nackSNs.empty () )
+ {
+ return -1;
+ }
+
+ int nack = m_my_nackSNs.front ();
+ m_my_nackSNs.pop_front ();
+
+ return nack;
+}
+
+
+
SequenceNumber10
LteRlcAmHeader::GetAckSn (void) const
{
@@ -242,6 +275,7 @@
{
std::list <uint8_t>::const_iterator it1 = m_extensionBits.begin ();
std::list <uint16_t>::const_iterator it2 = m_lengthIndicators.begin ();
+ std::list <int>::const_iterator it3 = m_my_nackSNs.begin ();
os << "Len=" << m_headerLength;
os << " D/C=" << (uint16_t)m_dataControlBit;
@@ -280,7 +314,14 @@
else // if ( m_dataControlBit == CONTROL_PDU )
{
os << " ACK_SN=" << m_ackSn;
- os << " NACK_SN=" << m_nackSn;
+
+ while ( it3 != m_my_nackSNs.end () )
+ {
+ os << " NACK_SN=" << (int)(*it3);
+ it3++;
+ }
+
+
}
}
@@ -295,6 +336,7 @@
std::list <uint8_t>::const_iterator it1 = m_extensionBits.begin ();
std::list <uint16_t>::const_iterator it2 = m_lengthIndicators.begin ();
+ std::list <int>::const_iterator it3 = m_my_nackSNs.begin ();
if ( m_dataControlBit == DATA_PDU )
{
@@ -347,7 +389,76 @@
i.WriteU8 ( ((CONTROL_PDU << 7) & 0x80) |
((m_controlPduType << 4) & 0x70) |
((m_ackSn.GetValue () >> 6) & 0x0F) );
- i.WriteU8 ( ((m_ackSn.GetValue () << 2) & 0xFC) );
+// i.WriteU8 ( ((m_ackSn.GetValue () << 2) & 0xFC) );
+
+ // Brett - Add code to serialize the NACKs
+ if ( it3 == m_my_nackSNs.end () )
+ {
+ // If there are no NACKs then this line adds the rest of the ACK
+ // along with 0x00, indicating an E1 value of 0 or no NACKs follow.
+ i.WriteU8 ( ((m_ackSn.GetValue () << 2) & 0xFC) );
+ }
+ else
+ {
+ int oddNack = *it3;
+ int evenNack = -1;
+ // Else write out a series of E1 = 1 and NACK values. Note since we
+ // are not supporting SO start/end the value of E2 will always be 0.
+
+
+ // First write out the ACK along with the very first NACK
+ // And the remaining NACK with 0x02 or 10 in binary to set
+ // E1 to 1, then Or in the first bit of the NACK
+ i.WriteU8 ( ((m_ackSn.GetValue () << 2) & 0xFC)
+ | (0x02)
+ | ((*it3 >> 9) & 0x01));
+
+ while ( it3 != m_my_nackSNs.end () )
+ {
+ // The variable oddNack has the current NACK value to write, also
+ // either the setup to enter this loop or the previous loop would
+ // have written the highest order bit to the previouse octet.
+ // Write the next set of bits (2 - 9) into the next octet
+ i.WriteU8( ((oddNack >> 1) & 0xFF) );
+
+ // Next check to see if there is going to be another NACK after
+ // this
+ it3++;
+ if ( it3 != m_my_nackSNs.end () )
+ {
+ // Yes there will be another NACK after this, so E1 will be 1
+ evenNack = *it3;
+ i.WriteU8( ((oddNack << 7) & 0x80)
+ | (0x40) // E1 = 1 E2 = 0, more NACKs
+ | ( (evenNack >> 5) & 0x1F) );
+
+ // The final octet of this loop will have the rest of the
+ // NACK and another E1, E2. Check to see if there will be
+ // one more NACK after this.
+ it3++;
+ if ( it3 != m_my_nackSNs.end () )
+ {
+ // Yes there is at least one more NACK. Finish writing
+ // this octet and the next iteration will do the rest.
+ oddNack = *it3;
+ i.WriteU8 ( ((evenNack << 3) & 0xF8)
+ | (0x04)
+ | ((oddNack >> 9) & 0x01));
+ }
+ else
+ {
+ // No, there are no more NACKs
+ i.WriteU8 ( ((evenNack << 3) & 0xF8) );
+ }
+ }
+ else
+ {
+ // No, this is the last NACK so E1 will be 0
+ i.WriteU8 ( ((oddNack << 7) & 0x80) );
+ }
+ }
+ }
+
}
}
@@ -431,7 +542,65 @@
m_controlPduType = (byte_1 & 0x70) >> 4;
m_ackSn = ((byte_1 & 0x0F) << 6 ) | ((byte_2 & 0xFC) >> 2);
- m_headerLength++;
+ int moreNacks = (byte_2 & 0x02) >> 1;
+ // Get the first NACK outside the loop as it is not preceded by an E2
+ // field but all following NACKs will.
+ if ( moreNacks == 1 )
+ {
+ byte_3 = i.ReadU8 ();
+ byte_4 = i.ReadU8 ();
+ m_headerLength = 4;
+
+ m_my_nackSNs.push_back (
+ ((byte_2 & 0x01) << 9)
+ | (byte_3 << 1)
+ | ((byte_4 & 0x80) >> 7)
+ );
+
+ // Loop until all NACKs are found
+ moreNacks = ((byte_4 & 0x40) >> 6);
+ uint8_t byte = byte_4;
+ uint8_t nextByte;
+ uint8_t finalByte;
+ while (moreNacks == 1)
+ {
+ // Ignore E2, read next NACK
+ nextByte = i.ReadU8 ();
+ m_my_nackSNs.push_back (
+ ((byte & 0x1F) << 5)
+ | ((nextByte & 0xF8) >> 3)
+ );
+
+ // Check for another NACK, after this any following NACKs will
+ // be aligned properly for the next iteration of this loop.
+ moreNacks = (nextByte & 0x04) >> 2;
+ byte = nextByte;
+ if (moreNacks == 1)
+ {
+ nextByte = i.ReadU8 ();
+ finalByte = i.ReadU8 ();
+
+ m_my_nackSNs.push_back (
+ ((byte & 0x01) << 9)
+ | (nextByte << 1)
+ | ((finalByte & 0x80) >> 7)
+ );
+
+ moreNacks = ((finalByte & 0x40) >> 6);
+ byte = finalByte;
+ m_headerLength+=3;
+ }
+ else
+ {
+ m_headerLength++;
+ }
+
+ }
+ }
+ else
+ {
+ m_headerLength++;
+ }
}
return GetSerializedSize ();
--- a/src/lte/model/lte-rlc-am-header.h Thu Oct 10 14:39:23 2013 +0200
+++ b/src/lte/model/lte-rlc-am-header.h Fri Oct 11 11:35:18 2013 +0200
@@ -136,6 +136,10 @@
virtual void Serialize (Buffer::Iterator start) const;
virtual uint32_t Deserialize (Buffer::Iterator start);
+ // Brett - Methods to handle NACKs
+ void PushNack (int nack);
+ int PopNack (void);
+
private:
uint16_t m_headerLength;
uint8_t m_dataControlBit;
@@ -158,6 +162,7 @@
// Status PDU fields
SequenceNumber10 m_ackSn;
SequenceNumber10 m_nackSn;
+ std::list <int> m_my_nackSNs;
std::list <uint8_t> m_extensionBits1; // Includes E1 after ACK_SN
std::list <uint8_t> m_extensionBits2;
--- a/src/lte/model/lte-rlc-am.cc Thu Oct 10 14:39:23 2013 +0200
+++ b/src/lte/model/lte-rlc-am.cc Fri Oct 11 11:35:18 2013 +0200
@@ -25,6 +25,7 @@
#include "ns3/lte-rlc-am.h"
#include "ns3/lte-rlc-sdu-status-tag.h"
#include "ns3/lte-rlc-tag.h"
+#include "ns3/random-variable.h"
NS_LOG_COMPONENT_DEFINE ("LteRlcAm");
@@ -32,6 +33,12 @@
NS_OBJECT_ENSURE_REGISTERED (LteRlcAm);
+// Brett
+// Used to keep track of when the RLC AM sliding window wraps back around. This
+// is required for knowning when a small ACK SN also ACKs a larger SN from the
+// before the window wrapped around.
+bool m_windowWrap = false;
+
LteRlcAm::LteRlcAm ()
{
NS_LOG_FUNCTION (this);
@@ -192,9 +199,45 @@
NS_LOG_LOGIC ("Sending STATUS PDU");
Ptr<Packet> packet = Create<Packet> ();
+ NS_LOG_LOGIC ( "Brett -- About to make AM header for STATUS PDU" );
LteRlcAmHeader rlcAmHeader;
rlcAmHeader.SetControlPdu (LteRlcAmHeader::STATUS_PDU);
- rlcAmHeader.SetAckSn (m_vrR);
+// rlcAmHeader.SetAckSn (m_vrR); // Brett, need to change this
+ rlcAmHeader.SetAckSn (m_vrH); // Brett, is this right?
+
+ //=======================================================================
+ // Brett
+ // Check the reception buffer and find which Sequence Numbers are missing
+ // based on what is being ACKed. All SNs missing from the reception
+ // buffer that come before the SN number that is being ACKed are going to
+ // be NACKed packets.
+
+ NS_LOG_LOGIC ("Brett - Receiver Side - Check for SNs to NACK");
+ NS_LOG_LOGIC ("Brett - Receiver Side - Check SNs from " << m_vrR.GetValue() << " to " << m_vrH.GetValue());
+ for ( int i = m_vrR.GetValue(); i < m_vrH.GetValue(); i++ ) {
+ // NS_LOG_LOGIC ("Brett - Receiver Side - Checking SN " << i );
+ // Need a different check but similar idea
+ /* struct PduBuffer
+ {
+ SequenceNumber10 m_seqNumber;
+ std::list < Ptr<Packet> > m_byteSegments;
+
+ bool m_pduComplete;
+ uint16_t m_totalSize;
+ uint16_t m_currSize;
+ };*/
+
+ int test = m_rxonBuffer[ i ].m_pduComplete;
+ //NS_LOG_LOGIC("Brett - Is PDU complete: " << m_rxonBuffer[ i ].m_pduComplete);
+ if ( test == 0 ) {
+// NS_LOG_LOGIC("Brett - NACK " << i );
+// rlcAmHeader.EnqueueNAckSn( SequenceNumber10( i ) );
+ rlcAmHeader.PushNack( SequenceNumber10( i ).GetValue() );
+ }
+ }
+ //=======================================================================
+
+
NS_LOG_LOGIC ("RLC header: " << rlcAmHeader);
packet->AddHeader (rlcAmHeader);
@@ -219,8 +262,7 @@
Ptr<Packet> packet = m_retxBuffer.at (m_vtA.GetValue ()).m_pdu->Copy ();
- if (( packet->GetSize () <= bytes )
- || m_txOpportunityForRetxAlwaysBigEnough)
+ if ( packet->GetSize () <= bytes )
{
LteRlcAmHeader rlcAmHeader;
packet->PeekHeader (rlcAmHeader);
@@ -257,7 +299,7 @@
NS_LOG_LOGIC ("Sending data from Transmission Buffer");
}
- else if ( m_txedBufferSize > 0 )
+ /* else if ( m_txedBufferSize > 0 )
{
NS_LOG_LOGIC ("Sending data from Transmitted Buffer");
@@ -267,9 +309,9 @@
uint16_t vta = m_vtA.GetValue ();
Ptr<Packet> packet = m_txedBuffer.at (vta)->Copy ();
- if (( packet->GetSize () <= bytes )
- || m_txOpportunityForRetxAlwaysBigEnough)
+ if ( packet->GetSize () <= bytes )
{
+ // Brett - Move to retransmission buffer
NS_LOG_INFO ("Move SN = " << vta << " to retxBuffer");
m_retxBuffer.at (vta).m_pdu = m_txedBuffer.at (vta)->Copy ();
m_retxBuffer.at (vta).m_retxCount = 1;
@@ -300,7 +342,7 @@
return;
}
}
- else
+*/ else
{
NS_LOG_LOGIC ("No data pending");
return;
@@ -430,7 +472,7 @@
// nextSegmentSize MUST be zero (only if segment is smaller or equal to 2047)
- // (NO more segments) → exit
+ // (NO more segments) ? exit
// break;
}
else if ( (nextSegmentSize - firstSegment->GetSize () <= 2) || (m_txonBuffer.size () == 0) )
@@ -461,7 +503,7 @@
// nextSegmentSize <= 2 (only if txBuffer is not empty)
- // (NO more segments) → exit
+ // (NO more segments) ? exit
// break;
}
else // (firstSegment->GetSize () < m_nextSegmentSize) && (m_txBuffer.size () > 0)
@@ -624,6 +666,7 @@
NS_LOG_FUNCTION (this);
}
+
void
LteRlcAm::DoReceivePdu (Ptr<Packet> p)
{
@@ -973,73 +1016,130 @@
NS_LOG_INFO ("VT(A) = " << m_vtA);
NS_LOG_INFO ("VT(S) = " << m_vtS);
- m_vtA.SetModulusBase (m_vtA);
- m_vtS.SetModulusBase (m_vtA);
- ackSn.SetModulusBase (m_vtA);
- while (m_vtA < ackSn && m_vtA < m_vtS)
+
+
+ // Check for the first NACK
+ uint16_t nackSN = rlcAmHeader.PopNack ();
+ // Flag to indicate if any NACKs exist in the header. This is used to
+ // determine when VT(A) should stop incrementing as it is the lower
+ // bound of the transmission window and should equal the highest ACKed
+ // SN in sequence (no NACKs previous).
+ bool foundNack = false;
+ // Used to iterate over the transmission window to apply the ACKs and
+ // NACKs from the control PDU.
+ uint16_t loopCount = m_vtA.GetValue ();
+
+ // Brett - Check to see if the transmission window needs to wrap around.
+ // This window needs to wrap if the current VT(A) (last ACKed PDU) plus
+ // the window size is larger than 1023 and the SN just received is for
+ // a PDU that somwhere in the range of 0 to the top of the window. If
+ // this is the case then set the window wrap flag.
+ if ((m_vtA.GetValue() + m_windowSize > 1023)
+ && 0 <= ackSn.GetValue() && ackSn.GetValue() < m_vtA.GetValue() + m_windowSize - 1023)
{
+ m_windowWrap = true;
+ }
+
+ // Brett - There is a problem with the transmitting window wrapping back
+ // to the beginning. An ACK is for the next sequence number expected, but
+ // the largest sequence number that can be sent is 1023. We cannot send
+ // 1024 or anything higher than this to ACK an SN of 1023. To resolve I
+ // am adding the OR condition to this while loop. In the situation that
+ // the check above finds that it is time for the window to wrap then the
+ // window wrap flag allows this loop to execute. The loop will ACK all
+ // SNs from VT(A) to SN 1023 before resetting the window wrap flag to
+ // false and resetting the last ACKed SN to 0.
+ NS_LOG_INFO("Brett - Check - Look at transmit window from " << loopCount << " to " << m_vtS.GetValue ());
+ while (( loopCount < ackSn.GetValue() && loopCount < m_vtS.GetValue ())
+ || m_windowWrap)
+ {
+ NS_LOG_INFO("Brett - Current NACK is " << nackSN);
// NS_LOG_INFO ("seqNumber = " << seqNumber);
// NS_LOG_INFO ("m_txedBuffer( VT(A) ).size = " << m_txedBuffer.size ());
- uint16_t seqNumberValue = m_vtA.GetValue ();
- if (m_pollRetransmitTimer.IsRunning ()
- && (seqNumberValue == m_pollSn.GetValue ()))
- {
- m_pollRetransmitTimer.Cancel ();
- }
+ //uint16_t seqNumberValue = m_vtA.GetValue ();
+ uint16_t seqNumberValue = loopCount;
+ if ( nackSN != seqNumberValue )
+ {
- if (m_txedBuffer.at (seqNumberValue))
- {
- NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from txedBuffer");
-// NS_LOG_INFO ("m_txedBuffer( " << m_vtA << " )->GetSize = " << m_txedBuffer.at (m_vtA.GetValue ())->GetSize ());
- m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
- m_txedBuffer.at (seqNumberValue) = 0;
- }
+ if (m_txedBuffer.at (seqNumberValue))
+ {
+ NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from txedBuffer");
+// NS_LOG_INFO ("m_txedBuffer( " << m_vtA << " )->GetSize = " << m_txedBuffer.at (m_vtA.GetValue ())->GetSize ());
+ m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
+ m_txedBuffer.at (seqNumberValue) = 0;
+ }
- if (m_retxBuffer.at (seqNumberValue).m_pdu)
- {
- NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from retxBuffer");
- m_retxBufferSize -= m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
- m_retxBuffer.at (seqNumberValue).m_pdu = 0;
- m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
- }
+ if (m_retxBuffer.at (seqNumberValue).m_pdu)
+ {
+ NS_LOG_INFO ("ACKed SN = " << seqNumberValue << " from retxBuffer");
+ m_retxBufferSize -= m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
+ m_retxBuffer.at (seqNumberValue).m_pdu = 0;
+ m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
+ }
m_vtA++;
m_vtA.SetModulusBase (m_vtA);
m_vtS.SetModulusBase (m_vtA);
ackSn.SetModulusBase (m_vtA);
+ }
+ else
+ {
+ foundNack = true;
+ if (m_txedBuffer.at (nackSN))
+ {
+ NS_LOG_INFO ("NACKed SN = " << seqNumberValue << " from txedBuffer");
+ // Brett - Move to Retransmission Buffer
+ NS_LOG_INFO ("Move SN = " << nackSN << " to retxBuffer");
+ m_retxBuffer.at (nackSN).m_pdu = m_txedBuffer.at (nackSN)->Copy ();
+ m_retxBuffer.at (nackSN).m_retxCount = 0;
+ m_retxBufferSize += m_retxBuffer.at (nackSN).m_pdu->GetSize ();
+
+ m_txedBufferSize -= m_txedBuffer.at (nackSN)->GetSize ();
+ m_txedBuffer.at (nackSN) = 0;
+ }
+ else if (m_retxBuffer.at (nackSN).m_pdu)
+ {
+ NS_LOG_INFO ("NACKed SN = " << seqNumberValue << " from retxBuffer");
+ m_retxBuffer.at (nackSN).m_retxCount++;
+ NS_LOG_INFO ("Incr RETX_COUNT for SN = " << nackSN);
+ if (m_retxBuffer.at (nackSN).m_retxCount >= m_maxRetxThreshold)
+ {
+ NS_LOG_INFO ("Max RETX_COUNT for SN = " << nackSN);
+ }
+ }
+
+ }
+ // Check to see if it is time to find the next NACK
+ if ( seqNumberValue == nackSN )
+ {
+ nackSN = rlcAmHeader.PopNack ();
+ }
+
+
+ // If m_vtA has an SN of 1023 and the window wrap flag is true then
+ // it is time to reset m_vtA to 0 an reset the window wrap flag.
+ if ( m_vtA.GetValue() == 1023 && m_windowWrap )
+ {
+ loopCount = 0;
+ m_vtA = 0;
+ m_windowWrap = false;
+
+ // Wrap around with the transmission window.
+ //nackSN = rlcAmHeader.PopNack ();
+ //NS_LOG_INFO( "Brett - Tx window wrap, new NACK is " << nackSN );
+ }
+ else
+ {
+ loopCount++;
+ if (!foundNack)
+ {
+ m_vtA++;
+ }
+ }
}
NS_LOG_INFO ("New VT(A) = " << m_vtA);
-
- SequenceNumber10 seqNumber = m_vtA;
- uint16_t seqNumberValue;
- while (seqNumber < m_vtS)
- {
- seqNumberValue = seqNumber.GetValue ();
- if (m_txedBuffer.at (seqNumberValue))
- {
- NS_LOG_INFO ("Move SN = " << seqNumberValue << " to retxBuffer");
- m_retxBuffer.at (seqNumberValue).m_pdu = m_txedBuffer.at (seqNumberValue)->Copy ();
- m_retxBuffer.at (seqNumberValue).m_retxCount = 0;
- m_retxBufferSize += m_retxBuffer.at (seqNumberValue).m_pdu->GetSize ();
-
- m_txedBufferSize -= m_txedBuffer.at (seqNumberValue)->GetSize ();
- m_txedBuffer.at (seqNumberValue) = 0;
- }
- else if (m_retxBuffer.at (seqNumberValue).m_pdu)
- {
- m_retxBuffer.at (seqNumberValue).m_retxCount++;
- NS_LOG_INFO ("Incr RETX_COUNT for SN = " << seqNumberValue);
- if (m_retxBuffer.at (seqNumberValue).m_retxCount >= m_maxRetxThreshold)
- {
- NS_LOG_INFO ("Max RETX_COUNT for SN = " << seqNumberValue);
- }
- }
-
- seqNumber++;
- }
-
return;
}
else