Loading...

# 13.ns3 Aqua-sim ng 水声通信模块源码解析

在本节中,将从源码的角度解析 ns3水声通信 模块,这是一个 ns3 的扩展模块,和无线模块一样,我们首要关注的还是 Packet 的发送流程。包括一个 packet 是如何从一台主机的 NetDevice 到另一台主机的 NetDevice

PS:

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

  2. 本节的源码来自于 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;
}

这里主要是做了三种情况的区分,

  1. 配置了路由协议,直接交由路由进行处理。

  2. 没配置路由协议,但配置了 MAC ,由 MAC 进行处理。

  3. 路由和 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::TxProcesspacket 进入 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;
}

这里是通过回调来继续上传

流程图如下所示:

Aqua-sim-ng水声模块流程图