# 8.ns3 wifi 设备定向发送的实现
在本节中,将介绍 ns3
中 wifi 设备定向发送的实现方案
# 起因
由于研究需求,需要用到节点具有定向发送,全向接收的功能,但 ns3 自带的功能没有定向实现,所以就写了一种定向的实现方法。
# 修改思路
第一想法是修改天线模块的代码,使其完成定向的功能,但是看了以下源码,感觉实现难度较大,修改比较困难,故 pass
了
第二想法是修改物理层或者数据链路层,在节点收到数据包时,底层判断发送节点的位置与接收节点的位置,只接收从指定方向传来的数据包,从而实现定向接收的功能。
于是就开始找数据发送的函数,与数据接收的处理函数,
最终发现在 yans-wifi-channel.cc
里发现有 send
函数和 receive
函数yans-wifi-channel.cc
:
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); | |
} | |
} | |
} | |
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; | |
} | |
RxPowerWattPerChannelBand rxPowerW; | |
rxPowerW.insert ({std::make_pair (0, 0), (DbmToW (rxPowerDbm + phy->GetRxGain ()))}); //dummy band for YANS | |
phy->StartReceivePreamble (ppdu, rxPowerW); | |
} |
阅读源码可以发现, ns3
中发送数据包的流程如下,遍历与发送数据包的节点物理层 ( YanWifiPhy
) 相连的节点。根据每个邻居节点与发送节点之间的距离和衰减模型中定义的公式来计算衰减。然后回调 receive
函数,来判断衰减后是否可以被接收。这为我们的想法提供了很好的条件。因为发送和接收节点的位置信息是可以由 MobilityModel
得到的,而控制是否接收该数据包,也可以通过修改衰减或者之间控制是否进行回调来解决。
# 第一版代码
所以自然而然的,第一版定向天线的代码就出来了
只需要在 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); | |
//motified by yeyu | |
// 发送节点的位置 | |
Vector sendVector=senderMobility->GetPosition(); | |
Vector sendVelocityVector=senderMobility->GetVelocity(); | |
// 接收节点的位置 | |
Vector receiverVector=receiverMobility->GetPosition(); | |
Vector receiverVelocityVector=receiverMobility->GetVelocity(); | |
NS_LOG_INFO("发送节点坐标:"<<sendVector<<" 速度矢量:"<<sendVelocityVector); | |
NS_LOG_INFO("接收节点坐标:"<<receiverVector<<" 速度矢量:"<<receiverVelocityVector); | |
// double angel=atan(receiverVector.x-sendVector.x,receiverVector.y-sendVector.y); | |
double tan=(receiverVector.y-sendVector.y)/(receiverVector.x-sendVector.x); | |
double angel=atan((receiverVector.y-sendVector.y)/(receiverVector.x-sendVector.x)); | |
angel=angel*180/M_PI; | |
if(angel<0){ | |
angel=angel+360; | |
} | |
double angelLimit=sender->getAngel(); | |
int canSend=NondirectionalLoss(angel,angelLimit); | |
NS_LOG_INFO("tan:"<<tan<<" angel: "<<angel<<" canSend:"<<canSend<< "angel limit:"<< angelLimit); | |
// 上下左右,每个方向 2*theta 度的接收角 (theta<45) | |
// rxPowerDbm=rxPowerDbm-NondirectionalLoss(angel,5); | |
if(canSend!=0){ | |
NS_LOG_INFO("不在指定方向内,跳过"); | |
continue; | |
} | |
//motified by yeyu | |
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); | |
} | |
} | |
} |
其中 NondirectionalLoss
是自定义的控制接收角度的函数,规定向下为 0 度,逆时针方向增长,angel 范围 0-360 度,theta 是上下左右四个方向接收角度的一半
int NondirectionalLoss(double angel,double theta){ | |
if((theta<angel && angel <90-theta)or(90+theta<angel && angel<180-theta)or(180+theta<angel && angel<270-theta)or(270+theta<angel && angel<360-theta)){ | |
return 999999; | |
} | |
return 0; | |
} |
这样修改完之后,确实可以定向发送,但是存在一个问题,所有的节点都是这样。没法单独设置单个节点。显然不能满足需求。于是接下来需要进一步修改。
# 改进方案
观察 send 函数,可以发现它是遍历 YansWifiPhy
, 大胆猜测,每个节点都有与之对应的 YansWifiPhy
。经过验证确实如此。
那么下面的任务就简单了,修改 YansWifiPhy
的源码,添加几个自定义的变量,对外提供对应的 get,set
函数。就可以修改指定节点的发送范围。
但是又出现了新的问题,如何获得单个节点的 YansWifiPhy
呢?
由于 ns3
封装的很好,不提供对单个节点 YansWifiPhy
的访问。
一个想法是找到绑定 node
与 YansWifiPhy
的地方,自己提供对外访问的方法。
于是就找到了 YansWifiHelper
, 从源码可以看出,就是这了,每个 node
都有与之对应的 YansWifiPhyHelper
。
Ptr<WifiPhy> YansWifiPhyHelper::Create (Ptr<Node> node, Ptr<NetDevice> device) const | |
{ | |
Ptr<YansWifiPhy> phy = m_phy.Create<YansWifiPhy> (); | |
Ptr<ErrorRateModel> error = m_errorRateModel.Create<ErrorRateModel> (); | |
phy->SetErrorRateModel (error); | |
if (m_frameCaptureModel.IsTypeIdSet ()) | |
{ | |
Ptr<FrameCaptureModel> capture = m_frameCaptureModel.Create<FrameCaptureModel> (); | |
phy->SetFrameCaptureModel (capture); | |
} | |
if (m_preambleDetectionModel.IsTypeIdSet ()) | |
{ | |
Ptr<PreambleDetectionModel> capture = m_preambleDetectionModel.Create<PreambleDetectionModel> (); | |
phy->SetPreambleDetectionModel (capture); | |
} | |
phy->SetChannel (m_channel); | |
phy->SetDevice (device); | |
return phy; | |
} |
那么接下来的事情就很简单了,在 YansWifiHelper
里添加一个存储 YansWifiPhy
的 vector
, 在 send
函数里将获得的 YansWifiPhy
添加到 vector
里,对外提供 get方法
。
理论上这样就可以了。但是事情并不像想象中的那么简单。
这样改完之后,并没有什么效果
经过一番查找。最后发现, YansWifiHelper
是继承 WifiHelper
的, YansWifiHelper
中的 Create
方法是由 WifiHelper
中的 install
方法调用的,并且返回的是 YansWifyPhy
的父类 WifiPhy
。
NetDeviceContainer | |
WifiHelper::Install (const WifiPhyHelper &phyHelper, | |
const WifiMacHelper &macHelper, | |
NodeContainer::Iterator first, | |
NodeContainer::Iterator last) const | |
{ | |
NetDeviceContainer devices; | |
for (NodeContainer::Iterator i = first; i != last; ++i) | |
{ | |
Ptr<Node> node = *i; | |
Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice> (); | |
auto it = wifiStandards.find (m_standard); | |
if (it == wifiStandards.end ()) | |
{ | |
NS_FATAL_ERROR ("Selected standard is not defined!"); | |
return devices; | |
} | |
if (it->second.phyStandard >= WIFI_PHY_STANDARD_80211n) | |
{ | |
Ptr<HtConfiguration> htConfiguration = CreateObject<HtConfiguration> (); | |
device->SetHtConfiguration (htConfiguration); | |
} | |
if ((it->second.phyStandard >= WIFI_PHY_STANDARD_80211ac) && (it->second.phyBand != WIFI_PHY_BAND_2_4GHZ)) | |
{ | |
Ptr<VhtConfiguration> vhtConfiguration = CreateObject<VhtConfiguration> (); | |
device->SetVhtConfiguration (vhtConfiguration); | |
} | |
if (it->second.phyStandard >= WIFI_PHY_STANDARD_80211ax) | |
{ | |
Ptr<HeConfiguration> heConfiguration = CreateObject<HeConfiguration> (); | |
device->SetHeConfiguration (heConfiguration); | |
} | |
Ptr<WifiRemoteStationManager> manager = m_stationManager.Create<WifiRemoteStationManager> (); | |
Ptr<WifiMac> mac = macHelper.Create (device); | |
Ptr<WifiPhy> phy = phyHelper.Create (node, device); | |
//add by yeyu | |
m_phyVector.push_back(phy); | |
//add by yeyu | |
mac->SetAddress (Mac48Address::Allocate ()); | |
mac->ConfigureStandard (m_standard); | |
phy->ConfigureStandardAndBand (it->second.phyStandard, it->second.phyBand); | |
device->SetMac (mac); | |
device->SetPhy (phy); | |
device->SetRemoteStationManager (manager); | |
node->AddDevice (device); | |
if ((it->second.phyStandard >= WIFI_PHY_STANDARD_80211ax) && (m_obssPdAlgorithm.IsTypeIdSet ())) | |
{ | |
Ptr<ObssPdAlgorithm> obssPdAlgorithm = m_obssPdAlgorithm.Create<ObssPdAlgorithm> (); | |
device->AggregateObject (obssPdAlgorithm); | |
obssPdAlgorithm->ConnectWifiNetDevice (device); | |
} | |
devices.Add (device); | |
NS_LOG_DEBUG ("node=" << node << ", mob=" << node->GetObject<MobilityModel> ()); | |
// Aggregate a NetDeviceQueueInterface object if a RegularWifiMac is installed | |
Ptr<RegularWifiMac> rmac = DynamicCast<RegularWifiMac> (mac); | |
if (rmac) | |
{ | |
Ptr<NetDeviceQueueInterface> ndqi; | |
BooleanValue qosSupported; | |
PointerValue ptr; | |
Ptr<WifiMacQueue> wmq; | |
Ptr<WifiAckPolicySelector> ackSelector; | |
rmac->GetAttributeFailSafe ("QosSupported", qosSupported); | |
if (qosSupported.Get ()) | |
{ | |
ndqi = CreateObjectWithAttributes<NetDeviceQueueInterface> ("NTxQueues", | |
UintegerValue (4)); | |
rmac->GetAttributeFailSafe ("BE_Txop", ptr); | |
ackSelector = m_ackPolicySelector[AC_BE].Create<WifiAckPolicySelector> (); | |
ackSelector->SetQosTxop (ptr.Get<QosTxop> ()); | |
ptr.Get<QosTxop> ()->SetAckPolicySelector (ackSelector); | |
wmq = ptr.Get<QosTxop> ()->GetWifiMacQueue (); | |
ndqi->GetTxQueue (0)->ConnectQueueTraces (wmq); | |
rmac->GetAttributeFailSafe ("BK_Txop", ptr); | |
ackSelector = m_ackPolicySelector[AC_BK].Create<WifiAckPolicySelector> (); | |
ackSelector->SetQosTxop (ptr.Get<QosTxop> ()); | |
ptr.Get<QosTxop> ()->SetAckPolicySelector (ackSelector); | |
wmq = ptr.Get<QosTxop> ()->GetWifiMacQueue (); | |
ndqi->GetTxQueue (1)->ConnectQueueTraces (wmq); | |
rmac->GetAttributeFailSafe ("VI_Txop", ptr); | |
ackSelector = m_ackPolicySelector[AC_VI].Create<WifiAckPolicySelector> (); | |
ackSelector->SetQosTxop (ptr.Get<QosTxop> ()); | |
ptr.Get<QosTxop> ()->SetAckPolicySelector (ackSelector); | |
wmq = ptr.Get<QosTxop> ()->GetWifiMacQueue (); | |
ndqi->GetTxQueue (2)->ConnectQueueTraces (wmq); | |
rmac->GetAttributeFailSafe ("VO_Txop", ptr); | |
ackSelector = m_ackPolicySelector[AC_VO].Create<WifiAckPolicySelector> (); | |
ackSelector->SetQosTxop (ptr.Get<QosTxop> ()); | |
ptr.Get<QosTxop> ()->SetAckPolicySelector (ackSelector); | |
wmq = ptr.Get<QosTxop> ()->GetWifiMacQueue (); | |
ndqi->GetTxQueue (3)->ConnectQueueTraces (wmq); | |
ndqi->SetSelectQueueCallback (m_selectQueueCallback); | |
} | |
else | |
{ | |
ndqi = CreateObject<NetDeviceQueueInterface> (); | |
rmac->GetAttributeFailSafe ("Txop", ptr); | |
wmq = ptr.Get<Txop> ()->GetWifiMacQueue (); | |
ndqi->GetTxQueue (0)->ConnectQueueTraces (wmq); | |
} | |
device->AggregateObject (ndqi); | |
} | |
} | |
return devices; | |
} |
那么现在就很简单了,直接修改 WifiPhy
, WifiHelper
WifiPhy.h
中添加下面代码
/* | |
*motified by yeyu | |
*/ | |
//0<m_angel<45 | |
double m_angel; | |
void setAngel(double angel); | |
double getAngel(); | |
//motified by yeyu |
WifiPhy.cc
中添加下面代码
//add by yeyu | |
void WifiPhy::setAngel(double angel){ | |
m_angel=angel; | |
} | |
double WifiPhy::getAngel(){ | |
return m_angel; | |
} | |
//add by yeyu |
WifiHelper.h
中添加下面代码
//add by yeyu | |
#include <vector> | |
//add by yeyu | |
/** | |
*add by yeyu, | |
* 存储节点与 Ptr<WiFiPhy > 的对应关系 | |
*/ | |
mutable std::vector<Ptr<WifiPhy>> m_phyVector; |
WifiHelper.cc
中添加下面代码
void | |
//add by yeyu | |
std::vector<Ptr<WifiPhy>> WifiHelper::getPhyVector(){ | |
return m_phyVector; | |
} | |
//add by yeyu |
WifiHelper.cc
中的 send 函数修改如下
NetDeviceContainer | |
WifiHelper::Install (const WifiPhyHelper &phyHelper, | |
const WifiMacHelper &macHelper, | |
NodeContainer::Iterator first, | |
NodeContainer::Iterator last) const | |
{ | |
*// 上面默认代码 | |
//add by yeyu | |
m_phyVector.push_back(phy); | |
//add by yeyu | |
*// 下面是默认代码 | |
} |
再加上 YansWifiChannel
里 send
函数里的修改,即可以实现自己想要的定向发送功能