radio medium patch: udgm uses hash table for efficient lookup in large networks + minor patches to dgrm

This commit is contained in:
fros4943 2009-10-27 10:10:03 +00:00
parent 59e1c464e7
commit a81f216acb
4 changed files with 639 additions and 443 deletions

View file

@ -1,3 +1,34 @@
/*
* 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.
*
* $Id: DGRMConfigurator.java,v 1.3 2009/10/27 10:10:03 fros4943 Exp $
*/
package se.sics.cooja.plugins;
import java.awt.BorderLayout;
@ -6,6 +37,7 @@ import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
@ -29,8 +61,13 @@ import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin;
import se.sics.cooja.radiomediums.DirectedGraphMedium;
import se.sics.cooja.radiomediums.DirectedGraphMedium.DGRMDestinationRadio;
/**
* Simple user interface for configuring edges for the Directed Graph
* Radio Medium (DGRM).
*
* @see DirectedGraphMedium
* @author Fredrik Osterlind
*/
@ClassDescription("DGRM Configurator")
@ -82,11 +119,11 @@ public class DGRMConfigurator extends VisPlugin {
combo.addItem(0.3);
combo.addItem(0.2);
combo.addItem(0.1);
combo.addItem(0.1);
combo.addItem(0.0);
}
if (column == IDX_DELAY) {
combo.removeAllItems();
combo.addItem(0);
combo.addItem(1);
combo.addItem(2);
combo.addItem(3);
@ -190,16 +227,21 @@ public class DGRMConfigurator extends VisPlugin {
DirectedGraphMedium.Edge newEdge;
if (dest.getSelectedItem() instanceof Mote) {
newEdge = new DirectedGraphMedium.Edge(
(Mote) source.getSelectedItem(),
(Mote) dest.getSelectedItem(),
((Mote) source.getSelectedItem()).getInterfaces().getRadio(),
new DGRMDestinationRadio(
((Mote) dest.getSelectedItem()).getInterfaces().getRadio(),
((Number)ratio.getValue()).doubleValue(),
((Number)delay.getValue()).longValue()
)
);
} else {
newEdge = new DirectedGraphMedium.Edge(
(Mote) source.getSelectedItem(),
((Mote) source.getSelectedItem()).getInterfaces().getRadio(),
new DGRMDestinationRadio(
null,
((Number)ratio.getValue()).doubleValue(),
((Number)delay.getValue()).longValue()
)
);
}
radioMedium.addEdge(newEdge);
@ -243,19 +285,19 @@ public class DGRMConfigurator extends VisPlugin {
if (edge.source == null) {
return "?";
}
return edge.source;
return edge.source.getMote();
}
if (column == IDX_DST) {
if (edge.dest == null) {
if (edge.superDest.toAll) {
return "ALL";
}
return edge.dest;
return edge.superDest.radio.getMote();
}
if (column == IDX_RATIO) {
return edge.successRatio;
return ((DGRMDestinationRadio)edge.superDest).ratio;
}
if (column == IDX_DELAY) {
return edge.delay / Simulation.MILLISECOND;
return ((DGRMDestinationRadio)edge.superDest).delay / Simulation.MILLISECOND;
}
if (column == IDX_DEL) {
return new Boolean(false);
@ -278,14 +320,16 @@ public class DGRMConfigurator extends VisPlugin {
DirectedGraphMedium.Edge edge = radioMedium.getEdges()[row];
if (column == IDX_RATIO) {
/* Success ratio */
edge.successRatio = ((Number)value).doubleValue();
radioMedium.setEdgesDirty();
((DGRMDestinationRadio)edge.superDest).ratio =
((Number)value).doubleValue();
radioMedium.requestEdgeAnalysis();
return;
}
if (column == IDX_DELAY) {
/* Propagation delay (ms) */
edge.delay = ((Number)value).longValue() * Simulation.MILLISECOND;
radioMedium.setEdgesDirty();
((DGRMDestinationRadio)edge.superDest).delay =
((Number)value).longValue() * Simulation.MILLISECOND;
radioMedium.requestEdgeAnalysis();
return;
}
if (column == IDX_DEL) {
@ -301,14 +345,14 @@ public class DGRMConfigurator extends VisPlugin {
return false;
}
Mote sourceMote = radioMedium.getEdges()[row].source;
Mote sourceMote = radioMedium.getEdges()[row].source.getMote();
if (column == IDX_SRC) {
gui.signalMoteHighlight(sourceMote);
return false;
}
if (column == IDX_DST) {
if (radioMedium.getEdges()[row].dest != null) {
gui.signalMoteHighlight(radioMedium.getEdges()[row].dest);
if (!radioMedium.getEdges()[row].superDest.toAll) {
gui.signalMoteHighlight(radioMedium.getEdges()[row].superDest.radio.getMote());
}
return false;
}

View file

@ -26,21 +26,36 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: AbstractRadioMedium.java,v 1.10 2009/05/26 14:17:29 fros4943 Exp $
* $Id: AbstractRadioMedium.java,v 1.11 2009/10/27 10:10:03 fros4943 Exp $
*/
package se.sics.cooja.radiomediums;
import java.util.*;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import org.apache.log4j.Logger;
import se.sics.cooja.*;
import se.sics.cooja.interfaces.*;
import se.sics.cooja.Mote;
import se.sics.cooja.RadioConnection;
import se.sics.cooja.RadioMedium;
import se.sics.cooja.RadioPacket;
import se.sics.cooja.Simulation;
import se.sics.cooja.TimeEvent;
import se.sics.cooja.interfaces.CustomDataRadio;
import se.sics.cooja.interfaces.Radio;
/**
* Abstract radio medium provides basic functionality for implementing radio
* mediums.
*
* The radio medium forwards both radio packets and custom data objects.
*
* The registered radios' signal strengths are updated whenever the radio medium
* changes. There are three fixed levels: no surrounding traffic heard, noise
* heard and data heard.
*
* It handles radio registrations, radio loggers, active connections and
* observes all registered radio interfaces.
*
@ -49,9 +64,15 @@ import se.sics.cooja.interfaces.*;
public abstract class AbstractRadioMedium extends RadioMedium {
private static Logger logger = Logger.getLogger(AbstractRadioMedium.class);
private Vector<Radio> registeredRadios = new Vector<Radio>();
/* Signal strengths in dBm.
* Approx. values measured on TmoteSky */
public static final double SS_NOTHING = -100;
public static final double SS_STRONG = -10;
public static final double SS_WEAK = -95;
private Vector<RadioConnection> activeConnections = new Vector<RadioConnection>();
private ArrayList<Radio> registeredRadios = new ArrayList<Radio>();
private ArrayList<RadioConnection> activeConnections = new ArrayList<RadioConnection>();
private RadioConnection lastConnection = null;
@ -74,8 +95,6 @@ public abstract class AbstractRadioMedium extends RadioMedium {
private RadioMediumObservable radioMediumObservable = new RadioMediumObservable();
private RadioConnection[] lastTickConnections = null;
/**
* This constructor should always be called from implemented radio mediums.
*
@ -88,8 +107,8 @@ public abstract class AbstractRadioMedium extends RadioMedium {
/**
* @return All registered radios
*/
public Vector<Radio> getRegisteredRadios() {
return registeredRadios;
public Radio[] getRegisteredRadios() {
return registeredRadios.toArray(new Radio[0]);
}
/**
@ -115,7 +134,35 @@ public abstract class AbstractRadioMedium extends RadioMedium {
* Updates all radio interfaces' signal strengths according to
* the current active connections.
*/
abstract public void updateSignalStrengths();
public void updateSignalStrengths() {
/* Reset signal strengths */
for (Radio radio : getRegisteredRadios()) {
radio.setCurrentSignalStrength(SS_NOTHING);
}
/* Set signal strength to strong on destinations */
RadioConnection[] conns = getActiveConnections();
for (RadioConnection conn : conns) {
conn.getSource().setCurrentSignalStrength(SS_STRONG);
for (Radio dstRadio : conn.getDestinations()) {
dstRadio.setCurrentSignalStrength(SS_STRONG);
}
}
/* Set signal strength to weak on interfered */
for (RadioConnection conn : conns) {
for (Radio intfRadio : conn.getInterfered()) {
intfRadio.setCurrentSignalStrength(SS_WEAK);
if (!intfRadio.isInterfered()) {
logger.warn("Radio was not interfered");
intfRadio.interfereAnyReception();
}
}
}
}
/**
* Remove given radio from any active connections.
@ -124,34 +171,40 @@ public abstract class AbstractRadioMedium extends RadioMedium {
* @param radio Radio
*/
private void removeFromActiveConnections(Radio radio) {
// Abort any reception
/* Abort ongoing receptions */
if (radio.isReceiving()) {
radio.interfereAnyReception();
radio.signalReceptionEnd();
}
// Remove radio from all active connections
RadioConnection connToRemove = null;
for (RadioConnection conn : activeConnections) {
conn.removeDestination(radio);
conn.removeInterfered(radio);
if (conn.getSource() == radio) {
// Radio is currently transmitting
connToRemove = conn;
for (Radio dstRadio : conn.getDestinations()) {
/* Connection source */
RadioConnection connection = getActiveConnectionFrom(radio);
if (connection != null) {
for (Radio dstRadio : connection.getDestinations()) {
dstRadio.interfereAnyReception();
dstRadio.signalReceptionEnd();
}
for (Radio dstRadio : conn.getInterfered()) {
for (Radio dstRadio : connection.getInterfered()) {
dstRadio.signalReceptionEnd();
}
activeConnections.remove(connection);
}
/* Connection destination and interfered */
for (RadioConnection conn : activeConnections) {
conn.removeDestination(radio);
conn.removeInterfered(radio);
}
}
if (connToRemove != null) {
activeConnections.remove(connToRemove);
private RadioConnection getActiveConnectionFrom(Radio source) {
for (RadioConnection conn : activeConnections) {
if (conn.getSource() == source) {
return conn;
}
}
return null;
}
/**
* This observer is responsible for detecting radio interface events, for example
@ -163,34 +216,32 @@ public abstract class AbstractRadioMedium extends RadioMedium {
logger.fatal("Radio event dispatched by non-radio object");
return;
}
Radio radio = (Radio) obs;
// Handle radio event
final Radio.RadioEvent event = radio.getLastEvent();
// Ignore reception events
if (event == Radio.RadioEvent.RECEPTION_STARTED
|| event == Radio.RadioEvent.RECEPTION_INTERFERED
|| event == Radio.RadioEvent.RECEPTION_FINISHED) {
if (event == Radio.RadioEvent.RECEPTION_STARTED ||
event == Radio.RadioEvent.RECEPTION_INTERFERED ||
event == Radio.RadioEvent.RECEPTION_FINISHED ||
event == Radio.RadioEvent.UNKNOWN) {
/* Ignored */
return;
}
if (event == Radio.RadioEvent.HW_OFF) {
// Destroy any(?) transfers
if (event == Radio.RadioEvent.HW_ON) {
/* Update signal strengths */
updateSignalStrengths();
} else if (event == Radio.RadioEvent.HW_OFF) {
/* Remove any radio connections from this radio */
removeFromActiveConnections(radio);
// Recalculate signal strengths on all radios
updateSignalStrengths();
} else if (event == Radio.RadioEvent.HW_ON) {
// No action
// TODO Maybe set signal strength levels now?
// Recalculate signal strengths on all radios
/* Update signal strengths */
updateSignalStrengths();
} else if (event == Radio.RadioEvent.TRANSMISSION_STARTED) {
/* Create radio connections */
/* Create new radio connection */
RadioConnection newConnection = createConnections(radio);
activeConnections.add(newConnection);
@ -213,27 +264,22 @@ public abstract class AbstractRadioMedium extends RadioMedium {
}
}
// Recalculate signal strengths on all radios
/* Update signal strengths */
updateSignalStrengths();
/* Notify observers */
radioMediumObservable.setRadioMediumChanged();
radioMediumObservable.setRadioMediumChangedAndNotify();
} else if (event == Radio.RadioEvent.TRANSMISSION_FINISHED) {
/* Remove active connection */
// Find corresponding connection of radio
RadioConnection connection = null;
for (RadioConnection conn : activeConnections) {
if (conn.getSource() == radio) {
connection = conn;
break;
}
}
/* Remove radio connection */
/* Connection */
RadioConnection connection = getActiveConnectionFrom(radio);
if (connection == null) {
logger.fatal("Can't find active connection to remove, source=" + radio);
} else {
logger.fatal("No radio connection found");
return;
}
activeConnections.remove(connection);
lastConnection = connection;
COUNTER_TX++;
@ -255,43 +301,53 @@ public abstract class AbstractRadioMedium extends RadioMedium {
simulation.getSimulationTime() + connection.getDestinationDelay(dstRadio));
}
}
for (Radio dstRadio : connection.getInterfered()) {
for (Radio intRadio : connection.getInterfered()) {
COUNTER_INTERFERED++;
dstRadio.signalReceptionEnd();
}
}
// Recalculate signal strengths on all radios
updateSignalStrengths();
/* Notify observers */
radioMediumObservable.setRadioMediumChanged();
radioMediumObservable.notifyObservers();
} else if (event == Radio.RadioEvent.CUSTOM_DATA_TRANSMITTED) {
/* Forward custom data, if any */
// Find corresponding connection of radio
RadioConnection connection = null;
for (RadioConnection conn : activeConnections) {
if (conn.getSource() == radio) {
connection = conn;
/* Check if radio is still interfered by some other connection */
boolean stillInterfered = false;
for (RadioConnection conn : getActiveConnections()) {
for (Radio r: conn.getInterfered()) {
if (intRadio == r) {
stillInterfered = true;
break;
}
}
}
if (!stillInterfered) {
intRadio.signalReceptionEnd();
}
}
/* Update signal strengths */
updateSignalStrengths();
/* Notify observers */
radioMediumObservable.setRadioMediumChangedAndNotify();
} else if (event == Radio.RadioEvent.CUSTOM_DATA_TRANSMITTED) {
/* Connection */
RadioConnection connection = getActiveConnectionFrom(radio);
if (connection == null) {
logger.fatal("Can't find active connection to forward custom data in");
logger.fatal("No radio connection found");
return;
}
/* Custom data object */
Object data = ((CustomDataRadio) radio).getLastCustomDataTransmitted();
if (data == null) {
logger.fatal("Custom data object is null");
logger.fatal("No custom data object to forward");
return;
}
for (Radio dstRadio : connection.getDestinations()) {
if (dstRadio instanceof CustomDataRadio) {
if (!radio.getClass().equals(dstRadio.getClass()) ||
!(radio instanceof CustomDataRadio)) {
/* Radios communicate via radio packets */
continue;
}
if (connection.getDestinationDelay(dstRadio) == 0) {
((CustomDataRadio) dstRadio).receiveCustomData(data);
} else {
@ -310,34 +366,32 @@ public abstract class AbstractRadioMedium extends RadioMedium {
}
}
}
} else if (event == Radio.RadioEvent.PACKET_TRANSMITTED) {
/* Forward packet, if any */
// Find corresponding connection of radio
RadioConnection connection = null;
for (RadioConnection conn : activeConnections) {
if (conn.getSource() == radio) {
connection = conn;
break;
}
}
/* Connection */
RadioConnection connection = getActiveConnectionFrom(radio);
if (connection == null) {
logger.fatal("Can't find active connection to forward packet in");
logger.fatal("No radio connection found");
return;
}
/* Radio packet */
RadioPacket packet = radio.getLastPacketTransmitted();
if (packet == null) {
logger.fatal("Radio packet is null");
logger.fatal("No radio packet to forward");
return;
}
Radio srcRadio = connection.getSource();
for (Radio dstRadio : connection.getDestinations()) {
if (!(srcRadio instanceof CustomDataRadio) ||
!(dstRadio instanceof CustomDataRadio)) {
if (radio.getClass().equals(dstRadio.getClass()) &&
radio instanceof CustomDataRadio) {
/* Radios instead communicate via custom data objects */
continue;
}
/* Forward radio packet */
if (connection.getDestinationDelay(dstRadio) == 0) {
dstRadio.setReceivedPacket(packet);
} else {
@ -353,13 +407,10 @@ public abstract class AbstractRadioMedium extends RadioMedium {
simulation.scheduleEvent(
delayedEvent,
simulation.getSimulationTime() + connection.getDestinationDelay(dstRadio));
}
}
}
} else if (event == Radio.RadioEvent.UNKNOWN) {
// Do nothing
}
} else {
logger.fatal("Unsupported radio event: " + event);
}
@ -375,19 +426,21 @@ public abstract class AbstractRadioMedium extends RadioMedium {
}
public void registerRadioInterface(Radio radio, Simulation sim) {
if (radio != null) {
// Register and start observing radio
if (radio == null) {
logger.warn("No radio to register");
return;
}
registeredRadios.add(radio);
radio.addObserver(radioEventsObserver);
// Set initial signal strength
/* Update signal strengths */
updateSignalStrengths();
}
}
public void unregisterRadioInterface(Radio radio, Simulation sim) {
if (!registeredRadios.contains(radio)) {
logger.warn("Could not find radio: " + radio + " to unregister");
logger.warn("No radio to unregister: " + radio);
return;
}
@ -395,6 +448,9 @@ public abstract class AbstractRadioMedium extends RadioMedium {
registeredRadios.remove(radio);
removeFromActiveConnections(radio);
/* Update signal strengths */
updateSignalStrengths();
}
public void addRadioMediumObserver(Observer observer) {

View file

@ -26,92 +26,116 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: DirectedGraphMedium.java,v 1.2 2009/06/08 12:42:10 fros4943 Exp $
* $Id: DirectedGraphMedium.java,v 1.3 2009/10/27 10:10:03 fros4943 Exp $
*/
package se.sics.cooja.radiomediums;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;
import java.util.Vector;
import org.jdom.Element;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.*;
import se.sics.cooja.interfaces.*;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.Mote;
import se.sics.cooja.RadioConnection;
import se.sics.cooja.Simulation;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.plugins.DGRMConfigurator;
/**
* Directed Graph Radio Medium.
*
* Can be used both stand-alone as a radio medium, and
* as a basis for other radio medium implementations.
*
* The stand-alone radio medium supports propagation delays and
* and single-value per-link transmission success ratio.
*
* @see UDGM
* @author Fredrik Osterlind
*/
@ClassDescription("Directed Graph Radio Medium (DGRM)")
public class DirectedGraphMedium extends AbstractRadioMedium {
private static Logger logger = Logger.getLogger(DirectedGraphMedium.class);
/* Signal strengths in dBm.
* Approx. values measured on TmoteSky */
public static final double SS_NOTHING = -100;
public static final double SS_STRONG = -10;
public static final double SS_WEAK = -95;
private Simulation simulation;
private boolean edgesDirty = false;
private Random random = null;
private Random random;
private DirectedGraphMedium.Edge edges[] = new DirectedGraphMedium.Edge[0];
private ArrayList<Edge> edges = new ArrayList<Edge>();
private boolean edgesDirty = true;
/* Used for optimizing lookup time */
private Hashtable<Radio,DestinationRadio[]> edgesTable = new Hashtable<Radio,DestinationRadio[]>();
public DirectedGraphMedium() {
/* Do not initialize radio medium: use only for hash table */
super(null);
}
public DirectedGraphMedium(Simulation simulation) {
super(simulation);
this.simulation = simulation;
random = simulation.getRandomGenerator();
setEdgesDirty();
requestEdgeAnalysis();
/* Register visualizer plugin */
/* Register plugin.
* TODO Should be unregistered when radio medium is removed */
simulation.getGUI().registerTemporaryPlugin(DGRMConfigurator.class);
this.simulation = simulation;
}
public void addEdge(Edge e) {
DirectedGraphMedium.Edge newEdges[] = new DirectedGraphMedium.Edge[edges.length+1];
System.arraycopy(edges, 0, newEdges, 0, edges.length);
newEdges[newEdges.length-1] = e;
edges = newEdges;
setEdgesDirty();
edges.add(e);
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public void removeEdge(Edge edge) {
ArrayList<Edge> list = new ArrayList<Edge>();
for (DirectedGraphMedium.Edge e: edges) {
list.add(e);
}
if (!list.contains(edge)) {
if (!edges.contains(edge)) {
logger.fatal("Cannot remove edge: " + edge);
return;
}
list.remove(edge);
DirectedGraphMedium.Edge newEdges[] = new DirectedGraphMedium.Edge[list.size()];
list.toArray(newEdges);
edges = newEdges;
setEdgesDirty();
edges.remove(edge);
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public void setEdgesDirty() {
edgesDirty = true;
public void clearEdges() {
edges.clear();
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public Edge[] getEdges() {
return edges;
return edges.toArray(new Edge[0]);
}
public void registerMote(Mote mote, Simulation sim) {
super.registerMote(mote, sim);
/**
* Signal that the configuration changed, and needs to be re-analyzed
* before used.
*/
public void requestEdgeAnalysis() {
edgesDirty = true;
}
public boolean needsEdgeAnalysis() {
return edgesDirty;
}
public void registerRadioInterface(Radio radio, Simulation sim) {
super.registerRadioInterface(radio, sim);
for (Edge edge: edges) {
if (edge.delayedLoadConfig == null) {
@ -124,107 +148,148 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
}
}
setEdgesDirty();
requestEdgeAnalysis();
}
public void unregisterMote(Mote mote, Simulation sim) {
super.unregisterMote(mote, sim);
public void unregisterRadioInterface(Radio radio, Simulation sim) {
super.unregisterRadioInterface(radio, sim);
for (Edge edge: edges) {
if (edge.source == mote || edge.dest == mote) {
if (radio == null) {
return;
}
for (Edge edge: getEdges()) {
if (edge.source == radio || edge.superDest.radio == radio) {
removeEdge(edge);
}
}
setEdgesDirty();
requestEdgeAnalysis();
}
private class DestinationRadio {
Radio radio;
double ratio;
long delay; /* us */
public static class DestinationRadio {
public Radio radio; /* destination radio */
public boolean toAll; /* to all destinations */
public DestinationRadio(Radio dest, double ratio, long delay) {
public DestinationRadio(Radio dest) {
this.radio = dest;
this.ratio = ratio;
this.delay = delay;
toAll = (radio == null);
}
public String toString() {
return radio.getMote().toString();
}
protected Object clone() {
return new DestinationRadio(radio);
}
}
/* Used for optimizing lookup time */
private Hashtable<Radio,DestinationRadio[]> edgesTable = new Hashtable<Radio,DestinationRadio[]>();
public static class DGRMDestinationRadio extends DestinationRadio {
public double ratio; /* Link success ratio (per packet). */
public long delay; /* EXPERIMENTAL: Propagation delay (us). */
private void analyzeEdges() {
Hashtable<Radio,ArrayList<DestinationRadio>> newTable =
public DGRMDestinationRadio(Radio dest, double ratio, long delay) {
super(dest);
this.ratio = ratio;
this.delay = delay;
}
protected Object clone() {
return new DGRMDestinationRadio(radio, ratio, delay);
}
}
/**
* Generates hash table using current edges for efficient lookup.
*/
protected void analyzeEdges() {
Hashtable<Radio,ArrayList<DestinationRadio>> listTable =
new Hashtable<Radio,ArrayList<DestinationRadio>>();
/* Fill edge hash table with all edges */
for (Edge edge: edges) {
if (edge.source == null) {
return; /* Still dirty, wait until all edges are loaded */
/* XXX Wait until edge configuration has been loaded */
logger.warn("DGRM edges not loaded");
return;
}
ArrayList<DestinationRadio> destRadios;
if (!newTable.containsKey(edge.source.getInterfaces().getRadio())) {
if (!listTable.containsKey(edge.source)) {
/* Create new source */
destRadios = new ArrayList<DestinationRadio>();
} else {
/* Extend source radio with another destination */
destRadios = newTable.get(edge.source.getInterfaces().getRadio());
destRadios = listTable.get(edge.source);
}
DestinationRadio destRadio;
if (edge.dest == null) {
/* All radios */
Vector<Radio> allRadios = getRegisteredRadios();
for (Radio r: allRadios) {
destRadio = new DestinationRadio(r, edge.successRatio, edge.delay);
destRadios.add(destRadio);
/* Explode special rule: to all radios */
if (edge.superDest.toAll) {
for (Radio r: getRegisteredRadios()) {
if (edge.source == r) {
continue;
}
DestinationRadio d = (DestinationRadio) edge.superDest.clone();
d.radio = r;
d.toAll = false;
destRadios.add(d);
}
} else {
destRadio = new DestinationRadio(edge.dest.getInterfaces().getRadio(), edge.successRatio, edge.delay);
destRadios.add(destRadio);
destRadios.add(edge.superDest);
}
newTable.put(edge.source.getInterfaces().getRadio(), destRadios);
listTable.put(edge.source, destRadios);
}
/* Convert to arrays */
Hashtable<Radio,DestinationRadio[]> newTable2 = new Hashtable<Radio,DestinationRadio[]>();
Enumeration<Radio> sources = newTable.keys();
Hashtable<Radio,DestinationRadio[]> arrTable =
new Hashtable<Radio,DestinationRadio[]>();
Enumeration<Radio> sources = listTable.keys();
while (sources.hasMoreElements()) {
Radio source = sources.nextElement();
ArrayList<DestinationRadio> list = newTable.get(source);
DestinationRadio[] arr = new DestinationRadio[list.size()];
list.toArray(arr);
newTable2.put(source, arr);
DestinationRadio[] arr =
listTable.get(source).toArray(new DestinationRadio[0]);
arrTable.put(source, arr);
}
this.edgesTable = newTable2;
this.edgesTable = arrTable;
edgesDirty = false;
}
/**
* Returns all potential destination radios, i.e. all radios "within reach".
* Does not consider radio channels, transmission success ratios etc.
*
* @param source Source radio
* @return All potential destination radios
*/
public DestinationRadio[] getPotentialDestinations(Radio source) {
if (edgesDirty) {
analyzeEdges();
}
return edgesTable.get(source);
}
public RadioConnection createConnections(Radio source) {
if (edgesDirty) {
analyzeEdges();
}
/* Create new radio connection using edge hash table */
DestinationRadio[] destinations = edgesTable.get(source);
if (destinations == null || destinations.length == 0) {
/* No destinations */
/*logger.info(sendingRadio + ": No dest");*/
if (edgesDirty) {
logger.fatal("Error when analyzing edges, aborting new radio connection");
return new RadioConnection(source);
}
/*logger.info(source + ": " + destinations.length + " potential destinations");*/
/* Create new radio connection using edge hash table */
RadioConnection newConn = new RadioConnection(source);
for (DestinationRadio dest: destinations) {
DestinationRadio[] destinations = getPotentialDestinations(source);
if (destinations == null || destinations.length == 0) {
/* No destinations */
/*logger.info(sendingRadio + ": No dest");*/
return newConn;
}
/*logger.info(source + ": " + destinations.length + " potential destinations");*/
for (DestinationRadio d: destinations) {
DGRMDestinationRadio dest = (DGRMDestinationRadio) d;
if (dest.radio == source) {
/* Fail: cannot receive our own transmission */
/*logger.info(source + ": Fail, receiver is sender");*/
@ -274,35 +339,8 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
return newConn;
}
public void updateSignalStrengths() {
if (edgesDirty) {
analyzeEdges();
}
for (Radio radio : getRegisteredRadios()) {
radio.setCurrentSignalStrength(SS_NOTHING);
}
for (RadioConnection conn : getActiveConnections()) {
conn.getSource().setCurrentSignalStrength(SS_STRONG);
for (Radio dstRadio : conn.getDestinations()) {
dstRadio.setCurrentSignalStrength(SS_STRONG);
}
}
for (RadioConnection conn : getActiveConnections()) {
for (Radio intfRadio : conn.getInterfered()) {
intfRadio.setCurrentSignalStrength(SS_WEAK);
if (!intfRadio.isInterfered()) {
intfRadio.interfereAnyReception();
}
}
}
}
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
ArrayList<Element> config = new ArrayList<Element>();
Element element;
for (Edge edge: edges) {
@ -319,66 +357,67 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
for (Element element : configXML) {
if (element.getName().equals("edge")) {
Edge edge = new Edge(null, null, 0, 0);
Edge edge = new Edge();
edge.delayedLoadConfig = element.getChildren();
addEdge(edge);
}
}
setEdgesDirty();
requestEdgeAnalysis();
return true;
}
public static class Edge {
public Mote source = null;
public Mote dest = null; /* null: all motes*/
public double successRatio = 1.0; /* Link success ratio (per packet). */
public long delay = 0; /* Propagation delay (us). */
public Radio source;
public DestinationRadio superDest;
public Edge(Mote source, double ratio, long delay) {
this.source = source;
this.successRatio = ratio;
this.delay = delay;
this.dest = null;
private Edge() {
/* Internal constructor: await config */
source = null;
superDest = null;
}
public Edge(Mote source, Mote dest, double ratio, long delay) {
public Edge(Radio source, DestinationRadio dest) {
this.source = source;
this.successRatio = ratio;
this.delay = delay;
this.dest = dest;
this.superDest = dest;
}
public Collection<Element> delayedLoadConfig = null; /* Used for restoring edges from config */
public Collection<Element> getConfigXML() {
/* Internal methods */
private Collection<Element> delayedLoadConfig = null; /* Used for restoring edges from config */
private Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
Element element;
element = new Element("src");
element.setText(source.toString());
element.setText(source.getMote().toString());
config.add(element);
element = new Element("dest");
if (dest == null) {
if (superDest.toAll) {
element.setText("ALL");
} else {
element.setText(dest.toString());
element.setText(superDest.radio.getMote().toString());
}
config.add(element);
if (superDest instanceof DGRMDestinationRadio) {
element = new Element("ratio");
element.setText("" + successRatio);
element.setText("" + ((DGRMDestinationRadio)superDest).ratio);
config.add(element);
element = new Element("delay");
element.setText("" + delay);
element.setText("" + ((DGRMDestinationRadio)superDest).delay);
config.add(element);
}
return config;
}
public boolean setConfigXML(Collection<Element> configXML, Simulation simulation) {
private boolean setConfigXML(Collection<Element> configXML, Simulation simulation) {
Radio dest = null;
double ratio = -1;
long delay = -1;
for (Element element : configXML) {
if (element.getName().equals("src")) {
String moteDescription = element.getText();
@ -387,7 +426,7 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
for (Mote m: simulation.getMotes()) {
if (moteDescription.equals(m.toString())) {
foundMote = true;
source = m;
source = m.getInterfaces().getRadio();
break;
}
}
@ -407,7 +446,7 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
for (Mote m: simulation.getMotes()) {
if (moteDescription.equals(m.toString())) {
foundMote = true;
dest = m;
dest = m.getInterfaces().getRadio();
break;
}
}
@ -418,14 +457,19 @@ public class DirectedGraphMedium extends AbstractRadioMedium {
}
if (element.getName().equals("ratio")) {
successRatio = Double.parseDouble(element.getText());
ratio = Double.parseDouble(element.getText());
}
if (element.getName().equals("delay")) {
delay = Long.parseLong(element.getText());
}
}
if (ratio < 0 || delay < 0) {
return false;
}
superDest = new DGRMDestinationRadio(dest, ratio, delay);
return true;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, Swedish Institute of Computer Science.
* Copyright (c) 2009, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,151 +26,221 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: UDGM.java,v 1.25 2009/05/26 14:17:29 fros4943 Exp $
* $Id: UDGM.java,v 1.26 2009/10/27 10:10:03 fros4943 Exp $
*/
package se.sics.cooja.radiomediums;
import java.util.*;
import org.jdom.Element;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import se.sics.cooja.*;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.Mote;
import se.sics.cooja.RadioConnection;
import se.sics.cooja.Simulation;
import se.sics.cooja.SimEventCentral.MoteCountListener;
import se.sics.cooja.contikimote.interfaces.ContikiRadio;
import se.sics.cooja.interfaces.*;
import se.sics.cooja.interfaces.Position;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.plugins.Visualizer;
import se.sics.cooja.plugins.skins.UDGMVisualizerSkin;
import se.sics.cooja.radiomediums.DirectedGraphMedium.DestinationRadio;
/**
* The Unit Disk Graph medium has two different range parameters; one for
* transmitting and one for interfering other transmissions.
* The Unit Disk Graph Radio Medium abstracts radio transmission range as circles.
*
* The radio medium supports both byte and packet radios.
* It uses two different range parameters: one for transmissions, and one for
* interfering with other radios and transmissions.
*
* The registered radios' signal strengths are updated whenever the radio medium
* changes. There are three fixed levels: no surrounding traffic heard, noise
* heard and data heard.
* 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.
*
* The radio output power indicator (0-100) is used in a very simple way; the
* total transmission (and interfering) range is multiplied with [power_ind]%.
* 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 UDGMVisualizerSkin
* @see DirectedGraphMedium, UDGMVisualizerSkin
* @author Fredrik Osterlind
*/
@ClassDescription("Unit Disk Graph Medium (UDGM)")
public class UDGM extends AbstractRadioMedium {
private static Logger logger = Logger.getLogger(UDGM.class);
/* Signal strengths in dBm.
* Approx. values measured on TmoteSky */
public static final double SS_NOTHING = -100;
public static final double SS_STRONG = -10;
public static final double SS_WEAK = -95;
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, a single radio does not 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 Simulation mySimulation;
private Simulation simulation;
private DirectedGraphMedium dgrm; /* Used only for efficient destination lookup */
private Random random = null;
public UDGM(Simulation simulation) {
super(simulation);
this.simulation = 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 DestinationRadio(dest)));
}
}
}
super.analyzeEdges();
}
};
/* Register visualizer skin */
/* TODO Should be unregistered when radio medium is removed */
/* 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.
* TODO Should be unregistered when radio medium is removed */
Visualizer.registerVisualizerSkin(UDGMVisualizerSkin.class);
mySimulation = simulation;
random = mySimulation.getRandomGenerator();
}
public RadioConnection createConnections(Radio sendingRadio) {
Position sendingPosition = sendingRadio.getPosition();
RadioConnection newConnection = new RadioConnection(sendingRadio);
public void setTxRange(double r) {
TRANSMITTING_RANGE = r;
dgrm.requestEdgeAnalysis();
}
// Fetch current output power indicator (scale with as percent)
double moteTransmissionRange = TRANSMITTING_RANGE
* ((double) sendingRadio.getCurrentOutputPowerIndicator() / (double) sendingRadio.getOutputPowerIndicatorMax());
double moteInterferenceRange = INTERFERENCE_RANGE
* ((double) sendingRadio.getCurrentOutputPowerIndicator() / (double) sendingRadio.getOutputPowerIndicatorMax());
public void setInterferenceRange(double r) {
INTERFERENCE_RANGE = r;
dgrm.requestEdgeAnalysis();
}
/* Fail transmission randomly (affects all receiving nodes) */
public RadioConnection createConnections(Radio sender) {
RadioConnection newConnection = new RadioConnection(sender);
/* Fail radio transmission randomly - no radios will hear this transmission */
if (SUCCESS_RATIO_TX < 1.0 && random.nextDouble() > SUCCESS_RATIO_TX) {
return newConnection;
}
// Loop through all radios
for (int listenNr = 0; listenNr < getRegisteredRadios().size(); listenNr++) {
Radio listeningRadio = getRegisteredRadios().get(listenNr);
Position listeningRadioPosition = listeningRadio.getPosition();
/* 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());
// Ignore sending radio and radios on different channels
if (sendingRadio == listeningRadio) {
/* 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;
Position recvPos = recv.getPosition();
/* Fail if radios are on different (but configured) channels */
if (sender.getChannel() >= 0 &&
recv.getChannel() >= 0 &&
sender.getChannel() != recv.getChannel()) {
continue;
}
if (sendingRadio.getChannel() >= 0 &&
listeningRadio.getChannel() >= 0 &&
sendingRadio.getChannel() != listeningRadio.getChannel()) {
continue;
}
if (!listeningRadio.isReceiverOn()) {
/* 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 (sendingRadio instanceof ContikiRadio &&
!(listeningRadio instanceof ContikiRadio)) {
if (sender instanceof ContikiRadio &&
!(recv instanceof ContikiRadio)) {
/*logger.info("Special case: creating connection to turned off radio");*/
} else {
continue;
}
}
double distance = sendingPosition.getDistanceTo(listeningRadioPosition);
double distance = senderPos.getDistanceTo(recvPos);
if (distance <= moteTransmissionRange) {
// Check if this radio is able to receive transmission
if (listeningRadio.isInterfered()) {
// Keep interfering radio
newConnection.addInterfered(listeningRadio);
/* Within transmission range */
} else if (listeningRadio.isReceiving() ||
if (recv.isInterfered()) {
/* Was interfered: keep interfering */
newConnection.addInterfered(recv);
} else if (recv.isReceiving() ||
(SUCCESS_RATIO_RX < 1.0 && random.nextDouble() > SUCCESS_RATIO_RX)) {
newConnection.addInterfered(listeningRadio);
/* Was receiving, or reception failed: start interfering */
newConnection.addInterfered(recv);
recv.interfereAnyReception();
// Start interfering radio
listeningRadio.interfereAnyReception();
// Update connection that is transmitting to this radio
RadioConnection existingConn = null;
/* Interfere receiver in all other active radio connections */
for (RadioConnection conn : getActiveConnections()) {
for (Radio dstRadio : conn.getDestinations()) {
if (dstRadio == listeningRadio) {
existingConn = conn;
if (dstRadio == recv) {
conn.removeDestination(recv);
conn.addInterfered(recv);
break;
}
}
}
if (existingConn != null) {
// Change radio from receiving to interfered
existingConn.removeDestination(listeningRadio);
existingConn.addInterfered(listeningRadio);
}
} else {
// Radio OK to receive
newConnection.addDestination(listeningRadio);
/* Success: radio starts receiving */
newConnection.addDestination(recv);
}
} else if (distance <= moteInterferenceRange) {
// Interfere radio
newConnection.addInterfered(listeningRadio);
listeningRadio.interfereAnyReception();
/* Within interference range */
newConnection.addInterfered(recv);
recv.interfereAnyReception();
}
}
@ -178,20 +248,16 @@ public class UDGM 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();
// }
/* Override: uses distance as signal strength factor */
// Reset signal strength on all radios
/* Reset signal strengths */
for (Radio radio : getRegisteredRadios()) {
radio.setCurrentSignalStrength(SS_NOTHING);
}
// Set signal strength on all OK transmissions
for (RadioConnection conn : getActiveConnections()) {
/* Set signal strength to below strong on destinations */
RadioConnection[] conns = getActiveConnections();
for (RadioConnection conn : conns) {
conn.getSource().setCurrentSignalStrength(SS_STRONG);
for (Radio dstRadio : conn.getDestinations()) {
double dist = conn.getSource().getPosition().getDistanceTo(dstRadio.getPosition());
@ -205,8 +271,8 @@ public class UDGM extends AbstractRadioMedium {
}
}
// Set signal strength on all interferences
for (RadioConnection conn : getActiveConnections()) {
/* Set signal strength to below weak on interfered */
for (RadioConnection conn : conns) {
for (Radio intfRadio : conn.getInterfered()) {
double dist = conn.getSource().getPosition().getDistanceTo(intfRadio.getPosition());
@ -222,37 +288,23 @@ public class UDGM extends AbstractRadioMedium {
}
if (!intfRadio.isInterfered()) {
// Set to interfered again
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]);
// }
}
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
ArrayList<Element> config = new ArrayList<Element>();
Element element;
// Transmitting range
/* Transmitting range */
element = new Element("transmitting_range");
element.setText(Double.toString(TRANSMITTING_RANGE));
config.add(element);
// Interference range
/* Interference range */
element = new Element("interference_range");
element.setText(Double.toString(INTERFERENCE_RANGE));
config.add(element);