# 13.ns3 Aqua-sim ng 水声通信模块源码解析
在本节中,将从源码的角度解析 ns3
的 水声通信
模块,这是一个 ns3
的扩展模块,和无线模块一样,我们首要关注的还是 Packet
的发送流程。包括一个 packet
是如何从一台主机的 NetDevice
到另一台主机的 NetDevice
。
PS:
本节很长,而且很枯燥,但是对扩展
ns3
功能,了解ns3
流程非常有帮助,请耐心观看。本节的源码来自于
ns3.27
, 不同版本之间有一些差异,但大致流程差不多,可以互相参考。
# 发送过程源码分析
首先还是从 NetDevice开始
src/aqua-sim-ng/model/aqua-sim-net-device.h
,
class AquaSimNetDevice : public NetDevice | |
{ | |
public: | |
AquaSimNetDevice (); | |
~AquaSimNetDevice (); | |
static TypeId GetTypeId (void); | |
//attach | |
void ConnectLayers(void); | |
void SetPhy (Ptr<AquaSimPhy> phy); | |
void SetMac (Ptr<AquaSimMac> mac, Ptr<AquaSimSync> sync = NULL, Ptr<AquaSimLocalization> loc = NULL); | |
void SetRouting (Ptr<AquaSimRouting> routing); | |
void SetChannel (Ptr<AquaSimChannel> channel); | |
void SetChannel (std::vector<Ptr<AquaSimChannel> > channel); //for multi-channel support | |
//void SetApp (Ptr<AquaSimApp> app); | |
void SetEnergyModel (Ptr<AquaSimEnergyModel> energyModel); | |
Ptr<AquaSimEnergyModel> GetEnergyModel(); | |
void SetAttackModel(Ptr<AquaSimAttackModel> attackModel); | |
void SetNamedData(Ptr<NamedData> ndn); | |
Ptr<AquaSimPhy> GetPhy (void); | |
Ptr<AquaSimMac> GetMac (void); | |
Ptr<AquaSimRouting> GetRouting (void); | |
//Ptr<AquaSimApp> GetApp (void); | |
//Not currently implemented | |
Ptr<AquaSimChannel> DoGetChannel (int channelId) const; | |
Ptr<AquaSimChannel> DoGetChannel(void) const; | |
Ptr<AquaSimSync> GetMacSync(void); | |
Ptr<AquaSimLocalization> GetMacLoc(void); | |
Ptr<AquaSimAttackModel> GetAttackModel(void); | |
Ptr<NamedData> GetNamedData(void); | |
virtual void DoDispose (void); | |
virtual void DoInitialize (void); | |
virtual double GetPropSpeed(void); //m/s | |
void ForwardUp (Ptr<Packet> packet, Ptr<MobilityModel> src, Ptr<MobilityModel> dst); //not used. | |
//inherited functions from NetDevice class | |
virtual void AddLinkChangeCallback (Callback<void> callback); | |
virtual Address GetAddress (void) const; | |
virtual Address GetBroadcast (void) const; | |
virtual Ptr<Channel> GetChannel (void) const; | |
virtual uint32_t GetIfIndex (void) const; | |
virtual uint16_t GetMtu (void) const; | |
virtual Address GetMulticast (Ipv4Address multicastGroup) const; | |
virtual Address GetMulticast (Ipv6Address addr) const; | |
virtual Ptr<Node> GetNode (void) const; | |
virtual bool IsBridge (void) const; | |
virtual bool IsBroadcast (void) const; | |
virtual bool IsLinkUp (void) const; | |
virtual bool IsMulticast (void) const; | |
virtual bool IsPointToPoint (void) const; | |
virtual bool NeedsArp (void) const; | |
virtual bool SendWithHeader (Ptr<Packet> packet, uint16_t protocolNumber); | |
virtual bool Send (Ptr<Packet> packet, const Address &dest, uint16_t protocolNumber); | |
virtual bool SendFrom (Ptr<Packet> packet, const Address &source, | |
const Address &dest, uint16_t protocolNumber); | |
virtual void SetAddress (Address address); | |
virtual void SetIfIndex (uint32_t index); | |
virtual bool SetMtu (const uint16_t mtu); | |
virtual void SetNode (Ptr<Node> node); | |
virtual void SetPromiscReceiveCallback (PromiscReceiveCallback cb); | |
virtual void SetReceiveCallback (ReceiveCallback cb); | |
virtual bool SupportsSendFrom (void) const; | |
/* | |
* Taken from AquaSimNode during consolidation | |
*/ | |
//bool Move(void); /*start the movement... should be handled within example*/ | |
//void Start(void); | |
//void CheckPosition(void); | |
inline Time &PositionUpdateTime(void) { return m_positionUpdateTime; } | |
void SetPositionUpdateTime(Time posUpdateTime) { m_positionUpdateTime = posUpdateTime; } | |
//sink related attributes | |
int ClearSinkStatus(void); | |
int SetSinkStatus(void); | |
inline int GetSinkStatus(void) { return m_sinkStatus; } | |
//VBF | |
inline double &CX(void) { return m_cX; } | |
inline double &CY(void) { return m_cY; } | |
inline double &CZ(void) { return m_cZ; } | |
inline bool FailureStatus(void) { return m_failureStatus; } | |
inline double FailurePro(void) { return m_failurePro; } | |
inline double FailureStatusPro(void) { return m_failureStatusPro; } | |
virtual void SetTransmissionStatus(TransStatus status); | |
virtual TransStatus GetTransmissionStatus(void); | |
inline bool CarrierSense(void) { return m_carrierSense; } | |
inline void ResetCarrierSense(void) { m_carrierSense = false; } | |
inline void SetCarrierSense(bool f){ | |
m_carrierSense = f; | |
m_carrierId = f; | |
} | |
inline bool CarrierId(void) { return m_carrierId; } | |
inline void ResetCarrierId(void) { m_carrierId = false; } | |
int m_nextHop; | |
int m_setHopStatus; | |
int m_sinkStatus; | |
int GetHopStatus(); | |
int GetNextHop(); | |
//void UpdatePosition(void); // UpdatePosition() out of date... should be using ns3's mobility module | |
bool IsMoving(void); | |
Vector GetPosition(void); | |
Ptr<AquaSimEnergyModel> EnergyModel(void) {return m_energyModel; } | |
bool IsAttacker(void); | |
int TotalSentPkts() {return m_totalSentPkts;} | |
inline bool MacEnabled() {return m_macEnabled;} | |
inline void MacEnabled(bool value) {m_macEnabled = value;} | |
protected: | |
void GenerateFailure(void); | |
private: | |
void CompleteConfig (void); | |
Ptr<AquaSimPhy> m_phy; | |
Ptr<AquaSimMac> m_mac; | |
Ptr<AquaSimRouting> m_routing; | |
//Ptr<AquaSimApp> m_app; | |
std::vector<Ptr<AquaSimChannel> > m_channel; | |
Ptr<Node> m_node; | |
Ptr<UniformRandomVariable> m_uniformRand; | |
Ptr<AquaSimEnergyModel> m_energyModel; | |
Ptr<AquaSimSync> m_macSync; | |
Ptr<AquaSimLocalization> m_macLoc; | |
Ptr<AquaSimAttackModel> m_attackModel; | |
Ptr<NamedData> m_ndn; | |
NetDevice::ReceiveCallback m_forwardUp; | |
bool m_configComplete; | |
bool m_attacker; | |
/* | |
* From AquaSimNode | |
*/ | |
TransStatus m_transStatus; | |
double m_statusChangeTime; //the time when changing m_preTransStatus to m_transStatus | |
bool m_failureStatus;// 1 if node fails, 0 otherwise | |
double m_failurePro; | |
double m_failureStatusPro; | |
//the following attributes are added by Peng Xie for RMAC and VBF | |
double m_cX; | |
double m_cY; | |
double m_cZ; | |
bool m_carrierSense; | |
bool m_carrierId; | |
Time m_positionUpdateTime; | |
uint32_t m_ifIndex; | |
uint16_t m_mtu; | |
int m_totalSentPkts; | |
bool m_macEnabled; | |
//XXX remove counters | |
}; // class AquaSimNetDevice |
很明显可以看出,其是继承自 NetDevice
并实现了 Send
方法,所以和 wifi
一样,起始都是 AquaSimNetDevice::Send
bool | |
AquaSimNetDevice::Send (Ptr< Packet > packet, const Address &dest, uint16_t protocolNumber) | |
{ | |
NS_LOG_FUNCTION(this << packet << dest << protocolNumber); | |
m_totalSentPkts++; //debugging | |
AquaSimHeader ash; | |
uint32_t pktSize = packet->GetSize(); | |
ash.SetSize(pktSize); | |
ash.SetSAddr(AquaSimAddress::ConvertFrom(GetAddress())); | |
ash.SetDAddr(AquaSimAddress::ConvertFrom(dest)); | |
packet->AddHeader(ash); | |
return SendWithHeader(packet, protocolNumber); | |
} |
很简单的一个方法,调用了 AquaSimNetDevice::SendWithHeader
bool | |
AquaSimNetDevice::SendWithHeader(Ptr<Packet> packet, uint16_t protocolNumber){ | |
AquaSimHeader ash; | |
packet->RemoveHeader(ash); | |
Address dest = ash.GetDAddr(); | |
uint32_t pktSize = ash.GetSize(); | |
//Quick hack. Named Data should be NULL pointer if unused/unset. | |
if (m_ndn) | |
{ | |
return m_ndn->Recv(packet); | |
} | |
if(m_routing) | |
{//Note : https://www.nsnam.org/docs/release/3.24/doxygen/uan-mac-cw_8cc_source.html#l00123 | |
ash.SetNextHop(AquaSimAddress::GetBroadcast()); | |
packet->AddHeader(ash); | |
NS_LOG_DEBUG("Me(" << AquaSimAddress::ConvertFrom(GetAddress()).GetAsInt() << "): Sending packet to Routing layer : " << ash.GetSize() << " bytes ; " << ash.GetTxTime().GetSeconds() << " sec. ; Dest: " << ash.GetDAddr().GetAsInt() << " ; Src: " << ash.GetSAddr().GetAsInt() << " ; Next H.: " << ash.GetNextHop().GetAsInt()); | |
return m_routing->Recv(packet, dest, protocolNumber); | |
} | |
else if (MacEnabled() && m_mac) | |
{ | |
ash.SetNextHop(ash.GetDAddr()); | |
packet->AddHeader(ash); | |
NS_LOG_DEBUG("Me(" << AquaSimAddress::ConvertFrom(GetAddress()).GetAsInt() << "): Sending packet to MAC layer : " << ash.GetSize() << " bytes ; " << ash.GetTxTime().GetSeconds() << " sec. ; Dest: " << ash.GetDAddr().GetAsInt() << " ; Src: " << ash.GetSAddr().GetAsInt() << " ; Next H.: " << ash.GetNextHop().GetAsInt()); | |
return m_mac->TxProcess(packet); | |
} | |
else if (m_phy) | |
{ | |
SetTransmissionStatus(SEND); | |
ash.SetNextHop(ash.GetDAddr()); | |
if(ash.m_channelSelect==1){ | |
ash.SetTxTime(m_phy->CalcAcousticsTxTime(pktSize)); | |
}else{ | |
ash.SetTxTime(m_phy->CalcOpticalTxTime(pktSize)); | |
} | |
NS_LOG_DEBUG("Me(" << AquaSimAddress::ConvertFrom(GetAddress()).GetAsInt() << "): Sending packet to Phy layer : " << ash.GetSize() << " bytes ; " << ash.GetTxTime().GetSeconds() << " sec. ; Dest: " << ash.GetDAddr().GetAsInt() << " ; Src: " << ash.GetSAddr().GetAsInt() << " ; Next H.: " << ash.GetNextHop().GetAsInt()); | |
Simulator::Schedule(ash.GetTxTime(), &AquaSimNetDevice::SetTransmissionStatus,this, NIDLE); | |
packet->AddHeader(ash); | |
//slightly awkard but for phy header Buffer | |
AquaSimPacketStamp pstamp; | |
packet->AddHeader(pstamp); | |
return m_phy->PktTransmit(packet, 0); | |
} | |
else NS_LOG_WARN("Routing/Mac/Phy layers are not attached to this device. Can not send."); | |
return false; | |
} |
这里主要是做了三种情况的区分,
配置了路由协议,直接交由路由进行处理。
没配置路由协议,但配置了
MAC
,由MAC
进行处理。路由和
MAC
都没配置,直接交由PHY
进行处理。
这里我们先看路由的 AquaSimRouting::Recv
/*avoid instantiation since UnderwaterRouting's behavior is not defined*/ | |
virtual bool Recv(Ptr<Packet> packet, const Address &dest, uint16_t protocolNumber)=0; //handler not implemented |
是个虚函数,具体实现方式由继承的子类,自行实现。
但是只要是进行通信,最后都会调用 AquaSimRouting::SendDown
方法。
/** | |
* send packet p to the lower layer | |
* | |
* @param p a packet | |
* @param next_hop the next hop to route packet p | |
* @param delay packet p will be sent in time of delay | |
* */ | |
bool | |
AquaSimRouting::SendDown(Ptr<Packet> p, AquaSimAddress nextHop, Time delay) | |
{ | |
//cmh->uw_flag() = true; | |
//cmh->addr_type() = NS_AF_INET; | |
NS_LOG_FUNCTION(this << p << nextHop << delay); | |
NS_ASSERT(p != NULL); | |
//add header to packet | |
AquaSimHeader header; | |
p->RemoveHeader(header); | |
//NS_LOG_DEBUG("Pktsize=" << header.GetSize()); | |
if(header.GetUId() == -1) header.SetUId(p->GetUid()); | |
header.SetDirection(AquaSimHeader::DOWN); | |
header.SetNextHop(nextHop); | |
header.SetSAddr(AquaSimAddress::ConvertFrom(m_device->GetAddress())); | |
p->AddHeader(header); | |
//send down after given delay | |
if (0) | |
{ | |
header.Print(std::cout); | |
} | |
/*Note this schedule will not work, should instead call internal function once | |
* event is executed which will internal call Mac's function directly. | |
* This should most likely be a callback. | |
*/ | |
//Simulator::Schedule(delay, &AquaSimMac::Recv, &p); | |
Simulator::Schedule(delay, &AquaSimRouting::SendPacket, this, p); | |
return true; | |
} |
这里做了一个 Uid
校验,如果不存在,添加一个新的 Uid
,然后调用了 AquaSimMac::TxProcess
, packet
进入 MAC
层
void | |
AquaSimRouting::SendPacket(Ptr<Packet> p) | |
{ | |
NS_LOG_FUNCTION(this << m_mac); | |
m_routingTxCbTrace(p); | |
if (!m_mac->TxProcess(p)) | |
NS_LOG_DEBUG(this << "Mac recv error"); | |
} |
AquaSimMac::TxProcess
// to process the outgoing packet | |
virtual bool TxProcess(Ptr<Packet> p)=0; |
不出意料,也是虚函数,实现由子类自己实现。
这里以 BroadcastMac
为例
bool | |
AquaSimBroadcastMac::TxProcess(Ptr<Packet> pkt) | |
{ | |
NS_LOG_FUNCTION(this << pkt); | |
AquaSimHeader ash; | |
MacHeader mach; | |
pkt->RemoveHeader(ash); | |
mach.SetDA(AquaSimAddress::GetBroadcast()); | |
mach.SetSA(AquaSimAddress::ConvertFrom(m_device->GetAddress())); | |
if( m_packetSize != 0 ) | |
ash.SetSize(m_packetSize); | |
else | |
ash.SetSize(m_packetHeaderSize + ash.GetSize()); | |
ash.SetTxTime(GetTxTime(pkt)); | |
switch( m_device->GetTransmissionStatus() ) | |
{ | |
case SLEEP: | |
PowerOn(); | |
break; | |
case NIDLE: | |
ash.SetDirection(AquaSimHeader::DOWN); | |
//ash->addr_type()=NS_AF_ILINK; | |
//add the sync hdr | |
pkt->AddHeader(mach); | |
pkt->AddHeader(ash); | |
//Phy()->SetPhyStatus(PHY_SEND); | |
SendDown(pkt); | |
m_backoffCounter=0; | |
return true; | |
case RECV: | |
{ | |
double backoff=m_rand->GetValue()*BC_BACKOFF; | |
NS_LOG_DEBUG("BACKOFF time:" << backoff << " on node:" << m_device->GetAddress() << "\n"); | |
//pkt->AddHeader(mach); | |
pkt->AddHeader(ash); | |
Simulator::Schedule(Seconds(backoff),&AquaSimBroadcastMac::BackoffHandler,this,pkt); | |
} | |
return true; | |
case SEND: | |
{ | |
double backoff=m_rand->GetValue()*BC_BACKOFF; | |
NS_LOG_DEBUG("BACKOFF time:" << backoff << " on node:" << m_device->GetAddress() << "\n"); | |
//pkt->AddHeader(mach); | |
pkt->AddHeader(ash); | |
Simulator::Schedule(Seconds(backoff),&AquaSimBroadcastMac::BackoffHandler,this,pkt); | |
} | |
return true; | |
/*pkt=0;*/ | |
default: | |
/* | |
* all cases have been processed above, so simply return | |
*/ | |
break; | |
} | |
return true; //may be bug due to Sleep/default cases | |
} |
针对不同的状态,进行了不同的处理,如果是有冲突的话,退避一段时间,在一定次数失败之后,放弃。
否则,直接 SendDown(pkt)
;
bool | |
AquaSimMac::SendDown(Ptr<Packet> p, TransStatus afterTrans) | |
{ | |
NS_ASSERT(m_device);// && m_phy && m_rout); | |
/* For debugging: | |
std::cout << "\nMac @SendDown check:\n"; | |
p->Print(std::cout); | |
std::cout << "\n"; | |
*/ | |
m_macTxTrace(p); | |
if (m_device->GetTransmissionStatus() == SLEEP) { | |
NS_LOG_DEBUG("SendDown::Sleeping, drop pkt"); | |
return false; | |
} | |
if (m_device->GetTransmissionStatus() == RECV) { | |
NS_LOG_DEBUG("SendDown::Recv, queuing pkt"); | |
SendQueuePush(std::make_pair(p, afterTrans)); | |
return true; | |
} | |
else { | |
m_device->SetTransmissionStatus(SEND); | |
AquaSimHeader ash; | |
p->RemoveHeader(ash); | |
if (ash.GetTxTime().IsNegative()) ash.SetTxTime(GetTxTime(p)); | |
NS_LOG_DEBUG("Me(" << this->m_address.GetAsInt() << "): Sending packet to Phy : " << ash.GetSize() << " bytes ; " << ash.GetTxTime().GetSeconds() << " sec. ; Dest: " << ash.GetDAddr().GetAsInt() << " ; Src: " << ash.GetSAddr().GetAsInt() << " ; Next H.: " << ash.GetNextHop().GetAsInt()); | |
Simulator::Schedule(ash.GetTxTime(), &AquaSimNetDevice::SetTransmissionStatus,m_device,afterTrans); | |
p->AddHeader(ash); | |
//slightly awkard but for phy header Buffer | |
AquaSimPacketStamp pstamp; | |
p->AddHeader(pstamp); | |
return Phy()->Recv(p); | |
} | |
} |
这里根据设备的状态决定丢弃、存入发送队列,直接发送至 Phy
。所以下一步是 AquaSimPhy::Recv
定义如下:
virtual bool Recv(Ptr<Packet> p) = 0; |
同样的,我们关注其子类实现
/** | |
* we will cache the incoming packet in phy layer | |
* and send it to MAC layer after receiving the entire one | |
*/ | |
bool | |
AquaSimPhyCmn::Recv(Ptr<Packet> p) | |
{ | |
NS_LOG_FUNCTION(this << p << "at time" << Simulator::Now().GetSeconds() << " on node " << GetNetDevice()->GetAddress()); | |
/*std::cout << "\nPhyCmn: @Recv check:\n"; | |
p->Print(std::cout); | |
std::cout << "\n";*/ | |
AquaSimPacketStamp pstamp; | |
AquaSimHeader asHeader; | |
p->RemoveHeader(pstamp); | |
p->PeekHeader(asHeader); | |
p->AddHeader(pstamp); | |
//NS_LOG_DEBUG ("direction=" << asHeader.GetDirection()); | |
if (asHeader.GetDirection() == AquaSimHeader::DOWN) { | |
NS_LOG_DEBUG("Phy_Recv DOWN. Pkt counter(" << outPktCounter++ << ") on node(" << | |
GetNetDevice()->GetAddress() << ")"); | |
PktTransmit(p); | |
} | |
else { | |
if (asHeader.GetDirection() != AquaSimHeader::UP) { | |
NS_LOG_WARN("Direction for pkt-flow not specified, " | |
"sending pkt up the stack on default."); | |
} | |
NS_LOG_DEBUG("Phy_Recv UP. Pkt counter(" << incPktCounter++ << ") on node(" << | |
GetNetDevice()->GetAddress() << ")"); | |
p = PrevalidateIncomingPkt(p); | |
if (p != NULL) { | |
//put the packet into the incoming queue | |
m_sC->AddNewPacket(p); | |
} | |
} | |
return true; | |
} |
这个方法分为从上 (MAC)
到下 Channel
和从下 Channel
到上 (MAC)
/** | |
* pass packet p to channel | |
*/ | |
bool | |
AquaSimPhyCmn::PktTransmit(Ptr<Packet> p, int channelId) { | |
NS_LOG_FUNCTION(this << p); | |
AquaSimPacketStamp pstamp; | |
AquaSimHeader asHeader; | |
p->RemoveHeader(pstamp); //awkward but for universal encapsulation. | |
p->PeekHeader(asHeader); | |
if (GetNetDevice()->FailureStatus()) { | |
NS_LOG_WARN("AquaSimPhyCmn nodeId=" << GetNetDevice()->GetNode()->GetId() << " fails!\n"); | |
p = 0; | |
return false; | |
} | |
if (GetNetDevice()->GetTransmissionStatus() == SLEEP || (NULL != EM() && EM()->GetEnergy() <= 0)) | |
{ | |
NS_LOG_DEBUG("Unable to reach phy layer (sleep/disable)"); | |
p = 0; | |
return false; | |
} | |
switch (GetNetDevice()->GetTransmissionStatus()){ | |
case SEND: | |
UpdateTxEnergy(asHeader.GetTxTime()); | |
break; | |
case NIDLE: | |
/* | |
* Something went wrong here... | |
*/ | |
NS_LOG_WARN("AquaSimPhyCmn node(" << GetNetDevice()->GetNode() << "," << GetNetDevice()->GetNode()->GetId() | |
<< "):mac forgot to change the status at time " << Simulator::Now()); | |
return false; | |
break; | |
case SLEEP: | |
NS_LOG_WARN("AquaSimPhyCmn node(" << GetNetDevice()->GetNode()->GetId() << ") is sleeping! (dropping pkt)"); | |
return false; | |
break; | |
default: | |
NS_LOG_WARN("AquaSimPhyCmn: wrong status (dropping pkt)"); | |
return false; | |
} | |
/* | |
* Stamp the packet with the interface arguments | |
*/ | |
StampTxInfo(p); | |
Time txSendDelay = this->CalcAcousticsTxTime(asHeader.GetSize(), &m_modulationName ); | |
Simulator::Schedule(txSendDelay, &AquaSimNetDevice::SetTransmissionStatus, GetNetDevice(), NIDLE); | |
//Simulator::Schedule(txSendDelay, &AquaSimPhyCmn::SetPhyStatus, this, PHY_IDLE); | |
/** | |
* here we simulate multi-channel (different frequencies), | |
* not multiple tranceiver, so we pass the packet to channel_ directly | |
* p' uw_txinfo_ carries channel frequency information | |
* | |
* NOTE channelId must be set by upper layer and AquaSimPhyCmn::Recv() should be edited accordingly. | |
*/ | |
NotifyTx(p); | |
m_txLogger(p, m_sC->GetNoise()); | |
return m_channel.at(channelId)->Recv(p, this); | |
} |
调用 channel->Recv
接收 Packet
bool | |
AquaSimChannel::Recv(Ptr<Packet> p, Ptr<AquaSimPhy> phy) | |
{ | |
/*std::cout << "\nChannel: @Recv check:\n"; | |
p->Print(std::cout); | |
std::cout << "\n";*/ | |
NS_LOG_FUNCTION(this << p << phy); | |
NS_ASSERT(p != NULL || phy != NULL); | |
return SendUp(p,phy); | |
} | |
bool | |
AquaSimChannel::SendUp (Ptr<Packet> p, Ptr<AquaSimPhy> tifp) | |
{ | |
NS_LOG_FUNCTION(this); | |
NS_LOG_DEBUG("Packet:" << p << " Phy:" << tifp << " Channel:" << this); | |
Ptr<AquaSimNetDevice> sender = Ptr<AquaSimNetDevice>(tifp->GetNetDevice()); | |
Ptr<AquaSimNetDevice> recver; | |
//std::vector<Ptr<AquaSimPhy> > rifp; //must support multiple recv phy in future | |
Ptr<AquaSimPhy> rifp; | |
//Ptr<Packet> pCopy; | |
Time pDelay; | |
/* | |
if(!m_sorted){ | |
SortLists(); | |
} | |
*/ | |
NS_LOG_ERROR("channel Packet:" << p << " Phy:" << tifp << " Channel:" << this); | |
std::vector<PktRecvUnit> * recvUnits = m_prop->ReceivedCopies(sender, p, m_deviceList); | |
allPktCounter++; //Debug... remove | |
for (std::vector<PktRecvUnit>::size_type i = 0; i < recvUnits->size(); i++) { | |
allRecvPktCounter++; //Debug .. remove | |
if (sender == (*recvUnits)[i].recver) | |
{ | |
continue; | |
} | |
//TODO remove ... this is a local addition for flooding_test. | |
if (FLOODING_TEST && (Distance(sender, (*recvUnits)[i].recver) > Distance((*recvUnits)[0].recver,(*recvUnits)[1].recver)*1.25/*arbitrary*/)) | |
{ | |
NS_LOG_DEBUG("Channel:SendUp: FloodTest(OutOfRange): sender(" << sender->GetAddress() << ") recver:(" << (*recvUnits)[i].recver->GetAddress() << ") dist(" << Distance(sender, (*recvUnits)[i].recver) << ")"); | |
continue; | |
} | |
sentPktCounter++; //Debug... remove | |
recver = (*recvUnits)[i].recver; | |
pDelay = GetPropDelay(sender, (*recvUnits)[i].recver); | |
//pDelay = (*recvUnits)[i].pDelay; | |
rifp = recver->GetPhy(); | |
//rifp = recver->ifhead().lh_first; | |
AquaSimPacketStamp pstamp; | |
AquaSimHeader asHeader; | |
p->RemoveHeader(pstamp); | |
p->RemoveHeader(asHeader); | |
pstamp.SetPr((*recvUnits)[i].pR); | |
pstamp.SetNoise(m_noiseGen->Noise((Simulator::Now() + pDelay), (GetMobilityModel(recver)->GetPosition()))); | |
asHeader.SetDirection(AquaSimHeader::UP); | |
asHeader.SetTxTime(pDelay); | |
p->AddHeader(asHeader); | |
p->AddHeader(pstamp); | |
/** | |
* Send to each interface a copy, and we will filter the packet | |
* in physical layer according to freq and modulation | |
*/ | |
NS_LOG_DEBUG ("Channel. NodeS:" << sender->GetAddress() << " NodeR:" << recver->GetAddress() << " S.Phy:" << sender->GetPhy() << " R.Phy:" << recver->GetPhy() << " packet:" << p | |
<< " TxTime:" << asHeader.GetTxTime() << pDelay<<"packet uid "<<p->GetUid()<<"packet size "<<p->GetSize()); | |
Simulator::Schedule(pDelay, &AquaSimPhy::Recv, rifp, p->Copy()); | |
/* TODO in future support multiple phy with below code. | |
* | |
* for (std::vector<Ptr<AquaSimPhy> >::iterator it = rifp.begin(); it!=rifp.end(); it++) { | |
* pCopy = p->Copy(); | |
* Simulator::Schedule(pDelay, it, &pCopy); | |
* } | |
* | |
*/ | |
} | |
p = 0; //smart pointer will unref automatically once out of scope | |
delete recvUnits; | |
return true; | |
} |
此处调用了 AquaSimPhy::Recv
,上传到 Phy
和之前一样,我们只关注于接收 Packet
部分
if (asHeader.GetDirection() != AquaSimHeader::UP) { | |
NS_LOG_WARN("Direction for pkt-flow not specified, " | |
"sending pkt up the stack on default."); | |
} | |
NS_LOG_DEBUG("Phy_Recv UP. Pkt counter(" << incPktCounter++ << ") on node(" << | |
GetNetDevice()->GetAddress() << ")"); | |
p = PrevalidateIncomingPkt(p); | |
if (p != NULL) { | |
//put the packet into the incoming queue | |
m_sC->AddNewPacket(p); | |
} |
这里是在 m_sC
进行处理,所以下一步是 AquaSimSignalCache::AddNewPacket
void | |
AquaSimSignalCache::AddNewPacket(Ptr<Packet> p){ | |
/** | |
* any packet error marked before this step means | |
* this packet is invalid and will be considered | |
* as noise to other packets only. | |
*/ | |
// TODO is packet collision even really tested or dealt with in this class??? | |
AquaSimHeader asHeader; | |
p->PeekHeader(asHeader); | |
Ptr<IncomingPacket> inPkt = CreateObject<IncomingPacket>(p, | |
asHeader.GetErrorFlag() ? AquaSimPacketStamp::INVALID : AquaSimPacketStamp::RECEPTION); | |
NS_LOG_DEBUG("AddNewPacket:" << p << " w/ Error flag:" << asHeader.GetErrorFlag() << " and incomingpkt:" << inPkt); | |
m_pktSubTimer->AddNewSubmission(inPkt); | |
inPkt->next = m_head->next; | |
m_head->next = inPkt; | |
m_pktNum++; | |
m_totalPS += m_phy->EM()->GetRxPower(); | |
UpdatePacketStatus(); | |
} |
下一步是在 PktSubmissionTimer::AddNewSubmission
中进行处理
void | |
PktSubmissionTimer::AddNewSubmission(Ptr<IncomingPacket> inPkt) { | |
AquaSimHeader asHeader; | |
(inPkt->packet)->PeekHeader(asHeader); | |
/*Time transmissionDelay = Seconds(inPkt->packet->GetSize() * 8 * //Byte conversion/ | |
m_sC->m_phy->GetMac()->GetEncodingEff() / | |
m_sC->m_phy->GetMac()->GetBitRate() ); */ | |
/* Need to calcuate modulation here, aka how long until entire packet is received */ | |
Time transmissionDelay ; | |
if(asHeader.m_channelSelect==1){ | |
transmissionDelay = m_sC->m_phy->CalcAcousticsTxTime(asHeader.GetSize()); | |
}else{ | |
transmissionDelay = m_sC->m_phy->CalcOpticalTxTime(asHeader.GetSize()); | |
} | |
NS_LOG_FUNCTION(this << "incomingPkt:" << inPkt << "txtime:" << | |
asHeader.GetTxTime() << " transmissionDelay:" << | |
transmissionDelay.ToDouble(Time::S)); | |
Simulator::Schedule(transmissionDelay,&PktSubmissionTimer::Expire, this, inPkt); | |
/*if (m_waitingList.empty() || m_waitingList.top().endT > transmissionDelay) | |
{ | |
Simulator::Schedule(transmissionDelay, &PktSubmissionTimer::Expire, this); | |
} | |
m_waitingList.push(PktSubmissionUnit(inPkt, transmissionDelay)); | |
*/ | |
} |
在计算完时延等之后,调用 PktSubmissionTimer::Expire
void | |
PktSubmissionTimer::Expire(Ptr<IncomingPacket> inPkt) | |
{ | |
/*Ptr<IncomingPacket> inPkt = m_waitingList.top().inPkt; | |
m_waitingList.pop(); | |
if(!m_waitingList.empty()) | |
{ | |
Simulator::Schedule(m_waitingList.top().endT, &PktSubmissionTimer::Expire, this); | |
} | |
*/ | |
NS_LOG_DEBUG("Expire. time:" << Simulator::Now().ToDouble(Time::S) << " inPkt:" << inPkt); | |
m_sC->SubmitPkt(inPkt); | |
} |
然后接着调用 SubmitPkt
void | |
AquaSimSignalCache::SubmitPkt(Ptr<IncomingPacket> inPkt) { | |
NS_LOG_FUNCTION(this << inPkt << inPkt->status); | |
status = inPkt->status; | |
Ptr<Packet> p = inPkt->packet; | |
DeleteIncomingPacket(p); //object pointed by inPkt is deleted here | |
/** | |
* modem has no idea about invalid packets, so release | |
* them here | |
*/ | |
if (status == AquaSimPacketStamp::INVALID) | |
{ | |
NS_LOG_DEBUG("Packet(" << p << ") dropped"); | |
p = 0; | |
} | |
else | |
m_phy->SignalCacheCallback(p); | |
} |
最终 SendPktUp
void | |
AquaSimPhyCmn::SignalCacheCallback(Ptr<Packet> p) { | |
NS_LOG_FUNCTION(this << p); | |
NS_LOG_DEBUG("PhyCmn::SignalCacheCallback: device(" << GetNetDevice()->GetAddress() | |
<< ") p_id:" << p->GetUid() << " at:" << Simulator::Now().GetSeconds()<<"\n"); | |
//TODO check for packet collision at signal cache before calling this | |
AquaSimHeader asHeader; | |
p->RemoveHeader(asHeader); | |
asHeader.SetTxTime(Seconds(0.01)); //arbitrary processing time here | |
p->AddHeader(asHeader); | |
pktRecvCounter++; //debugging... | |
SendPktUp(p); | |
} |
SendPktUp
定义如下:
/** | |
* send packet to upper layer, supposed to be MAC layer, | |
* but actually go to any specificed module. | |
*/ | |
void | |
AquaSimPhyCmn::SendPktUp(Ptr<Packet> p) | |
{ | |
NS_LOG_FUNCTION(this); | |
AquaSimHeader ash; | |
MacHeader mach; | |
p->RemoveHeader(ash); | |
p->PeekHeader(mach); | |
p->AddHeader(ash); | |
NotifyRx(p); | |
m_rxLogger(p, m_sC->GetNoise()); | |
//This can be shifted to within the switch to target specific packet types. | |
if (GetNetDevice()->IsAttacker()){ | |
GetNetDevice()->GetAttackModel()->Recv(p); | |
return; | |
} | |
switch (mach.GetDemuxPType()){ | |
case MacHeader::UWPTYPE_OTHER: | |
if(m_device->MacEnabled()) | |
if (!GetMac()->RecvProcess(p)) | |
NS_LOG_DEBUG(this << "Mac Recv error"); | |
break; | |
case MacHeader::UWPTYPE_LOC: | |
GetNetDevice()->GetMacLoc()->Recv(p); | |
break; | |
case MacHeader::UWPTYPE_SYNC: | |
GetNetDevice()->GetMacSync()->RecvSync(p); | |
break; | |
case MacHeader::UWPTYPE_SYNC_BEACON: | |
GetNetDevice()->GetMacSync()->RecvSyncBeacon(p); | |
break; | |
case MacHeader::UWPTYPE_NDN: | |
GetNetDevice()->GetNamedData()->Recv(p); | |
break; | |
default: | |
NS_LOG_DEBUG("SendPKtUp: Something went wrong."); | |
} | |
} |
这里分为多种 Header
,
其中 UWPTYPE_LOC
是水下定位包,通过 AOA 等方式确定位置。
这里普通数据包一般是直接是调用 AquaSimMac::RecvProcess
这里以广播 MAC 为例
/* | |
this program is used to handle the received packet, | |
it should be virtual function, different class may have | |
different versions. | |
*/ | |
bool | |
AquaSimBroadcastMac::RecvProcess (Ptr<Packet> pkt) | |
{ | |
NS_LOG_FUNCTION(this); | |
/*std::cout << "\nBMac @RecvProcess check:\n"; | |
pkt->Print(std::cout); | |
std::cout << "\n";*/ | |
AquaSimHeader ash; | |
MacHeader mach; | |
pkt->RemoveHeader(ash); | |
pkt->RemoveHeader(mach); | |
AquaSimAddress dst = mach.GetDA(); | |
//get a packet from modem, remove the sync hdr from txtime first | |
//cmh->txtime() -= getSyncHdrLen(); | |
if (ash.GetErrorFlag()) | |
{ | |
NS_LOG_DEBUG("BroadcastMac:RecvProcess: received corrupt packet."); | |
pkt=0; | |
return false; | |
} | |
if (dst == AquaSimAddress::GetBroadcast() || dst == AquaSimAddress::ConvertFrom(m_device->GetAddress())) | |
{ | |
if (m_packetSize == 0) | |
{ | |
ash.SetSize(ash.GetSize() - m_packetHeaderSize); | |
} | |
pkt->AddHeader(ash); //leave MacHeader off since sending to upper layers | |
return SendUp(pkt); | |
} | |
// printf("underwaterAquaSimBroadcastMac: this is neither broadcast nor my packet, just drop it\n"); | |
pkt=0; | |
return false; | |
} |
最后调用 SendUp(pkt)
传到路由
bool | |
AquaSimMac::SendUp(Ptr<Packet> p) | |
{ | |
NS_ASSERT(m_device); | |
AquaSimHeader ash; | |
p->PeekHeader(ash); | |
NS_LOG_DEBUG("Me(" << this->m_address.GetAsInt() << "): Received packet from Phy : " << ash.GetSize() << " bytes ; " << ash.GetTxTime().GetSeconds() << " sec. ; Dest: " << ash.GetDAddr().GetAsInt() << " ; Src: " << ash.GetSAddr().GetAsInt() << " ; Next H.: " << ash.GetNextHop().GetAsInt()); | |
if (Routing()) { | |
m_routingRxTrace(p); | |
return Routing()->Recv(p,ash.GetDAddr(),0); | |
} | |
if (ash.GetDAddr() == AquaSimAddress::ConvertFrom(m_device->GetAddress())) { | |
//I am sink, no pass up implemented. | |
NS_LOG_INFO("Mac:SendUp : packet at destination node:" << m_device->GetAddress() << | |
", with end-to-end delay of " << (Simulator::Now()-ash.GetTimeStamp()).ToDouble(Time::S)); | |
m_routingRxTrace(p); | |
return true; | |
} | |
m_routingRxTrace(p); | |
/* if no routing layer is currently implemented */ | |
if(m_dummyRouting) | |
{ | |
//Change to DOWN and send packet (mac layer wise). | |
p->RemoveHeader(ash); | |
ash.SetDirection(AquaSimHeader::DOWN); | |
p->AddHeader(ash); | |
if (!TxProcess(p)) { | |
NS_LOG_DEBUG(this << "Mac recv error"); | |
} | |
} | |
return false; | |
} |
最终路由 SendUp
/** | |
* send packet p to the upper layer, i.e., port dmux | |
* | |
* @param p a packet | |
* */ | |
bool | |
AquaSimRouting::SendUp(Ptr<Packet> p) | |
{ | |
//port_dmux->recv(p); // (Handler*)NULL | |
AquaSimHeader ash; | |
p->PeekHeader(ash); | |
//std::cout << "\nRouting::SinkRecv:" << m_device->GetAddress() <<",pkt#" << p->GetUid() << ",ts:" << ash.GetTimeStamp().ToDouble(Time::S) << " @" << Simulator::Now().ToDouble(Time::S); | |
//std::cout << (Simulator::Now()-ash.GetTimeStamp()).ToDouble(Time::S) << "\n"; | |
NS_LOG_FUNCTION(this << p << " : currently a dummy sendup on nodeAddr:" << | |
AquaSimAddress::ConvertFrom(m_device->GetAddress()).GetAsInt()); | |
m_sendUpPktCount++; | |
NS_LOG_INFO("Me(" << AquaSimAddress::ConvertFrom(m_device->GetAddress()).GetAsInt() << "): SendUp: " | |
<< ash.GetSize() << " bytes ; " | |
<< ash.GetTxTime().GetSeconds() << " sec. ; Dest: " | |
<< ash.GetDAddr().GetAsInt() | |
<< " ; Src: " << ash.GetSAddr().GetAsInt() | |
<< " ; Forwards: " << ash.GetNumForwards() << " ; Packet counter=" | |
<< m_sendUpPktCount); | |
/*TODO this needs to be fully implemented with the multiplexer | |
Or at least sent up for further processing | |
ie. Sync, Localization, Application driven | |
NOTE: AquaSimPhyCmn::SendPktUp() | |
*/ | |
m_routingRxCbTrace(p); | |
return true; | |
} |
这里是通过回调来继续上传
流程图如下所示: