离散事件仿真器,主要原理是在当前的时刻,计算某个未来事件的发生时刻[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的功率延迟分布