WifiPhy class
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 02 Oct 2007 12:41:04 +0200
changeset 1889 41589561e4a7
parent 1888 dbeef4fcff5a
child 1890 a2b30c6456c9
WifiPhy class
src/devices/wifi/wifi-phy.cc
src/devices/wifi/wifi-phy.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/wifi-phy.cc	Tue Oct 02 12:41:04 2007 +0200
@@ -0,0 +1,937 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2005,2006 INRIA
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ */
+
+#include "wifi-phy.h"
+#include "bpsk-mode.h"
+#include "qam-mode.h"
+#include "propagation-model.h"
+#include "ns3/simulator.h"
+#include "ns3/packet.h"
+#include "ns3/random-variable.h"
+
+#include <cassert>
+#include <math.h>
+
+
+#define nopePHY80211_DEBUG 1
+#define nopePHY80211_STATE_DEBUG 1
+
+/* All the state transitions are marked by these macros. */
+#ifdef PHY80211_STATE_DEBUG
+#include <iostream>
+#  define STATE_FROM(from) \
+std::cout << "PHY self=" << this << " old=" << StateToString (from);
+#  define STATE_TO(to) \
+std::cout << " new=" << StateToString (to);
+#  define STATE_AT(at) \
+std::cout << " at=" << at << std::endl;
+#else
+#  define STATE_FROM(from)
+#  define STATE_TO(from)
+#  define STATE_AT(at)
+#endif
+
+#ifdef PHY80211_DEBUG
+#include <iostream>
+#  define TRACE(x) \
+  std::cout << "PHY80211 TRACE " << Simulator::Now ().GetSeconds () << " " << x << std::endl;
+#else
+#  define TRACE(x)
+#endif
+
+
+namespace ns3 {
+
+/****************************************************************
+ *       This destructor is needed.
+ ****************************************************************/
+
+Phy80211Listener::~Phy80211Listener ()
+{}
+
+
+/****************************************************************
+ *       Phy event class
+ ****************************************************************/
+
+class RxEvent {
+public:
+  RxEvent (uint32_t size, uint8_t payloadMode, 
+           Time duration, double rxPower)
+    : m_size (size),
+      m_payloadMode (payloadMode),
+      m_startTime (Simulator::Now ()),
+      m_endTime (m_startTime + duration),
+      m_rxPowerW (rxPower),
+      m_refCount (1)
+  {}
+  ~RxEvent ()
+  {}
+  
+  void Ref (void) const {
+    m_refCount++;
+  }
+  void Unref (void) const {
+    m_refCount--;
+    if (m_refCount == 0) {
+      delete this;
+    }
+  }
+  Time GetDuration (void) const {
+    return m_endTime - m_startTime;
+  }
+  Time GetStartTime (void) const {
+    return m_startTime;
+  }
+  Time GetEndTime (void) const {
+    return m_endTime;
+  }
+  bool Overlaps (Time time) const {
+    if (m_startTime <= time &&
+        m_endTime >= time) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+  double GetRxPowerW (void) const {
+    return m_rxPowerW;
+  }
+  uint32_t GetSize (void) const {
+    return m_size;
+  }
+  uint8_t GetPayloadMode (void) const {
+    return m_payloadMode;
+  }
+  uint8_t GetHeaderMode (void) const {
+    return 0;
+  }
+
+private:
+  uint32_t m_size;
+  uint8_t m_payloadMode;
+  Time m_startTime;
+  Time m_endTime;
+  double m_rxPowerW;
+  mutable int m_refCount;
+};
+
+
+/****************************************************************
+ *       Class which records SNIR change events for a 
+ *       short period of time.
+ ****************************************************************/
+
+Phy80211::NiChange::NiChange (Time time, double delta)
+  : m_time (time), m_delta (delta) 
+{}
+Time
+Phy80211::NiChange::GetTime (void) const
+{
+  return m_time;
+}
+double 
+Phy80211::NiChange::GetDelta (void) const
+{
+  return m_delta;
+}
+bool 
+Phy80211::NiChange::operator < (Phy80211::NiChange const &o) const
+{
+  return (m_time < o.m_time)?true:false;
+}
+
+
+
+/****************************************************************
+ *       The actual Phy80211 class
+ ****************************************************************/
+
+Phy80211::Phy80211 ()
+  : m_syncing (false),
+    m_endTx (Seconds (0)),
+    m_endSync (Seconds (0)),
+    m_endCcaBusy (Seconds (0)),
+    m_startTx (Seconds (0)),
+    m_startSync (Seconds (0)),
+    m_startCcaBusy (Seconds (0)),
+    m_previousStateChangeTime (Seconds (0)),
+    m_endSyncEvent (),
+    m_random (0.0, 1.0)
+{}
+
+Phy80211::~Phy80211 ()
+{
+  m_events.clear ();
+  for (ModesCI j = m_modes.begin (); j != m_modes.end (); j++) {
+    delete (*j);
+  }
+  m_modes.erase (m_modes.begin (), m_modes.end ());
+}
+
+
+void 
+Phy80211::SetPropagationModel (PropagationModel *propagation)
+{
+  m_propagation = propagation;
+}
+
+void 
+Phy80211::SetReceiveOkCallback (SyncOkCallback callback)
+{
+  m_syncOkCallback = callback;
+}
+void 
+Phy80211::SetReceiveErrorCallback (SyncErrorCallback callback)
+{
+  m_syncErrorCallback = callback;
+}
+void 
+Phy80211::ReceivePacket (Packet const packet, 
+                         double rxPowerW,
+                         uint8_t txMode,
+                         uint8_t stuff)
+{
+  Time rxDuration = CalculateTxDuration (packet.GetSize (), txMode);
+  Time endRx = Simulator::Now () + rxDuration;
+  m_startRxLogger (rxDuration, rxPowerW);
+
+  Ptr<RxEvent> event = Create<RxEvent> (packet.GetSize (), 
+                                        txMode,
+                                        rxDuration,
+                                        rxPowerW);
+  AppendEvent (event);
+
+  switch (GetState ()) {
+  case Phy80211::SYNC:
+    TRACE ("drop packet because already in Sync (power="<<
+           rxPowerW<<"W)");
+    if (endRx > m_endSync) 
+      {
+        goto maybeCcaBusy;
+      }
+    break;
+  case Phy80211::TX:
+    TRACE ("drop packet because already in Tx (power="<<
+           rxPowerW<<"W)");
+    if (endRx > m_endTx) 
+      {
+        goto maybeCcaBusy;
+      }
+    break;
+  case Phy80211::CCA_BUSY:
+  case Phy80211::IDLE:
+    if (rxPowerW > m_edThresholdW) 
+      {
+        // sync to signal
+        NotifySyncStart (rxDuration);
+        SwitchToSync (rxDuration);
+        m_startSyncLogger (rxDuration, rxPowerW);
+        assert (m_endSyncEvent.IsExpired ());
+        m_endSyncEvent = Simulator::Schedule (rxDuration, &Phy80211::EndSync, this, 
+                                              packet,
+                                              event, 
+                                              stuff);
+      } 
+    else 
+      {
+        TRACE ("drop packet because signal power too Small ("<<
+               rxPowerW<<"<"<<m_edThresholdW<<")");
+        goto maybeCcaBusy;
+      }
+    break;
+  }
+
+  event->Unref ();
+  return;
+
+ maybeCcaBusy:
+
+  if (rxPowerW > m_edThresholdW) 
+    {
+      SwitchMaybeToCcaBusy (rxDuration);
+      NotifyCcaBusyStart (rxDuration);
+    } 
+  else 
+    {
+      double threshold = m_edThresholdW - rxPowerW;
+      NiChanges ni;
+      CalculateNoiseInterferenceW (event, &ni);
+      double noiseInterferenceW = 0.0;
+      Time end = Simulator::Now ();
+      for (NiChangesI i = ni.begin (); i != ni.end (); i++) 
+        {
+          noiseInterferenceW += i->GetDelta ();
+          if (noiseInterferenceW < threshold) 
+            {
+              break;
+            }
+          end = i->GetTime ();
+        }
+      if (end > Simulator::Now ()) 
+        {
+          Time delta = end - Simulator::Now ();
+          SwitchMaybeToCcaBusy (delta);
+          NotifyCcaBusyStart (delta);
+        }
+    }
+
+  event->Unref ();
+}
+void 
+Phy80211::SendPacket (Packet const packet, uint8_t txMode, uint8_t txPower, uint8_t stuff)
+{
+  /* Transmission can happen if:
+   *  - we are syncing on a packet. It is the responsability of the
+   *    MAC layer to avoid doing this but the PHY does nothing to 
+   *    prevent it.
+   *  - we are idle
+   */
+  assert (!IsStateTx ());
+
+  if (IsStateSync ()) {
+    m_endSyncEvent.Cancel ();
+  }
+
+  Time txDuration = CalculateTxDuration (packet.GetSize (), txMode);
+  m_startTxLogger (txDuration, GetModeBitRate (txMode), GetPowerDbm (txPower));
+  NotifyTxStart (txDuration);
+  SwitchToTx (txDuration);
+  m_propagation->Send (packet, GetPowerDbm (txPower), txMode, stuff);
+}
+
+void 
+Phy80211::SetEdThresholdDbm (double edThreshold)
+{
+  m_edThresholdW = DbmToW (edThreshold);
+}
+void 
+Phy80211::SetRxNoiseDb (double rxNoise)
+{
+  m_rxNoiseRatio = DbToRatio (rxNoise);
+}
+void 
+Phy80211::SetTxPowerIncrementsDbm (double txPowerBase, 
+                                   double txPowerEnd, 
+                                   int nTxPower)
+{
+  m_txPowerBaseDbm = txPowerBase;
+  m_txPowerEndDbm = txPowerEnd;
+  m_nTxPower = nTxPower;
+}
+uint32_t 
+Phy80211::GetNModes (void) const
+{
+  return m_modes.size ();
+}
+uint32_t 
+Phy80211::GetModeBitRate (uint8_t mode) const
+{
+  return GetMode (mode)->GetRate ();
+}
+uint32_t 
+Phy80211::GetNTxpower (void) const
+{
+  return m_nTxPower;
+}
+
+double 
+Phy80211::CalculateSnr (uint8_t txMode, double ber) const
+{
+  return GetSnrForBer (GetMode (txMode), ber);;
+}
+
+double 
+Phy80211::GetSnrForBer (TransmissionMode *mode, double ber) const
+{
+  double low, high, precision;
+  low = 1e-25;
+  high = 1e25;
+  precision = 1e-12;
+  while (high - low > precision) 
+    {
+      assert (high >= low);
+      double middle = low + (high - low) / 2;
+      if ((1 - mode->GetChunkSuccessRate (middle, 1)) > ber) 
+        {
+          low = middle;
+        } 
+      else 
+        {
+          high = middle;
+        }
+    }
+  return low;
+}
+
+void
+Phy80211::Configure80211a (void)
+{
+  m_plcpPreambleDelayUs = 20;
+  m_plcpHeaderLength = 4 + 1 + 12 + 1 + 6 + 16 + 6;
+  /* 4095 bytes at a 6Mb/s rate with a 1/2 coding rate. */
+  m_maxPacketDuration = Seconds (4095.0*8.0/6000000.0*(1.0/2.0));
+
+  AddTxRxMode (new FecBpskMode (20e6, 6000000, 0.5,   10, 11));
+  AddTxRxMode (new FecBpskMode (20e6, 9000000, 0.75,  5, 8));
+  AddTxRxMode (new FecQamMode (20e6, 12000000, 0.5,   4, 10, 11, 0));
+  AddTxRxMode (new FecQamMode (20e6, 18000000, 0.75,  4, 5, 8, 31));
+  //AddTxRxMode (new FecQamMode (20e6, 24000000, 0.5,   16, 10, 11, 0));
+  AddTxRxMode (new FecQamMode (20e6, 36000000, 0.75,  16, 5, 8, 31));
+  //AddTxRxMode (new FecQamMode (20e6, 48000000, 0.666, 64, 6, 1, 16));
+  AddTxRxMode (new FecQamMode (20e6, 54000000, 0.75,  64, 5, 8, 31));
+
+#ifdef PHY80211_DEBUG
+  for (double db = 0; db < 30; db+= 0.5) {
+    std::cout <<db<<" ";
+    for (uint8_t i = 0; i < GetNModes (); i++) {
+      TransmissionMode *mode = GetMode (i);
+      double ber = 1-mode->GetChunkSuccessRate (DbToRatio (db), 1);
+      std::cout <<ber<< " ";
+    }
+    std::cout << std::endl;
+  }
+#endif
+}
+
+void 
+Phy80211::RegisterListener (Phy80211Listener *listener)
+{
+  m_listeners.push_back (listener);
+}
+
+bool 
+Phy80211::IsStateCcaBusy (void)
+{
+  return GetState () == CCA_BUSY;
+}
+
+bool 
+Phy80211::IsStateIdle (void)
+{
+  return (GetState () == IDLE)?true:false;
+}
+bool 
+Phy80211::IsStateBusy (void)
+{
+  return (GetState () != IDLE)?true:false;
+}
+bool 
+Phy80211::IsStateSync (void)
+{
+  return (GetState () == SYNC)?true:false;
+}
+bool 
+Phy80211::IsStateTx (void)
+{
+  return (GetState () == TX)?true:false;
+}
+
+Time
+Phy80211::GetStateDuration (void)
+{
+  return Simulator::Now () - m_previousStateChangeTime;
+}
+Time
+Phy80211::GetDelayUntilIdle (void)
+{
+  Time retval;
+
+  switch (GetState ()) {
+  case SYNC:
+    retval = m_endSync - Simulator::Now ();
+    break;
+  case TX:
+    retval = m_endTx - Simulator::Now ();
+    break;
+  case CCA_BUSY:
+    retval = m_endCcaBusy - Simulator::Now ();
+    break;
+  case IDLE:
+    retval = Seconds (0);
+    break;
+  default:
+    assert (false);
+    // NOTREACHED
+    retval = Seconds (0);
+    break;
+  }
+  retval = Max (retval, Seconds (0));
+  return retval;
+}
+
+
+Time
+Phy80211::CalculateTxDuration (uint32_t size, uint8_t payloadMode) const
+{
+  uint64_t delay = m_plcpPreambleDelayUs;
+  delay += m_plcpHeaderLength * 1000000 / GetMode (0)->GetDataRate ();
+  uint64_t nbits = size * 8;
+  delay += nbits * 1000000 / GetMode (payloadMode)->GetDataRate ();
+  return MicroSeconds (delay);
+}
+
+char const *
+Phy80211::StateToString (enum Phy80211State state)
+{
+  switch (state) {
+  case TX:
+    return "TX";
+    break;
+  case CCA_BUSY:
+    return "CCA_BUSY";
+    break;
+  case IDLE:
+    return "IDLE";
+    break;
+  case SYNC:
+    return "SYNC";
+    break;
+  default:
+    return "XXX";
+    break;
+  }
+}
+enum Phy80211::Phy80211State 
+Phy80211::GetState (void)
+{
+  if (m_endTx > Simulator::Now ()) 
+    {
+      return Phy80211::TX;
+    } 
+  else if (m_syncing) 
+    {
+      return Phy80211::SYNC;
+    } 
+  else if (m_endCcaBusy > Simulator::Now ()) 
+    {
+      return Phy80211::CCA_BUSY;
+    } 
+  else 
+    {
+      return Phy80211::IDLE;
+    }
+}
+
+double 
+Phy80211::DbToRatio (double dB) const
+{
+  double ratio = pow(10.0,dB/10.0);
+  return ratio;
+}
+
+double 
+Phy80211::DbmToW (double dBm) const
+{
+  double mW = pow(10.0,dBm/10.0);
+  return mW / 1000.0;
+}
+
+double
+Phy80211::GetEdThresholdW (void) const
+{
+  return m_edThresholdW;
+}
+
+Time
+Phy80211::GetMaxPacketDuration (void) const
+{
+  return m_maxPacketDuration;
+}
+
+void
+Phy80211::AddTxRxMode (TransmissionMode *mode)
+{
+  m_modes.push_back (mode);
+}
+
+TransmissionMode *
+Phy80211::GetMode (uint8_t mode) const
+{
+  return m_modes[mode];
+}
+
+double 
+Phy80211::GetPowerDbm (uint8_t power) const
+{
+  assert (m_txPowerBaseDbm <= m_txPowerEndDbm);
+  assert (m_nTxPower > 0);
+  double dbm = m_txPowerBaseDbm + (m_txPowerEndDbm - m_txPowerBaseDbm) / m_nTxPower;
+  return dbm;
+}
+
+void 
+Phy80211::NotifyTxStart (Time duration)
+{
+  for (ListenersCI i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifyTxStart (duration);
+  }
+}
+void 
+Phy80211::NotifySyncStart (Time duration)
+{
+  for (ListenersCI i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifyRxStart (duration);
+  }
+}
+void 
+Phy80211::NotifySyncEndOk (void)
+{
+  for (ListenersCI i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifyRxEndOk ();
+  }
+}
+void 
+Phy80211::NotifySyncEndError (void)
+{
+  for (ListenersCI i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifyRxEndError ();
+  }
+}
+void 
+Phy80211::NotifyCcaBusyStart (Time duration)
+{
+  for (ListenersCI i = m_listeners.begin (); i != m_listeners.end (); i++) {
+    (*i)->NotifyCcaBusyStart (duration);
+  }
+}
+
+void
+Phy80211::LogPreviousIdleAndCcaBusyStates (void)
+{
+  Time now = Simulator::Now ();
+  Time idleStart = Max (m_endCcaBusy, m_endSync);
+  idleStart = Max (idleStart, m_endTx);
+  assert (idleStart <= now);
+  if (m_endCcaBusy > m_endSync && 
+      m_endCcaBusy > m_endTx) {
+    Time ccaBusyStart = Max (m_endTx, m_endSync);
+    ccaBusyStart = Max (ccaBusyStart, m_startCcaBusy);
+    m_stateLogger (ccaBusyStart, idleStart - ccaBusyStart, 2);
+  }
+  m_stateLogger (idleStart, now - idleStart, 3);
+}
+
+void
+Phy80211::SwitchToTx (Time txDuration)
+{
+  Time now = Simulator::Now ();
+  switch (GetState ()) {
+  case Phy80211::SYNC:
+    /* The packet which is being received as well
+     * as its endSync event are cancelled by the caller.
+     */
+    m_syncing = false;
+    m_stateLogger (m_startSync, now - m_startSync, 1);
+    break;
+  case Phy80211::CCA_BUSY: {
+    Time ccaStart = Max (m_endSync, m_endTx);
+    ccaStart = Max (ccaStart, m_startCcaBusy);
+    m_stateLogger (ccaStart, now - ccaStart, 2);
+  } break;
+  case Phy80211::IDLE:
+    LogPreviousIdleAndCcaBusyStates ();
+    break;
+  default:
+    assert (false);
+    break;
+  }
+  m_stateLogger (now, txDuration, 0);
+  m_previousStateChangeTime = now;
+  m_endTx = now + txDuration;
+  m_startTx = now;
+}
+void
+Phy80211::SwitchToSync (Time rxDuration)
+{
+  assert (IsStateIdle () || IsStateCcaBusy ());
+  assert (!m_syncing);
+  Time now = Simulator::Now ();
+  switch (GetState ()) {
+  case Phy80211::IDLE:
+    LogPreviousIdleAndCcaBusyStates ();
+    break;
+  case Phy80211::CCA_BUSY: {
+    Time ccaStart = Max (m_endSync, m_endTx);
+    ccaStart = Max (ccaStart, m_startCcaBusy);
+    m_stateLogger (ccaStart, now - ccaStart, 2);
+  } break;
+  case Phy80211::SYNC:
+  case Phy80211::TX:
+    assert (false);
+    break;
+  }
+  m_previousStateChangeTime = now;
+  m_syncing = true;
+  m_startSync = now;
+  m_endSync = now + rxDuration;
+  assert (IsStateSync ());
+}
+void
+Phy80211::SwitchFromSync (void)
+{
+  assert (IsStateSync ());
+  assert (m_syncing);
+
+  Time now = Simulator::Now ();
+  m_stateLogger (m_startSync, now - m_startSync, 1);
+  m_previousStateChangeTime = now;
+  m_syncing = false;
+
+  assert (IsStateIdle () || IsStateCcaBusy ());
+}
+void
+Phy80211::SwitchMaybeToCcaBusy (Time duration)
+{
+  Time now = Simulator::Now ();
+  switch (GetState ()) {
+  case Phy80211::IDLE:
+    LogPreviousIdleAndCcaBusyStates ();
+  break;
+  case Phy80211::CCA_BUSY:
+    break;
+  case Phy80211::SYNC:
+    break;
+  case Phy80211::TX:
+    break;
+  }
+  m_startCcaBusy = now;
+  m_endCcaBusy = Max (m_endCcaBusy, now + duration);
+}
+
+void 
+Phy80211::AppendEvent (Ptr<RxEvent> event)
+{
+  /* attempt to remove the events which are 
+   * not useful anymore. 
+   * i.e.: all events which end _before_
+   *       now - m_maxPacketDuration
+   */
+  
+  if (Simulator::Now () > GetMaxPacketDuration ())
+    {
+      Time end = Simulator::Now () - GetMaxPacketDuration ();
+      Events::iterator i = m_events.begin ();
+      while (i != m_events.end () &&
+             (*i)->GetEndTime () <= end) 
+        {
+          i++;
+        }
+      m_events.erase (m_events.begin (), i);
+    } 
+  m_events.push_back (event);
+}
+
+
+
+/**
+ * Stuff specific to the BER model here.
+ */
+
+double
+Phy80211::CalculateSnr (double signal, double noiseInterference, TransmissionMode *mode) const
+{
+  // thermal noise at 290K in J/s = W
+  static const double BOLTZMANN = 1.3803e-23;
+  double Nt = BOLTZMANN * 290.0 * mode->GetSignalSpread ();
+  // receiver noise Floor (W)
+  double noiseFloor = m_rxNoiseRatio * Nt;
+  double noise = noiseFloor + noiseInterference;
+  double snr = signal / noise;
+  return snr;
+}
+
+double
+Phy80211::CalculateNoiseInterferenceW (Ptr<RxEvent> event, NiChanges *ni) const
+{
+  Events::const_iterator i = m_events.begin ();
+  double noiseInterference = 0.0;
+  while (i != m_events.end ()) 
+    {
+      if (event == (*i)) 
+        {
+          i++;
+          continue;
+        }
+      if (event->Overlaps ((*i)->GetStartTime ())) 
+        {
+          ni->push_back (NiChange ((*i)->GetStartTime (), (*i)->GetRxPowerW ()));
+        }
+      if (event->Overlaps ((*i)->GetEndTime ())) 
+        {
+          ni->push_back (NiChange ((*i)->GetEndTime (), -(*i)->GetRxPowerW ()));
+        }
+      if ((*i)->Overlaps (event->GetStartTime ())) 
+        {
+          noiseInterference += (*i)->GetRxPowerW ();
+        }
+      i++;
+    }
+  ni->push_back (NiChange (event->GetStartTime (), noiseInterference));
+  ni->push_back (NiChange (event->GetEndTime (), 0));
+
+  /* quicksort vector of NI changes by time. */
+  std::sort (ni->begin (), ni->end (), std::less<NiChange> ());
+
+  return noiseInterference;
+}
+
+double
+Phy80211::CalculateChunkSuccessRate (double snir, Time duration, TransmissionMode *mode) const
+{
+  if (duration == NanoSeconds (0)) {
+    return 1.0;
+  }
+  uint32_t rate = mode->GetRate ();
+  uint64_t nbits = (uint64_t)(rate * duration.GetSeconds ());
+  double csr = mode->GetChunkSuccessRate (snir, (uint32_t)nbits);
+  return csr;
+}
+
+double 
+Phy80211::CalculatePer (Ptr<const RxEvent> event, NiChanges *ni) const
+{  
+  double psr = 1.0; /* Packet Success Rate */
+  NiChangesI j = ni->begin ();
+  Time previous = (*j).GetTime ();
+  Time plcpHeaderStart = (*j).GetTime () + MicroSeconds (m_plcpPreambleDelayUs);
+  Time plcpPayloadStart = plcpHeaderStart + 
+    Seconds (m_plcpHeaderLength / GetMode (event->GetHeaderMode ())->GetDataRate ());
+  double noiseInterferenceW = (*j).GetDelta ();
+  double powerW = event->GetRxPowerW ();
+  TransmissionMode *payloadMode = GetMode (event->GetPayloadMode ());
+  TransmissionMode *headerMode = GetMode (event->GetHeaderMode ());
+
+  j++;
+  while (ni->end () != j) 
+    {
+      Time current = (*j).GetTime ();
+      assert (current >= previous);
+    
+      if (previous >= plcpPayloadStart) 
+        {
+          psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                          noiseInterferenceW, payloadMode), 
+                                            current - previous,
+                                            payloadMode);
+        } 
+      else if (previous >= plcpHeaderStart) 
+        {
+          if (current >= plcpPayloadStart) 
+            {
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              headerMode), 
+                                                plcpPayloadStart - previous,
+                                                headerMode);
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              payloadMode),
+                                                current - plcpPayloadStart,
+                                                payloadMode);
+            } 
+          else 
+            {
+              assert (current >= plcpHeaderStart);
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              headerMode), 
+                                                current - previous,
+                                                headerMode);
+            }
+        } 
+      else 
+        {
+          if (current >= plcpPayloadStart) 
+            {
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              headerMode), 
+                                                plcpPayloadStart - plcpHeaderStart,
+                                                headerMode);
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              payloadMode), 
+                                                current - plcpPayloadStart,
+                                                payloadMode);
+            } 
+          else if (current >= plcpHeaderStart) 
+            {
+              psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, 
+                                                              noiseInterferenceW, 
+                                                              headerMode), 
+                                                current - plcpHeaderStart,
+                                                headerMode);
+            }
+        }
+
+      noiseInterferenceW += (*j).GetDelta ();
+      previous = (*j).GetTime ();
+      j++;
+    }
+
+  double per = 1 - psr;
+  return per;
+}
+
+
+void
+Phy80211::EndSync (Packet const packet, Ptr<RxEvent> event, uint8_t stuff)
+{
+  assert (IsStateSync ());
+  assert (event->GetEndTime () == Simulator::Now ());
+
+  NiChanges ni;
+  double noiseInterferenceW = CalculateNoiseInterferenceW (event, &ni);
+  double snr = CalculateSnr (event->GetRxPowerW (),
+                             noiseInterferenceW,
+                             GetMode (event->GetPayloadMode ()));
+  
+  /* calculate the SNIR at the start of the packet and accumulate
+   * all SNIR changes in the snir vector.
+   */
+  double per = CalculatePer (event, &ni);
+  TRACE ("mode="<<((uint32_t)event->GetPayloadMode ())<<
+         ", ber="<<(1-GetMode (event->GetPayloadMode ())->GetChunkSuccessRate (snr, 1))<<
+         ", snr="<<snr<<", per="<<per<<", size="<<packet.GetSize ());
+  
+  if (m_random.GetValue () > per) 
+    {
+      m_endSyncLogger (true);
+      NotifySyncEndOk ();
+      SwitchFromSync ();
+      m_syncOkCallback (packet, snr, event->GetPayloadMode (), stuff);
+    } 
+  else 
+    {
+      /* failure. */
+      m_endSyncLogger (false);
+      NotifySyncEndError ();
+      SwitchFromSync ();
+      m_syncErrorCallback (packet, snr);
+    }
+  event->Unref ();
+}
+
+
+
+
+}; // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/wifi-phy.h	Tue Oct 02 12:41:04 2007 +0200
@@ -0,0 +1,234 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2005,2006 INRIA
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as 
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ */
+
+#ifndef PHY_80211_H
+#define PHY_80211_H
+
+#include <vector>
+#include <list>
+#include <stdint.h>
+#include "ns3/callback.h"
+#include "ns3/event-id.h"
+#include "ns3/packet.h"
+#include "ns3/callback-trace-source.h"
+#include "ns3/nstime.h"
+#include "ns3/ptr.h"
+#include "ns3/random-variable.h"
+
+
+namespace ns3 {
+
+class TransmissionMode;
+class PropagationModel;
+class RandomUniform;
+class RxEvent;
+class TraceContainer;
+
+class Phy80211Listener {
+public:
+  virtual ~Phy80211Listener ();
+
+  /* we have received the first bit of a packet. We decided
+   * that we could synchronize on this packet. It does not mean
+   * we will be able to successfully receive completely the
+   * whole packet. It means we will report a BUSY status.
+   * r.end will be invoked later to report whether or not
+   * the packet was successfully received.
+   */
+  virtual void NotifyRxStart (Time duration) = 0;
+  /* we have received the last bit of a packet for which
+   * rxStart was invoked first. 
+   */
+  virtual void NotifyRxEndOk (void) = 0;
+  virtual void NotifyRxEndError (void) = 0;
+  /* we start the transmission of a packet.
+   */
+  virtual void NotifyTxStart (Time duration) = 0;
+  virtual void NotifyCcaBusyStart (Time duration) = 0;
+};
+
+
+
+class Phy80211
+{
+public:
+  typedef Callback<void,Packet const , double, uint8_t, uint8_t> SyncOkCallback;
+  typedef Callback<void,Packet const , double> SyncErrorCallback;
+
+  Phy80211 ();
+  virtual ~Phy80211 ();
+
+  void SetPropagationModel (PropagationModel *propagation);
+  void SetReceiveOkCallback (SyncOkCallback callback);
+  void SetReceiveErrorCallback (SyncErrorCallback callback);
+
+  /* rxPower unit is Watt */
+  void ReceivePacket (Packet const packet,
+                      double rxPowerW,
+                      uint8_t txMode,
+                      uint8_t stuff);
+  void SendPacket (Packet const packet, uint8_t txMode, uint8_t txPower, uint8_t stuff);
+
+  void RegisterListener (Phy80211Listener *listener);
+
+  bool IsStateCcaBusy (void);
+  bool IsStateIdle (void);
+  bool IsStateBusy (void);
+  bool IsStateSync (void);
+  bool IsStateTx (void);
+  Time GetStateDuration (void);
+  Time GetDelayUntilIdle (void);
+
+  Time CalculateTxDuration (uint32_t size, uint8_t payloadMode) const;
+
+  void Configure80211a (void);
+  void SetEdThresholdDbm (double rxThreshold);
+  void SetRxNoiseDb (double rxNoise);  
+  void SetTxPowerIncrementsDbm (double txPowerBase, 
+              double txPowerEnd, 
+              int nTxPower);
+  uint32_t GetNModes (void) const;
+  uint32_t GetModeBitRate (uint8_t mode) const;
+  uint32_t GetNTxpower (void) const;
+  /* return snr: W/W */
+  double CalculateSnr (uint8_t txMode, double ber) const;
+
+private:
+  enum Phy80211State {
+    SYNC,
+    TX,
+    CCA_BUSY,
+    IDLE
+  };
+  class NiChange {
+  public:
+    NiChange (Time time, double delta);
+    Time GetTime (void) const;
+    double GetDelta (void) const;
+    bool operator < (NiChange const &o) const;
+  private:
+    Time m_time;
+    double m_delta;
+  };
+  typedef std::vector<TransmissionMode *> Modes;
+  typedef std::vector<TransmissionMode *>::const_iterator ModesCI;
+  typedef std::list<Phy80211Listener *> Listeners;
+  typedef std::list<Phy80211Listener *>::const_iterator ListenersCI;
+  typedef std::list<Ptr<RxEvent> > Events;
+  typedef std::vector <NiChange> NiChanges;
+  typedef std::vector <NiChange>::iterator NiChangesI;
+
+private:  
+  char const *StateToString (enum Phy80211State state);
+  enum Phy80211State GetState (void);
+  double GetEdThresholdW (void) const;
+  double DbmToW (double dbm) const;
+  double DbToRatio (double db) const;
+  Time GetMaxPacketDuration (void) const;
+  void AddTxRxMode (TransmissionMode *mode);
+  void CancelRx (void);
+  TransmissionMode *GetMode (uint8_t txMode) const;
+  double GetPowerDbm (uint8_t power) const;
+  void NotifyTxStart (Time duration);
+  void NotifyWakeup (void);
+  void NotifySyncStart (Time duration);
+  void NotifySyncEndOk (void);
+  void NotifySyncEndError (void);
+  void NotifyCcaBusyStart (Time duration);
+  void LogPreviousIdleAndCcaBusyStates (void);
+  void SwitchToTx (Time txDuration);
+  void SwitchToSync (Time syncDuration);
+  void SwitchFromSync (void);
+  void SwitchMaybeToCcaBusy (Time duration);
+  void AppendEvent (Ptr<RxEvent> event);
+  double CalculateNoiseInterferenceW (Ptr<RxEvent> event, NiChanges *ni) const;
+  double CalculateSnr (double signal, double noiseInterference, TransmissionMode *mode) const;
+  double CalculateChunkSuccessRate (double snir, Time delay, TransmissionMode *mode) const;
+  double CalculatePer (Ptr<const RxEvent> event, NiChanges *ni) const;
+  void EndSync (Packet const packet, Ptr<RxEvent> event, uint8_t stuff);
+  double GetSnrForBer (TransmissionMode *mode, double ber) const;
+private:
+  uint64_t m_txPrepareDelayUs;
+  uint64_t m_plcpPreambleDelayUs;
+  uint32_t m_plcpHeaderLength;
+  Time     m_maxPacketDuration;
+
+  double   m_edThresholdW; /* unit: W */
+  double   m_rxNoiseRatio;
+  double   m_txPowerBaseDbm;
+  double   m_txPowerEndDbm;
+  uint32_t m_nTxPower;
+
+  
+  bool m_syncing;
+  Time m_endTx;
+  Time m_endSync;
+  Time m_endCcaBusy;
+  Time m_startTx;
+  Time m_startSync;
+  Time m_startCcaBusy;
+  Time m_previousStateChangeTime;
+
+  PropagationModel *m_propagation;
+  SyncOkCallback m_syncOkCallback;
+  SyncErrorCallback m_syncErrorCallback;
+  Modes m_modes;
+  Listeners m_listeners;
+  EventId m_endSyncEvent;
+  Events m_events;
+  UniformVariable m_random;
+  /* param1: - true: sync completed ok
+   *   - false: sync completed with failure
+   * Invoked when the last bit of a Signal (which was
+   * synchronized upon) is received.
+   * Reports whether or not the signal was received
+   * successfully.
+   */
+  CallbackTraceSource<bool> m_endSyncLogger;
+  /* param1: Duration 
+   * param2: signal Energy (w)
+   * Invoked whenever the first bit of a signal is received.
+   */
+  CallbackTraceSource<Time,double> m_startRxLogger;
+  /* param1: Duration
+   * param2: signal Energy (w)
+   * Invoked whenever the first bit of a signal is 
+   * synchronized upon.
+   */
+  CallbackTraceSource<Time,double> m_startSyncLogger;
+  /* param1: Duration 
+   * param2: tx Mode (bit rate: bit/s)
+   * param3: tx Power (dbm)
+   * Invoked whenever we send the first bit of a signal.
+   */
+  CallbackTraceSource<Time, uint32_t, double> m_startTxLogger;
+  /* 80211-phy-state
+   * param1: Start
+   * param2: Duration
+   * param3: state: 0 -> TX, 1 -> SYNC, 2 -> CCA, 3 -> IDLE
+   */
+  CallbackTraceSource<Time,Time,uint8_t> m_stateLogger;
+};
+
+}; // namespace ns3
+
+
+#endif /* PHY_80211_H */