Loading...

# 11.ns3 wifi 模块流程源码解析

在本节中,将从源码的角度解析 ns3wifi 模块,包括一个 packet 是如何从一台主机的 WifiNetDevice 到另一台主机的 WifiNetDevice

PS:

  1. 本节很长,而且很枯燥,但是对扩展 ns3 功能,了解 ns3 流程非常有帮助,请耐心观看。

  2. 本节的源码来自于 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 的子类有:

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 ,就会进入类 QosTxopQueue 函数;
如果不支持 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;

该函数是个虚函数具体实现是由其子类实现

WifiPhy继承关系

因此这里主要看一下 YansWifiPhyStartTx 方法, 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 函数中,主要做了这样三件事

  1. 计算时延 delay

  2. 计算信号衰减

  3. delay 后调用回调函数 YansWifiChannel::Receive 接收数据包。

读到这里已经可以松一口气了,我们的发送过程已经结束了,让我们再回顾一下流程

# 发送的流程图

wifi模块发送流程图

接下来就是从 Channel 再将数据传回 WifiNetDevice

# 接收过程源码分析

那么开始的起点,自然也还是从 ChannelRecive ,即 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);
    }
}

# 完整流程图

ns3.32wifi模块流程图

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

jluyeyu 微信支付

微信支付

jluyeyu 支付宝

支付宝