/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* 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: Junling Bu <linlinjavaer@gmail.com>
*/
#include "channel-coordinator.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("ChannelCoordinator");
/****************************************************************
* This destructor is needed.
****************************************************************/
ChannelCoordinationListener::~ChannelCoordinationListener (void)
{
}
/****************************************************************/
NS_OBJECT_ENSURE_REGISTERED (ChannelCoordinator);
TypeId
ChannelCoordinator::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ChannelCoordinator")
.SetParent<Object> ()
.SetGroupName ("Wave")
.AddConstructor<ChannelCoordinator> ()
.AddAttribute ("CchInterval", "CCH Interval, default value is 50ms.",
TimeValue (GetDefaultCchInterval ()),
MakeTimeAccessor (&ChannelCoordinator::m_cchi),
MakeTimeChecker ())
.AddAttribute ("SchInterval", "SCH Interval, default value is 50ms.",
TimeValue (GetDefaultSchInterval ()),
MakeTimeAccessor (&ChannelCoordinator::m_schi),
MakeTimeChecker ())
.AddAttribute ("GuardInterval", "Guard Interval, default value is 4ms.",
TimeValue (GetDefaultGuardInterval ()),
MakeTimeAccessor (&ChannelCoordinator::m_gi),
MakeTimeChecker ())
;
return tid;
}
ChannelCoordinator::ChannelCoordinator ()
: m_guardCount (0)
{
NS_LOG_FUNCTION (this);
}
ChannelCoordinator::~ChannelCoordinator ()
{
NS_LOG_FUNCTION (this);
}
void
ChannelCoordinator::DoInitialize (void)
{
NS_LOG_FUNCTION (this);
StartChannelCoordination ();
}
void
ChannelCoordinator::DoDispose (void)
{
NS_LOG_FUNCTION (this);
StopChannelCoordination ();
UnregisterAllListeners ();
}
Time
ChannelCoordinator::GetDefaultCchInterval (void)
{
NS_LOG_FUNCTION_NOARGS ();
// refer to Annex H of IEEE 1609.4-2010
const static uint8_t DEFAULT_CCH_INTERVAL = 50;
return MilliSeconds (DEFAULT_CCH_INTERVAL);
}
Time
ChannelCoordinator::GetDefaultSchInterval (void)
{
NS_LOG_FUNCTION_NOARGS ();
// refer to Annex H of IEEE 1609.4-2010
const static uint8_t DEFAULT_SCH_INTERVAL = 50;
return MilliSeconds (DEFAULT_SCH_INTERVAL);
}
Time
ChannelCoordinator::GetDefaultSyncInterval (void)
{
NS_LOG_FUNCTION_NOARGS ();
return GetDefaultCchInterval () + GetDefaultSchInterval ();
}
Time
ChannelCoordinator::GetDefaultGuardInterval (void)
{
NS_LOG_FUNCTION_NOARGS ();
// refer to Annex H of IEEE 1609.4-2010
const static uint8_t SyncTolerance = 2;
const static uint8_t MaxChSwitchTime = 2;
const static uint8_t DEFAULT_GUARD_INTERVAL = SyncTolerance + MaxChSwitchTime;
return MilliSeconds (DEFAULT_GUARD_INTERVAL);
}
void
ChannelCoordinator::SetCchInterval (Time cchInterval)
{
NS_LOG_FUNCTION (this << cchInterval);
m_cchi = cchInterval;
}
Time
ChannelCoordinator::GetCchInterval (void) const
{
NS_LOG_FUNCTION (this);
return m_cchi;
}
void
ChannelCoordinator::SetSchInterval (Time schInterval)
{
NS_LOG_FUNCTION (this << schInterval);
m_schi = schInterval;
}
Time
ChannelCoordinator::GetSchInterval (void) const
{
NS_LOG_FUNCTION (this);
return m_schi;
}
Time
ChannelCoordinator::GetSyncInterval (void) const
{
NS_LOG_FUNCTION (this);
return GetCchInterval () + GetSchInterval ();
}
void
ChannelCoordinator::SetGuardInterval (Time guard)
{
NS_LOG_FUNCTION (this);
m_gi = guard;
}
Time
ChannelCoordinator::GetGuardInterval (void) const
{
NS_LOG_FUNCTION (this);
return m_gi;
}
Time
ChannelCoordinator::GetSchSlot (void) const
{
NS_LOG_FUNCTION (this);
return m_schi - m_gi;
}
Time
ChannelCoordinator::GetCchSlot (void) const
{
NS_LOG_FUNCTION (this);
return m_cchi - m_gi;
}
bool
ChannelCoordinator::IsCchInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
Time future = GetIntervalTime (duration);
return (future < m_cchi);
}
bool
ChannelCoordinator::IsSchInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
return !IsCchInterval (duration);
}
bool
ChannelCoordinator::IsGuardInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
Time future = GetIntervalTime (duration);
// the interval is either in CchInterval or SchInterval
Time interval = future < m_cchi ? future : future - m_cchi;
return interval < m_gi;
}
bool
ChannelCoordinator::IsValidConfig (void) const
{
NS_LOG_FUNCTION (this);
if (GetCchInterval ().GetMilliSeconds () == 0 || GetSchInterval ().GetMilliSeconds () == 0
|| GetGuardInterval ().GetMilliSeconds () == 0)
{
NS_LOG_WARN ("the channel interval should not be zero");
return false;
}
// 1000 is 1000ms which is one UTC second
if ((1000 % GetSyncInterval ().GetMilliSeconds ()) != 0)
{
NS_LOG_WARN ("every UTC second shall be an integer number of SyncInterval");
return false;
}
if (GetCchInterval () <= GetGuardInterval ())
{
NS_LOG_WARN ("CCH Interval should be large than GuardInterval");
return false;
}
if (GetSchInterval () <= GetGuardInterval ())
{
NS_LOG_WARN ("SCH Interval should be large than GuardInterval");
return false;
}
// at last, GguardInterval should be larger than real channel switch time of PHY layer.
// However there is no method such as GetChannelSwitchTime in the WifiPhy to support test here.
return true;
}
Time
ChannelCoordinator::NeedTimeToCchInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
if (IsCchInterval (duration))
{
return MilliSeconds (0);
}
return GetSyncInterval () - GetIntervalTime (duration);
}
Time
ChannelCoordinator::NeedTimeToSchInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
if (IsSchInterval (duration))
{
return MilliSeconds (0);
}
return GetCchInterval () - GetIntervalTime (duration);
}
Time
ChannelCoordinator::NeedTimeToGuardInterval (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
if (IsGuardInterval (duration))
{
return MilliSeconds (0);
}
if (IsCchInterval (duration))
{
// the time to Guard Interval of SCH Interval
return (GetCchInterval () - GetIntervalTime (duration));
}
// the time to Guard Interval of next CCH Interval
return (GetSyncInterval () - GetIntervalTime (duration));
}
Time
ChannelCoordinator::GetIntervalTime (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
Time future = Now () + duration;
Time sync = GetSyncInterval ();
uint32_t n = future.GetMilliSeconds () / sync.GetMilliSeconds ();
return future - MilliSeconds (n * sync.GetMilliSeconds ());
}
Time
ChannelCoordinator::GetRemainTime (Time duration) const
{
NS_LOG_FUNCTION (this << duration);
return GetSyncInterval () - GetIntervalTime (duration);
}
void
ChannelCoordinator::RegisterListener (Ptr<ChannelCoordinationListener> listener)
{
NS_LOG_FUNCTION (this << listener);
NS_ASSERT (listener != 0);
m_listeners.push_back (listener);
}
void
ChannelCoordinator::UnregisterListener (Ptr<ChannelCoordinationListener> listener)
{
NS_LOG_FUNCTION (this << listener);
NS_ASSERT (listener != 0);
for (ListenersI i = m_listeners.begin (); i != m_listeners.end (); ++i)
{
if ((*i) == listener)
{
m_listeners.erase (i);
return;
}
}
}
void
ChannelCoordinator::UnregisterAllListeners (void)
{
NS_LOG_FUNCTION (this);
m_listeners.clear ();
}
void
ChannelCoordinator::StartChannelCoordination (void)
{
NS_LOG_FUNCTION (this);
Time now = Now ();
if ((now.GetMilliSeconds () % 1000) != 0)
{
// see chapter 5.5.2
NS_FATAL_ERROR ("the coordination event order should start with the beginning of 1 second");
}
if (!IsValidConfig ())
{
NS_FATAL_ERROR ("the channel intervals configured for channel coordination events are invalid");
}
m_guardCount = 0;
NotifyGuardSlot ();
}
void
ChannelCoordinator::StopChannelCoordination (void)
{
if (!m_coordination.IsExpired ())
{
m_coordination.Cancel ();
}
m_guardCount = 0;
}
void
ChannelCoordinator::NotifySchSlot (void)
{
NS_LOG_FUNCTION (this);
m_coordination = Simulator::Schedule (GetSchSlot (), &ChannelCoordinator::NotifyGuardSlot, this);
for (ListenersI i = m_listeners.begin (); i != m_listeners.end (); ++i)
{
(*i)->NotifySchSlotStart (GetSchSlot ());
}
}
void
ChannelCoordinator::NotifyCchSlot (void)
{
NS_LOG_FUNCTION (this);
m_coordination = Simulator::Schedule (GetCchSlot (), &ChannelCoordinator::NotifyGuardSlot, this);
for (ListenersI i = m_listeners.begin (); i != m_listeners.end (); ++i)
{
(*i)->NotifyCchSlotStart (GetCchSlot ());
}
}
void
ChannelCoordinator::NotifyGuardSlot (void)
{
NS_LOG_FUNCTION (this);
Time guardSlot = GetGuardInterval ();
bool inCchi = ((m_guardCount % 2) == 0);
if (inCchi)
{
m_coordination = Simulator::Schedule (guardSlot, &ChannelCoordinator::NotifyCchSlot, this);
}
else
{
m_coordination = Simulator::Schedule (guardSlot, &ChannelCoordinator::NotifySchSlot, this);
}
for (ListenersI i = m_listeners.begin (); i != m_listeners.end (); ++i)
{
(*i)->NotifyGuardSlotStart (guardSlot, inCchi);
}
m_guardCount++;
}
} // namespace ns3