/** * Jeff Rupp * Master's Thesis * 2005 * * * Will simulate the 802.11 protocol for carrier sense and collision avoidance: * * The MAC layer is a set of protocols which is responsible for maintaining order in * the use of a shared medium. The 802.11 standard specifies a carrier sense multiple * access with collision avoidance (CSMA/CA) protocol. In this protocol, when a node * receives a packet to be transmitted, it first listens to ensure no other node is * transmitting. If the channel is clear, it then transmits the packet. Otherwise, it * chooses a random "backoff factor" which determines the amount of time the node must * wait until it is allowed to transmit its packet. During periods in which the channel * is clear, the transmitting node decrements its backoff counter. (When the channel is * busy it does not decrement its backoff counter.) When the backoff counter reaches zero, * the node transmits the packet. Since the probability that two nodes will choose the * same backoff factor is small, collisions between packets are minimized. Collision * detection, as is employed in Ethernet, cannot be used for the radio frequency transmissions * of IEEE 802.11. The reason for this is that when a node is transmitting it cannot hear * any other node in the system which may be transmitting, since its own signal will drown * out any others arriving at the node. * * Whenever a packet is to be transmitted, the transmitting node first sends out a short * ready-to-send (RTS) packet containing information on the length of the packet. If the * receiving node hears the RTS, it responds with a short clear-to-send (CTS) packet. After * this exchange, the transmitting node sends its packet. When the packet is received * successfully, as determined by a cyclic redundancy check (CRC), the receiving node * transmits an acknowledgment (ACK) packet. This back-and-forth exchange is necessary * to avoid the "hidden node" problem, node A can * communicate with node B, and node B can communicate with node C. However, node A cannot * communicate node C. Thus, for instance, although node A may sense the channel to be clear, * node C may in fact be transmitting to node B. The protocol described above alerts node A * that node B is busy, and hence it must wait before transmitting its packet. * */ package jdr.mobisim; import java.util.*; import java.awt.*; import org.apache.log4j.*; public class Protocol_802_11 extends ProtocolIF implements EventCallbackIF { private static Logger m_logger = Logger.getLogger(Protocol_802_11.class); // this must be set to false during normal runs, or the logging will slow the // simulation horribly private static final boolean DO_QUEUE_LOGS = false; private static final boolean DO_EXCESSIVE_LOGS = false; private static final boolean DO_SCHEDULER_LOGS = false; public static final int s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER = 5; // actual PC's CPU mSec timeout waiting for a response public static final int s_CTS_WAIT_TICS = 100; // sim tics increment while waiting to timeout public static final int s_RESPONSE_TIMEOUT_SIM_TICS = 3000; // sim tics timeout waiting for a response public static final int s_RTS_RETRY_TIMEOUT_SIM_TICS_MULTIPLIER = 30; // sim tics timeout waiting for a CTS for our RTS private static int s_instanceCount = 0; private int m_instanceNum = 0; private PacketIF m_rtsPacket = null; private int m_expectedSequenceNumber = -1; private int m_seqNumToUseAsAck = -1; private int m_rtsPacketLengthInBits = 10; private PacketIF m_ctsPacket = null; private Random m_ctsAckRand = new Random(); protected java.lang.Object m_ctsLock = new java.lang.Object(); private int m_lastNodeWeSentCtsTo = -1; protected int m_transmitDestinationNodeNumber = -1; protected int m_historyTransmitDestinationNodeNumber = -1; protected long m_lastTrasmitTime = 0; // the protocol's own queue, to put ACK and CTS packets into, so we don't try to sent the CTS or ACK in the same thread protected Vector m_ackCtsPackets = new Vector(); protected java.lang.Object m_ackCtsQueueLock = new java.lang.Object(); private Vector m_nodesHeard = new Vector(); private EventCallbackIF m_nodeEventCallbackIf = null; private EDU.oswego.cs.dl.util.concurrent.Semaphore m_sem = new EDU.oswego.cs.dl.util.concurrent.Semaphore(0); private EDU.oswego.cs.dl.util.concurrent.Mutex m_lockTransmit = new EDU.oswego.cs.dl.util.concurrent.Mutex(); private boolean m_awaitingCts = false; private boolean m_awaitingAck = false; private java.lang.Object m_awaitingCtsAckLock = new java.lang.Object(); public Protocol_802_11() { // lock the seed and utilize to set up our cts/ack backoff interval try { m_lockSeed.acquire(); m_ctsAckRand.setSeed(s_randSeed); s_randSeed += m_ctsAckRand.nextInt(s_randSeed); s_randSeed = Math.abs(s_randSeed); m_lockSeed.release(); } catch(InterruptedException iex) { } m_instanceNum = s_instanceCount++; m_rtsPacket = PacketIF.getNewInstance(); byte [] rtsdata = new byte[MobilePacket.BEGIN_GENERIC_DATA_BYTE_OFFSET + PacketIF.CRC_SIZE_BYTES]; m_rtsPacket.setData(rtsdata); m_rtsPacket.insertInt(rtsdata, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsdata, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsdata, MobilePacket.SOURCE_NODE_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsdata, MobilePacket.DESTINATION_NODE_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsdata, MobilePacket.LAST_NODE_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsdata, MobilePacket.NEXT_NODE_BYTE_OFFSET, 0); rtsdata[MobilePacket.MESSAGE_LENGTH_BYTE_OFFSET] = 0; rtsdata[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET] = MobilePacket.RTS; m_rtsPacketLengthInBits = rtsdata.length * 8; } public void setNodeEventCallbackIf(EventCallbackIF nodeEvtIf) { m_nodeEventCallbackIf = nodeEvtIf; } public boolean AddPacket(PacketIF packet) { boolean addedToCtsAckQueue = false; // need to get the source node number, so we know if it is the continuance // of the conversation started with our CTS byte [] data = packet.getData(); int sourceNode = MobilePacket.extractInt(data, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int destinationNode = MobilePacket.extractInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); byte messageType = data[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET]; int ackNumber = MobilePacket.extractInt(data, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET); int seqNumber = MobilePacket.extractInt(data, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET); if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " wants packet added to ackCts queue from node: " + sourceNode); } long simTimeNow = Scheduler.getInstance().GetSimulationTime(); switch(messageType) { // ??? note that this method sends the CTS and the ACK back in the same thread that sent them // ??? yep, sure enough this breaks, as the ACK comes in before we are waiting for it case MobilePacket.INSIDE_CLUSTER: case MobilePacket.BETWEEN_CLUSTER: case MobilePacket.CHOOSE_HEADS: case MobilePacket.TELL_HEAD: case MobilePacket.REJECT_HEAD: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { synchronized(m_ackCtsQueueLock) { m_ackCtsPackets.addElement(packet); addedToCtsAckQueue = true; if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " added packet to ackCts queue from node: " + sourceNode); } Scheduler.getInstance().ScheduleEvent(this, simTimeNow, DO_SCHEDULER_LOGS); } } break; case MobilePacket.RTS: if(destinationNode == m_myNodeNumber) { synchronized(m_ackCtsQueueLock) { m_ackCtsPackets.addElement(packet); addedToCtsAckQueue = true; if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " added packet to ackCts queue from node: " + sourceNode); } Scheduler.getInstance().ScheduleEvent(this, simTimeNow, DO_SCHEDULER_LOGS); } } break; // take care of the CTS and ACK packets here in the protocol case MobilePacket.CTS: if(destinationNode == m_myNodeNumber) { // got the CTS NotifyProtocolThatNewDataHasArrived(ackNumber, seqNumber); } break; case MobilePacket.ACK: if(destinationNode == m_myNodeNumber) { // got the ACK NotifyProtocolThatNewDataHasArrived(ackNumber, seqNumber); } break; default: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { synchronized(m_ackCtsQueueLock) { m_ackCtsPackets.addElement(packet); addedToCtsAckQueue = true; if(DO_QUEUE_LOGS) { m_logger.debug("adding packet to ctsAck queue, from which it will be transfered to the receive queue"); } Scheduler.getInstance().ScheduleEvent(this, simTimeNow, DO_SCHEDULER_LOGS); } } break; } return addedToCtsAckQueue; } /** * scheduler callback method, to get our own thread to do the cts and ack packets in */ public boolean ExecuteEvent(long timeNow) { PacketIF packet = null; synchronized(m_ackCtsQueueLock) { if(m_ackCtsPackets.size() > 0) { packet = (PacketIF)m_ackCtsPackets.remove(0); } } if(packet != null) { byte [] data = packet.getData(); int sourceNode = MobilePacket.extractInt(data, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int destinationNode = MobilePacket.extractInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); byte messageType = data[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET]; int ackNumber = MobilePacket.extractInt(data, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET); int seqNumber = MobilePacket.extractInt(data, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET); if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " wants packet added to receive queue from node: " + sourceNode); } switch(messageType) { // ??? note that this method sends the CTS and the ACK back in the same thread that sent them // ??? yep, sure enough this breaks, as the ACK comes in before we are waiting for it case MobilePacket.INSIDE_CLUSTER: case MobilePacket.BETWEEN_CLUSTER: case MobilePacket.CHOOSE_HEADS: case MobilePacket.TELL_HEAD: case MobilePacket.REJECT_HEAD: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { byte xmitLevel = GetMinTransmitLevel(sourceNode); double xmitPwr = MobileNode.MIN_TRANSMIT_POWER_DBM + (m_helloTransmitIncrement * (xmitLevel+1)); SendAck(packet, timeNow, xmitPwr, DO_EXCESSIVE_LOGS); synchronized(m_packetQueueLock) { m_receivedPackets.addElement(packet); Scheduler.getInstance().ScheduleEvent(m_nodeEventCallbackIf, DO_SCHEDULER_LOGS); if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " added packet to queue from node: " + sourceNode); } } } break; case MobilePacket.RTS: if(destinationNode == m_myNodeNumber) { byte xmitLevel = GetMinTransmitLevel(sourceNode); double xmitPwr = MobileNode.MIN_TRANSMIT_POWER_DBM + (m_helloTransmitIncrement * (xmitLevel+1)); SendCts(packet, timeNow, xmitPwr, DO_EXCESSIVE_LOGS ); } break; // take care of the CTS and ACK packets here in the protocol case MobilePacket.CTS: case MobilePacket.ACK: m_logger.error("a CTS or ACK packet got through to node: "+m_myNodeNumber+ " from node: "+sourceNode+" this should have been taken care of during the AddPacket"); break; default: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { synchronized(m_packetQueueLock) { m_receivedPackets.addElement(packet); Scheduler.getInstance().ScheduleEvent(m_nodeEventCallbackIf, DO_SCHEDULER_LOGS); if(DO_QUEUE_LOGS) { m_logger.debug("protocol passing packet of type: "+ messageType + " on to node: "+m_myNodeNumber+ " packet added to queue"); } } } break; } } else { m_logger.warn("node: "+m_myNodeNumber+" ExecuteEvent called, but packet queue empty"); } return true; } /** * gets all the nodes in range of the transmitting node and sends the packet on * to them * @param packet The data to transmit * @param source the location of the source node * @param powerLevel the dBm to transmit at, this is used for the distance transmitted * * @return true if transmit was successful */ public boolean TransmitData(long xmitTime, int lengthInBits, PacketIF packet, jdr.utils.FloatPoint source, double powerLeveldBm) { return TransmitData(xmitTime, lengthInBits, packet, source, powerLeveldBm, false); } public boolean TransmitData(long xmitTime, int lengthInBits, PacketIF packet, jdr.utils.FloatPoint source, double powerLeveldBm, boolean doLogs) { boolean rc = false; boolean transmittedOK = false; long timePassed = 0; try { while(!transmittedOK) { long startTransmitTimeoutTime = xmitTime; int startTransmitTimeoutTics = 10; while(!m_lockTransmit.attempt(100)) { if((timePassed) > s_RESPONSE_TIMEOUT_SIM_TICS) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+" failed to lock transmit"); timePassed = 0; } Scheduler.getInstance().IncrementSimulationTics(startTransmitTimeoutTime, startTransmitTimeoutTics); timePassed += startTransmitTimeoutTics; startTransmitTimeoutTime += startTransmitTimeoutTics; } timePassed = 0; // check that we don't have a CTS outstanding, don't want to start a conversation if so synchronized(m_ctsLock) { if(m_lastNodeWeSentCtsTo != -1) { m_lockTransmit.release(); Scheduler.getInstance().IncrementSimulationTics(startTransmitTimeoutTime, startTransmitTimeoutTics); timePassed += startTransmitTimeoutTics; startTransmitTimeoutTime += startTransmitTimeoutTics; // sleep some real time to avoid killing the CPU Thread.sleep(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * startTransmitTimeoutTics); continue; } } PropagationIF prop = PropagationIF.getInstance(); // first check the carrier detect to see if anybody else is transmitting while(prop.CarrierDetect(xmitTime, source)) { // detected another carrier, backoff a little random amount xmitTime += GetNextBackoff(); } byte [] xmitData = packet.getData(); int sourceNode = packet.extractInt(xmitData, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int destNode = packet.extractInt(xmitData, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); m_transmitDestinationNodeNumber = destNode; m_historyTransmitDestinationNodeNumber = destNode; m_lastTrasmitTime = xmitTime; // then send RTS to the target node byte [] rtsData = m_rtsPacket.getData(); m_expectedSequenceNumber = PacketIF.GetNextSequenceNumber(); m_rtsPacket.insertInt(rtsData, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, m_expectedSequenceNumber); m_rtsPacket.insertInt(rtsData, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, 0); m_rtsPacket.insertInt(rtsData, MobilePacket.SOURCE_NODE_BYTE_OFFSET, sourceNode); m_rtsPacket.insertInt(rtsData, MobilePacket.DESTINATION_NODE_BYTE_OFFSET, destNode); /* don't care about the next/last node in RTS m_rtsPacket.insertInt(rtsData, MobilePacket.LAST_NODE_BYTE_OFFSET, sourceNode); m_rtsPacket.insertInt(rtsData, MobilePacket.NEXT_NODE_BYTE_OFFSET, destNode); */ double xmitWatts = (Math.pow(10.0, (powerLeveldBm / 10))) / 1000; // next calculate what portion of an hour based on the packet size and // our data rate. xmitWatts = (packet.getTransmitedBitCount() / m_dataRateBps) / s_SECONDS_PER_HOUR; // last thing to do to the packet before sending is insert the CRC PacketIF.AddCrc(rtsData); // Set thread to wait (needs to be notified when the node gets data???) try { long ctsTimeoutTime = xmitTime; long rtsRtransTime = xmitTime; int ctsTimeoutTics = GetNextCtsAckTimeout(); boolean gotCts = false; synchronized(m_awaitingCtsAckLock) { m_awaitingCts = true; } if(m_rtsPacket instanceof MobilePacket) { ((MobilePacket)m_rtsPacket).setTransmitTime(ctsTimeoutTime); } prop.TransmitData(ctsTimeoutTime, m_rtsPacketLengthInBits, m_rtsPacket, source, powerLeveldBm, doLogs); // this power inc will tell us how much power total consumed by RTS's for this node m_rtsPacket.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " acquiring semaphore to get the CTS from node: "+ destNode+ " for sequence number: "+ m_expectedSequenceNumber); } gotCts = m_sem.attempt(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * ctsTimeoutTics); long totalCtsTimePassed = 0; while(!gotCts) { // try sending the rts again, in case the other side was busy if((timePassed) > (s_RTS_RETRY_TIMEOUT_SIM_TICS_MULTIPLIER * ctsTimeoutTics)) { totalCtsTimePassed += timePassed; timePassed = 0; if(m_rtsPacket instanceof MobilePacket) { ((MobilePacket)m_rtsPacket).setTransmitTime(ctsTimeoutTime); } prop.TransmitData(rtsRtransTime, m_rtsPacketLengthInBits, m_rtsPacket, source, powerLeveldBm, doLogs); // this power inc will tell us how much power total consumed by RTS's for this node m_rtsPacket.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); } else { rtsRtransTime = ctsTimeoutTime; timePassed += ctsTimeoutTics; } if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " reacquiring semaphore to get the CTS from node: "+ destNode+ " time passed: "+timePassed); } gotCts = m_sem.attempt(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * ctsTimeoutTics); if(gotCts) { synchronized(m_awaitingCtsAckLock) { m_awaitingCts = false; } if(DO_EXCESSIVE_LOGS) { m_logger.debug("node: "+m_myNodeNumber+" got CTS ok from node: "+ m_transmitDestinationNodeNumber); } // break out of the while(!gotCts) loop, and continue break; } else { Scheduler.getInstance().IncrementSimulationTics(ctsTimeoutTime, ctsTimeoutTics); ctsTimeoutTime += ctsTimeoutTics; ctsTimeoutTics = GetNextCtsAckTimeout(); // decide if we need to let loose the transmit lock, then start over again if(totalCtsTimePassed > s_RESPONSE_TIMEOUT_SIM_TICS) { totalCtsTimePassed = 0; timePassed = 0; // sleep some sim tics and try again // need to release the m_lockTransmit while sleeping, since we've given up and don't // expect a CTS now. synchronized(m_awaitingCtsAckLock) { m_awaitingCts = false; m_lockTransmit.release(); } if(DO_EXCESSIVE_LOGS) { m_logger.debug("node: "+m_myNodeNumber+" timed out waiting for CTS from node: "+ m_transmitDestinationNodeNumber); } Thread.sleep(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * ctsTimeoutTics); // break to re-start the TransmitData break; } } } synchronized(m_awaitingCtsAckLock) { m_awaitingCts = false; } if(!gotCts) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " failed to get the CTS from node: "+ destNode+ " for sequence number: "+ m_expectedSequenceNumber+ " in: "+s_RESPONSE_TIMEOUT_SIM_TICS +" sim tics"); m_lockTransmit.release(); // need to sleep for a bit, to let somebody else try to contact us Scheduler.getInstance().IncrementSimulationTics(ctsTimeoutTime, ctsTimeoutTics); Thread.sleep(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * GetNextCtsAckTimeout()); // some random time // continue back to the start of the while(!transmittedOK) loop, restart the whole TransmitData continue; } } catch(InterruptedException iex) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+ " semaphore attempt got exception", iex); } // The thread only wakes when the data in the node's rcv queue is the CTS from // the node the sending node wants to talk to, due to matching the seq number // in the NotifyProtocolThatNewDataHasArrived method m_expectedSequenceNumber = PacketIF.GetNextSequenceNumber(); byte [] sendData = packet.getData(); packet.insertInt(sendData, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, m_expectedSequenceNumber); packet.insertInt(sendData, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, m_seqNumToUseAsAck); synchronized(m_awaitingCtsAckLock) { m_awaitingAck = true; } rc = prop.TransmitData(xmitTime,lengthInBits,packet,source,powerLeveldBm,doLogs); // this power inc will tell us how much power total consumed by RTS's for this node packet.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); // ??? Need to add collision detecting // once the data is sent, wait for the ack from the target node try { synchronized(m_awaitingCtsAckLock) { m_awaitingAck = true; } if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " acquiring semaphore to get the ACK from node: "+ destNode+ " for sequence number: "+ m_expectedSequenceNumber); } long timeingOutTime = xmitTime; int timeingOutTics = GetNextCtsAckTimeout(); boolean gotAck = m_sem.attempt(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * timeingOutTics); while(!gotAck) { // adapt the real PC clock to the sim tics if((timePassed) > s_RESPONSE_TIMEOUT_SIM_TICS) { synchronized(m_awaitingCtsAckLock) { m_awaitingAck = false; } timePassed = 0; break; } Scheduler.getInstance().IncrementSimulationTics(timeingOutTime, timeingOutTics); timePassed += timeingOutTics; timeingOutTime += timeingOutTics; timeingOutTics = GetNextCtsAckTimeout(); if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " reacquiring semaphore to get the ACK from node: "+ destNode+ " for sequence number: "+ m_expectedSequenceNumber); } gotAck = m_sem.attempt(s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER * timeingOutTics); } synchronized(m_awaitingCtsAckLock) { m_awaitingAck = false; } if(!gotAck) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+" source node: "+ sourceNode + " failed to get the ACK from node: "+ destNode+ " for sequence number: "+ m_expectedSequenceNumber+ " in: "+ s_RESPONSE_TIMEOUT_SIM_TICS +" sim tics"); m_lockTransmit.release(); continue; } else { if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+ " node: "+m_myNodeNumber+" got ACK from node: "+ destNode); } transmittedOK = true; } } catch(InterruptedException iex) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+ " semaphore attempt got exception", iex); } m_lockTransmit.release(); } } catch(java.lang.InterruptedException iex) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+ " lock transmit acquire got exception", iex); } m_transmitDestinationNodeNumber = -1; // got the ack, so we can finally return return rc; } /** * send the CTS message to the node that sent us the RTS */ protected void SendCts(PacketIF requestingPacket, long xmitTime, double xmitPwr) { SendCts(requestingPacket, xmitTime, xmitPwr, false); } protected void SendCts(PacketIF requestingPacket, long xmitTime, double xmitPwr, boolean do_logs) { byte [] reqdata = requestingPacket.getData(); int sourceNode = MobilePacket.extractInt(reqdata, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int reqSeqNum = MobilePacket.extractInt(reqdata, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET); if(conversationInProgress()) { // don't send a CTS if we have already sent one and haven't gotten the remainder of the conversation m_logger.error("node: "+m_myNodeNumber+ " can't send CTS to node: "+ sourceNode + " conversationInProgress to node: " + m_transmitDestinationNodeNumber); if(sourceNode != m_transmitDestinationNodeNumber) // allow send CTS to node we're trying to talk to { return; } } synchronized(m_ctsLock) { if(m_lastNodeWeSentCtsTo == -1) { m_lastNodeWeSentCtsTo = sourceNode; } else if(m_lastNodeWeSentCtsTo != sourceNode) { // we've got a CTS outstanding, so we can't commit to Another conversation until we've completed this one // don't send a CTS if we have already sent one and haven't gotten the remainder of the conversation m_logger.error("node: "+m_myNodeNumber+ " can't send CTS to node: "+ sourceNode + ", have a CTS outstanding to node: " + m_lastNodeWeSentCtsTo); return; } } if(do_logs) { m_logger.debug("node: " + m_myNodeNumber + " sending CTS to node: "+sourceNode+ " at power: " + xmitPwr); } PropagationIF prop = PropagationIF.getInstance(); // first check the carrier detect to see if anybody else is transmitting while(prop.CarrierDetect(xmitTime, m_location)) { // detected another carrier, backoff a little random amount xmitTime += ProtocolIF.GetNextBackoff(); } // send the CTS back to the requestingNode PacketIF packet = PacketIF.getNewInstance(); byte [] data = new byte[MobilePacket.BEGIN_GENERIC_DATA_BYTE_OFFSET + PacketIF.CRC_SIZE_BYTES]; packet.setData(data); packet.insertInt(data, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, PacketIF.GetNextSequenceNumber()); packet.insertInt(data, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, reqSeqNum); packet.insertInt(data, MobilePacket.SOURCE_NODE_BYTE_OFFSET, m_myNodeNumber); packet.insertInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET, sourceNode); packet.insertInt(data, MobilePacket.LAST_NODE_BYTE_OFFSET, m_myNodeNumber); packet.insertInt(data, MobilePacket.NEXT_NODE_BYTE_OFFSET, sourceNode); packet.insertInt(data, MobilePacket.MESSAGE_NUMBER_BYTE_OFFSET, packet.GetNextMessageNumber()); data[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET] = MobilePacket.CTS; data[MobilePacket.MESSAGE_LENGTH_BYTE_OFFSET] = 0; double xmitWatts = (Math.pow(10.0, (xmitPwr / 10))) / 1000; // next calculate what portion of an hour based on the packet size and // our data rate. xmitWatts = (packet.getTransmitedBitCount() / m_dataRateBps) / s_SECONDS_PER_HOUR; // last thing to do to the packet before sending is insert the CRC PacketIF.AddCrc(data); boolean rc = prop.TransmitData(xmitTime, data.length * 8, packet, m_location, xmitPwr, DO_EXCESSIVE_LOGS); // this power inc will tell us how much power total consumed by RTS's for this node packet.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); } protected void SendAck(PacketIF packetToAck, long xmitTime, double xmitPwr) { SendAck(packetToAck, xmitTime, xmitPwr, false); } protected void SendAck(PacketIF packetToAck, long xmitTime, double xmitPwr, boolean do_logs) { byte [] toAckData = packetToAck.getData(); int sourceNode = MobilePacket.extractInt(toAckData, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int reqSeqNum = MobilePacket.extractInt(toAckData, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET); if(sourceNode == -1) { // don't ack broadcast return; } if(do_logs) { m_logger.debug("node: " + m_myNodeNumber + " sending ACK to node: "+sourceNode+ " with ack number: "+reqSeqNum+ " at power: " + xmitPwr); } PropagationIF prop = PropagationIF.getInstance(); // first check the carrier detect to see if anybody else is transmitting while(prop.CarrierDetect(xmitTime, m_location)) { // detected another carrier, backoff a little random amount xmitTime += ProtocolIF.GetNextBackoff(); } // send the CTS back to the requestingNode PacketIF packet = PacketIF.getNewInstance(); // the hello is a broadcast, so no need to do the whole RTS/CTS/Send/ACK sequence byte [] data = new byte[MobilePacket.BEGIN_GENERIC_DATA_BYTE_OFFSET + PacketIF.CRC_SIZE_BYTES]; packet.setData(data); // don't care about sequence number when broadcasting packet.insertInt(data, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, PacketIF.GetNextSequenceNumber()); packet.insertInt(data, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, reqSeqNum); packet.insertInt(data, MobilePacket.SOURCE_NODE_BYTE_OFFSET, m_myNodeNumber); packet.insertInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET, sourceNode); packet.insertInt(data, MobilePacket.LAST_NODE_BYTE_OFFSET, m_myNodeNumber); packet.insertInt(data, MobilePacket.NEXT_NODE_BYTE_OFFSET, sourceNode); packet.insertInt(data, MobilePacket.MESSAGE_NUMBER_BYTE_OFFSET, packet.GetNextMessageNumber()); data[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET] = MobilePacket.ACK; data[MobilePacket.MESSAGE_LENGTH_BYTE_OFFSET] = 0; double xmitWatts = (Math.pow(10.0, (xmitPwr / 10))) / 1000; // next calculate what portion of an hour based on the packet size and // our data rate. xmitWatts *= (packet.getTransmitedBitCount() / m_dataRateBps) / s_SECONDS_PER_HOUR; // last thing to do to the packet before sending is insert the CRC PacketIF.AddCrc(data); boolean rc = prop.TransmitData(xmitTime, data.length * 8, packet, m_location, xmitPwr, DO_EXCESSIVE_LOGS); // this power inc will tell us how much power total consumed by RTS's for this node packet.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); // finished the conversation we commited to via the last CTS we sent out synchronized(m_ctsLock) { m_lastNodeWeSentCtsTo = -1; } } public void AddNodeHeard(NodeHeard hrd) { synchronized(m_nodesHeard) { m_nodesHeard.addElement(hrd); } } protected byte GetMinTransmitLevel(int sourceNode) { byte xmitLevel = 0; // determine the transmit level needed to communicate with the // node we want to communicate with synchronized(m_nodesHeard) { int numHrd = m_nodesHeard.size(); for(int i = 0; i < numHrd; ++i) { NodeHeard testHrd = ((NodeHeard)m_nodesHeard.get(i)); if(testHrd.m_nodeNumber == sourceNode) { xmitLevel = testHrd.m_lowestLevelHeard; break; } } } if(DO_EXCESSIVE_LOGS) { m_logger.debug("node: " + m_myNodeNumber +" heard node: "+sourceNode + " at level: "+xmitLevel); } return xmitLevel; } public PacketIF GetNextPacket() { synchronized(m_ackCtsQueueLock) { return (PacketIF)m_ackCtsPackets.get(0); } } public boolean conversationInProgress() { boolean imTalkingNow = true; try { imTalkingNow = !m_lockTransmit.attempt(0); if(!imTalkingNow) { m_lockTransmit.release(); } } catch(InterruptedException iex) { m_logger.error("Protocol_802_11 instance: "+m_instanceNum+ " interrupted acquiring transmit lock"); } return imTalkingNow; } public boolean NotifyProtocolThatNewDataHasArrived(int ackNumber, int seqNumber) { if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+" node: "+m_myNodeNumber+ " received notify ack: "+ackNumber+" seq: "+seqNumber+ " expecting: "+m_expectedSequenceNumber+" as the ack"); } boolean rc = false; // wake up the thread if(ackNumber == m_expectedSequenceNumber) { m_seqNumToUseAsAck = seqNumber; rc = true; // only give the semaphore if we are currently waiting on it boolean awaiting = false; synchronized(m_awaitingCtsAckLock) { awaiting = m_awaitingCts || m_awaitingAck; } if(awaiting && (m_sem.permits() <= 0)) { while(m_sem.permits() < 1) { m_sem.release(); } if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+ " releasing ack/cts semaphore, permits after release: "+ m_sem.permits()); } } else { if(DO_EXCESSIVE_LOGS) { m_logger.debug("Protocol_802_11 instance: "+m_instanceNum+ " did not release ack/cts semaphore, permits: "+ m_sem.permits() +" m_awaitingCts or Ack: "+awaiting); } } } return rc; } protected int GetNextCtsAckTimeout() { return(m_ctsAckRand.nextInt(s_CTS_WAIT_TICS)+1); } public int getNodeNumber() { return m_myNodeNumber; } public void Draw(Graphics2D graphics, Rectangle bounds, double xScale, double yScale) { int destX = 0; int destY = 0; Color origColor = graphics.getColor(); Color lineColor = Color.red; NodeIF destnode = null; synchronized(m_awaitingCtsAckLock) { if(m_awaitingCts) // sent a RTS { destnode = AllNodes.Instance().GetNodeByNumber(m_transmitDestinationNodeNumber); lineColor = Color.magenta; } else if(m_awaitingAck) // sent a message { destnode = AllNodes.Instance().GetNodeByNumber(m_transmitDestinationNodeNumber); lineColor = Color.pink; } else if(m_lastNodeWeSentCtsTo != -1) // sent a CTS { destnode = AllNodes.Instance().GetNodeByNumber(m_lastNodeWeSentCtsTo); lineColor = Color.blue; } else if(m_transmitDestinationNodeNumber != -1) // some sort of conversation going on { destnode = AllNodes.Instance().GetNodeByNumber(m_transmitDestinationNodeNumber); lineColor = Color.yellow; } else if(System.getProperty("jdr.DrawLastTransmitInterval") != null) { int lastXmitInterval = Integer.parseInt(System.getProperty("jdr.DrawLastTransmitInterval")); if((Scheduler.getInstance().GetSimulationTime() - m_lastTrasmitTime) <= lastXmitInterval) { destnode = AllNodes.Instance().GetNodeByNumber(m_historyTransmitDestinationNodeNumber); lineColor = Color.blue; } } else if(System.getProperty("jdr.DrawLastTransmit") != null) { destnode = AllNodes.Instance().GetNodeByNumber(m_historyTransmitDestinationNodeNumber); lineColor = Color.white; } } if(destnode != null) { jdr.utils.FloatPoint loc = destnode.getLocation(); destX = (int)(loc.getX() / xScale) + 5; // add an offset, so concurrent lines destY = (int)(loc.getY() / yScale) + 5; // between 2 nodes aren't on top of each other graphics.setColor(lineColor); int srcX = (int)((m_location.getX() / xScale)); int srcY = (int)((m_location.getY() / yScale)); graphics.drawLine(srcX, srcY, destX, destY); graphics.drawString("S", srcX, srcY); // draw an arrow 1/2 way along the line to indicate direction graphics.setColor(origColor); } } public String toString() { return "Protocol_802_11 node number: " + m_myNodeNumber; } public String DumpPacketQueue() { String logStr = ""; if(m_lastNodeWeSentCtsTo != -1) { logStr += " last node we sent CTS to: "+m_lastNodeWeSentCtsTo; } logStr += super.DumpPacketQueue(); return logStr; } //============================================================================ // inner classes //============================================================================ } // end class definition