ns3 uan模块数据收发过程分析

 离散事件仿真器,主要原理是在当前的时刻,计算某个未来事件的发生时刻[1],并向仿真调度器中调度。仿真器按照时戳大小,对未来事件进行调度[2][3]。离散仿真器中的时间与操作系统时钟无关。
 参考博客[3]。比如一条传输链路,网卡的发送速率10Mbps,单向传输时延100ms。当网卡设备向外发送一个数据包A(长度为L字节)时,会模拟处理时延:

Time txTime = m_bps.CalculateBytesTxTime (p->GetSize ());
bool result = m_channel->TransmitStart (p, this, txTime);//发送到channel中模拟链路传输时延

经过 Time txCompleteTime = txTime之后,网卡才能处理下一个数据包B。数据包A在链路上传输时,有一个传输时延(100ms,m_delay)。也就是经过txTime + m_delay的时间后,仿真器才会向下一跳的网卡传输数据。

bool
PointToPointChannel::TransmitStart (
  Ptr<const Packet> p,
  Ptr<PointToPointNetDevice> src,
  Time txTime)
{
  Simulator::ScheduleWithContext (m_link[wire].m_dst->GetNode ()->GetId (),txTime + m_delay, &PointToPointNetDevice::Receive, m_link[wire].m_dst, p->Copy ());//经过txTime + m_delay时延后,通过回调进行数据上传
}

 以uan-cw-example.cc为例,分析水声通信模块的数据包处理流程。
 打包程序采用的是OnOffHelper,为仿真节点Node(可以理解为传感器节点或者主机)安装发包应用。

    OnOffHelper app ("ns3::PacketSocketFactory", Address (socket));
    app.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
    app.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
    app.SetAttribute ("DataRate", DataRateValue (m_dataRate));
    app.SetAttribute ("PacketSize", UintegerValue (m_packetSize));

    ApplicationContainer apps = app.Install (nc);
    apps.Start (Seconds (0.5));//Applicaion 的启动时间

 OnOffHelper在安装ns3::OnOffApplication应用的时候,会对OnOffApplication设置属性值。

OnOffHelper::OnOffHelper (std::string protocol, Address address)
{
  m_factory.SetTypeId ("ns3::OnOffApplication");
  m_factory.Set ("Protocol", StringValue (protocol));
  m_factory.Set ("Remote", AddressValue (address));
}
ApplicationContainer
OnOffHelper::Install (Ptr<Node> node) const
{
  return ApplicationContainer (InstallPriv (node));
}
Ptr<Application>
OnOffHelper::InstallPriv (Ptr<Node> node) const
{
  Ptr<Application> app = m_factory.Create<Application> ();
  node->AddApplication (app);

  return app;
}

 在主文件uan-cw-example.cc中,app.SetAttribute函数第一个参数表示属性,在OnOffApplication有对应的配置点。没有配置的属性,则采用默认值。m_cbrRate数据包发送速率,m_pktSize数据包长度。m_tid代表SocketFactory的类型,属性字符串为"Protocol",在主程序中被配置为"ns3::PacketSocketFactory"。

TypeId
OnOffApplication::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::OnOffApplication")
    .SetParent<Application> ()
    .SetGroupName("Applications")
    .AddConstructor<OnOffApplication> ()
    .AddAttribute ("DataRate", "The data rate in on state.",
                   DataRateValue (DataRate ("500kb/s")),
                   MakeDataRateAccessor (&OnOffApplication::m_cbrRate),
                   MakeDataRateChecker ())
    .AddAttribute ("PacketSize", "The size of packets sent in on state",
                   UintegerValue (512),
                   MakeUintegerAccessor (&OnOffApplication::m_pktSize),
                   MakeUintegerChecker<uint32_t> (1))
    .AddAttribute ("Remote", "The address of the destination",
                   AddressValue (),
                   MakeAddressAccessor (&OnOffApplication::m_peer),
                   MakeAddressChecker ())
    .AddAttribute ("Local",
                   "The Address on which to bind the socket. If not set, it is generated automatically.",
                   AddressValue (),
                   MakeAddressAccessor (&OnOffApplication::m_local),
                   MakeAddressChecker ())
    .AddAttribute ("OnTime", "A RandomVariableStream used to pick the duration of the 'On' state.",
                   StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"),
                   MakePointerAccessor (&OnOffApplication::m_onTime),
                   MakePointerChecker <RandomVariableStream>())
    .AddAttribute ("OffTime", "A RandomVariableStream used to pick the duration of the 'Off' state.",
                   StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"),
                   MakePointerAccessor (&OnOffApplication::m_offTime),
                   MakePointerChecker <RandomVariableStream>())
    .AddAttribute ("MaxBytes", 
                   "The total number of bytes to send. Once these bytes are sent, "
                   "no packet is sent again, even in on state. The value zero means "
                   "that there is no limit.",
                   UintegerValue (0),
                   MakeUintegerAccessor (&OnOffApplication::m_maxBytes),
                   MakeUintegerChecker<uint64_t> ())
    .AddAttribute ("Protocol", "The type of protocol to use. This should be "
                   "a subclass of ns3::SocketFactory",
                   TypeIdValue (UdpSocketFactory::GetTypeId ()),
                   MakeTypeIdAccessor (&OnOffApplication::m_tid),
                   // This should check for SocketFactory as a parent
                   MakeTypeIdChecker ())
  ;
  return tid;
}

 ns3中可能有三种SocketFactory:UdpSocketFactory,TcpSocketFactory,PacketSocketFactory,分别可以创建UdpSocket,TcpSocket和PacketSocket。PacketSocket可能主要针对一些传感器网络。
 apps.Start (Seconds (0.5)),仿真器在0.5秒时刻启动OnOffApplication。m_tid对应PacketSocketFactory。

void OnOffApplication::StartApplication () // Called at time specified by Start
{
 m_socket = Socket::CreateSocket (GetNode (), m_tid);
 ScheduleStartEvent ();
}
//socket.cc
Socket::CreateSocket (Ptr<Node> node, TypeId tid)
{
  NS_LOG_FUNCTION (node << tid);
  Ptr<Socket> s;
  NS_ASSERT (node != 0);
  Ptr<SocketFactory> socketFactory = node->GetObject<SocketFactory> (tid);
  NS_ASSERT (socketFactory != 0);
  s = socketFactory->CreateSocket ();
  NS_ASSERT (s != 0);
  return s;
}
//packet-socket-factory.cc
Ptr<Socket> PacketSocketFactory::CreateSocket (void)
{
  NS_LOG_FUNCTION (this);
  Ptr<Node> node = GetObject<Node> ();
  Ptr<PacketSocket> socket = CreateObject<PacketSocket> ();
  socket->SetNode (node);
  return socket;
}

 经过offInterval时刻后,OnOffApplication向网络中发包。假设时刻t1,ScheduleStartEvent向仿真器的调度器中注册一个事件(OnOffApplication::StartSending,距离当前时刻的间隔为offInterval,t2=t1+offInterval)。当t1时刻到来,仿真器就会回调StartSending函数。

void OnOffApplication::ScheduleStartEvent ()
{  // Schedules the event to start sending data (switch to the "On" state)
  NS_LOG_FUNCTION (this);

  Time offInterval = Seconds (m_offTime->GetValue ());
  NS_LOG_LOGIC ("start at " << offInterval.As (Time::S));
  m_startStopEvent = Simulator::Schedule (offInterval, &OnOffApplication::StartSending, this);
}

 StartSending。ScheduleNextTx中按照配置的速率向外发送数据包。仿真器中需要频繁地注册未来事件,Simulator::Schedule的返回值可以认为是一个定时器事件。

// Event handlers
void OnOffApplication::StartSending ()
{
  NS_LOG_FUNCTION (this);
  m_lastStartTime = Simulator::Now ();
  ScheduleNextTx ();  // Schedule the send packet event
  ScheduleStopEvent ();
}
void OnOffApplication::ScheduleNextTx ()
{
  NS_LOG_FUNCTION (this);

  if (m_maxBytes == 0 || m_totBytes < m_maxBytes)
    {
      NS_ABORT_MSG_IF (m_residualBits > m_pktSize * 8, "Calculation to compute next send time will overflow");
      uint32_t bits = m_pktSize * 8 - m_residualBits;
      NS_LOG_LOGIC ("bits = " << bits);
      Time nextTime (Seconds (bits /
                              static_cast<double>(m_cbrRate.GetBitRate ()))); // Time till next packet
      NS_LOG_LOGIC ("nextTime = " << nextTime.As (Time::S));
      m_sendEvent = Simulator::Schedule (nextTime,
                                         &OnOffApplication::SendPacket, this);
    }
}

 SendPacket会调用m_socket(类型为PacketSocket)将数据包发送出去。

void OnOffApplication::SendPacket (){
int actual = m_socket->Send (packet);
}

 看下PacketSocket的发送逻辑。

int
PacketSocket::Send (Ptr<Packet> p, uint32_t flags)
{
  NS_LOG_FUNCTION (this << p << flags);
  if (m_state == STATE_OPEN ||
      m_state == STATE_BOUND)
    {
      m_errno = ERROR_NOTCONN;
      return -1;
    }
  return SendTo (p, flags, m_destAddr);
}
int
PacketSocket::SendTo (Ptr<Packet> p, uint32_t flags, const Address &address)
{
device->Send (p, dest, ad.GetProtocol ())
}

 device发送数据包。主文件uan-cw-example.cc,在安装channnel时候,返回了网卡设备。

//uan-cw-example.cc
NetDeviceContainer devices = uan.Install (nc, channel);
//uan-helper.cc
NetDeviceContainer
UanHelper::Install (NodeContainer c, Ptr<UanChannel> channel) const
{
  NetDeviceContainer devices;
  for (NodeContainer::Iterator i = c.Begin (); i != c.End (); i++)
    {
      Ptr<Node> node = *i;

      Ptr<UanNetDevice> device = Install (node, channel);

      devices.Add (device);
      NS_LOG_DEBUG ("node=" << node << ", mob=" << node->GetObject<MobilityModel> ());
    }
  return devices;
}
Ptr<UanNetDevice>
UanHelper::Install (Ptr<Node> node, Ptr<UanChannel> channel) const
{
  Ptr<UanNetDevice> device = CreateObject<UanNetDevice> ();

  Ptr<UanMac> mac = m_mac.Create<UanMac> ();
  Ptr<UanPhy> phy = m_phy.Create<UanPhy> ();
  Ptr<UanTransducer> trans = m_transducer.Create<UanTransducer> ();

  mac->SetAddress (Mac8Address::Allocate ());
  device->SetMac (mac);
  device->SetPhy (phy);
  device->SetTransducer (trans);
  device->SetChannel (channel);

  node->AddDevice (device);

  return device;
}

 device->Send,在UanNetDevice类中查看Send函数的实现。

//uan-net-devices.cc
bool
UanNetDevice::Send (Ptr<Packet> packet, const Address &dest, uint16_t protocolNumber)
{
  uint8_t tmp [6];
  dest.CopyTo (tmp);
  Mac8Address udest (tmp[0]);

  return m_mac->Enqueue (packet, protocolNumber, udest);
}

 UanHelper默认的mac协议实现为UanMacAloha,默认的trans为UanTransducerHd,phy的默认配置为UanPhyGen。

//uan-helper.cc
UanHelper::UanHelper ()
{
  m_mac.SetTypeId ("ns3::UanMacAloha");
  m_phy.SetTypeId ("ns3::UanPhyGen");
  m_transducer.SetTypeId ("ns3::UanTransducerHd");
}

 针对m_mac->Enqueue,在类UanMacAloha查看其实现。

//uan-mac-aloha.cc
bool
UanMacAloha::Enqueue (Ptr<Packet> packet, uint16_t protocolNumber, const Address &dest)
{
m_phy->SendPacket (packet, GetTxModeIndex ());
}

 在类UanPhyGen,查看m_phy->SendPacket的实现。GetTxModeIndex ()获取调制模式。查找代码,没有看到对调制模式的配置,但是有这样的接口(UanMac::SetTxModeIndex)。UanTxMode 中罗列的调制模式有PSK,QAM,FSK。

//uan-phy-gen.cc
void
UanPhyGen::SendPacket (Ptr<Packet> pkt, uint32_t modeNum)
{
  NS_LOG_DEBUG ("PHY " << m_mac->GetAddress () << ": Transmitting packet");
  if (m_state == DISABLED)
    {
      NS_LOG_DEBUG ("Energy depleted, node cannot transmit any packet. Dropping.");
      return;
    }

  if (m_state == TX)
    {
      NS_LOG_DEBUG ("PHY requested to TX while already Transmitting.  Dropping packet.");
      return;
    }
  else if (m_state == SLEEP)
    {
      NS_LOG_DEBUG ("PHY requested to TX while sleeping.  Dropping packet.");
      return;
    }

  UanTxMode txMode = GetMode (modeNum);

  if (m_pktRx != 0)
    {
      m_minRxSinrDb = -1e30;
      m_pktRx = 0;
    }

  m_transducer->Transmit (Ptr<UanPhy> (this), pkt, m_txPwrDb, txMode);
  m_state = TX;
  UpdatePowerConsumption (TX);
  double txdelay = pkt->GetSize () * 8.0 / txMode.GetDataRateBps ();
  m_pktTx = pkt;
  m_txEndEvent = Simulator::Schedule (Seconds (txdelay), &UanPhyGen::TxEndEvent, this);
  NS_LOG_DEBUG ("PHY " << m_mac->GetAddress () << " notifying listeners");
  NotifyListenersTxStart (Seconds (txdelay));
  m_txLogger (pkt, m_txPwrDb, txMode);
}

  m_transducer->Transmit函数中携带有发送功率和调制模式参数。在类UanTransducerHd,查看m_transducer->Transmit的实现。如果已经处于传输模式,说明设备忙,丢弃数据包,反之,则进入else之后的处理。不同的调制模式,传输速率不同。

//uan-transducer-hd.cc
void
UanTransducerHd::Transmit (Ptr<UanPhy> src,
                           Ptr<Packet> packet,
                           double txPowerDb,
                           UanTxMode txMode)
{
  if (m_state == TX)
    {
      m_endTxEvent.Cancel ();
      src->NotifyTxDrop(packet);           // traced source netanim
    }
  else
    {
      m_state = TX;
      src->NotifyTxBegin(packet);             // traced source netanim
    }
//不同的调制模式,传输速率不同。
  Time delay = Seconds (packet->GetSize () * 8.0 / txMode.GetDataRateBps ());
  UanPhyList::const_iterator it = m_phyList.begin ();
  for (; it != m_phyList.end (); it++)
    {
      if (src != (*it))
        {
          (*it)->NotifyTransStartTx (packet, txPowerDb, txMode);
        }
    }
  m_channel->TxPacket (Ptr<UanTransducer> (this), packet, txPowerDb, txMode);


  delay = std::max (delay, m_endTxTime - Simulator::Now ());

  m_endTxEvent = Simulator::Schedule (delay, &UanTransducerHd::EndTx, this);
  m_endTxTime = Simulator::Now () + delay;
  Simulator::Schedule(delay, &UanPhy::NotifyTxEnd, src, packet);    // traced source netanim
}

 m_channel->TxPacket,查看其实现。

//uan-channel.cc
void
UanChannel::TxPacket (Ptr<UanTransducer> src, Ptr<Packet> packet,
                      double txPowerDb, UanTxMode txMode)
{
  UanDeviceList::const_iterator i = m_devList.begin ();
  for (; i != m_devList.end (); i++)
    {
      if (src != i->second)
        {
          NS_LOG_DEBUG ("Scheduling " << i->first->GetMac ()->GetAddress ());
          Ptr<MobilityModel> rcvrMobility = i->first->GetNode ()->GetObject<MobilityModel> ();
          Time delay = m_prop->GetDelay (senderMobility, rcvrMobility, txMode);
          UanPdp pdp = m_prop->GetPdp (senderMobility, rcvrMobility, txMode);
          double rxPowerDb = txPowerDb - m_prop->GetPathLossDb (senderMobility,
                                                                rcvrMobility,
                                                                txMode);

          NS_LOG_DEBUG ("txPowerDb=" << txPowerDb << "dB, rxPowerDb="
                                     << rxPowerDb << "dB, distance="
                                     << senderMobility->GetDistanceFrom (rcvrMobility)
                                     << "m, delay=" << delay);

          uint32_t dstNodeId = i->first->GetNode ()->GetId ();
          Ptr<Packet> copy = packet->Copy ();
          Simulator::ScheduleWithContext (dstNodeId, delay,
                                          &UanChannel::SendUp,
                                          this,
                                          j,
                                          copy,
                                          rxPowerDb,
                                          txMode,
                                          pdp);
        }
      j++;
    }
}

 主文件uan-cw-example.cc中,就配置了一个channel,所有的网络节点共用此channel,符合传感器网络通信的特点。在上述代码中,数据包会向所有的节点,提交数据包的拷贝( Ptr<Packet> copy=packet->Copy ())。另外这里有一些路径损失(GetPathLossDb )的计算,信道模型用到功率延迟分布(PDP,power delay profile),细节参见博客[4][5]。

Ptr<UanChannel> channel = CreateObjectWithAttributes<UanChannel> ("PropagationModel", PointerValue (prop));

 UanChannel向目标节点发送数据包过程。UanTransducerHd的m_state == RX,才能成功收包。

//uan-channel.cc
void
UanChannel::SendUp (uint32_t i, Ptr<Packet> packet, double rxPowerDb,
                    UanTxMode txMode, UanPdp pdp)
{
  NS_LOG_DEBUG ("Channel:  In sendup");
  m_devList[i].second->Receive (packet, rxPowerDb, txMode, pdp);
}
//uan-transducer-hd.cc
void
UanTransducerHd::Receive (Ptr<Packet> packet,
                          double rxPowerDb,
                          UanTxMode txMode,
                          UanPdp pdp)
{
  NS_LOG_FUNCTION (this << packet << rxPowerDb << txMode << pdp);
  //Apply receiver gain in dB
  rxPowerDb = ApplyRxGainDb (rxPowerDb, txMode);

  UanPacketArrival arrival (packet,
                            rxPowerDb,
                            txMode,
                            pdp,
                            Simulator::Now ());

  m_arrivalList.push_back (arrival);
  Time txDelay = Seconds (packet->GetSize () * 8.0 / txMode.GetDataRateBps ());
  Simulator::Schedule (txDelay, &UanTransducerHd::RemoveArrival, this, arrival);
  NS_LOG_DEBUG (Now ().As (Time::S) << " Transducer in receive");
  if (m_state == RX)
    {
      NS_LOG_DEBUG ("Transducer state = RX");
      UanPhyList::const_iterator it = m_phyList.begin ();
      for (; it != m_phyList.end (); it++)
        {
          NS_LOG_DEBUG ("Calling StartRx");
          (*it)->StartRxPacket (packet, rxPowerDb, txMode, pdp);
        }
    }
}

 StartRxPacket函数中,需要计算SINR(Signal to Interference plus Noise Ratio),设置一些状态装换。txdelay = pkt->GetSize () * 8.0 / txMode.GetDataRateBps (),模拟接受侧网卡的处理时延(解调?)。

//uan-phy-gen.cc
void
UanPhyGen::StartRxPacket (Ptr<Packet> pkt, double rxPowerDb, UanTxMode txMode, UanPdp pdp)
{
  NS_LOG_DEBUG ("PHY " << m_mac->GetAddress () << ": rx power after RX gain = " << rxPowerDb << " dB re uPa");

  switch (m_state)
    {
    case DISABLED:
      NS_LOG_DEBUG ("Energy depleted, node cannot receive any packet. Dropping.");
      NotifyRxDrop (pkt); // traced source netanim
      return;
    case TX:
      NotifyRxDrop (pkt); // traced source netanim
      NS_ASSERT (false);
      break;
    case RX:
      {
        NS_ASSERT (m_pktRx);
        double newSinrDb = CalculateSinrDb (m_pktRx, m_pktRxArrTime, m_rxRecvPwrDb, m_pktRxMode, m_pktRxPdp);
        m_minRxSinrDb  =  (newSinrDb < m_minRxSinrDb) ? newSinrDb : m_minRxSinrDb;
        NS_LOG_DEBUG ("PHY " << m_mac->GetAddress () << ": Starting RX in RX mode.  SINR of pktRx = " << m_minRxSinrDb);
        NotifyRxBegin (pkt); // traced source netanim
      }
      break;

    case CCABUSY:
    case IDLE:
      {
        NS_ASSERT (!m_pktRx);
        bool hasmode = false;
        for (uint32_t i = 0; i < GetNModes (); i++)
          {
            if (txMode.GetUid () == GetMode (i).GetUid ())
              {
                hasmode = true;
                break;
              }
          }
        if (!hasmode)
          {
            break;
          }


        double newsinr = CalculateSinrDb (pkt, Simulator::Now (), rxPowerDb, txMode, pdp);
        NS_LOG_DEBUG ("PHY " << m_mac->GetAddress () << ": Starting RX in IDLE mode.  SINR = " << newsinr);
        if (newsinr > m_rxThreshDb)
          {
            m_state = RX;
            UpdatePowerConsumption (RX);
            NotifyRxBegin (pkt); // traced source netanim
            m_rxRecvPwrDb = rxPowerDb;
            m_minRxSinrDb = newsinr;
            m_pktRx = pkt;
            m_pktRxArrTime = Simulator::Now ();
            m_pktRxMode = txMode;
            m_pktRxPdp = pdp;
            double txdelay = pkt->GetSize () * 8.0 / txMode.GetDataRateBps ();
            m_rxEndEvent = Simulator::Schedule (Seconds (txdelay), &UanPhyGen::RxEndEvent, this, pkt, rxPowerDb, txMode);
            NotifyListenersRxStart ();
          }

      }
      break;
    case SLEEP:
      NS_LOG_DEBUG ("Sleep mode. Dropping packet.");
      NotifyRxDrop (pkt); // traced source netanim
      break;
    }

  if (m_state == IDLE && GetInterferenceDb ( (Ptr<Packet>) 0) > m_ccaThreshDb)
    {
      m_state = CCABUSY;
      NotifyListenersCcaStart ();
    }

}

 UanPhyGen::RxEndEvent中会计算packer error rate(m_per->CalcPer),m_pg是个随机数生成器,生成值高于per,才会继续向上提交数据包。

void
UanPhyGen::RxEndEvent (Ptr<Packet> pkt, double rxPowerDb, UanTxMode txMode)
{
  if (m_pg->GetValue (0, 1) > m_per->CalcPer (m_pktRx, m_minRxSinrDb, txMode))
    {
      m_rxOkLogger (pkt, m_minRxSinrDb, txMode);
      NotifyListenersRxGood ();
      if (!m_recOkCb.IsNull ())
        {
          m_recOkCb (pkt, m_minRxSinrDb, txMode);
        }

    }
}

 m_recOkCb的回调函数的配置点。m_recOkCb最终会调用UanMacAloha::RxPacketGood函数。目的地址是自身的设备地址,才会继续向上层提交(m_forUpCb )。

void
UanMacAloha::AttachPhy (Ptr<UanPhy> phy)
{
  m_phy = phy;
  m_phy->SetReceiveOkCallback (MakeCallback (&UanMacAloha::RxPacketGood, this));
  m_phy->SetReceiveErrorCallback (MakeCallback (&UanMacAloha::RxPacketError, this));
}
void
UanMacAloha::RxPacketGood (Ptr<Packet> pkt, double sinr, UanTxMode txMode)
{
  NS_UNUSED (sinr);
  UanHeaderCommon header;
  pkt->RemoveHeader (header);
  NS_LOG_DEBUG ("Receiving packet from " << header.GetSrc () << " For " << header.GetDest ());

  if (header.GetDest () == GetAddress () || header.GetDest () == Mac8Address::GetBroadcast ())
    {
      m_forUpCb (pkt, header.GetProtocolNumber (), header.GetSrc ());
    }

}

 m_forUpCb的设置函数为SetForwardUpCb 。m_forUpCb最终调用UanNetDevice::ForwardUp函数。

void
UanMacAloha::SetForwardUpCb (Callback<void, Ptr<Packet>, uint16_t, const Mac8Address&> cb)
{
  m_forUpCb = cb;
}
void
UanNetDevice::SetMac (Ptr<UanMac> mac)
{
m_mac->SetForwardUpCb (MakeCallback (&UanNetDevice::ForwardUp, this));
}

 UanNetDevice::ForwardUp的处理过程。

void
UanNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb)
{
  m_forwardUp = cb;
}
void
UanNetDevice::ForwardUp (Ptr<Packet> pkt, uint16_t protocolNumber, const Mac8Address &src)
{
  NS_LOG_DEBUG ("Forwarding packet up to application");
  m_rxLogger (pkt, src);
  m_forwardUp (this, pkt, protocolNumber, src);

}

 m_forwardUp是在哪里被设置的呢?

Ptr<UanNetDevice>
UanHelper::Install (Ptr<Node> node, Ptr<UanChannel> channel) const
{
Ptr<UanNetDevice> device = CreateObject<UanNetDevice> ();
node->AddDevice (device);
}
uint32_t
Node::AddDevice (Ptr<NetDevice> device)
{
device->SetReceiveCallback (MakeCallback (&Node::NonPromiscReceiveFromDevice, this));
}
void
UanNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb)
{
  m_forwardUp = cb;
}

 m_forwardUp最终调用Node::NonPromiscReceiveFromDevice。

bool
Node::NonPromiscReceiveFromDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                                   const Address &from)
{
  NS_LOG_FUNCTION (this << device << packet << protocol << &from);
  return ReceiveFromDevice (device, packet, protocol, from, device->GetAddress (), NetDevice::PacketType (0), false);
}
bool
Node::ReceiveFromDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                         const Address &from, const Address &to, NetDevice::PacketType packetType, bool promiscuous)
{
  for (ProtocolHandlerList::iterator i = m_handlers.begin ();
       i != m_handlers.end (); i++){
        i->handler (device, packet, protocol, from, to, packetType);
}
}

 m_handlers的注册过程:

int
PacketSocket::DoBind (const PacketSocketAddress &address)
{
  m_node->RegisterProtocolHandler (MakeCallback (&PacketSocket::ForwardUp, this),
                                   address.GetProtocol (), dev);
}
void
Node::RegisterProtocolHandler (ProtocolHandler handler, 
                               uint16_t protocolType,
                               Ptr<NetDevice> device,
                               bool promiscuous){
m_handlers.push_back (entry);
}

 i->handler最终调用的函数为PacketSocket::ForwardUp。

void 
PacketSocket::ForwardUp (Ptr<NetDevice> device, Ptr<const Packet> packet, 
                         uint16_t protocol, const Address &from,
                         const Address &to, NetDevice::PacketType packetType)
{
  if ((m_rxAvailable + packet->GetSize ()) <= m_rcvBufSize)
    {
        m_deliveryQueue.push (std::make_pair (copy, address));
       NotifyDataRecv ();
    }
}

 NotifyDataRecv会通知上层Application读取数据包。主程序uan-cw-example.cc设置了数据包读取函数。

sinkSocket->SetRecvCallback (MakeCallback (&Experiment::ReceivePacket, this));
void
Experiment::ReceivePacket (Ptr<Socket> socket)
{
  Ptr<Packet> packet;
  while ((packet = socket->Recv ()))
    {
      m_bytesTotal += packet->GetSize ();
    }
}

 物理层的退避,信噪比的计算,需要相关领域的人员具体分析。我不是搞无线网络的。

Reference:
[1]ns3 simulator离散模拟机制
[2]ns3源码阅读(一)UdpSocketFactory的创建
[3]ns3源码阅读(二)net-device和channel
[4]基于PDP的信道建模
[5]学习笔记(十六):商用Wi-Fi的功率延迟分布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容