contiki/tools/cooja/java/org/contikios/cooja/radiomediums/UDGM.java
Atis Elsts 67427b9b86 Improve multichannel support for Cooja.
* Fix CCA detection in Cooja in the case when the receiver swicthes on the right channel during an ongoing transmission. Always add a connection on transmission, even when the channel is not correct. Initially the connection is in a dormant state; this mimics what Cooja is doing when the receiver radio is turned off;
when the receiver is turned on and switched to the right channel, `updateSignalStrengths()` is called, and the connection starts to recieve PHY-level traffic.

* Add "channel" property for DGRM edges.

* Avoid cross-channel interference on DGRM and MRM radio mediums
2015-04-28 18:43:55 +02:00

406 lines
15 KiB
Java

/*
* Copyright (c) 2009, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package org.contikios.cooja.radiomediums;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import org.apache.log4j.Logger;
import org.jdom.Element;
import org.contikios.cooja.ClassDescription;
import org.contikios.cooja.Mote;
import org.contikios.cooja.RadioConnection;
import org.contikios.cooja.SimEventCentral.MoteCountListener;
import org.contikios.cooja.Simulation;
import org.contikios.cooja.interfaces.Position;
import org.contikios.cooja.interfaces.Radio;
import org.contikios.cooja.plugins.Visualizer;
import org.contikios.cooja.plugins.skins.UDGMVisualizerSkin;
/**
* The Unit Disk Graph Radio Medium abstracts radio transmission range as circles.
*
* It uses two different range parameters: one for transmissions, and one for
* interfering with other radios and transmissions.
*
* Both radio ranges grow with the radio output power indicator.
* The range parameters are multiplied with [output power]/[maximum output power].
* For example, if the transmission range is 100m, the current power indicator
* is 50, and the maximum output power indicator is 100, then the resulting transmission
* range becomes 50m.
*
* For radio transmissions within range, two different success ratios are used [0.0-1.0]:
* one for successful transmissions, and one for successful receptions.
* If the transmission fails, no radio will hear the transmission.
* If one of receptions fail, only that receiving radio will not receive the transmission,
* but will be interfered throughout the entire radio connection.
*
* The received radio packet signal strength grows inversely with the distance to the
* transmitter.
*
* @see #SS_STRONG
* @see #SS_WEAK
* @see #SS_NOTHING
*
* @see DirectedGraphMedium
* @see UDGMVisualizerSkin
* @author Fredrik Osterlind
*/
@ClassDescription("Unit Disk Graph Medium (UDGM): Distance Loss")
public class UDGM extends AbstractRadioMedium {
private static Logger logger = Logger.getLogger(UDGM.class);
public double SUCCESS_RATIO_TX = 1.0; /* Success ratio of TX. If this fails, no radios receive the packet */
public double SUCCESS_RATIO_RX = 1.0; /* Success ratio of RX. If this fails, the single affected receiver does not receive the packet */
public double TRANSMITTING_RANGE = 50; /* Transmission range. */
public double INTERFERENCE_RANGE = 100; /* Interference range. Ignored if below transmission range. */
private DirectedGraphMedium dgrm; /* Used only for efficient destination lookup */
private Random random = null;
public UDGM(Simulation simulation) {
super(simulation);
random = simulation.getRandomGenerator();
dgrm = new DirectedGraphMedium() {
protected void analyzeEdges() {
/* Create edges according to distances.
* XXX May be slow for mobile networks */
clearEdges();
for (Radio source: UDGM.this.getRegisteredRadios()) {
Position sourcePos = source.getPosition();
for (Radio dest: UDGM.this.getRegisteredRadios()) {
Position destPos = dest.getPosition();
/* Ignore ourselves */
if (source == dest) {
continue;
}
double distance = sourcePos.getDistanceTo(destPos);
if (distance < Math.max(TRANSMITTING_RANGE, INTERFERENCE_RANGE)) {
/* Add potential destination */
addEdge(
new DirectedGraphMedium.Edge(source,
new DGRMDestinationRadio(dest)));
}
}
}
super.analyzeEdges();
}
};
/* Register as position observer.
* If any positions change, re-analyze potential receivers. */
final Observer positionObserver = new Observer() {
public void update(Observable o, Object arg) {
dgrm.requestEdgeAnalysis();
}
};
/* Re-analyze potential receivers if radios are added/removed. */
simulation.getEventCentral().addMoteCountListener(new MoteCountListener() {
public void moteWasAdded(Mote mote) {
mote.getInterfaces().getPosition().addObserver(positionObserver);
dgrm.requestEdgeAnalysis();
}
public void moteWasRemoved(Mote mote) {
mote.getInterfaces().getPosition().deleteObserver(positionObserver);
dgrm.requestEdgeAnalysis();
}
});
for (Mote mote: simulation.getMotes()) {
mote.getInterfaces().getPosition().addObserver(positionObserver);
}
dgrm.requestEdgeAnalysis();
/* Register visualizer skin */
Visualizer.registerVisualizerSkin(UDGMVisualizerSkin.class);
}
public void removed() {
super.removed();
Visualizer.unregisterVisualizerSkin(UDGMVisualizerSkin.class);
}
public void setTxRange(double r) {
TRANSMITTING_RANGE = r;
dgrm.requestEdgeAnalysis();
}
public void setInterferenceRange(double r) {
INTERFERENCE_RANGE = r;
dgrm.requestEdgeAnalysis();
}
public RadioConnection createConnections(Radio sender) {
RadioConnection newConnection = new RadioConnection(sender);
/* Fail radio transmission randomly - no radios will hear this transmission */
if (getTxSuccessProbability(sender) < 1.0 && random.nextDouble() > getTxSuccessProbability(sender)) {
return newConnection;
}
/* Calculate ranges: grows with radio output power */
double moteTransmissionRange = TRANSMITTING_RANGE
* ((double) sender.getCurrentOutputPowerIndicator() / (double) sender.getOutputPowerIndicatorMax());
double moteInterferenceRange = INTERFERENCE_RANGE
* ((double) sender.getCurrentOutputPowerIndicator() / (double) sender.getOutputPowerIndicatorMax());
/* Get all potential destination radios */
DestinationRadio[] potentialDestinations = dgrm.getPotentialDestinations(sender);
if (potentialDestinations == null) {
return newConnection;
}
/* Loop through all potential destinations */
Position senderPos = sender.getPosition();
for (DestinationRadio dest: potentialDestinations) {
Radio recv = dest.radio;
/* Fail if radios are on different (but configured) channels */
if (sender.getChannel() >= 0 &&
recv.getChannel() >= 0 &&
sender.getChannel() != recv.getChannel()) {
/* Add the connection in a dormant state;
it will be activated later when the radio will be
turned on and switched to the right channel. This behavior
is consistent with the case when receiver is turned off. */
newConnection.addInterfered(recv);
continue;
}
Position recvPos = recv.getPosition();
/* Fail if radio is turned off */
// if (!recv.isReceiverOn()) {
// /* Special case: allow connection if source is Contiki radio,
// * and destination is something else (byte radio).
// * Allows cross-level communication with power-saving MACs. */
// if (sender instanceof ContikiRadio &&
// !(recv instanceof ContikiRadio)) {
// /*logger.info("Special case: creating connection to turned off radio");*/
// } else {
// recv.interfereAnyReception();
// continue;
// }
// }
double distance = senderPos.getDistanceTo(recvPos);
if (distance <= moteTransmissionRange) {
/* Within transmission range */
if (!recv.isRadioOn()) {
newConnection.addInterfered(recv);
recv.interfereAnyReception();
} else if (recv.isInterfered()) {
/* Was interfered: keep interfering */
newConnection.addInterfered(recv);
} else if (recv.isTransmitting()) {
newConnection.addInterfered(recv);
} else if (recv.isReceiving() ||
(random.nextDouble() > getRxSuccessProbability(sender, recv))) {
/* Was receiving, or reception failed: start interfering */
newConnection.addInterfered(recv);
recv.interfereAnyReception();
/* Interfere receiver in all other active radio connections */
for (RadioConnection conn : getActiveConnections()) {
if (conn.isDestination(recv)) {
conn.addInterfered(recv);
}
}
} else {
/* Success: radio starts receiving */
newConnection.addDestination(recv);
}
} else if (distance <= moteInterferenceRange) {
/* Within interference range */
newConnection.addInterfered(recv);
recv.interfereAnyReception();
}
}
return newConnection;
}
public double getSuccessProbability(Radio source, Radio dest) {
return getTxSuccessProbability(source) * getRxSuccessProbability(source, dest);
}
public double getTxSuccessProbability(Radio source) {
return SUCCESS_RATIO_TX;
}
public double getRxSuccessProbability(Radio source, Radio dest) {
double distance = source.getPosition().getDistanceTo(dest.getPosition());
double distanceSquared = Math.pow(distance,2.0);
double distanceMax = TRANSMITTING_RANGE *
((double) source.getCurrentOutputPowerIndicator() / (double) source.getOutputPowerIndicatorMax());
if (distanceMax == 0.0) {
return 0.0;
}
double distanceMaxSquared = Math.pow(distanceMax,2.0);
double ratio = distanceSquared / distanceMaxSquared;
if (ratio > 1.0) {
return 0.0;
}
return 1.0 - ratio*(1.0-SUCCESS_RATIO_RX);
}
public void updateSignalStrengths() {
/* Override: uses distance as signal strength factor */
/* Reset signal strengths */
for (Radio radio : getRegisteredRadios()) {
radio.setCurrentSignalStrength(getBaseRssi(radio));
}
/* Set signal strength to below strong on destinations */
RadioConnection[] conns = getActiveConnections();
for (RadioConnection conn : conns) {
if (conn.getSource().getCurrentSignalStrength() < SS_STRONG) {
conn.getSource().setCurrentSignalStrength(SS_STRONG);
}
for (Radio dstRadio : conn.getDestinations()) {
if (conn.getSource().getChannel() >= 0 &&
dstRadio.getChannel() >= 0 &&
conn.getSource().getChannel() != dstRadio.getChannel()) {
continue;
}
double dist = conn.getSource().getPosition().getDistanceTo(dstRadio.getPosition());
double maxTxDist = TRANSMITTING_RANGE
* ((double) conn.getSource().getCurrentOutputPowerIndicator() / (double) conn.getSource().getOutputPowerIndicatorMax());
double distFactor = dist/maxTxDist;
double signalStrength = SS_STRONG + distFactor*(SS_WEAK - SS_STRONG);
if (dstRadio.getCurrentSignalStrength() < signalStrength) {
dstRadio.setCurrentSignalStrength(signalStrength);
}
}
}
/* Set signal strength to below weak on interfered */
for (RadioConnection conn : conns) {
for (Radio intfRadio : conn.getInterfered()) {
if (conn.getSource().getChannel() >= 0 &&
intfRadio.getChannel() >= 0 &&
conn.getSource().getChannel() != intfRadio.getChannel()) {
continue;
}
double dist = conn.getSource().getPosition().getDistanceTo(intfRadio.getPosition());
double maxTxDist = TRANSMITTING_RANGE
* ((double) conn.getSource().getCurrentOutputPowerIndicator() / (double) conn.getSource().getOutputPowerIndicatorMax());
double distFactor = dist/maxTxDist;
if (distFactor < 1) {
double signalStrength = SS_STRONG + distFactor*(SS_WEAK - SS_STRONG);
if (intfRadio.getCurrentSignalStrength() < signalStrength) {
intfRadio.setCurrentSignalStrength(signalStrength);
}
} else {
intfRadio.setCurrentSignalStrength(SS_WEAK);
if (intfRadio.getCurrentSignalStrength() < SS_WEAK) {
intfRadio.setCurrentSignalStrength(SS_WEAK);
}
}
if (!intfRadio.isInterfered()) {
/*logger.warn("Radio was not interfered: " + intfRadio);*/
intfRadio.interfereAnyReception();
}
}
}
}
public Collection<Element> getConfigXML() {
Collection<Element> config = super.getConfigXML();
Element element;
/* Transmitting range */
element = new Element("transmitting_range");
element.setText(Double.toString(TRANSMITTING_RANGE));
config.add(element);
/* Interference range */
element = new Element("interference_range");
element.setText(Double.toString(INTERFERENCE_RANGE));
config.add(element);
/* Transmission success probability */
element = new Element("success_ratio_tx");
element.setText("" + SUCCESS_RATIO_TX);
config.add(element);
/* Reception success probability */
element = new Element("success_ratio_rx");
element.setText("" + SUCCESS_RATIO_RX);
config.add(element);
return config;
}
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
super.setConfigXML(configXML, visAvailable);
for (Element element : configXML) {
if (element.getName().equals("transmitting_range")) {
TRANSMITTING_RANGE = Double.parseDouble(element.getText());
}
if (element.getName().equals("interference_range")) {
INTERFERENCE_RANGE = Double.parseDouble(element.getText());
}
/* Backwards compatibility */
if (element.getName().equals("success_ratio")) {
SUCCESS_RATIO_TX = Double.parseDouble(element.getText());
logger.warn("Loading old Cooja Config, XML element \"sucess_ratio\" parsed at \"sucess_ratio_tx\"");
}
if (element.getName().equals("success_ratio_tx")) {
SUCCESS_RATIO_TX = Double.parseDouble(element.getText());
}
if (element.getName().equals("success_ratio_rx")) {
SUCCESS_RATIO_RX = Double.parseDouble(element.getText());
}
}
return true;
}
}