# 11.ns3 wifi 模块流程源码解析
在本节中,将从源码的角度解析 ns3
的 wifi
模块,包括一个 packet
是如何从一台主机的 WifiNetDevice
到另一台主机的 WifiNetDevice
。
PS:
本节很长,而且很枯燥,但是对扩展
ns3
功能,了解ns3
流程非常有帮助,请耐心观看。本节的源码来自于
ns3.32
, 不同版本之间有一些差异,但大致流程差不多,可以互相参考。
# 发送过程源码分析
首先从发送 packet
开始
src/wifi/model/wifi-net-device.cc
,
WifiNetDevice::send
函数
bool | |
WifiNetDevice::Send (Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber) | |
{ | |
NS_LOG_FUNCTION (this << packet << dest << protocolNumber); | |
NS_ASSERT (Mac48Address::IsMatchingType (dest)); | |
Mac48Address realTo = Mac48Address::ConvertFrom (dest); | |
LlcSnapHeader llc; | |
llc.SetType (protocolNumber); | |
packet->AddHeader (llc); | |
m_mac->NotifyTx (packet); | |
m_mac->Enqueue (packet, realTo); | |
return true; | |
} |
m_mac
参数的类型是 Ptr<WifiMac>
, NotifyTx
方法是 Trace
追踪发送的数据包, Enqueue
方法将 packet
加入队列。
接下来就是 WifiMac::Enqueue
virtual void Enqueue (Ptr<Packet> packet, Mac48Address to, Mac48Address from) = 0; |
这是 WiFiMac::Enqueue
方法的声明,是一个纯虚函数,子类必须实现这个方法。
WiFiMac
的子类有:
接下来我们分别看一下这些子类的 Enqueue
分别都做了什么。
RegularWifiMac::Enqueue
期望子类重写该方法,无实质的内容。
void | |
RegularWifiMac::Enqueue (Ptr<Packet> packet, | |
Mac48Address to, Mac48Address from) | |
{ | |
//We expect RegularWifiMac subclasses which do support forwarding (e.g., | |
//AP) to override this method. Therefore, we throw a fatal error if | |
//someone tries to invoke this method on a class which has not done | |
//this. | |
NS_FATAL_ERROR ("This MAC entity (" << this << ", " << GetAddress () | |
<< ") does not support Enqueue() with from address"); | |
} |
AdhocWifiMac::Enqueue
void | |
AdhocWifiMac::Enqueue (Ptr<Packet> packet, Mac48Address to) | |
{ | |
... | |
if (GetQosSupported ()) | |
{ | |
//Sanity check that the TID is valid | |
NS_ASSERT (tid < 8); | |
m_edca[QosUtilsMapTidToAc (tid)]->Queue (packet, hdr); | |
} | |
else | |
{ | |
m_txop->Queue (packet, hdr); | |
} | |
} |
本段最核心的代码已经摘出
如果网络支持 Qos
,就会进入类 QosTxop
的 Queue
函数;
如果不支持 Qos
,就会进入 Txop
类的 Queue
函数。
而 QosTxop
继承自 Txop
,并且没有重写 Queue
方法
/** | |
* \brief Handle packet fragmentation and retransmissions for QoS data frames as well | |
* as MSDU aggregation (A-MSDU) and block ack sessions, for a given access class. | |
* \ingroup wifi | |
* | |
* This class implements the packet fragmentation and retransmission policy for | |
* QoS data frames. It uses the ns3::MacLow and ns3::ChannelAccessManager helper classes | |
* to respectively send packets and decide when to send them. Packets are stored | |
* in a ns3::WifiMacQueue until they can be sent. | |
* | |
* This queue contains packets for a particular access class. | |
* Possibles access classes are: | |
* - AC_VO : voice, TID = 6,7 | |
* - AC_VI : video, TID = 4,5 | |
* - AC_BE : best-effort, TID = 0,3 | |
* - AC_BK : background, TID = 1,2 | |
* | |
* This class also implements block ack sessions and MSDU aggregation (A-MSDU). | |
* If A-MSDU is enabled for that access class, it picks several packets from the | |
* queue instead of a single one and sends the aggregated packet to ns3::MacLow. | |
* | |
* The fragmentation policy currently implemented uses a simple | |
* threshold: any packet bigger than this threshold is fragmented | |
* in fragments whose size is smaller than the threshold. | |
* | |
* The retransmission policy is also very simple: every packet is | |
* retransmitted until it is either successfully transmitted or | |
* it has been retransmitted up until the SSRC or SLRC thresholds. | |
* | |
* The RTS/CTS policy is similar to the fragmentation policy: when | |
* a packet is bigger than a threshold, the RTS/CTS protocol is used. | |
*/ | |
class QosTxop : public Txop | |
{ | |
... | |
} |
那么接下来就看 Txop::Queue
函数
void | |
Txop::Queue (Ptr<Packet> packet, const WifiMacHeader &hdr) | |
{ | |
NS_LOG_FUNCTION (this << packet << &hdr); | |
// remove the priority tag attached, if any | |
SocketPriorityTag priorityTag; | |
packet->RemovePacketTag (priorityTag); | |
if (m_channelAccessManager->NeedBackoffUponAccess (this)) | |
{ | |
GenerateBackoff (); | |
} | |
m_queue->Enqueue (Create<WifiMacQueueItem> (packet, hdr)); | |
StartAccessIfNeeded (); | |
} |
从该方法中可以看出, packet
加入队列, m_queue
的类型为 Ptr<WifiMacQueue>
,功能就是讲 packet
加入队列。 StartAccessIfNeeded ()
; 这一行代码就会判断当前信道空闲与否,随机回退值等。
接下来就是 Txop::StartAccessIfNeeded
void | |
Txop::StartAccessIfNeeded (void) | |
{ | |
NS_LOG_FUNCTION (this); | |
if (m_currentPacket == 0 | |
&& !m_queue->IsEmpty () | |
&& !IsAccessRequested () | |
&& !m_low->IsCfPeriod ()) | |
{ | |
m_channelAccessManager->RequestAccess (this); | |
} | |
} |
m_currentPacket
代表当前正在发送的 packet
。m_queue
就是 packet
队列。
这里主要就是做了判断,然后请求获取信道权限
接下来就是 ChannelAccessManager::RequestAccess
void | |
ChannelAccessManager::RequestAccess (Ptr<Txop> txop, bool isCfPeriod) | |
{ | |
NS_LOG_FUNCTION (this << txop); | |
if (m_phy) | |
{ | |
m_phy->NotifyChannelAccessRequested (); | |
} | |
//Deny access if in sleep mode or off | |
if (m_sleeping || m_off) | |
{ | |
return; | |
} | |
if (isCfPeriod) | |
{ | |
txop->NotifyAccessRequested (); | |
Time delay = (MostRecent ({GetAccessGrantStart (true), Simulator::Now ()}) - Simulator::Now ()); | |
m_accessTimeout = Simulator::Schedule (delay, &ChannelAccessManager::DoGrantPcfAccess, this, txop); | |
return; | |
} | |
/* | |
* EDCAF operations shall be performed at slot boundaries (Sec. 10.22.2.4 of 802.11-2016) | |
*/ | |
Time accessGrantStart = GetAccessGrantStart () + (txop->GetAifsn () * GetSlot ()); | |
if (txop->IsQosTxop () && txop->GetBackoffStart () > accessGrantStart) | |
{ | |
// The backoff start time reported by the EDCAF is more recent than the last | |
// time the medium was busy plus an AIFS, hence we need to align it to the | |
// next slot boundary. | |
Time diff = txop->GetBackoffStart () - accessGrantStart; | |
uint32_t nIntSlots = (diff / GetSlot ()).GetHigh () + 1; | |
txop->UpdateBackoffSlotsNow (0, accessGrantStart + (nIntSlots * GetSlot ())); | |
} | |
UpdateBackoff (); | |
NS_ASSERT (!txop->IsAccessRequested ()); | |
txop->NotifyAccessRequested (); | |
DoGrantDcfAccess (); | |
DoRestartAccessTimeoutIfNeeded (); | |
} |
这块的重点是 ChannelAccessManager::DoGrantDcfAccess
void | |
ChannelAccessManager::DoGrantDcfAccess (void) | |
{ | |
NS_LOG_FUNCTION (this); | |
uint32_t k = 0; | |
for (Txops::iterator i = m_txops.begin (); i != m_txops.end (); k++) | |
{ | |
Ptr<Txop> txop = *i; | |
if (txop->IsAccessRequested () | |
&& GetBackoffEndFor (txop) <= Simulator::Now () ) | |
{ | |
/** | |
* This is the first Txop we find with an expired backoff and which | |
* needs access to the medium. i.e., it has data to send. | |
*/ | |
NS_LOG_DEBUG ("dcf " << k << " needs access. backoff expired. access granted. slots=" << txop->GetBackoffSlots ()); | |
i++; //go to the next item in the list. | |
k++; | |
std::vector<Ptr<Txop> > internalCollisionTxops; | |
for (Txops::iterator j = i; j != m_txops.end (); j++, k++) | |
{ | |
Ptr<Txop> otherTxop = *j; | |
if (otherTxop->IsAccessRequested () | |
&& GetBackoffEndFor (otherTxop) <= Simulator::Now ()) | |
{ | |
NS_LOG_DEBUG ("dcf " << k << " needs access. backoff expired. internal collision. slots=" << | |
otherTxop->GetBackoffSlots ()); | |
/** | |
* all other Txops with a lower priority whose backoff | |
* has expired and which needed access to the medium | |
* must be notified that we did get an internal collision. | |
*/ | |
internalCollisionTxops.push_back (otherTxop); | |
} | |
} | |
/** | |
* Now, we notify all of these changes in one go. It is necessary to | |
* perform first the calculations of which Txops are colliding and then | |
* only apply the changes because applying the changes through notification | |
* could change the global state of the manager, and, thus, could change | |
* the result of the calculations. | |
*/ | |
txop->NotifyAccessGranted (); | |
for (auto collidingTxop : internalCollisionTxops) | |
{ | |
collidingTxop->NotifyInternalCollision (); | |
} | |
break; | |
} | |
i++; | |
} | |
} |
在该函数中,请求完权限后又跳回到 Txop:NotifyAccessGranted
;
void | |
Txop::NotifyAccessGranted (void) | |
{ | |
NS_LOG_FUNCTION (this); | |
NS_ASSERT (m_accessRequested); | |
m_accessRequested = false; | |
if (m_currentPacket == 0) | |
{ | |
if (m_queue->IsEmpty ()) | |
{ | |
NS_LOG_DEBUG ("queue empty"); | |
return; | |
} | |
Ptr<WifiMacQueueItem> item = m_queue->Dequeue (); | |
NS_ASSERT (item != 0); | |
m_currentPacket = item->GetPacket (); | |
m_currentHdr = item->GetHeader (); | |
NS_ASSERT (m_currentPacket != 0); | |
uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor (&m_currentHdr); | |
m_currentHdr.SetSequenceNumber (sequence); | |
m_stationManager->UpdateFragmentationThreshold (); | |
m_currentHdr.SetFragmentNumber (0); | |
m_currentHdr.SetNoMoreFragments (); | |
m_currentHdr.SetNoRetry (); | |
m_fragmentNumber = 0; | |
NS_LOG_DEBUG ("dequeued size=" << m_currentPacket->GetSize () << | |
", to=" << m_currentHdr.GetAddr1 () << | |
", seq=" << m_currentHdr.GetSequenceControl ()); | |
} | |
if (m_currentHdr.GetAddr1 ().IsGroup ()) | |
{ | |
m_currentParams.DisableRts (); | |
m_currentParams.DisableAck (); | |
m_currentParams.DisableNextData (); | |
NS_LOG_DEBUG ("tx broadcast"); | |
GetLow ()->StartTransmission (Create<WifiMacQueueItem> (m_currentPacket, m_currentHdr), | |
m_currentParams, this); | |
} | |
else | |
{ | |
m_currentParams.EnableAck (); | |
if (NeedFragmentation ()) | |
{ | |
m_currentParams.DisableRts (); | |
WifiMacHeader hdr; | |
Ptr<Packet> fragment = GetFragmentPacket (&hdr); | |
if (IsLastFragment ()) | |
{ | |
NS_LOG_DEBUG ("fragmenting last fragment size=" << fragment->GetSize ()); | |
m_currentParams.DisableNextData (); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("fragmenting size=" << fragment->GetSize ()); | |
m_currentParams.EnableNextData (GetNextFragmentSize ()); | |
} | |
GetLow ()->StartTransmission (Create<WifiMacQueueItem> (fragment, hdr), | |
m_currentParams, this); | |
} | |
else | |
{ | |
uint32_t size = m_currentHdr.GetSize () + m_currentPacket->GetSize () + WIFI_MAC_FCS_LENGTH; | |
if (m_stationManager->NeedRts (m_currentHdr, size) && !m_low->IsCfPeriod ()) | |
{ | |
m_currentParams.EnableRts (); | |
} | |
else | |
{ | |
m_currentParams.DisableRts (); | |
} | |
m_currentParams.DisableNextData (); | |
GetLow ()->StartTransmission (Create<WifiMacQueueItem> (m_currentPacket, m_currentHdr), | |
m_currentParams, this); | |
} | |
} | |
} |
上面的代码会调用 Low ()->StartTransmission
方法。 Low ()
返回的是 MacLow
类对象。
所以下一步该调用的是 MacLow::StartTransmission
方法
void | |
MacLow::StartTransmission (Ptr<WifiMacQueueItem> mpdu, | |
MacLowTransmissionParameters params, | |
Ptr<Txop> txop) | |
{ | |
NS_LOG_FUNCTION (this << *mpdu << params << txop); | |
NS_ASSERT (!m_cfAckInfo.expectCfAck); | |
if (m_phy->IsStateOff ()) | |
{ | |
NS_LOG_DEBUG ("Cannot start TX because device is OFF"); | |
return; | |
} | |
/* m_currentPacket is not NULL because someone started | |
* a transmission and was interrupted before one of: | |
* - ctsTimeout | |
* - sendDataAfterCTS | |
* expired. This means that one of these timers is still | |
* running. They are all cancelled below anyway by the | |
* call to CancelAllEvents (because of at least one | |
* of these two timers) which will trigger a call to the | |
* previous listener's cancel method. | |
* | |
* This typically happens because the high-priority | |
* QapScheduler has taken access to the channel from | |
* one of the EDCA of the QAP. | |
*/ | |
m_currentPacket = Create<WifiPsdu> (mpdu, false); | |
const WifiMacHeader& hdr = mpdu->GetHeader (); | |
CancelAllEvents (); | |
m_currentTxop = txop; | |
m_txParams = params; | |
if (hdr.IsCtl ()) | |
{ | |
m_currentTxVector = GetRtsTxVector (mpdu); | |
} | |
else | |
{ | |
m_currentTxVector = GetDataTxVector (mpdu); | |
} | |
/* The packet received by this function can be any of the following: | |
* (a) a management frame dequeued from the Txop | |
* (b) a non-QoS data frame dequeued from the Txop | |
* (c) a non-group addressed QoS Data frame peeked or dequeued from a QosTxop | |
* (d) a group addressed QoS data or DELBA Request frame dequeued from a QosTxop | |
* (e) a BlockAckReq or ADDBA Request frame | |
* (f) a fragment of non-QoS/QoS Data frame dequeued from the Txop/QosTxop | |
*/ | |
if (hdr.IsQosData () && !hdr.GetAddr1 ().IsGroup () | |
&& !hdr.IsMoreFragments () && hdr.GetFragmentNumber () == 0) | |
{ | |
// We get here if the received packet is a non-broadcast QoS data frame | |
uint8_t tid = hdr.GetQosTid (); | |
Ptr<QosTxop> qosTxop = m_edca.find (QosUtilsMapTidToAc (tid))->second; | |
// if a TXOP limit exists, compute the remaining TXOP duration | |
Time txopLimit = Time::Min (); | |
if (m_currentTxop->GetTxopLimit ().IsStrictlyPositive ()) | |
{ | |
txopLimit = m_currentTxop->GetTxopRemaining () - CalculateOverheadTxTime (mpdu, m_txParams); | |
NS_ASSERT (txopLimit.IsPositive ()); | |
} | |
// QosTxop may send us a peeked frame | |
Ptr<const WifiMacQueueItem> tmp = qosTxop->PeekNextFrame (); | |
bool isPeeked = (tmp != 0 && tmp->GetPacket () == mpdu->GetPacket ()); | |
Ptr<WifiMacQueueItem> newMpdu; | |
// If the frame has been peeked, dequeue it if it meets the size and duration constraints | |
if (isPeeked) | |
{ | |
newMpdu = qosTxop->DequeuePeekedFrame (mpdu, m_currentTxVector, true, 0, txopLimit); | |
} | |
else if (IsWithinSizeAndTimeLimits (mpdu, m_currentTxVector, 0, txopLimit)) | |
{ | |
newMpdu = mpdu; | |
} | |
if (newMpdu == 0) | |
{ | |
// if the frame has been dequeued, then there is no BA agreement with the | |
// receiver (otherwise the frame would have been peeked). Hence, the frame | |
// has been sent under Normal Ack policy, not acknowledged and now retransmitted. | |
// If we cannot send it now, let the QosTxop retransmit it again. | |
// If the frame has been just peeked, reset the current packet at QosTxop. | |
if (isPeeked) | |
{ | |
qosTxop->UpdateCurrentPacket (Create<WifiMacQueueItem> (nullptr, WifiMacHeader ())); | |
} | |
return; | |
} | |
// Update the current packet at QosTxop, given that A-MSDU aggregation may have | |
// been performed on the peeked frame | |
qosTxop->UpdateCurrentPacket (newMpdu); | |
//Perform MPDU aggregation if possible | |
std::vector<Ptr<WifiMacQueueItem>> mpduList; | |
if (m_mpduAggregator != 0) | |
{ | |
mpduList = m_mpduAggregator->GetNextAmpdu (newMpdu, m_currentTxVector, txopLimit); | |
} | |
if (mpduList.size () > 1) | |
{ | |
m_currentPacket = Create<WifiPsdu> (mpduList); | |
NS_LOG_DEBUG ("tx unicast A-MPDU containing " << mpduList.size () << " MPDUs"); | |
qosTxop->SetAmpduExist (hdr.GetAddr1 (), true); | |
} | |
else if (m_currentTxVector.GetMode ().GetModulationClass () == WIFI_MOD_CLASS_VHT | |
|| m_currentTxVector.GetMode ().GetModulationClass () == WIFI_MOD_CLASS_HE) | |
{ | |
// VHT/HE single MPDU | |
m_currentPacket = Create<WifiPsdu> (newMpdu, true); | |
NS_LOG_DEBUG ("tx unicast S-MPDU with sequence number " << hdr.GetSequenceNumber ()); | |
qosTxop->SetAmpduExist (hdr.GetAddr1 (), true); | |
} | |
else // HT | |
{ | |
m_currentPacket = Create<WifiPsdu> (newMpdu, false); | |
} | |
// A QoS Txop must have an installed ack policy selector | |
NS_ASSERT (qosTxop->GetAckPolicySelector () != 0); | |
qosTxop->GetAckPolicySelector ()->UpdateTxParams (m_currentPacket, m_txParams); | |
qosTxop->GetAckPolicySelector ()->SetAckPolicy (m_currentPacket, m_txParams); | |
} | |
NS_LOG_DEBUG ("startTx size=" << m_currentPacket->GetSize () << | |
", to=" << m_currentPacket->GetAddr1 () << ", txop=" << m_currentTxop); | |
if (m_txParams.MustSendRts ()) | |
{ | |
SendRtsForPacket (); | |
} | |
else | |
{ | |
if ((m_ctsToSelfSupported || m_stationManager->GetUseNonErpProtection ()) && NeedCtsToSelf ()) | |
{ | |
SendCtsToSelf (); | |
} | |
else | |
{ | |
SendDataPacket (); | |
} | |
} | |
/* When this method completes, either we have taken ownership of the medium or the device switched off in the meantime. */ | |
NS_ASSERT (m_phy->IsStateTx () || m_phy->IsStateOff ()); | |
} |
一般情况下,三种发送:
SendRtsForPacket ();// 发送 RTS | |
SendCtsToSelf ();// 发送 CTS | |
SendDataPacket ();// 发送数据 |
RTS/CTS
众所周知,主要看一下 SendDatePacket
void | |
MacLow::SendDataPacket (void) | |
{ | |
... | |
ForwardDown (m_currentPacket, m_currentTxVector); | |
} |
MacLow::ForwardDown
void | |
MacLow::ForwardDown (Ptr<const WifiPsdu> psdu, WifiTxVector txVector) | |
{ | |
NS_LOG_FUNCTION (this << psdu << txVector); | |
NS_ASSERT (psdu->GetNMpdus ()); | |
const WifiMacHeader& hdr = (*psdu->begin ())->GetHeader (); | |
NS_LOG_DEBUG ("send " << hdr.GetTypeString () << | |
", to=" << hdr.GetAddr1 () << | |
", size=" << psdu->GetSize () << | |
", mode=" << txVector.GetMode () << | |
", preamble=" << txVector.GetPreambleType () << | |
", duration=" << hdr.GetDuration () << | |
", seq=0x" << std::hex << hdr.GetSequenceControl () << std::dec); | |
if (hdr.IsCfPoll () && m_stationManager->GetPcfSupported ()) | |
{ | |
Simulator::Schedule (GetPifs () + m_phy->CalculateTxDuration (psdu->GetSize (), txVector, m_phy->GetPhyBand ()), &MacLow::CfPollTimeout, this); | |
} | |
if (hdr.IsBeacon () && m_stationManager->GetPcfSupported ()) | |
{ | |
if (Simulator::Now () > m_lastBeacon + m_beaconInterval) | |
{ | |
m_cfpForeshortening = (Simulator::Now () - m_lastBeacon - m_beaconInterval); | |
} | |
m_lastBeacon = Simulator::Now (); | |
} | |
else if (hdr.IsCfEnd () && m_stationManager->GetPcfSupported ()) | |
{ | |
m_cfpStart = NanoSeconds (0); | |
m_cfpForeshortening = NanoSeconds (0); | |
m_cfAckInfo.appendCfAck = false; | |
m_cfAckInfo.expectCfAck = false; | |
} | |
else if (IsCfPeriod () && hdr.HasData ()) | |
{ | |
m_cfAckInfo.expectCfAck = true; | |
} | |
if (psdu->IsSingle ()) | |
{ | |
txVector.SetAggregation (true); | |
NS_LOG_DEBUG ("Sending S-MPDU"); | |
} | |
else if (psdu->IsAggregate ()) | |
{ | |
txVector.SetAggregation (true); | |
NS_LOG_DEBUG ("Sending A-MPDU"); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Sending non aggregate MPDU"); | |
} | |
for (auto& mpdu : *PeekPointer (psdu)) | |
{ | |
if (mpdu->GetHeader ().IsQosData ()) | |
{ | |
auto edcaIt = m_edca.find (QosUtilsMapTidToAc (mpdu->GetHeader ().GetQosTid ())); | |
edcaIt->second->CompleteMpduTx (mpdu); | |
} | |
} | |
m_phy->Send (psdu, txVector); | |
} |
MacLow::ForwardDown
会调用 m_phy
对象的 SendPacket
方法。
m_phy
就是 WifiPhy
类型,代表物理层。
void | |
WifiPhy::Send (Ptr<const WifiPsdu> psdu, WifiTxVector txVector) | |
{ | |
NS_LOG_FUNCTION (this << *psdu << txVector); | |
/* Transmission can happen if: | |
* - we are syncing on a packet. It is the responsibility of the | |
* MAC layer to avoid doing this but the PHY does nothing to | |
* prevent it. | |
* - we are idle | |
*/ | |
NS_ASSERT (!m_state->IsStateTx () && !m_state->IsStateSwitching ()); | |
NS_ASSERT (m_endTxEvent.IsExpired ()); | |
if (txVector.GetNss () > GetMaxSupportedTxSpatialStreams ()) | |
{ | |
NS_FATAL_ERROR ("Unsupported number of spatial streams!"); | |
} | |
if (m_state->IsStateSleep ()) | |
{ | |
NS_LOG_DEBUG ("Dropping packet because in sleep mode"); | |
NotifyTxDrop (psdu); | |
return; | |
} | |
Time txDuration = CalculateTxDuration (psdu->GetSize (), txVector, GetPhyBand ()); | |
NS_ASSERT (txDuration.IsStrictlyPositive ()); | |
if ((m_currentEvent != 0) && (m_currentEvent->GetEndTime () > (Simulator::Now () + m_state->GetDelayUntilIdle ()))) | |
{ | |
//that packet will be noise _after_ the transmission. | |
MaybeCcaBusyDuration (); | |
} | |
if (m_currentEvent != 0) | |
{ | |
AbortCurrentReception (RECEPTION_ABORTED_BY_TX); | |
} | |
if (m_powerRestricted) | |
{ | |
NS_LOG_DEBUG ("Transmitting with power restriction"); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Transmitting without power restriction"); | |
} | |
if (m_state->GetState () == WifiPhyState::OFF) | |
{ | |
NS_LOG_DEBUG ("Transmission canceled because device is OFF"); | |
return; | |
} | |
double txPowerW = DbmToW (GetTxPowerForTransmission (txVector) + GetTxGain ()); | |
NotifyTxBegin (psdu, txPowerW); | |
m_phyTxPsduBeginTrace (psdu, txVector, txPowerW); | |
NotifyMonitorSniffTx (psdu, GetFrequency (), txVector); | |
m_state->SwitchToTx (txDuration, psdu->GetPacket (), GetPowerDbm (txVector.GetTxPowerLevel ()), txVector); | |
Ptr<WifiPpdu> ppdu = Create<WifiPpdu> (psdu, txVector, txDuration, GetPhyBand ()); | |
if (m_wifiRadioEnergyModel != 0 && m_wifiRadioEnergyModel->GetMaximumTimeInState (WifiPhyState::TX) < txDuration) | |
{ | |
ppdu->SetTruncatedTx (); | |
} | |
m_endTxEvent = Simulator::Schedule (txDuration, &WifiPhy::NotifyTxEnd, this, psdu); | |
StartTx (ppdu); | |
m_channelAccessRequested = false; | |
m_powerRestricted = false; | |
} |
在此函数中调用了 WifiPhy::StartTx
方法,该方法原型如下:
/** | |
* \param ppdu the PPDU to send | |
*/ | |
virtual void StartTx (Ptr<WifiPpdu> ppdu) = 0; |
该函数是个虚函数具体实现是由其子类实现
因此这里主要看一下 YansWifiPhy
的 StartTx
方法, SpectrumWifiPhy
也类似。
void | |
YansWifiPhy::StartTx (Ptr<WifiPpdu> ppdu) | |
{ | |
NS_LOG_FUNCTION (this << ppdu); | |
WifiTxVector txVector = ppdu->GetTxVector (); | |
NS_LOG_DEBUG ("Start transmission: signal power before antenna gain=" << GetPowerDbm (txVector.GetTxPowerLevel ()) << "dBm"); | |
m_channel->Send (this, ppdu, GetTxPowerForTransmission (txVector) + GetTxGain ()); | |
}rtTx (txParams); | |
} |
都是调用 Channel::send
方法,因此对应的就是 YansWifiChannel::Send
void | |
YansWifiChannel::Send (Ptr<YansWifiPhy> sender, Ptr<const WifiPpdu> ppdu, double txPowerDbm) const | |
{ | |
NS_LOG_FUNCTION (this << sender << ppdu << txPowerDbm); | |
Ptr<MobilityModel> senderMobility = sender->GetMobility (); | |
NS_ASSERT (senderMobility != 0); | |
for (PhyList::const_iterator i = m_phyList.begin (); i != m_phyList.end (); i++) | |
{ | |
if (sender != (*i)) | |
{ | |
//For now don't account for inter channel interference nor channel bonding | |
if ((*i)->GetChannelNumber () != sender->GetChannelNumber ()) | |
{ | |
continue; | |
} | |
Ptr<MobilityModel> receiverMobility = (*i)->GetMobility ()->GetObject<MobilityModel> (); | |
Time delay = m_delay->GetDelay (senderMobility, receiverMobility); | |
double rxPowerDbm = m_loss->CalcRxPower (txPowerDbm, senderMobility, receiverMobility); | |
NS_LOG_DEBUG ("propagation: txPower=" << txPowerDbm << "dbm, rxPower=" << rxPowerDbm << "dbm, " << | |
"distance=" << senderMobility->GetDistanceFrom (receiverMobility) << "m, delay=" << delay); | |
Ptr<WifiPpdu> copy = Copy (ppdu); | |
Ptr<NetDevice> dstNetDevice = (*i)->GetDevice (); | |
uint32_t dstNode; | |
if (dstNetDevice == 0) | |
{ | |
dstNode = 0xffffffff; | |
} | |
else | |
{ | |
dstNode = dstNetDevice->GetNode ()->GetId (); | |
} | |
Simulator::ScheduleWithContext (dstNode, | |
delay, &YansWifiChannel::Receive, | |
(*i), copy, rxPowerDbm); | |
} | |
} | |
} |
在 Send
函数中,主要做了这样三件事
计算时延
delay
计算信号衰减
在
delay
后调用回调函数YansWifiChannel::Receive
接收数据包。
读到这里已经可以松一口气了,我们的发送过程已经结束了,让我们再回顾一下流程
# 发送的流程图
接下来就是从 Channel
再将数据传回 WifiNetDevice
。
# 接收过程源码分析
那么开始的起点,自然也还是从 Channel
的 Recive
,即 YansWifiChannel::Receive
void | |
YansWifiChannel::Receive (Ptr<YansWifiPhy> phy, Ptr<WifiPpdu> ppdu, double rxPowerDbm) | |
{ | |
NS_LOG_FUNCTION (phy << ppdu << rxPowerDbm); | |
// Do no further processing if signal is too weak | |
// Current implementation assumes constant RX power over the PPDU duration | |
if ((rxPowerDbm + phy->GetRxGain ()) < phy->GetRxSensitivity ()) | |
{ | |
NS_LOG_INFO ("Received signal too weak to process: " << rxPowerDbm << " dBm"); | |
return; | |
} | |
phy->StartReceivePreamble (ppdu, DbmToW (rxPowerDbm + phy->GetRxGain ())); | |
} |
虽然这里调用的是 YansWifiPhy::StartReceivePreamble
,但是子类中不含该方法,所以实际调用的是
父类 WifiPhy
的方法
void | |
WifiPhy::StartReceivePreamble (Ptr<WifiPpdu> ppdu, double rxPowerW) | |
{ | |
NS_LOG_FUNCTION (this << *ppdu << rxPowerW); | |
WifiTxVector txVector = ppdu->GetTxVector (); | |
Time rxDuration = ppdu->GetTxDuration (); | |
Ptr<const WifiPsdu> psdu = ppdu->GetPsdu (); | |
Ptr<Event> event = m_interference.Add (ppdu, txVector, rxDuration, rxPowerW); | |
Time endRx = Simulator::Now () + rxDuration; | |
if (m_state->GetState () == WifiPhyState::OFF) | |
{ | |
NS_LOG_DEBUG ("Cannot start RX because device is OFF"); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
MaybeCcaBusyDuration (); | |
} | |
return; | |
} | |
if (ppdu->IsTruncatedTx ()) | |
{ | |
NS_LOG_DEBUG ("Packet reception stopped because transmitter has been switched off"); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
MaybeCcaBusyDuration (); | |
} | |
return; | |
} | |
if (!txVector.GetModeInitialized ()) | |
{ | |
//If SetRate method was not called above when filling in txVector, this means the PHY does support the rate indicated in PHY SIG headers | |
NS_LOG_DEBUG ("drop packet because of unsupported RX mode"); | |
NotifyRxDrop (psdu, UNSUPPORTED_SETTINGS); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
MaybeCcaBusyDuration (); | |
} | |
return; | |
} | |
switch (m_state->GetState ()) | |
{ | |
case WifiPhyState::SWITCHING: | |
NS_LOG_DEBUG ("drop packet because of channel switching"); | |
NotifyRxDrop (psdu, CHANNEL_SWITCHING); | |
/* | |
* Packets received on the upcoming channel are added to the event list | |
* during the switching state. This way the medium can be correctly sensed | |
* when the device listens to the channel for the first time after the | |
* switching e.g. after channel switching, the channel may be sensed as | |
* busy due to other devices' transmissions started before the end of | |
* the switching. | |
*/ | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
//that packet will be noise _after_ the completion of the channel switching. | |
MaybeCcaBusyDuration (); | |
} | |
break; | |
case WifiPhyState::RX: | |
NS_ASSERT (m_currentEvent != 0); | |
if (m_frameCaptureModel != 0 | |
&& m_frameCaptureModel->IsInCaptureWindow (m_timeLastPreambleDetected) | |
&& m_frameCaptureModel->CaptureNewFrame (m_currentEvent, event)) | |
{ | |
AbortCurrentReception (FRAME_CAPTURE_PACKET_SWITCH); | |
NS_LOG_DEBUG ("Switch to new packet"); | |
StartRx (event, rxPowerW); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Drop packet because already in Rx (power=" << | |
rxPowerW << "W)"); | |
NotifyRxDrop (psdu, RXING); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
//that packet will be noise _after_ the reception of the currently-received packet. | |
MaybeCcaBusyDuration (); | |
} | |
} | |
break; | |
case WifiPhyState::TX: | |
NS_LOG_DEBUG ("Drop packet because already in Tx (power=" << | |
rxPowerW << "W)"); | |
NotifyRxDrop (psdu, TXING); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
//that packet will be noise _after_ the transmission of the currently-transmitted packet. | |
MaybeCcaBusyDuration (); | |
} | |
break; | |
case WifiPhyState::CCA_BUSY: | |
if (m_currentEvent != 0) | |
{ | |
if (m_frameCaptureModel != 0 | |
&& m_frameCaptureModel->IsInCaptureWindow (m_timeLastPreambleDetected) | |
&& m_frameCaptureModel->CaptureNewFrame (m_currentEvent, event)) | |
{ | |
AbortCurrentReception (FRAME_CAPTURE_PACKET_SWITCH); | |
NS_LOG_DEBUG ("Switch to new packet"); | |
StartRx (event, rxPowerW); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Drop packet because already in Rx (power=" << | |
rxPowerW << "W)"); | |
NotifyRxDrop (psdu, RXING); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
//that packet will be noise _after_ the reception of the currently-received packet. | |
MaybeCcaBusyDuration (); | |
} | |
} | |
} | |
else | |
{ | |
StartRx (event, rxPowerW); | |
} | |
break; | |
case WifiPhyState::IDLE: | |
StartRx (event, rxPowerW); | |
break; | |
case WifiPhyState::SLEEP: | |
NS_LOG_DEBUG ("Drop packet because in sleep mode"); | |
NotifyRxDrop (psdu, SLEEPING); | |
if (endRx > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
//that packet will be noise _after_ the sleep period. | |
MaybeCcaBusyDuration (); | |
} | |
break; | |
default: | |
NS_FATAL_ERROR ("Invalid WifiPhy state."); | |
break; | |
} | |
} |
实际是根据 Phy
的状态采取不同的策略。
如果此时可以接收的话,下一步是 WifiPhy::StartRx
void | |
WifiPhy::StartRx (Ptr<Event> event, double rxPowerW) | |
{ | |
NS_LOG_FUNCTION (this << *event << rxPowerW); | |
NS_LOG_DEBUG ("sync to signal (power=" << rxPowerW << "W)"); | |
m_interference.NotifyRxStart (); //We need to notify it now so that it starts recording events | |
if (!m_endPreambleDetectionEvent.IsRunning ()) | |
{ | |
Time startOfPreambleDuration = GetPreambleDetectionDuration (); | |
Time remainingRxDuration = event->GetDuration () - startOfPreambleDuration; | |
m_endPreambleDetectionEvent = Simulator::Schedule (startOfPreambleDuration, &WifiPhy::StartReceiveHeader, this, event); | |
} | |
else if ((m_frameCaptureModel != 0) && (rxPowerW > m_currentEvent->GetRxPowerW ())) | |
{ | |
NS_LOG_DEBUG ("Received a stronger signal during preamble detection: drop current packet and switch to new packet"); | |
NotifyRxDrop (m_currentEvent->GetPsdu (), PREAMBLE_DETECTION_PACKET_SWITCH); | |
m_interference.NotifyRxEnd (); | |
m_endPreambleDetectionEvent.Cancel (); | |
m_interference.NotifyRxStart (); | |
Time startOfPreambleDuration = GetPreambleDetectionDuration (); | |
Time remainingRxDuration = event->GetDuration () - startOfPreambleDuration; | |
m_endPreambleDetectionEvent = Simulator::Schedule (startOfPreambleDuration, &WifiPhy::StartReceiveHeader, this, event); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Drop packet because RX is already decoding preamble"); | |
NotifyRxDrop (event->GetPsdu (), BUSY_DECODING_PREAMBLE); | |
return; | |
} | |
m_currentEvent = event; | |
} |
接下来是 WifiPhy::StartReceiveHeader
void | |
WifiPhy::StartReceiveHeader (Ptr<Event> event) | |
{ | |
NS_LOG_FUNCTION (this << *event); | |
NS_ASSERT (!IsStateRx ()); | |
NS_ASSERT (m_endPhyRxEvent.IsExpired ()); | |
NS_ASSERT (m_currentEvent != 0); | |
NS_ASSERT (event->GetStartTime () == m_currentEvent->GetStartTime ()); | |
NS_ASSERT (event->GetEndTime () == m_currentEvent->GetEndTime ()); | |
InterferenceHelper::SnrPer snrPer = m_interference.CalculateNonHtPhyHeaderSnrPer (event); | |
double snr = snrPer.snr; | |
NS_LOG_DEBUG ("snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per); | |
NS_LOG_WARN ("snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per); | |
if (!m_preambleDetectionModel || (m_preambleDetectionModel->IsPreambleDetected (event->GetRxPowerW (), snr, m_channelWidth))) | |
{ | |
NotifyRxBegin (event->GetPsdu ()); | |
m_timeLastPreambleDetected = Simulator::Now (); | |
WifiTxVector txVector = event->GetTxVector (); | |
if ((txVector.GetMode ().GetModulationClass () == WIFI_MOD_CLASS_HT) && (txVector.GetPreambleType () == WIFI_PREAMBLE_HT_GF)) | |
{ | |
//No non-HT PHY header for HT GF | |
Time remainingPreambleHeaderDuration = CalculatePhyPreambleAndHeaderDuration (txVector) - GetPreambleDetectionDuration (); | |
m_state->SwitchMaybeToCcaBusy (remainingPreambleHeaderDuration); | |
m_endPhyRxEvent = Simulator::Schedule (remainingPreambleHeaderDuration, &WifiPhy::StartReceivePayload, this, event); | |
} | |
else | |
{ | |
//Schedule end of non-HT PHY header | |
Time remainingPreambleAndNonHtHeaderDuration = GetPhyPreambleDuration (txVector) + GetPhyHeaderDuration (txVector) - GetPreambleDetectionDuration (); | |
m_state->SwitchMaybeToCcaBusy (remainingPreambleAndNonHtHeaderDuration); | |
m_endPhyRxEvent = Simulator::Schedule (remainingPreambleAndNonHtHeaderDuration, &WifiPhy::ContinueReceiveHeader, this, event); | |
} | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Drop packet because PHY preamble detection failed"); | |
NS_LOG_WARN ("Drop packet because PHY preamble detection failed"); | |
NotifyRxDrop (event->GetPsdu (), PREAMBLE_DETECT_FAILURE); | |
m_interference.NotifyRxEnd (); | |
m_currentEvent = 0; | |
// Like CCA-SD, CCA-ED is governed by the 4μs CCA window to flag CCA-BUSY | |
// for any received signal greater than the CCA-ED threshold. | |
if (event->GetEndTime () > (Simulator::Now () + m_state->GetDelayUntilIdle ())) | |
{ | |
MaybeCcaBusyDuration (); | |
} | |
} | |
} |
之后是 WifiPhy::StartReceivePayload
void | |
WifiPhy::StartReceivePayload (Ptr<Event> event) | |
{ | |
NS_LOG_FUNCTION (this << *event); | |
NS_ASSERT (m_endPhyRxEvent.IsExpired ()); | |
NS_ASSERT (m_endRxEvent.IsExpired ()); | |
WifiTxVector txVector = event->GetTxVector (); | |
WifiMode txMode = txVector.GetMode (); | |
bool canReceivePayload; | |
if (txMode.GetModulationClass () >= WIFI_MOD_CLASS_HT) | |
{ | |
InterferenceHelper::SnrPer snrPer; | |
snrPer = m_interference.CalculateHtPhyHeaderSnrPer (event); | |
NS_LOG_DEBUG ("snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per); | |
canReceivePayload = (m_random->GetValue () > snrPer.per); | |
} | |
else | |
{ | |
//If we are here, this means non-HT PHY header was already successfully received | |
canReceivePayload = true; | |
} | |
Time payloadDuration = event->GetEndTime () - event->GetStartTime () - CalculatePhyPreambleAndHeaderDuration (txVector); | |
if (canReceivePayload) //PHY reception succeeded | |
{ | |
if (txVector.GetNss () > GetMaxSupportedRxSpatialStreams ()) | |
{ | |
NS_LOG_DEBUG ("Packet reception could not be started because not enough RX antennas"); | |
NotifyRxDrop (event->GetPsdu (), UNSUPPORTED_SETTINGS); | |
} | |
else if ((txVector.GetChannelWidth () >= 40) && (txVector.GetChannelWidth () > GetChannelWidth ())) | |
{ | |
NS_LOG_DEBUG ("Packet reception could not be started because not enough channel width"); | |
NotifyRxDrop (event->GetPsdu (), UNSUPPORTED_SETTINGS); | |
} | |
else if (!IsModeSupported (txMode) && !IsMcsSupported (txMode)) | |
{ | |
NS_LOG_DEBUG ("Drop packet because it was sent using an unsupported mode (" << txMode << ")"); | |
NotifyRxDrop (event->GetPsdu (), UNSUPPORTED_SETTINGS); | |
} | |
else | |
{ | |
m_statusPerMpdu.clear(); | |
if (event->GetPsdu ()->GetNMpdus () > 1) | |
{ | |
ScheduleEndOfMpdus (event); | |
} | |
m_state->SwitchToRx (payloadDuration); | |
m_endRxEvent = Simulator::Schedule (payloadDuration, &WifiPhy::EndReceive, this, event); | |
NS_LOG_DEBUG ("Receiving PSDU"); | |
m_phyRxPayloadBeginTrace (txVector, payloadDuration); //this callback (equivalent to PHY-RXSTART primitive) is triggered only if headers have been correctly decoded and that the mode within is supported | |
if (txMode.GetModulationClass () == WIFI_MOD_CLASS_HE) | |
{ | |
HePreambleParameters params; | |
params.rssiW = event->GetRxPowerW (); | |
params.bssColor = event->GetTxVector ().GetBssColor (); | |
NotifyEndOfHePreamble (params); | |
} | |
return; | |
} | |
} | |
else //PHY reception failed | |
{ | |
NS_LOG_DEBUG ("Drop packet because HT PHY header reception failed"); | |
NotifyRxDrop (event->GetPsdu (), SIG_A_FAILURE); | |
} | |
m_endRxEvent = Simulator::Schedule (payloadDuration, &WifiPhy::ResetReceive, this, event); | |
} |
然后结束后调用 WifiPhy::EndReceive
void | |
WifiPhy::EndReceive (Ptr<Event> event) | |
{ | |
Time psduDuration = event->GetEndTime () - event->GetStartTime (); | |
NS_LOG_FUNCTION (this << *event << psduDuration); | |
NS_ASSERT (GetLastRxEndTime () == Simulator::Now ()); | |
NS_ASSERT (event->GetEndTime () == Simulator::Now ()); | |
Ptr<const WifiPsdu> psdu = event->GetPsdu (); | |
if (psdu->GetNMpdus () == 1) | |
{ | |
//We do not enter here for A-MPDU since this is done in WifiPhy::EndOfMpdu | |
std::pair<bool, SignalNoiseDbm> rxInfo = GetReceptionStatus (psdu, event, NanoSeconds (0), psduDuration); | |
m_signalNoise = rxInfo.second; | |
m_statusPerMpdu.push_back (rxInfo.first); | |
} | |
NotifyRxEnd (psdu); | |
double snr = m_interference.CalculateSnr (event); | |
if (std::count (m_statusPerMpdu.begin (), m_statusPerMpdu.end (), true)) | |
{ | |
//At least one MPDU has been successfully received | |
WifiTxVector txVector = event->GetTxVector (); | |
NotifyMonitorSniffRx (psdu, GetFrequency (), txVector, m_signalNoise, m_statusPerMpdu); | |
m_state->SwitchFromRxEndOk (Copy (psdu), snr, txVector, m_statusPerMpdu); | |
} | |
else | |
{ | |
m_state->SwitchFromRxEndError (Copy (psdu), snr); | |
} | |
m_interference.NotifyRxEnd (); | |
m_currentEvent = 0; | |
MaybeCcaBusyDuration (); | |
} | |
std::pair<bool, SignalNoiseDbm> | |
WifiPhy::GetReceptionStatus (Ptr<const WifiPsdu> psdu, Ptr<Event> event, Time relativeMpduStart, Time mpduDuration) | |
{ | |
NS_LOG_FUNCTION (this << *psdu << *event << relativeMpduStart << mpduDuration); | |
InterferenceHelper::SnrPer snrPer; | |
snrPer = m_interference.CalculatePayloadSnrPer (event, std::make_pair (relativeMpduStart, relativeMpduStart + mpduDuration)); | |
NS_LOG_DEBUG ("mode=" << (event->GetTxVector ().GetMode ().GetDataRate (event->GetTxVector ())) << | |
", snr(dB)=" << RatioToDb (snrPer.snr) << ", per=" << snrPer.per << ", size=" << psdu->GetSize () << | |
", relativeStart = " << relativeMpduStart.GetNanoSeconds () << "ns, duration = " << mpduDuration.GetNanoSeconds () << "ns"); | |
// There are two error checks: PER and receive error model check. | |
// PER check models is typical for Wi-Fi and is based on signal modulation; | |
// Receive error model is optional, if we have an error model and | |
// it indicates that the packet is corrupt, drop the packet. | |
SignalNoiseDbm signalNoise; | |
signalNoise.signal = WToDbm (event->GetRxPowerW ()); | |
signalNoise.noise = WToDbm (event->GetRxPowerW () / snrPer.snr); | |
if (m_random->GetValue () > snrPer.per && | |
!(m_postReceptionErrorModel && m_postReceptionErrorModel->IsCorrupt (psdu->GetPacket ()->Copy ()))) | |
{ | |
NS_LOG_DEBUG ("Reception succeeded: " << psdu); | |
return std::make_pair (true, signalNoise); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("Reception failed: " << psdu); | |
return std::make_pair (false, signalNoise); | |
} | |
} |
之后调用 WifiPhyStateHelper::SwitchFromRxEndOk
void | |
WifiPhyStateHelper::SwitchFromRxEndOk (Ptr<WifiPsdu> psdu, double snr, WifiTxVector txVector, std::vector<bool> statusPerMpdu) | |
{ | |
NS_LOG_FUNCTION (this << *psdu << snr << txVector << statusPerMpdu.size () << | |
std::all_of(statusPerMpdu.begin(), statusPerMpdu.end(), [](bool v) { return v; })); //returns true if all true | |
NS_ASSERT (statusPerMpdu.size () != 0); | |
NS_ASSERT (m_endRx == Simulator::Now ()); | |
m_rxOkTrace (psdu->GetPacket (), snr, txVector.GetMode (), txVector.GetPreambleType ()); | |
NotifyRxEndOk (); | |
DoSwitchFromRx (); | |
if (!m_rxOkCallback.IsNull ()) | |
{ | |
m_rxOkCallback (psdu, snr, txVector, statusPerMpdu); | |
} | |
} |
其中, m_rxOkCallback
是个回调函数指针,这个指针的值的设置代码如下:
void | |
MacLow::SetPhy (const Ptr<WifiPhy> phy) | |
{ | |
m_phy = phy; | |
m_phy->TraceConnectWithoutContext ("PhyRxPayloadBegin", MakeCallback (&MacLow::RxStartIndication, this)); | |
m_phy->SetReceiveOkCallback (MakeCallback (&MacLow::DeaggregateAmpduAndReceive, this)); | |
m_phy->SetReceiveErrorCallback (MakeCallback (&MacLow::ReceiveError, this)); | |
SetupPhyMacLowListener (phy); | |
} |
因此实际调用的回调函数是 MacLow::DeaggregateAmpduAndReceive
void | |
MacLow::DeaggregateAmpduAndReceive (Ptr<WifiPsdu> psdu, double rxSnr, WifiTxVector txVector, std::vector<bool> statusPerMpdu) | |
{ | |
NS_LOG_FUNCTION (this); | |
bool normalAck = false; | |
bool ampduSubframe = txVector.IsAggregation (); //flag indicating the packet belongs to an A-MPDU and is not a VHT/HE single MPDU | |
//statusPerMpdu is empty for intermediate MPDU forwarding. | |
//This function is called also once the PPDU has been fully received by the PHY, | |
//in that case statusPerMpdu carries the information about the received MPDUs. | |
if (ampduSubframe && !statusPerMpdu.empty ()) | |
{ | |
//We are here if a S-MPDU is received or if all MPDUs of an A-MPDU have been | |
//received (but have been already forwarded up, so ReceiveOk won't be called) | |
NS_ASSERT (psdu->IsAggregate ()); | |
ampduSubframe = true; | |
auto n = psdu->begin (); | |
auto status = statusPerMpdu.begin (); | |
NS_ABORT_MSG_IF (psdu->GetNMpdus () != statusPerMpdu.size (), "Should have one receive status per MPDU"); | |
WifiMacHeader firsthdr = (*n)->GetHeader (); | |
if (firsthdr.GetAddr1 () == m_self) | |
{ | |
//Iterate over all MPDUs and notify reception only if status OK | |
for (; n != psdu->end (); ++n, ++status) | |
{ | |
firsthdr = (*n)->GetHeader (); | |
NS_ABORT_MSG_IF (firsthdr.GetAddr1 () != m_self, "All MPDUs of A-MPDU should have the same destination address"); | |
if (*status) //PER and thus CRC check succeeded | |
{ | |
if (psdu->IsSingle ()) | |
{ | |
//If the MPDU is sent as a VHT/HE single MPDU (EOF=1 in A-MPDU subframe header), then the responder sends an Ack. | |
NS_LOG_DEBUG ("Receive S-MPDU"); | |
ampduSubframe = false; | |
} | |
else if (!m_sendAckEvent.IsRunning () && firsthdr.IsQosAck ()) // Implicit BAR Ack Policy | |
{ | |
m_sendAckEvent = Simulator::Schedule (GetSifs (), | |
&MacLow::SendBlockAckAfterAmpdu, this, | |
firsthdr.GetQosTid (), | |
firsthdr.GetAddr2 (), | |
firsthdr.GetDuration (), | |
txVector, rxSnr); | |
} | |
if (firsthdr.IsAck () || firsthdr.IsBlockAck () || firsthdr.IsBlockAckReq ()) | |
{ | |
ReceiveOk ((*n), rxSnr, txVector, ampduSubframe); | |
} | |
else if (firsthdr.IsData () || firsthdr.IsQosData ()) | |
{ | |
NS_LOG_DEBUG ("Deaggregate packet from " << firsthdr.GetAddr2 () << " with sequence=" << firsthdr.GetSequenceNumber ()); | |
if (psdu->IsSingle ()) | |
{ | |
ReceiveOk ((*n), rxSnr, txVector, ampduSubframe); | |
} | |
if (firsthdr.IsQosAck ()) | |
{ | |
NS_LOG_DEBUG ("Normal Ack"); | |
normalAck = true; | |
} | |
} | |
else | |
{ | |
NS_FATAL_ERROR ("Received A-MPDU with invalid first MPDU type"); | |
} | |
if (!psdu->IsSingle ()) | |
{ | |
if (normalAck) | |
{ | |
//send BlockAck | |
if (firsthdr.IsBlockAckReq ()) | |
{ | |
NS_FATAL_ERROR ("Sending a BlockAckReq with QosPolicy equal to Normal Ack"); | |
} | |
uint8_t tid = firsthdr.GetQosTid (); | |
AgreementsI it = m_bAckAgreements.find (std::make_pair (firsthdr.GetAddr2 (), tid)); | |
if (it != m_bAckAgreements.end ()) | |
{ | |
/* See section 11.5.3 in IEEE 802.11 for the definition of this timer */ | |
ResetBlockAckInactivityTimerIfNeeded (it->second.first); | |
NS_LOG_DEBUG ("rx A-MPDU/sendImmediateBlockAck from=" << firsthdr.GetAddr2 ()); | |
NS_ASSERT (m_sendAckEvent.IsRunning ()); | |
} | |
else | |
{ | |
NS_LOG_DEBUG ("There's not a valid agreement for this block ack request."); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
else | |
{ | |
/* An MPDU has been received */ | |
NS_ASSERT (!psdu->IsAggregate ()); | |
ReceiveOk ((*psdu->begin ()), rxSnr, txVector, ampduSubframe); | |
} | |
} |
ReceiveOk
方法都会进入 rxPacket
标记位置,
void | |
MacLow::ReceiveOk (Ptr<WifiMacQueueItem> mpdu, double rxSnr, WifiTxVector txVector, bool ampduSubframe) | |
{ | |
NS_LOG_FUNCTION (this << *mpdu << rxSnr << txVector); | |
... | |
m_rxCallback (mpdu); | |
return; | |
} |
继续运行代码。 m_rxCallback
也是一个函数指针。该值的设置位置是在 RegularWifiMac的构造函数里
,代码:
m_low->SetRxCallback (MakeCallback (&MacRxMiddle::Receive, m_rxMiddle)); |
所以实际调用的是 MacRxMiddle::Receive
void | |
MacRxMiddle::Receive (Ptr<WifiMacQueueItem> mpdu) | |
{ | |
NS_LOG_FUNCTION (*mpdu); | |
const WifiMacHeader* hdr = &mpdu->GetHeader (); | |
Ptr<Packet> packet = mpdu->GetPacket ()->Copy (); | |
NS_ASSERT (hdr->IsData () || hdr->IsMgt ()); | |
if (!m_pcfCallback.IsNull ()) | |
{ | |
m_pcfCallback (); | |
} | |
OriginatorRxStatus *originator = Lookup (hdr); | |
/** | |
* The check below is really unneeded because it can fail in a lot of | |
* normal cases. Specifically, it is possible for sequence numbers to | |
* loop back to zero once they reach 0xfff0 and to go up to 0xf7f0 in | |
* which case the check below will report the two sequence numbers to | |
* not have the correct order relationship. | |
* So, this check cannot be used to discard old duplicate frames. It is | |
* thus here only for documentation purposes. | |
*/ | |
if (!(SequenceNumber16 (originator->GetLastSequenceControl ()) < SequenceNumber16 (hdr->GetSequenceControl ()))) | |
{ | |
NS_LOG_DEBUG ("Sequence numbers have looped back. last recorded=" << originator->GetLastSequenceControl () << | |
" currently seen=" << hdr->GetSequenceControl ()); | |
} | |
//filter duplicates. | |
if (IsDuplicate (hdr, originator)) | |
{ | |
NS_LOG_DEBUG ("duplicate from=" << hdr->GetAddr2 () << | |
", seq=" << hdr->GetSequenceNumber () << | |
", frag=" << +hdr->GetFragmentNumber ()); | |
return; | |
} | |
Ptr<Packet> aggregate = HandleFragments (packet, hdr, originator); | |
if (aggregate == 0) | |
{ | |
return; | |
} | |
NS_LOG_DEBUG ("forwarding data from=" << hdr->GetAddr2 () << | |
", seq=" << hdr->GetSequenceNumber () << | |
", frag=" << +hdr->GetFragmentNumber ()); | |
if (!hdr->GetAddr1 ().IsGroup ()) | |
{ | |
originator->SetSequenceControl (hdr->GetSequenceControl ()); | |
} | |
if (aggregate == packet) | |
{ | |
m_callback (mpdu); | |
} | |
else | |
{ | |
// We could do this in all cases, but passing the received mpdu in case of | |
// A-MSDUs saves us the time to deaggregate the A-MSDU in MSDUs (which are | |
// kept separate in the received mpdu) and allows us to pass the originally | |
// transmitted packets (i.e., with the same UID) to the receiver. | |
m_callback (Create<WifiMacQueueItem> (aggregate, *hdr)); | |
} | |
} |
这也有一个 m_callback
,其设置位置 RegularWifiMac的构造函数
,代码为
m_rxMiddle->SetForwardCallback (MakeCallback (&RegularWifiMac::Receive, this)); |
所以实际的调用是 RegularWifiMac::Receive
void | |
RegularWifiMac::Receive (Ptr<WifiMacQueueItem> mpdu) | |
{ | |
NS_LOG_FUNCTION (this << *mpdu); | |
const WifiMacHeader* hdr = &mpdu->GetHeader (); | |
Ptr<Packet> packet = mpdu->GetPacket ()->Copy (); | |
Mac48Address to = hdr->GetAddr1 (); | |
Mac48Address from = hdr->GetAddr2 (); | |
//We don't know how to deal with any frame that is not addressed to | |
//us (and odds are there is nothing sensible we could do anyway), | |
//so we ignore such frames. | |
// | |
//The derived class may also do some such filtering, but it doesn't | |
//hurt to have it here too as a backstop. | |
if (to != GetAddress ()) | |
{ | |
return; | |
} | |
if (hdr->IsMgt () && hdr->IsAction ()) | |
{ | |
//There is currently only any reason for Management Action | |
//frames to be flying about if we are a QoS STA. | |
NS_ASSERT (m_qosSupported); | |
WifiActionHeader actionHdr; | |
packet->RemoveHeader (actionHdr); | |
switch (actionHdr.GetCategory ()) | |
{ | |
case WifiActionHeader::BLOCK_ACK: | |
switch (actionHdr.GetAction ().blockAck) | |
{ | |
case WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST: | |
{ | |
MgtAddBaRequestHeader reqHdr; | |
packet->RemoveHeader (reqHdr); | |
//We've received an ADDBA Request. Our policy here is | |
//to automatically accept it, so we get the ADDBA | |
//Response on it's way immediately. | |
SendAddBaResponse (&reqHdr, from); | |
//This frame is now completely dealt with, so we're done. | |
return; | |
} | |
case WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE: | |
{ | |
MgtAddBaResponseHeader respHdr; | |
packet->RemoveHeader (respHdr); | |
//We've received an ADDBA Response. We assume that it | |
//indicates success after an ADDBA Request we have | |
//sent (we could, in principle, check this, but it | |
//seems a waste given the level of the current model) | |
//and act by locally establishing the agreement on | |
//the appropriate queue. | |
AcIndex ac = QosUtilsMapTidToAc (respHdr.GetTid ()); | |
m_edca[ac]->GotAddBaResponse (&respHdr, from); | |
//This frame is now completely dealt with, so we're done. | |
return; | |
} | |
case WifiActionHeader::BLOCK_ACK_DELBA: | |
{ | |
MgtDelBaHeader delBaHdr; | |
packet->RemoveHeader (delBaHdr); | |
if (delBaHdr.IsByOriginator ()) | |
{ | |
//This DELBA frame was sent by the originator, so | |
//this means that an ingoing established | |
//agreement exists in MacLow and we need to | |
//destroy it. | |
m_low->DestroyBlockAckAgreement (from, delBaHdr.GetTid ()); | |
} | |
else | |
{ | |
//We must have been the originator. We need to | |
//tell the correct queue that the agreement has | |
//been torn down | |
AcIndex ac = QosUtilsMapTidToAc (delBaHdr.GetTid ()); | |
m_edca[ac]->GotDelBaFrame (&delBaHdr, from); | |
} | |
//This frame is now completely dealt with, so we're done. | |
return; | |
} | |
default: | |
NS_FATAL_ERROR ("Unsupported Action field in Block Ack Action frame"); | |
return; | |
} | |
default: | |
NS_FATAL_ERROR ("Unsupported Action frame received"); | |
return; | |
} | |
} | |
NS_FATAL_ERROR ("Don't know how to handle frame (type=" << hdr->GetType ()); | |
} |
目前没有找到任何可行的调用方式,回看其继承关系
)
我们观察到,该类下面有很多子类,大胆推测其转发是由子类各自实现的,
所以我们观察 AdhocWifiMac::Receive
,
void | |
AdhocWifiMac::Receive (Ptr<WifiMacQueueItem> mpdu) | |
{ | |
NS_LOG_FUNCTION (this << *mpdu); | |
const WifiMacHeader* hdr = &mpdu->GetHeader (); | |
NS_ASSERT (!hdr->IsCtl ()); | |
Mac48Address from = hdr->GetAddr2 (); | |
Mac48Address to = hdr->GetAddr1 (); | |
if (m_stationManager->IsBrandNew (from)) | |
{ | |
//In ad hoc mode, we assume that every destination supports all the rates we support. | |
if (GetHtSupported ()) | |
{ | |
m_stationManager->AddAllSupportedMcs (from); | |
m_stationManager->AddStationHtCapabilities (from, GetHtCapabilities ()); | |
} | |
if (GetVhtSupported ()) | |
{ | |
m_stationManager->AddStationVhtCapabilities (from, GetVhtCapabilities ()); | |
} | |
if (GetHeSupported ()) | |
{ | |
m_stationManager->AddStationHeCapabilities (from, GetHeCapabilities ()); | |
} | |
m_stationManager->AddAllSupportedModes (from); | |
m_stationManager->RecordDisassociated (from); | |
} | |
if (hdr->IsData ()) | |
{ | |
if (hdr->IsQosData () && hdr->IsQosAmsdu ()) | |
{ | |
NS_LOG_DEBUG ("Received A-MSDU from" << from); | |
DeaggregateAmsduAndForward (mpdu); | |
} | |
else | |
{ | |
ForwardUp (mpdu->GetPacket ()->Copy (), from, to); | |
} | |
return; | |
} | |
//Invoke the receive handler of our parent class to deal with any | |
//other frames. Specifically, this will handle Block Ack-related | |
//Management Action frames. | |
RegularWifiMac::Receive (mpdu); | |
} |
不出意料,发现了下一个突破口, ForwardUp
,并且子类调用父类的 Receive
方法证明了猜测是正确的。
由于子类不含该方法,所以该方法是在父类 RegularWifiMac:::ForwardUp
中实现
void | |
RegularWifiMac::ForwardUp (Ptr<const Packet> packet, Mac48Address from, Mac48Address to) | |
{ | |
NS_LOG_FUNCTION (this << packet << from << to); | |
m_forwardUp (packet, from, to); | |
} } | |
} | |
NS_FATAL_ERROR ("Don't know how to handle frame (type=" << hdr->GetType ()); | |
} |
很简单的方法, RegularWifiMac::ForwardUp
方法会继续向上层传递接收到的 packet,下面就是跟踪 m_forwardUp
的赋值
其赋值位置在 WifiNetDevice::CompleteConfig
里面
m_mac->SetForwardUpCallback (MakeCallback (&WifiNetDevice::ForwardUp, this)); |
所以最终调用的是 WifiNetDevice::ForwardUp
void | |
WifiNetDevice::ForwardUp (Ptr<const Packet> packet, Mac48Address from, Mac48Address to) | |
{ | |
NS_LOG_FUNCTION (this << packet << from << to); | |
LlcSnapHeader llc; | |
NetDevice::PacketType type; | |
if (to.IsBroadcast ()) | |
{ | |
type = NetDevice::PACKET_BROADCAST; | |
} | |
else if (to.IsGroup ()) | |
{ | |
type = NetDevice::PACKET_MULTICAST; | |
} | |
else if (to == m_mac->GetAddress ()) | |
{ | |
type = NetDevice::PACKET_HOST; | |
} | |
else | |
{ | |
type = NetDevice::PACKET_OTHERHOST; | |
} | |
Ptr<Packet> copy = packet->Copy (); | |
if (type != NetDevice::PACKET_OTHERHOST) | |
{ | |
m_mac->NotifyRx (packet); | |
copy->RemoveHeader (llc); | |
m_forwardUp (this, copy, llc.GetType (), from); | |
} | |
else | |
{ | |
copy->RemoveHeader (llc); | |
} | |
if (!m_promiscRx.IsNull ()) | |
{ | |
m_mac->NotifyPromiscRx (copy); | |
m_promiscRx (this, copy, llc.GetType (), from, to, type); | |
} | |
} |