/** * Jeff Rupp * Master's Thesis * 2005 * * Uses the formula for free air propagation of radio: * pathloss in dB = 20*log10((1*pi*dist)/wavelength) * where wavelength is 300/(frequency in MHz) * the dist and wavelength are in the same units * I reverse this formula to say that at a path loss of 70 dB, what is the distance * The 70 dB is based on the Crossbow site which claims a 433 MHz mote can transmit * over a distance of 1000 feet * Based on the maximum power output of the 433MHz mote of 10dBm, the lowest power it * can receive at is -60dBm * * Conversion from watts to dBm: * dBm = 10 log [Signal (mW)/1mW] * */ package jdr.mobisim; import java.util.*; import org.apache.log4j.*; import java.awt.*; public class FreeAirPropagation extends PropagationIF { private static Logger m_logger = Logger.getLogger(FreeAirPropagation.class); private static final double FREQEUNCY_IN_MHZ = 433.0; // this must be set to false during normal runs, or the logging will slow the // simulation horribly private static final boolean DO_EXCESSIVE_LOGS = false; private static final boolean DO_NEW_TIP_LOGS = false; private static FreeAirPropagation s_instance = null; private static final double MAX_RANGE = 100.0; // need to limit our history for carrier detect. I have no idea what a good number will // be since we have the potential for lots of parallelism going on ??? private static final int s_CARRIER_DETECT_HISTORY_LENGTH = 100; private Vector m_carrierDetectHistory = new Vector(s_CARRIER_DETECT_HISTORY_LENGTH); public FreeAirPropagation() { } /** * 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) { // the distance is calculated based on free air radio propagation double dist = GetTransmitDistance(powerLeveldBm); // keep track of the blocked out area and duration of that block-out int xmitDuration = (int)(lengthInBits * ((1.0 / (double)m_dataRateBps) * Scheduler.s_ticsPerSecond)); TransmitInProgress cdHist = new TransmitInProgress(xmitTime, xmitTime + xmitDuration, source, dist); synchronized(m_carrierDetectHistory) { m_carrierDetectHistory.addElement(cdHist); if(m_carrierDetectHistory.size() > s_CARRIER_DETECT_HISTORY_LENGTH) { m_carrierDetectHistory.removeElement(m_carrierDetectHistory.get(0)); } } // increment the simulator clock for the time it takes to transmit Scheduler.getInstance().IncrementSimulationTics(xmitTime, xmitDuration); HashMap nodesInRange = AllNodes.Instance().GetNodesNear(source, dist, DO_EXCESSIVE_LOGS); if(DO_EXCESSIVE_LOGS) { m_logger.debug("TransmitData got " + nodesInRange.size() + " total nodes near"); } if(doLogs) { m_logger.debug("TransmitData packet: " + packet.toString()); } Iterator iter = nodesInRange.keySet().iterator(); while(iter.hasNext()) { NodeIF node = (NodeIF)(nodesInRange.get(iter.next())); boolean passPacket = true; if(s_performPacketRouting) { byte [] data = packet.getData(); int destinationNode = MobilePacket.extractInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); passPacket = ((destinationNode == -1 ) || (node.getNodeNumber() == destinationNode)); } if(passPacket) { node.ReceivePacket(packet.duplicate()); } } // set the transmit power on the packet packet.SetPacketTransmitPowerDbm(powerLeveldBm); // ??? Need to add collision detecting return true; } /** * @return true if another carrier is detected */ public boolean CarrierDetect(long xmitTime, jdr.utils.FloatPoint source) { if(DO_EXCESSIVE_LOGS) { m_logger.debug("Checking for carrier near node at " + source.getX() + ", " + source.getY() + " at time: "+xmitTime+ " carrier History Size: "+ m_carrierDetectHistory.size()); } boolean cts = false; synchronized(m_carrierDetectHistory) { Iterator iter = m_carrierDetectHistory.iterator(); while(iter.hasNext()) { TransmitInProgress tip = (TransmitInProgress)iter.next(); if(tip.IsCarrierAlreadyPresent(xmitTime, source)) { cts = true; break; } } } return cts; } /** * uses the standard free air transmission formula: * pathloss in dB = 20*log10((1*pi*dist)/wavelength) * distance is straight line with no obstacles */ public double GetReceiveSignalStrengthDbm(jdr.utils.FloatPoint source, jdr.utils.FloatPoint dest, double transmitPwrDbm) { // calculate distance between nodes, straight line with no obstacles double dist = Math.sqrt(Math.pow((dest.getX() - source.getX()),2) + Math.pow((dest.getY() - source.getY()),2)); // calculate how much attenuation has occured to the signal over this distance double dbLoss = 20*Math.log10((1*Math.PI*dist)/(300/FREQEUNCY_IN_MHZ)); return (transmitPwrDbm - dbLoss); } /** * uses the standard free air transmission formula: * pathloss in dB = 20*log10((1*pi*dist)/wavelength) * based on max power from a Crossbow mote can handle about 70 dB of path loss * and still be received by another mote. * The 433 MHz mote is specd at a max power output of 10dBm */ public double GetTransmitDistance(double powerIndBm) { double acceptableLossdB = 60.0 - (-powerIndBm); double dist = (Math.pow(10.0, (acceptableLossdB / 20.0)) / (4.0 * Math.PI)) * (300.0 / FREQEUNCY_IN_MHZ); if(DO_EXCESSIVE_LOGS) { m_logger.debug("distance: "+ dist+" for power in dBm of: "+ powerIndBm); } return dist; } public void DrawTransmitHistory(int historyCount, Graphics2D graphics, Rectangle bounds, double xScale, double yScale) { synchronized(m_carrierDetectHistory) { int index = m_carrierDetectHistory.size(); int stopIndex = index - historyCount; if(stopIndex < 0) { stopIndex = 0; } --index; for(; index >= stopIndex; --index) { TransmitInProgress tip = (TransmitInProgress)m_carrierDetectHistory.get (index); if(tip != null) { Color origColor = graphics.getColor(); graphics.setColor(Color.white); tip.Draw(graphics, bounds, xScale, yScale); graphics.setColor(origColor); } } } } public void DrawTransmitsAtTime(Graphics2D graphics, Rectangle bounds, double xScale, double yScale, long simTime) { synchronized(m_carrierDetectHistory) { long currentSimTime = Scheduler.getInstance().GetSimulationTime(); Iterator iter = m_carrierDetectHistory.iterator(); while(iter.hasNext()) { TransmitInProgress tip = (TransmitInProgress)iter.next(); if(tip != null) { if((tip.m_transmitStartTime >= currentSimTime) && (tip.m_transmitStopTime <= currentSimTime)) { Color origColor = graphics.getColor(); graphics.setColor(Color.yellow); tip.Draw(graphics, bounds, xScale, yScale); graphics.setColor(origColor); } } } } } //============================================================================ // inner classes //============================================================================ /** * Class to keep track of carrier detect block out area */ protected class TransmitInProgress { private Logger m_logger = Logger.getLogger(TransmitInProgress.class); public long m_transmitStartTime = 0; public long m_transmitStopTime = 0; public jdr.utils.FloatPoint m_sourceLocation=null; public double m_range = 0.0; public TransmitInProgress(long xmitStartTime, long xmitStopTime, jdr.utils.FloatPoint source, double range) { m_transmitStartTime = xmitStartTime; m_transmitStopTime = xmitStopTime; m_sourceLocation = source; m_range = range; if(DO_NEW_TIP_LOGS) { m_logger.debug("m_transmitStartTime: "+m_transmitStartTime+ " m_transmitStopTime: "+m_transmitStopTime+ " m_sourceLocation: "+m_sourceLocation.getX()+", "+m_sourceLocation.getY()+ " m_range: "+m_range); } } /** * This needs to be as fast as possible or it will slow the sim a lot */ public boolean IsCarrierAlreadyPresent(long xmitTime, jdr.utils.FloatPoint source) { boolean carrierDetected = false; // do the simple integer math first if((xmitTime >= m_transmitStartTime) && (xmitTime <= m_transmitStopTime)) { // transmit occurs at the same time, check if it is in range // have to do the slower floating point math double distanceFromOurSource = Math.sqrt(Math.pow((m_sourceLocation.getX() - source.getX()), 2) + Math.pow((m_sourceLocation.getY() - source.getY()), 2)); if(distanceFromOurSource <= m_range) { carrierDetected = true; } } return carrierDetected; } public void Draw(Graphics2D graphics, Rectangle bounds, double xScale, double yScale) { graphics.drawOval((int)((m_sourceLocation.getX() / xScale) - ((m_range / xScale) - 3)), (int)((m_sourceLocation.getY() / yScale) - ((m_range / yScale) - 3)), (int)(((m_range / xScale) * 2)), (int)(((m_range / yScale) * 2))); } } } // end class definition