diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java b/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java index 4024af655..b90d6de0e 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java @@ -696,7 +696,7 @@ public class AreaViewer extends VisPlugin { String logHtml = "" + - trackedComponents.log.replace("\n", "
") + + trackedComponents.log.replace("\n", "
").replace(" pi", " π") + ""; t.setTipText(logHtml); diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java b/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java index ee564dad9..0babcf6e2 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java @@ -1327,11 +1327,14 @@ public class ChannelModel { * the random variable mean, and the second is the variance. */ public double[] getReceivedSignalStrength(double sourceX, double sourceY, double destX, double destY) { - return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH); + return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH, null); } - + public double[] getReceivedSignalStrength(double sourceX, double sourceY, double destX, double destY, Double txPower) { + return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH, txPower); + } + // TODO Fix better data type support - private double[] getTransmissionData(double sourceX, double sourceY, double destX, double destY, TransmissionData dataType) { + private double[] getTransmissionData(double sourceX, double sourceY, double destX, double destY, TransmissionData dataType, Double txPower) { Point2D source = new Point2D.Double(sourceX, sourceY); Point2D dest = new Point2D.Double(destX, destY); double accumulatedVariance = 0; @@ -1515,7 +1518,12 @@ public class ChannelModel { // Using formula (dB) // Received power = Output power + System gain + Transmitter gain + Path Loss + Receiver gain // TODO Update formulas - double outputPower = getParameterDoubleValue("tx_power"); + double outputPower; + if (txPower == null) { + outputPower = getParameterDoubleValue("tx_power"); + } else { + outputPower = txPower; + } double systemGain = getParameterDoubleValue("system_gain_mean"); if (getParameterBooleanValue("apply_random")) { Random random = new Random(); /* TODO Use main random generator? */ @@ -1579,19 +1587,19 @@ public class ChannelModel { * variable. This method uses current parameters such as transmitted power, * obstacles, overall system loss etc. * - * @param sourceX - * Source position X - * @param sourceY - * Source position Y - * @param destX - * Destination position X - * @param destY - * Destination position Y - * @return Received SNR (dB) random variable. The first value is the random - * variable mean, and the second is the variance. The third value is the received signal strength which may be used in comparison with interference etc. + * @param sourceX Source position X + * @param sourceY Source position Y + * @param destX Destination position X + * @param destY Destination position Y + * @return Received SNR (dB) random variable: + * The first value in the array is the random variable mean. + * The second is the variance. + * The third value is the received signal strength which may be used in comparison with interference etc. */ public double[] getSINR(double sourceX, double sourceY, double destX, double destY, double interference) { - + /* TODO Cache values: called repeatedly with noise sources. */ + + // Calculate received signal strength double[] signalStrength = getReceivedSignalStrength(sourceX, sourceY, destX, destY); @@ -1619,27 +1627,26 @@ public class ChannelModel { snrData[1] += noiseVariance; if (logMode) { - logInfo.append("\nReceived SNR: " + String.format("%2.3f", snrData[0]) + " (variance " + snrData[1] + ")\n"); + logInfo.append("\nReceived SNR: " + String.format("%2.3f", snrData[0]) + " dB (variance " + snrData[1] + ")\n"); } return snrData; } /** - * Calculates and returns probability that a receiver at given destination receives a packet from a transmitter at given source. + * Calculates probability that a receiver at given destination receives + * a packet from a transmitter at given source. * This method uses current parameters such as transmitted power, - * obstacles, overall system loss, packet size etc. TODO Packet size?! TODO Interfering signal strength + * obstacles, overall system loss, packet size etc. + * + * TODO Packet size + * TODO External interference/Background noise * - * @param sourceX - * Source position X - * @param sourceY - * Source position Y - * @param destX - * Destination position X - * @param destY - * Destination position Y - * @param interference - * Current interference at destination (dBm) + * @param sourceX Source position X + * @param sourceY Source position Y + * @param destX Destination position X + * @param destY Destination position Y + * @param interference Current interference at destination (dBm) * @return [Probability of reception, signal strength at destination] */ public double[] getProbability(double sourceX, double sourceY, double destX, double destY, double interference) { @@ -1675,11 +1682,9 @@ public class ChannelModel { // current threshold. // (Using error algorithm method, much faster than taylor approximation!) - double probReception = 1 - GaussianWrapper.cdfErrorAlgo( - threshold, snrMean, snrStdDev); + double probReception = 1 - GaussianWrapper.cdfErrorAlgo(threshold, snrMean, snrStdDev); if (logMode) { - logInfo.append("\nReceived SNR: " + String.format("%2.3f", snrData[0]) + " (variance " + snrData[1] + ")\n"); logInfo.append("Reception probability: " + String.format("%1.1f%%", 100*probReception) + "\n"); } @@ -1703,7 +1708,7 @@ public class ChannelModel { * @return RMS delay spread */ public double getRMSDelaySpread(double sourceX, double sourceY, double destX, double destY) { - return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.DELAY_SPREAD)[1]; + return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.DELAY_SPREAD, null)[1]; } /** diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java b/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java index e44264579..383562221 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java @@ -43,13 +43,14 @@ import org.jdom.Element; import se.sics.cooja.ClassDescription; import se.sics.cooja.RadioConnection; import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.NoiseSourceRadio; +import se.sics.cooja.interfaces.NoiseSourceRadio.NoiseLevelListener; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; import se.sics.cooja.radiomediums.AbstractRadioMedium; /** - * This is the main class of the COOJA Multi-path Ray-tracing Medium (MRM) - * package. + * Multi-path Ray-tracing radio medium (MRM). * * MRM is an alternative to the simpler radio mediums available in * COOJA. It is packet based and uses a 2D ray-tracing approach to approximate @@ -57,22 +58,27 @@ import se.sics.cooja.radiomediums.AbstractRadioMedium; * ray-tracing only supports reflections and refractions through homogeneous * obstacles. * - * MRM registers two plugins: a plugin for visualizing the radio - * environments, and a plugin for configuring the radio medium parameters. + * MRM provides two plugins: one for visualizing the radio environment, + * and one for configuring the radio medium parameters. * * Future work includes adding support for diffraction and scattering. * + * MRM supports noise source radios. + * + * @see NoiseSourceRadio * @author Fredrik Osterlind */ @ClassDescription("Multi-path Ray-tracer Medium (MRM)") public class MRM extends AbstractRadioMedium { private static Logger logger = Logger.getLogger(MRM.class); - private ChannelModel currentChannelModel = null; - - private Random random = null; + public final static boolean WITH_NOISE = true; /* NoiseSourceRadio:s */ + public final static boolean WITH_CAPTURE_EFFECT = true; private Simulation sim; + private Random random = null; + private ChannelModel currentChannelModel = null; + /** * Notifies observers when this radio medium has changed settings. */ @@ -83,11 +89,9 @@ public class MRM extends AbstractRadioMedium { */ public MRM(Simulation simulation) { super(simulation); - sim = simulation; - - random = simulation.getRandomGenerator(); - // Create the channel model + sim = simulation; + random = simulation.getRandomGenerator(); currentChannelModel = new ChannelModel(); /* Register plugins */ @@ -95,6 +99,26 @@ public class MRM extends AbstractRadioMedium { sim.getGUI().registerPlugin(FormulaViewer.class); } + private NoiseLevelListener noiseListener = new NoiseLevelListener() { + public void noiseLevelChanged(NoiseSourceRadio radio, int signal) { + updateSignalStrengths(); + }; + }; + public void registerRadioInterface(Radio radio, Simulation sim) { + super.registerRadioInterface(radio, sim); + + if (radio instanceof NoiseSourceRadio) { + ((NoiseSourceRadio)radio).addNoiseLevelListener(noiseListener); + } + } + public void unregisterRadioInterface(Radio radio, Simulation sim) { + super.unregisterRadioInterface(radio, sim); + + if (radio instanceof NoiseSourceRadio) { + ((NoiseSourceRadio)radio).removeNoiseLevelListener(noiseListener); + } + } + public void removed() { super.removed(); @@ -103,90 +127,82 @@ public class MRM extends AbstractRadioMedium { sim.getGUI().unregisterPlugin(FormulaViewer.class); } - public MRMRadioConnection createConnections(Radio sendingRadio) { - Position sendingPosition = sendingRadio.getPosition(); - MRMRadioConnection newConnection = new MRMRadioConnection(sendingRadio); + public MRMRadioConnection createConnections(Radio sender) { + MRMRadioConnection newConnection = new MRMRadioConnection(sender); + Position senderPos = sender.getPosition(); - // Loop through all radios - for (Radio listeningRadio: getRegisteredRadios()) { - // Ignore sending radio and radios on different channels - if (sendingRadio == listeningRadio) { - continue; - } - if (sendingRadio.getChannel() >= 0 && - listeningRadio.getChannel() >= 0 && - sendingRadio.getChannel() != listeningRadio.getChannel()) { + /* TODO Cache potential destination in DGRM */ + /* Loop through all potential destinations */ + for (Radio recv: getRegisteredRadios()) { + if (sender == recv) { continue; } - /* TODO Use DGRM to cache link information. - * (No need to loop over all receivers) */ - - double listeningPositionX = listeningRadio.getPosition().getXCoordinate(); - double listeningPositionY = listeningRadio.getPosition().getYCoordinate(); + /* Fail if radios are on different (but configured) channels */ + if (sender.getChannel() >= 0 && + recv.getChannel() >= 0 && + sender.getChannel() != recv.getChannel()) { + continue; + } + Position recvPos = recv.getPosition(); - // Calculate probability of reception of listening radio + /* Calculate receive probability */ double[] probData = currentChannelModel.getProbability( - sendingPosition.getXCoordinate(), - sendingPosition.getYCoordinate(), - listeningPositionX, - listeningPositionY, - -Double.MAX_VALUE + senderPos.getXCoordinate(), + senderPos.getYCoordinate(), + recvPos.getXCoordinate(), + recvPos.getYCoordinate(), + -Double.MAX_VALUE /* TODO Include interference */ ); - //logger.info("Probability of reception is " + probData[0]); - //logger.info("Signal strength at destination is " + probData[1]); - if (random.nextFloat() < probData[0]) { - // Check if this radio is able to receive transmission - if (listeningRadio.isInterfered()) { - // Keep interfering radio - newConnection.addInterfered(listeningRadio, probData[1]); + double recvProb = probData[0]; + double recvSignalStrength = probData[1]; + if (recvProb == 1.0 || random.nextDouble() < recvProb) { + /* Yes, the receiver *may* receive this packet (it's strong enough) */ + if (!recv.isReceiverOn()) { + newConnection.addInterfered(recv); + recv.interfereAnyReception(); + } else if (recv.isInterfered()) { + /* Was interfered: keep interfering */ + newConnection.addInterfered(recv, recvSignalStrength); + } else if (recv.isTransmitting()) { + newConnection.addInterfered(recv, recvSignalStrength); + } else if (recv.isReceiving()) { + /* Was already receiving: start interfering. + * Assuming no continuous preambles checking */ + + double currSignal = recv.getCurrentSignalStrength(); + /* Capture effect: recv-radio is already receiving. + * Are we strong enough to interfere? */ + if (WITH_CAPTURE_EFFECT && + recvSignalStrength < currSignal - 3 /* config */) { + /* No, we are too weak */ + } else { + newConnection.addInterfered(recv, recvSignalStrength); + recv.interfereAnyReception(); - } else if (listeningRadio.isReceiving()) { - newConnection.addInterfered(listeningRadio, probData[1]); - - // Start interfering radio - listeningRadio.interfereAnyReception(); - - // Update connection that is transmitting to this radio - MRMRadioConnection existingConn = null; - for (RadioConnection conn : getActiveConnections()) { - for (Radio dstRadio : ((MRMRadioConnection) conn).getDestinations()) { - if (dstRadio == listeningRadio) { - existingConn = (MRMRadioConnection) conn; - break; + /* Interfere receiver in all other active radio connections */ + for (RadioConnection conn : getActiveConnections()) { + if (conn.isDestination(recv)) { + conn.addInterfered(recv); } } - } - if (existingConn != null) { - /* Flag radio as interfered */ - existingConn.addInterfered(listeningRadio); - listeningRadio.interfereAnyReception(); - } + } + } else { - // Radio OK to receive - //logger.info("OK, creating connection and starting to transmit"); - newConnection.addDestination(listeningRadio, probData[1]); + /* Success: radio starts receiving */ + newConnection.addDestination(recv, recvSignalStrength); } - } else if (probData[1] > currentChannelModel.getParameterDoubleValue("bg_noise_mean")) { - // Interfere radio - newConnection.addInterfered(listeningRadio, probData[1]); - listeningRadio.interfereAnyReception(); - - // TODO Radios always get interfered right now, should recalculate probability -// if (maxInterferenceSignalStrength + SOME_RELEVANT_LIMIT > transmissionSignalStrength) { -// // Recalculating probability of delivery -// double[] probData = currentChannelModel.getProbability( -// mySource.source.position.getXCoordinate(), -// mySource.source.position.getYCoordinate(), -// myDestination.position.getXCoordinate(), -// myDestination.position.getYCoordinate(), -// maxInterferenceSignalStrength); -// -// if (new Random().nextFloat() >= probData[0]) { -// return true; -// } + } else if (recvSignalStrength > currentChannelModel.getParameterDoubleValue("bg_noise_mean")) { + /* The incoming signal is strong, but strong enough to interfere? */ + if (!WITH_CAPTURE_EFFECT) { + newConnection.addInterfered(recv, recvSignalStrength); + recv.interfereAnyReception(); + } else { + /* TODO Implement new type: newConnection.addNoise()? + * Currently, this connection will never disturb this radio... */ + } } } @@ -195,57 +211,92 @@ public class MRM extends AbstractRadioMedium { } public void updateSignalStrengths() { - // // Save old signal strengths - // double[] oldSignalStrengths = new double[registeredRadios.size()]; - // for (int i = 0; i < registeredRadios.size(); i++) { - // oldSignalStrengths[i] = registeredRadios.get(i) - // .getCurrentSignalStrength(); - // } - // Reset signal strength on all radios + /* Reset: Background noise */ + double background = + currentChannelModel.getParameterDoubleValue(("bg_noise_mean")); for (Radio radio : getRegisteredRadios()) { - radio.setCurrentSignalStrength(currentChannelModel.getParameterDoubleValue(("bg_noise_mean"))); + radio.setCurrentSignalStrength(background); } - // Set signal strength on all OK transmissions - for (RadioConnection conn : getActiveConnections()) { -// ((MRMRadioConnection) conn).getSource().setCurrentSignalStrength(12345); // TODO Set signal strength on source? + /* Active radio connections */ + RadioConnection[] conns = getActiveConnections(); + for (RadioConnection conn : conns) { for (Radio dstRadio : ((MRMRadioConnection) conn).getDestinations()) { double signalStrength = ((MRMRadioConnection) conn).getDestinationSignalStrength(dstRadio); - if (signalStrength > dstRadio.getCurrentSignalStrength()) { + if (dstRadio.getCurrentSignalStrength() < signalStrength) { dstRadio.setCurrentSignalStrength(signalStrength); } } } - // Set signal strength on all interferences - for (RadioConnection conn : getActiveConnections()) { - for (Radio interferedRadio : ((MRMRadioConnection) conn).getInterfered()) { - double signalStrength = ((MRMRadioConnection) conn).getInterferenceSignalStrength(interferedRadio); - if (signalStrength > interferedRadio.getCurrentSignalStrength()) { - interferedRadio.setCurrentSignalStrength(signalStrength); + /* Interfering/colliding radio connections */ + for (RadioConnection conn : conns) { + for (Radio intfRadio : ((MRMRadioConnection) conn).getInterfered()) { + double signalStrength = ((MRMRadioConnection) conn).getInterferenceSignalStrength(intfRadio); + if (intfRadio.getCurrentSignalStrength() < signalStrength) { + intfRadio.setCurrentSignalStrength(signalStrength); } - if (!interferedRadio.isInterfered()) { - // Set to interfered again - interferedRadio.interfereAnyReception(); + if (!intfRadio.isInterfered()) { + /*logger.warn("Radio was not interfered: " + intfRadio);*/ + intfRadio.interfereAnyReception(); } } } - // // Fetch new signal strengths - // double[] newSignalStrengths = new double[registeredRadios.size()]; - // for (int i = 0; i < registeredRadios.size(); i++) { - // newSignalStrengths[i] = registeredRadios.get(i) - // .getCurrentSignalStrength(); - // } - // - // // Compare new and old signal strengths - // for (int i = 0; i < registeredRadios.size(); i++) { - // if (oldSignalStrengths[i] != newSignalStrengths[i]) - // logger.warn("Signal strengths changed on radio[" + i + "]: " - // + oldSignalStrengths[i] + " -> " + newSignalStrengths[i]); - // } + /* Check for noise sources */ + if (!WITH_NOISE) return; + for (Radio noiseRadio: getRegisteredRadios()) { + if (!(noiseRadio instanceof NoiseSourceRadio)) { + continue; + } + NoiseSourceRadio radio = (NoiseSourceRadio) noiseRadio; + int signalStrength = radio.getNoiseLevel(); + if (signalStrength == Integer.MIN_VALUE) { + continue; + } + + /* Calculate how noise source affects surrounding radios */ + for (Radio affectedRadio : getRegisteredRadios()) { + if (noiseRadio == affectedRadio) { + continue; + } + + /* Update noise levels */ + double[] signalMeanVar = currentChannelModel.getReceivedSignalStrength( + noiseRadio.getPosition().getXCoordinate(), + noiseRadio.getPosition().getYCoordinate(), + affectedRadio.getPosition().getXCoordinate(), + affectedRadio.getPosition().getYCoordinate(), + (double) signalStrength); /* TODO Convert to dBm */ + double signal = signalMeanVar[0]; + if (signal < background) { + continue; + } + + /* TODO Additive signals strengths? */ + /* TODO XXX Consider radio channels */ + /* TODO XXX Potentially interfere even when signal is weaker (~3dB)... + * (we may alternatively just use the getSINR method...) */ + if (affectedRadio.getCurrentSignalStrength() < signal) { + affectedRadio.setCurrentSignalStrength(signal); + + /* TODO Interfere with radio connections? */ + if (affectedRadio.isReceiving() && !affectedRadio.isInterfered()) { + for (RadioConnection conn : conns) { + if (conn.isDestination(affectedRadio)) { + /* Intefere with current reception, mark radio as interfered */ + conn.addInterfered(affectedRadio); + if (!affectedRadio.isInterfered()) { + affectedRadio.interfereAnyReception(); + } + } + } + } + } + } + } } public Collection getConfigXML() { @@ -279,20 +330,11 @@ public class MRM extends AbstractRadioMedium { settingsObservable.deleteObserver(obs); } - /** - * Returns position of given radio. - * - * @param radio Registered radio - * @return Position of given radio - */ - public Position getRadioPosition(Radio radio) { - return radio.getPosition(); - } - /** * @return Number of registered radios. */ public int getRegisteredRadioCount() { + /* TODO Expensive operation */ return getRegisteredRadios().length; }