INET Framework for OMNeT++/OMNEST
|
MAC module which provides non-persistent CSMA. More...
#include <CSMAMacLayer.h>
Public Member Functions | |
CSMAMacLayer () | |
virtual | ~CSMAMacLayer () |
Protected Member Functions | |
virtual void | initialize (int) |
Initialization of the module and some variables. | |
virtual void | registerInterface () |
Register the interface in InterfaceTable. | |
virtual void | finish () |
Delete all dynamically allocated objects of the module. | |
virtual void | handleLowerMsg (cPacket *) |
Handle packets from lower layer. | |
virtual void | handleCommand (cMessage *) |
Handle commands from upper layer. | |
virtual void | handleUpperMsg (cPacket *) |
Handle packets from upper layer. | |
virtual void | handleSelfMsg (cMessage *) |
Handle self messages such as timers. | |
virtual MacPkt * | encapsMsg (cPacket *netw) |
Encapsulate the given higher-layer packet into MacPkt. | |
virtual void | receiveChangeNotification (int category, const cPolymorphic *details) |
Called by the NotificationBoard whenever a change occurs we're interested in. | |
Protected Attributes | |
MACAddress | myMacAddr |
mac address | |
RadioState::State | radioState |
Current state of the radio (kept updated by receiveChangeNotification()) | |
cQueue | macQueue |
A queue to store packets from upper layer in case another packet is still waiting for transmission.. | |
int | queueLength |
length of the queue | |
cMessage * | timer |
Timer for backoff in case the channel is busy. | |
simtime_t | sendTime |
Used to store the last time a message was sent. |
MAC module which provides non-persistent CSMA.
This is an implementation of a simple non-persistent CSMA. The idea of nonpersistent CSMA is "listen before talk". Before attempting to transmit, a station will sense the medium for a carrier signal, which, if present, indicates that some other station is sending.
If the channel is busy a random waiting time is computed and after this time the channel is sensed again. Once the channel gets idle the message is sent. (State of the channel is obtained from SnrEval via NotificationBoard.)
An option of this module is to use a queue. If a packet from the upper layer arrives although there is still another packet waiting for its transmission or being transmitted the new packet can be stored in this queue. The length of this queue can be specified by the user in the omnetpp.ini file. By default the length is 0. If the queue is full or there is no queue (length = 0) new packet(s) will be deleted.
ATTENTION: Imagine the following scenario:
Several stations receive a broadcast request packet, usally exactly at the same time. They will all try to answer at exactly the same time, i.e. they all will sense the channel at exactly the same time and start to transmit because the channel was sensed idle by all of them. Therefore a small random delay should be built into one/some of the upper layers to simulate a random processing time!
The TestApplLayer e.g. implements such a processing delay!
CSMAMacLayer::CSMAMacLayer | ( | ) |
{ timer = NULL; }
CSMAMacLayer::~CSMAMacLayer | ( | ) | [virtual] |
{ cancelAndDelete(timer); }
MacPkt * CSMAMacLayer::encapsMsg | ( | cPacket * | netw | ) | [protected, virtual] |
Encapsulate the given higher-layer packet into MacPkt.
Encapsulates the received network-layer packet into a MacPkt and set all needed header fields.
Referenced by handleUpperMsg().
{ MacPkt *pkt = new MacPkt(netw->getName()); pkt->setBitLength(272); // copy dest address from the Control Info attached to the network // mesage by the network layer Ieee802Ctrl *ctrl = check_and_cast<Ieee802Ctrl *>(netw->removeControlInfo()); EV << "ctrl removed, mac addr=" << ctrl->getDest() << endl; pkt->setDestAddr(ctrl->getDest()); //delete the control info delete ctrl; //set the src address to own mac address pkt->setSrcAddr(myMacAddr); //encapsulate the network packet pkt->encapsulate(netw); EV << "pkt encapsulated\n"; return pkt; }
void CSMAMacLayer::finish | ( | ) | [protected, virtual] |
Delete all dynamically allocated objects of the module.
{ }
void CSMAMacLayer::handleCommand | ( | cMessage * | msg | ) | [protected, virtual] |
Handle commands from upper layer.
Implements WirelessMacBase.
{ // no commands supported by CSMAMacLayer error("Non-packet message arrived from higher layer: (%s)%s", msg->getClassName(), msg->getName()); }
void CSMAMacLayer::handleLowerMsg | ( | cPacket * | msg | ) | [protected, virtual] |
Handle packets from lower layer.
Compare the address of this Host with the destination address in frame. If they are equal or the frame is broadcast, we send this frame to the upper layer. If not delete it.
Implements WirelessMacBase.
{ MacPkt *mac = check_and_cast<MacPkt *>(msg); //only foward to upper layer if message is for me or broadcast if (mac->getDestAddr() == myMacAddr || mac->getDestAddr().isBroadcast()) { EV << "sending pkt to upper...\n"; sendUp(mac); } else { EV << "packet not for me, deleting...\n"; delete mac; } }
void CSMAMacLayer::handleSelfMsg | ( | cMessage * | msg | ) | [protected, virtual] |
Handle self messages such as timers.
After the timer expires try to retransmit the message by calling handleUpperMsg again.
Implements WirelessMacBase.
{ EV << "timer expired, calling handleUpperMsg again.. time: " << simTime() << endl; // timer expired for a buffered frame, try to send again handleUpperMsg((MacPkt *) msg->getContextPointer()); }
void CSMAMacLayer::handleUpperMsg | ( | cPacket * | msg | ) | [protected, virtual] |
Handle packets from upper layer.
First it has to be checked whether a frame is currently being transmitted or waiting to be transmitted. If so the newly arrived message is stored in a queue. If there is no queue or it is full print a warning.
Before transmitting a frame it is tested whether the channel is busy at the moment or not. If the channel is busy, a short random time will be generated and the MacPkt is buffered for this time, before a next attempt to send the packet is started.
If channel is idle the frame will be transmitted immediately.
Implements WirelessMacBase.
Referenced by handleSelfMsg().
{ MacPkt *mac = encapsMsg(msg); // message has to be queued if another message is waiting to be send // or if we are already trying to send another message // the comparison with sendTime is necessary so that concurrently // arriving messages are handled sequentially. As soon as one // message arrived at simTime() is passed to lower layers all other // messages arriving at the same time will be buffered. if (timer->isScheduled() || radioState == RadioState::TRANSMIT || sendTime == simTime()) { // if there is no queue the message will be deleted if (queueLength == 0) { EV << "New packet arrived though another is still waiting for being sent, " " and buffer size is zero. New packet is deleted.\n"; // TODO: Signal this to upper layer! delete mac; return; } // the queue is not full yet so we can queue the message if (macQueue.length() < queueLength) { EV << "already transmitting, putting pkt into queue...\n"; macQueue.insert(mac); return; } // queue is full, message has to be deleted else { EV << "New packet arrived, but queue is FULL, so new packet is deleted\n"; // TODO: Signal this to upper layer!! delete mac; return; } } // no message is scheduled for sending or currently being sent // check the radio status and transmit the message if the channel is // idle. Otherwise backoff for a random time and try again if (radioState == RadioState::IDLE) { EV << "CHANNEL IDLE, send...\n"; sendDown(mac); //store the sending time sendTime = simTime(); } else { timer->setContextPointer(mac); simtime_t randomTime = intuniform(0, 10) / 100.0; scheduleAt(simTime() + randomTime, timer); EV << "CHANNEL BUSY, I will try to retransmit at " << simTime() + randomTime << ".\n"; } }
void CSMAMacLayer::initialize | ( | int | stage | ) | [protected, virtual] |
Initialization of the module and some variables.
Reimplemented from WirelessMacBase.
{ WirelessMacBase::initialize(stage); if (stage == 0) { queueLength = hasPar("queueLength") ? (int)par("queueLength") : 0; EV << "queueLength = " << queueLength << endl; //subscribe for the information of the carrier sense nb->subscribe(this, NF_RADIOSTATE_CHANGED); // initialize the timer timer = new cMessage("backoff"); radioState = RadioState::IDLE; // until 1st receiveChangeNotification() // get registered in IInterfaceTable registerInterface(); } }
void CSMAMacLayer::receiveChangeNotification | ( | int | category, |
const cPolymorphic * | details | ||
) | [protected, virtual] |
Called by the NotificationBoard whenever a change occurs we're interested in.
Update the internal copy of the RadioState.
If the RadioState switched from TRANSMIT to IDLE and there are still messages in the queue, call handleUpperMsg in order to try to send those now.
Implements INotifiable.
{ Enter_Method("receiveChangeNotification(%s, %s)", notificationCategoryName(category), details?details->info().c_str() : "n/a"); printNotificationBanner(category, details); if (category == NF_RADIOSTATE_CHANGED) { // update the local copy of the radio state radioState = check_and_cast<RadioState *>(details)->getState(); // NOTE: we may be invoked during INIT STAGE 1 too, when SnrEval notifies us // about the initial radio state. This function has to work correctly // even when called during initialization phase! // if the channel is idle now, the queue is not empty and no timer // is scheduled, this means that sending the previous message is // complete and the next one can be taken out of the queue if (radioState == RadioState::IDLE && !macQueue.empty() && !timer->isScheduled()) { timer->setContextPointer(macQueue.pop()); simtime_t randomTime = intuniform(0, 10) / 100.0; scheduleAt(simTime() + randomTime, timer); EV << "taking next pkt out of queue, schedule at " << simTime() + randomTime << endl; } } }
void CSMAMacLayer::registerInterface | ( | ) | [protected, virtual] |
Register the interface in InterfaceTable.
Referenced by initialize().
{ InterfaceEntry *e = new InterfaceEntry(); // interface name: NetworkInterface module's name without special characters ([]) char *interfaceName = new char[strlen(getParentModule()->getFullName()) + 1]; char *d = interfaceName; for (const char *s = getParentModule()->getFullName(); *s; s++) if (isalnum(*s)) *d++ = *s; *d = '\0'; e->setName(interfaceName); delete [] interfaceName; const char *addrstr = par("address"); if (!strcmp(addrstr, "auto")) { // assign automatic address myMacAddr = MACAddress::generateAutoAddress(); // change module parameter from "auto" to concrete address par("address").setStringValue(myMacAddr.str().c_str()); } else { myMacAddr.setAddress(addrstr); } e->setMACAddress(myMacAddr); // generate interface identifier for IPv6 e->setInterfaceToken(myMacAddr.formInterfaceIdentifier()); // MTU on 802.11 = ? e->setMtu(1500); // FIXME // capabilities e->setBroadcast(true); e->setMulticast(true); e->setPointToPoint(false); // add IInterfaceTable *ift = InterfaceTableAccess().get(); ift->addInterface(e, this); }
cQueue CSMAMacLayer::macQueue [protected] |
A queue to store packets from upper layer in case another packet is still waiting for transmission..
Referenced by handleUpperMsg(), and receiveChangeNotification().
MACAddress CSMAMacLayer::myMacAddr [protected] |
mac address
Referenced by encapsMsg(), handleLowerMsg(), and registerInterface().
int CSMAMacLayer::queueLength [protected] |
length of the queue
Referenced by handleUpperMsg(), and initialize().
RadioState::State CSMAMacLayer::radioState [protected] |
Current state of the radio (kept updated by receiveChangeNotification())
Referenced by handleUpperMsg(), initialize(), and receiveChangeNotification().
simtime_t CSMAMacLayer::sendTime [protected] |
Used to store the last time a message was sent.
Referenced by handleUpperMsg().
cMessage* CSMAMacLayer::timer [protected] |
Timer for backoff in case the channel is busy.
Referenced by CSMAMacLayer(), handleUpperMsg(), initialize(), receiveChangeNotification(), and ~CSMAMacLayer().