* bugfixed and rewrote MRM to improve configurability

* improved MRM GUI, including MRM visualizer skin that shows prr and rss
* added experimental support for directional antennas
This commit is contained in:
Fredrik Osterlind 2011-11-01 11:37:19 +01:00
parent 77057ede8a
commit eb84fbeb79
6 changed files with 1126 additions and 622 deletions

View file

@ -63,14 +63,16 @@ import java.io.File;
import java.net.URL; import java.net.URL;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.Random; import java.util.Random;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
@ -99,11 +101,12 @@ import org.jdom.Element;
import se.sics.cooja.ClassDescription; import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI; import se.sics.cooja.GUI;
import se.sics.cooja.PluginType; import se.sics.cooja.PluginType;
import se.sics.cooja.RadioConnection;
import se.sics.cooja.Simulation; import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin; import se.sics.cooja.VisPlugin;
import se.sics.cooja.interfaces.DirectionalAntennaRadio;
import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Position;
import se.sics.cooja.interfaces.Radio; import se.sics.cooja.interfaces.Radio;
import se.sics.mrm.ChannelModel.TxPair;
/** /**
* The class AreaViewer belongs to the MRM package. * The class AreaViewer belongs to the MRM package.
@ -118,14 +121,13 @@ import se.sics.cooja.interfaces.Radio;
* @see MRM * @see MRM
* @author Fredrik Osterlind * @author Fredrik Osterlind
*/ */
@ClassDescription("MRM - Area Viewer") @ClassDescription("MRM Radio environment")
@PluginType(PluginType.SIM_PLUGIN) @PluginType(PluginType.SIM_PLUGIN)
public class AreaViewer extends VisPlugin { public class AreaViewer extends VisPlugin {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AreaViewer.class); private static Logger logger = Logger.getLogger(AreaViewer.class);
private final JPanel canvas; private final JPanel canvas;
private final VisPlugin thisPlugin;
ChannelModel.TransmissionData dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH; ChannelModel.TransmissionData dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH;
ButtonGroup visTypeSelectionGroup; ButtonGroup visTypeSelectionGroup;
@ -144,7 +146,7 @@ public class AreaViewer extends VisPlugin {
private boolean drawCalculatedObstacles = true; private boolean drawCalculatedObstacles = true;
private boolean drawChannelProbabilities = true; private boolean drawChannelProbabilities = true;
private boolean drawRadios = true; private boolean drawRadios = true;
private boolean drawRadioActivity = true; //private boolean drawRadioActivity = true;
private boolean drawScaleArrow = true; private boolean drawScaleArrow = true;
// Background drawing parameters (meters) // Background drawing parameters (meters)
@ -171,7 +173,7 @@ public class AreaViewer extends VisPlugin {
private Image channelImage = null; private Image channelImage = null;
private JSlider resolutionSlider; private JSlider resolutionSlider;
private JPanel controlPanel; private Box controlPanel;
private JScrollPane scrollControlPanel; private JScrollPane scrollControlPanel;
private Simulation currentSimulation; private Simulation currentSimulation;
@ -200,20 +202,25 @@ public class AreaViewer extends VisPlugin {
private JCheckBox obstaclesCheckBox; private JCheckBox obstaclesCheckBox;
private JCheckBox channelCheckBox; private JCheckBox channelCheckBox;
private JCheckBox radiosCheckBox; private JCheckBox radiosCheckBox;
private JCheckBox radioActivityCheckBox; // private JCheckBox radioActivityCheckBox;
private JCheckBox arrowCheckBox; private JCheckBox arrowCheckBox;
private JRadioButton noneButton = null; private JRadioButton noneButton = null;
private JRadioButton selectModeButton;
private JRadioButton panModeButton;
private JRadioButton zoomModeButton;
private JRadioButton trackModeButton; private JRadioButton trackModeButton;
private Action paintEnvironmentAction;
/** /**
* Initializes an AreaViewer. * Initializes an AreaViewer.
* *
* @param simulationToVisualize Simulation using MRM * @param simulationToVisualize Simulation using MRM
*/ */
public AreaViewer(Simulation simulationToVisualize, GUI gui) { public AreaViewer(Simulation simulationToVisualize, GUI gui) {
super("MRM - Area Viewer", gui); super("MRM Radio environment", gui);
currentSimulation = simulationToVisualize; currentSimulation = simulationToVisualize;
currentRadioMedium = (MRM) currentSimulation.getRadioMedium(); currentRadioMedium = (MRM) currentSimulation.getRadioMedium();
@ -227,7 +234,6 @@ public class AreaViewer extends VisPlugin {
// Set initial size etc. // Set initial size etc.
setSize(500, 500); setSize(500, 500);
setVisible(true); setVisible(true);
thisPlugin = this;
// Canvas mode radio buttons + show settings checkbox // Canvas mode radio buttons + show settings checkbox
showSettingsBox = new JCheckBox ("settings", true); showSettingsBox = new JCheckBox ("settings", true);
@ -236,20 +242,20 @@ public class AreaViewer extends VisPlugin {
showSettingsBox.setActionCommand("toggle show settings"); showSettingsBox.setActionCommand("toggle show settings");
showSettingsBox.addActionListener(canvasModeHandler); showSettingsBox.addActionListener(canvasModeHandler);
JRadioButton selectModeButton = new JRadioButton ("select"); selectModeButton = new JRadioButton ("select");
selectModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); selectModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
selectModeButton.setContentAreaFilled(false); selectModeButton.setContentAreaFilled(false);
selectModeButton.setActionCommand("set select mode"); selectModeButton.setActionCommand("set select mode");
selectModeButton.addActionListener(canvasModeHandler); selectModeButton.addActionListener(canvasModeHandler);
selectModeButton.setSelected(true); selectModeButton.setSelected(true);
JRadioButton panModeButton = new JRadioButton ("pan"); panModeButton = new JRadioButton ("pan");
panModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); panModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
panModeButton.setContentAreaFilled(false); panModeButton.setContentAreaFilled(false);
panModeButton.setActionCommand("set pan mode"); panModeButton.setActionCommand("set pan mode");
panModeButton.addActionListener(canvasModeHandler); panModeButton.addActionListener(canvasModeHandler);
JRadioButton zoomModeButton = new JRadioButton ("zoom"); zoomModeButton = new JRadioButton ("zoom");
zoomModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); zoomModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT);
zoomModeButton.setContentAreaFilled(false); zoomModeButton.setContentAreaFilled(false);
zoomModeButton.setActionCommand("set zoom mode"); zoomModeButton.setActionCommand("set zoom mode");
@ -322,10 +328,10 @@ public class AreaViewer extends VisPlugin {
radiosCheckBox.addActionListener(selectGraphicsHandler); radiosCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(radiosCheckBox); graphicsComponentsPanel.add(radiosCheckBox);
radioActivityCheckBox = new JCheckBox("Radio Activity", true); // radioActivityCheckBox = new JCheckBox("Radio Activity", true);
radioActivityCheckBox.setActionCommand("toggle radio activity"); // radioActivityCheckBox.setActionCommand("toggle radio activity");
radioActivityCheckBox.addActionListener(selectGraphicsHandler); // radioActivityCheckBox.addActionListener(selectGraphicsHandler);
graphicsComponentsPanel.add(radioActivityCheckBox); // graphicsComponentsPanel.add(radioActivityCheckBox);
arrowCheckBox = new JCheckBox("Scale arrow", true); arrowCheckBox = new JCheckBox("Scale arrow", true);
arrowCheckBox.setActionCommand("toggle arrow"); arrowCheckBox.setActionCommand("toggle arrow");
@ -366,8 +372,7 @@ public class AreaViewer extends VisPlugin {
graphicsComponentsPanel.add(customButton); graphicsComponentsPanel.add(customButton);
// Create visualize channel output panel // Create visualize channel output panel
JPanel visualizeChannelPanel = new JPanel(); Box visualizeChannelPanel = Box.createVerticalBox();
visualizeChannelPanel.setLayout(new BoxLayout(visualizeChannelPanel, BoxLayout.Y_AXIS));
visualizeChannelPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); visualizeChannelPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT); visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
@ -400,6 +405,8 @@ public class AreaViewer extends VisPlugin {
visualizeChannelPanel.add(fixedVsRelative); visualizeChannelPanel.add(fixedVsRelative);
coloringIntervalPanel = new JPanel() { coloringIntervalPanel = new JPanel() {
private static final long serialVersionUID = 8247374386307237940L;
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
@ -572,20 +579,22 @@ public class AreaViewer extends VisPlugin {
visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20))); visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20)));
JButton recalculateVisibleButton = new JButton("Paint radio channel"); JButton recalculateVisibleButton = new JButton("Paint radio channel");
recalculateVisibleButton.setActionCommand("recalculate visible area"); paintEnvironmentAction = new AbstractAction("Paint radio channel") {
recalculateVisibleButton.addActionListener(formulaHandler); private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
repaintRadioEnvironment();
}
};
paintEnvironmentAction.setEnabled(false);
recalculateVisibleButton.setAction(paintEnvironmentAction);
visualizeChannelPanel.add(recalculateVisibleButton); visualizeChannelPanel.add(recalculateVisibleButton);
// Create control panel // Create control panel
controlPanel = new JPanel(); controlPanel = Box.createVerticalBox();
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
graphicsComponentsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); graphicsComponentsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(graphicsComponentsPanel); controlPanel.add(graphicsComponentsPanel);
controlPanel.add(new JSeparator()); controlPanel.add(new JSeparator());
controlPanel.add(Box.createRigidArea(new Dimension(0, 5)));
visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(visualizeChannelPanel); controlPanel.add(visualizeChannelPanel);
controlPanel.setPreferredSize(new Dimension(250,700));
controlPanel.setAlignmentX(Component.LEFT_ALIGNMENT); controlPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
scrollControlPanel = new JScrollPane( scrollControlPanel = new JScrollPane(
controlPanel, controlPanel,
@ -627,15 +636,47 @@ public class AreaViewer extends VisPlugin {
*/ */
private MouseAdapter canvasMouseHandler = new MouseAdapter() { private MouseAdapter canvasMouseHandler = new MouseAdapter() {
private Popup popUpToolTip = null; private Popup popUpToolTip = null;
private boolean temporaryZoom = false;
private boolean temporaryPan = false;
private boolean trackedPreviously = false;
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
if (temporaryZoom) {
temporaryZoom = false;
if (trackedPreviously) {
trackModeButton.doClick();
} else {
selectModeButton.doClick();
}
}
if (temporaryPan) {
temporaryPan = false;
if (trackedPreviously) {
trackModeButton.doClick();
} else {
selectModeButton.doClick();
}
}
if (popUpToolTip != null) { if (popUpToolTip != null) {
popUpToolTip.hide(); popUpToolTip.hide();
popUpToolTip = null; popUpToolTip = null;
} }
} }
public void mousePressed(MouseEvent e) { public void mousePressed(final MouseEvent e) {
if (e.isControlDown()) {
temporaryZoom = true;
trackedPreviously = inTrackMode;
zoomModeButton.doClick();
}
if (e.isAltDown()) {
temporaryPan = true;
trackedPreviously = inTrackMode;
panModeButton.doClick();
//canvasModeHandler.actionPerformed(new ActionEvent(e, 0, "set zoom mode"));
}
if (popUpToolTip != null) { if (popUpToolTip != null) {
popUpToolTip.hide(); popUpToolTip.hide();
popUpToolTip = null; popUpToolTip = null;
@ -646,32 +687,40 @@ public class AreaViewer extends VisPlugin {
zoomCenterX = e.getX() / currentZoomX - currentPanX; zoomCenterX = e.getX() / currentZoomX - currentPanX;
zoomCenterY = e.getY() / currentZoomY - currentPanY; zoomCenterY = e.getY() / currentZoomY - currentPanY;
zoomCenterPoint = e.getPoint(); zoomCenterPoint = e.getPoint();
if (temporaryZoom || temporaryPan) {
e.consume();
return;
}
/* Select */ /* Select */
if (inSelectMode) { if (inSelectMode) {
Vector<Radio> hitRadios = trackClickedRadio(e.getPoint()); ArrayList<Radio> hitRadios = trackClickedRadio(e.getPoint());
if (hitRadios == null || hitRadios.size() == 0) { if (hitRadios == null || hitRadios.size() == 0) {
if (e.getButton() != MouseEvent.BUTTON1) { if (e.getButton() != MouseEvent.BUTTON1) {
selectedRadio = null; selectedRadio = null;
channelImage = null; channelImage = null;
trackModeButton.setEnabled(false); trackModeButton.setEnabled(false);
paintEnvironmentAction.setEnabled(false);
canvas.repaint(); canvas.repaint();
} }
return; return;
} }
if (hitRadios.size() == 1 && hitRadios.firstElement() == selectedRadio) { if (hitRadios.size() == 1 && hitRadios.get(0) == selectedRadio) {
return; return;
} }
if (selectedRadio == null || !hitRadios.contains(selectedRadio)) { if (selectedRadio == null || !hitRadios.contains(selectedRadio)) {
selectedRadio = hitRadios.firstElement(); selectedRadio = hitRadios.get(0);
trackModeButton.setEnabled(true); trackModeButton.setEnabled(true);
paintEnvironmentAction.setEnabled(true);
} else { } else {
selectedRadio = hitRadios.get( selectedRadio = hitRadios.get(
(hitRadios.indexOf(selectedRadio)+1) % hitRadios.size() (hitRadios.indexOf(selectedRadio)+1) % hitRadios.size()
); );
trackModeButton.setEnabled(true); trackModeButton.setEnabled(true);
paintEnvironmentAction.setEnabled(true);
} }
channelImage = null; channelImage = null;
@ -681,30 +730,42 @@ public class AreaViewer extends VisPlugin {
/* Track */ /* Track */
if (inTrackMode && selectedRadio != null) { if (inTrackMode && selectedRadio != null) {
double realClickedX = e.getX() / currentZoomX - currentPanX; TxPair txPair = new TxPair() {
double realClickedY = e.getY() / currentZoomY - currentPanY; public double getFromX() { return selectedRadio.getPosition().getXCoordinate(); }
public double getFromY() { return selectedRadio.getPosition().getYCoordinate(); }
Position radioPosition = selectedRadio.getPosition(); public double getToX() { return e.getX() / currentZoomX - currentPanX; }
final double radioX = radioPosition.getXCoordinate(); public double getToY() { return e.getY() / currentZoomY - currentPanY; }
final double radioY = radioPosition.getYCoordinate(); public double getTxPower() { return selectedRadio.getCurrentOutputPower(); }
public double getTxGain() {
trackedComponents = currentChannelModel.getRaysOfTransmission(radioX, radioY, realClickedX, realClickedY); if (!(selectedRadio instanceof DirectionalAntennaRadio)) {
return 0;
}
DirectionalAntennaRadio r = (DirectionalAntennaRadio)selectedRadio;
double txGain = r.getRelativeGain(r.getDirection() + getAngle(), getDistance());
//logger.debug("tx gain: " + txGain + " (angle " + String.format("%1.1f", Math.toDegrees(r.getDirection() + getAngle())) + ")");
return txGain;
}
public double getRxGain() {
return 0;
}
};
trackedComponents = currentChannelModel.getRaysOfTransmission(txPair);
canvas.repaint(); canvas.repaint();
/* Show popup */ /* Show popup */
JToolTip t = AreaViewer.this.createToolTip(); JToolTip t = AreaViewer.this.createToolTip();
String logHtml = String logHtml =
"<html>" + "<html>" +
trackedComponents.log.replace("\n", "<br>").replace(" pi", " &pi;") + trackedComponents.log.replace("\n", "<br>").replace(" pi", " &pi;") +
"</html>"; "</html>";
t.setTipText(logHtml); t.setTipText(logHtml);
if (t.getTipText() == null || t.getTipText().equals("")) { if (t.getTipText() == null || t.getTipText().equals("")) {
return; return;
} }
popUpToolTip = PopupFactory.getSharedInstance().getPopup( popUpToolTip = PopupFactory.getSharedInstance().getPopup(
AreaViewer.this, t, e.getXOnScreen(), e.getYOnScreen()); AreaViewer.this, t, e.getXOnScreen(), e.getYOnScreen());
popUpToolTip.show(); popUpToolTip.show();
} }
} }
@ -806,9 +867,8 @@ public class AreaViewer extends VisPlugin {
} else { } else {
scrollControlPanel.setVisible(false); scrollControlPanel.setVisible(false);
} }
thisPlugin.invalidate(); AreaViewer.this.invalidate();
thisPlugin.revalidate(); AreaViewer.this.revalidate();
} }
} }
}; };
@ -826,8 +886,8 @@ public class AreaViewer extends VisPlugin {
drawChannelProbabilities = ((JCheckBox) e.getSource()).isSelected(); drawChannelProbabilities = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle radios")) { } else if (e.getActionCommand().equals("toggle radios")) {
drawRadios = ((JCheckBox) e.getSource()).isSelected(); drawRadios = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle radio activity")) { // } else if (e.getActionCommand().equals("toggle radio activity")) {
drawRadioActivity = ((JCheckBox) e.getSource()).isSelected(); // drawRadioActivity = ((JCheckBox) e.getSource()).isSelected();
} else if (e.getActionCommand().equals("toggle arrow")) { } else if (e.getActionCommand().equals("toggle arrow")) {
drawScaleArrow = ((JCheckBox) e.getSource()).isSelected(); drawScaleArrow = ((JCheckBox) e.getSource()).isSelected();
} }
@ -871,6 +931,7 @@ public class AreaViewer extends VisPlugin {
} }
class ImageSettingsDialog extends JDialog { class ImageSettingsDialog extends JDialog {
private static final long serialVersionUID = 3026474554976919518L;
private double private double
virtualStartX = 0.0, virtualStartX = 0.0,
@ -1205,7 +1266,7 @@ public class AreaViewer extends VisPlugin {
} }
currentChannelModel.notifySettingsChanged(); currentChannelModel.notifySettingsChanged();
thisPlugin.repaint(); AreaViewer.this.repaint();
} catch (Exception ex) { } catch (Exception ex) {
if (pm.isCanceled()) { if (pm.isCanceled()) {
@ -1230,6 +1291,8 @@ public class AreaViewer extends VisPlugin {
}; };
class ObstacleFinderDialog extends JDialog { class ObstacleFinderDialog extends JDialog {
private static final long serialVersionUID = -8963997923536967296L;
private NumberFormat intFormat = NumberFormat.getIntegerInstance(); private NumberFormat intFormat = NumberFormat.getIntegerInstance();
private BufferedImage imageToAnalyze = null; private BufferedImage imageToAnalyze = null;
private BufferedImage obstacleImage = null; private BufferedImage obstacleImage = null;
@ -1449,6 +1512,7 @@ public class AreaViewer extends VisPlugin {
// Preview image // Preview image
tempPanel = new JPanel() { tempPanel = new JPanel() {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
g.drawImage(imageToAnalyze, 0, 0, getWidth(), getHeight(), this); g.drawImage(imageToAnalyze, 0, 0, getWidth(), getHeight(), this);
@ -1609,6 +1673,7 @@ public class AreaViewer extends VisPlugin {
selectedRadio = null; selectedRadio = null;
channelImage = null; channelImage = null;
trackModeButton.setEnabled(false); trackModeButton.setEnabled(false);
paintEnvironmentAction.setEnabled(false);
canvas.repaint(); canvas.repaint();
} }
}; };
@ -1675,13 +1740,7 @@ public class AreaViewer extends VisPlugin {
return (alpha << 24) | (red << 16) | (green << 8) | blue; return (alpha << 24) | (red << 16) | (green << 8) | blue;
} }
/** private void repaintRadioEnvironment() {
* Helps user adjust and calculate the channel propagation formula
*/
private ActionListener formulaHandler = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("recalculate visible area")) {
// Get resolution of new image // Get resolution of new image
final Dimension resolution = new Dimension( final Dimension resolution = new Dimension(
resolutionSlider.getValue(), resolutionSlider.getValue(),
@ -1734,15 +1793,36 @@ public class AreaViewer extends VisPlugin {
double[][] imageValues = new double[resolution.width][resolution.height]; double[][] imageValues = new double[resolution.width][resolution.height];
for (int x=0; x < resolution.width; x++) { for (int x=0; x < resolution.width; x++) {
for (int y=0; y < resolution.height; y++) { for (int y=0; y < resolution.height; y++) {
final double xx = x;
final double yy = y;
TxPair txPair = new TxPair() {
public double getDistance() {
double w = getFromX() - getToX();
double h = getFromY() - getToY();
return Math.sqrt(w*w+h*h);
}
public double getFromX() { return radioX; }
public double getFromY() { return radioY; }
public double getToX() { return startX + width * xx/resolution.width; }
public double getToY() { return startY + height * yy/resolution.height; }
public double getTxPower() { return selectedRadio.getCurrentOutputPower(); }
public double getTxGain() {
if (!(selectedRadio instanceof DirectionalAntennaRadio)) {
return 0;
}
DirectionalAntennaRadio r = (DirectionalAntennaRadio)selectedRadio;
double txGain = r.getRelativeGain(r.getDirection() + getAngle(), getDistance());
//logger.debug("tx gain: " + txGain + " (angle " + String.format("%1.1f", Math.toDegrees(r.getDirection() + getAngle())) + ")");
return txGain;
}
public double getRxGain() {
return 0;
}
};
if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH) { if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH) {
// Attenuate // Attenuate
double[] signalStrength = currentChannelModel.getReceivedSignalStrength( double[] signalStrength = currentChannelModel.getReceivedSignalStrength(txPair);
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
);
// Collecting signal strengths // Collecting signal strengths
if (signalStrength[0] < lowestImageValue) { if (signalStrength[0] < lowestImageValue) {
@ -1756,12 +1836,7 @@ public class AreaViewer extends VisPlugin {
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR) { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR) {
// Attenuate // Attenuate
double[] signalStrength = currentChannelModel.getReceivedSignalStrength( double[] signalStrength = currentChannelModel.getReceivedSignalStrength(txPair);
radioX,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
);
// Collecting variances // Collecting variances
if (signalStrength[1] < lowestImageValue) { if (signalStrength[1] < lowestImageValue) {
@ -1776,10 +1851,7 @@ public class AreaViewer extends VisPlugin {
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR) { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR) {
// Get signal to noise ratio // Get signal to noise ratio
double[] snr = currentChannelModel.getSINR( double[] snr = currentChannelModel.getSINR(
radioX, txPair,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE -Double.MAX_VALUE
); );
@ -1796,10 +1868,7 @@ public class AreaViewer extends VisPlugin {
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) {
// Get signal to noise ratio // Get signal to noise ratio
double[] snr = currentChannelModel.getSINR( double[] snr = currentChannelModel.getSINR(
radioX, txPair,
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE -Double.MAX_VALUE
); );
@ -1815,11 +1884,7 @@ public class AreaViewer extends VisPlugin {
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) {
// Get probability of receiving a packet TODO What size? Does it matter? // Get probability of receiving a packet TODO What size? Does it matter?
double probability = currentChannelModel.getProbability( double probability = currentChannelModel.getProbability(
radioX, txPair, -Double.MAX_VALUE
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height,
-Double.MAX_VALUE
)[0]; )[0];
// Collecting variances // Collecting variances
@ -1834,10 +1899,7 @@ public class AreaViewer extends VisPlugin {
} else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) {
// Get RMS delay spread of receiving a packet // Get RMS delay spread of receiving a packet
double rmsDelaySpread = currentChannelModel.getRMSDelaySpread( double rmsDelaySpread = currentChannelModel.getRMSDelaySpread(
radioX, txPair
radioY,
startX + width * x/resolution.width,
startY + height * y/resolution.height
); );
// Collecting variances // Collecting variances
@ -1909,7 +1971,7 @@ public class AreaViewer extends VisPlugin {
channelHeight = height; channelHeight = height;
channelImage = tempChannelImage; channelImage = tempChannelImage;
thisPlugin.repaint(); AreaViewer.this.repaint();
coloringIntervalPanel.repaint(); coloringIntervalPanel.repaint();
} catch (Exception ex) { } catch (Exception ex) {
@ -1927,9 +1989,7 @@ public class AreaViewer extends VisPlugin {
// Start thread // Start thread
attenuatorThread = new Thread(runnable); attenuatorThread = new Thread(runnable);
attenuatorThread.start(); attenuatorThread.start();
} }
}
};
/** /**
* Repaint the canvas * Repaint the canvas
@ -2112,66 +2172,66 @@ public class AreaViewer extends VisPlugin {
} }
// -- Draw radio activity -- // -- Draw radio activity --
if (drawRadioActivity) { // if (drawRadioActivity) {
for (RadioConnection connection: currentRadioMedium.getActiveConnections()) { // for (RadioConnection connection: currentRadioMedium.getActiveConnections()) {
Position sourcePosition = connection.getSource().getPosition(); // Position sourcePosition = connection.getSource().getPosition();
//
// Paint scaled (otherwise bad rounding to integers may occur) // // Paint scaled (otherwise bad rounding to integers may occur)
g2d.setTransform(realWorldTransformScaled); // g2d.setTransform(realWorldTransformScaled);
g2d.setStroke(new BasicStroke((float) 0.0)); // g2d.setStroke(new BasicStroke((float) 0.0));
//
for (Radio receivingRadio: connection.getDestinations()) { // for (Radio receivingRadio: connection.getDestinations()) {
g2d.setColor(Color.GREEN); // g2d.setColor(Color.GREEN);
//
// Get source and destination coordinates // // Get source and destination coordinates
Position destinationPosition = receivingRadio.getPosition(); // Position destinationPosition = receivingRadio.getPosition();
//
g2d.draw(new Line2D.Double( // g2d.draw(new Line2D.Double(
sourcePosition.getXCoordinate()*100.0, // sourcePosition.getXCoordinate()*100.0,
sourcePosition.getYCoordinate()*100.0, // sourcePosition.getYCoordinate()*100.0,
destinationPosition.getXCoordinate()*100.0, // destinationPosition.getXCoordinate()*100.0,
destinationPosition.getYCoordinate()*100.0 // destinationPosition.getYCoordinate()*100.0
)); // ));
} // }
//
for (Radio interferedRadio: connection.getInterfered()) { // for (Radio interferedRadio: connection.getInterfered()) {
g2d.setColor(Color.RED); // g2d.setColor(Color.RED);
//
// Get source and destination coordinates // // Get source and destination coordinates
Position destinationPosition = interferedRadio.getPosition(); // Position destinationPosition = interferedRadio.getPosition();
//
g2d.draw(new Line2D.Double( // g2d.draw(new Line2D.Double(
sourcePosition.getXCoordinate()*100.0, // sourcePosition.getXCoordinate()*100.0,
sourcePosition.getYCoordinate()*100.0, // sourcePosition.getYCoordinate()*100.0,
destinationPosition.getXCoordinate()*100.0, // destinationPosition.getXCoordinate()*100.0,
destinationPosition.getYCoordinate()*100.0 // destinationPosition.getYCoordinate()*100.0
)); // ));
} // }
//
g2d.setColor(Color.BLUE); // g2d.setColor(Color.BLUE);
g2d.setTransform(realWorldTransform); // g2d.setTransform(realWorldTransform);
//
g2d.translate( // g2d.translate(
sourcePosition.getXCoordinate(), // sourcePosition.getXCoordinate(),
sourcePosition.getYCoordinate() // sourcePosition.getYCoordinate()
); // );
//
// Fetch current translation // // Fetch current translation
double xPos = g2d.getTransform().getTranslateX(); // double xPos = g2d.getTransform().getTranslateX();
double yPos = g2d.getTransform().getTranslateY(); // double yPos = g2d.getTransform().getTranslateY();
//
// Jump to identity transform and paint without scaling // // Jump to identity transform and paint without scaling
g2d.setTransform(new AffineTransform()); // g2d.setTransform(new AffineTransform());
//
g2d.fillOval( // g2d.fillOval(
(int) xPos, // (int) xPos,
(int) yPos, // (int) yPos,
5, // 5,
5 // 5
); // );
//
} // }
} // }
// -- Draw scale arrow -- // -- Draw scale arrow --
if (drawScaleArrow) { if (drawScaleArrow) {
@ -2210,7 +2270,7 @@ public class AreaViewer extends VisPlugin {
} }
// -- Draw tracked components (if any) -- // -- Draw tracked components (if any) --
if (inTrackMode && trackedComponents != null) { if (!currentSimulation.isRunning() && inTrackMode && trackedComponents != null) {
g2d.setTransform(realWorldTransformScaled); g2d.setTransform(realWorldTransformScaled);
g2d.setStroke(new BasicStroke((float) 0.0)); g2d.setStroke(new BasicStroke((float) 0.0));
@ -2238,8 +2298,8 @@ public class AreaViewer extends VisPlugin {
* @param clickedPoint On-screen position * @param clickedPoint On-screen position
* @return All hit radios * @return All hit radios
*/ */
protected Vector<Radio> trackClickedRadio(Point clickedPoint) { protected ArrayList<Radio> trackClickedRadio(Point clickedPoint) {
Vector<Radio> hitRadios = new Vector<Radio>(); ArrayList<Radio> hitRadios = new ArrayList<Radio>();
if (currentRadioMedium.getRegisteredRadioCount() == 0) { if (currentRadioMedium.getRegisteredRadioCount() == 0) {
return null; return null;
} }
@ -2297,9 +2357,16 @@ public class AreaViewer extends VisPlugin {
* @return XML element collection * @return XML element collection
*/ */
public Collection<Element> getConfigXML() { public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>(); ArrayList<Element> config = new ArrayList<Element>();
Element element; Element element;
/* Selected mote */
if (selectedRadio != null) {
element = new Element("selected");
element.setAttribute("mote", "" + selectedRadio.getMote().getID());
config.add(element);
}
// Controls visible // Controls visible
element = new Element("controls_visible"); element = new Element("controls_visible");
element.setText(Boolean.toString(showSettingsBox.isSelected())); element.setText(Boolean.toString(showSettingsBox.isSelected()));
@ -2332,9 +2399,9 @@ public class AreaViewer extends VisPlugin {
element = new Element("show_radios"); element = new Element("show_radios");
element.setText(Boolean.toString(drawRadios)); element.setText(Boolean.toString(drawRadios));
config.add(element); config.add(element);
element = new Element("show_activity"); // element = new Element("show_activity");
element.setText(Boolean.toString(drawRadioActivity)); // element.setText(Boolean.toString(drawRadioActivity));
config.add(element); // config.add(element);
element = new Element("show_arrow"); element = new Element("show_arrow");
element.setText(Boolean.toString(drawScaleArrow)); element.setText(Boolean.toString(drawScaleArrow));
config.add(element); config.add(element);
@ -2382,7 +2449,12 @@ public class AreaViewer extends VisPlugin {
*/ */
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) { public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
for (Element element : configXML) { for (Element element : configXML) {
if (element.getName().equals("controls_visible")) { if (element.getName().equals("selected")) {
int id = Integer.parseInt(element.getAttributeValue("mote"));
selectedRadio = currentSimulation.getMoteWithID(id).getInterfaces().getRadio();
trackModeButton.setEnabled(true);
paintEnvironmentAction.setEnabled(true);
} else if (element.getName().equals("controls_visible")) {
showSettingsBox.setSelected(Boolean.parseBoolean(element.getText())); showSettingsBox.setSelected(Boolean.parseBoolean(element.getText()));
canvasModeHandler.actionPerformed(new ActionEvent(showSettingsBox, canvasModeHandler.actionPerformed(new ActionEvent(showSettingsBox,
ActionEvent.ACTION_PERFORMED, showSettingsBox.getActionCommand())); ActionEvent.ACTION_PERFORMED, showSettingsBox.getActionCommand()));
@ -2411,9 +2483,9 @@ public class AreaViewer extends VisPlugin {
selectGraphicsHandler.actionPerformed(new ActionEvent(radiosCheckBox, selectGraphicsHandler.actionPerformed(new ActionEvent(radiosCheckBox,
ActionEvent.ACTION_PERFORMED, radiosCheckBox.getActionCommand())); ActionEvent.ACTION_PERFORMED, radiosCheckBox.getActionCommand()));
} else if (element.getName().equals("show_activity")) { } else if (element.getName().equals("show_activity")) {
radioActivityCheckBox.setSelected(Boolean.parseBoolean(element.getText())); // radioActivityCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(radioActivityCheckBox, // selectGraphicsHandler.actionPerformed(new ActionEvent(radioActivityCheckBox,
ActionEvent.ACTION_PERFORMED, radioActivityCheckBox.getActionCommand())); // ActionEvent.ACTION_PERFORMED, radioActivityCheckBox.getActionCommand()));
} else if (element.getName().equals("show_arrow")) { } else if (element.getName().equals("show_arrow")) {
arrowCheckBox.setSelected(Boolean.parseBoolean(element.getText())); arrowCheckBox.setSelected(Boolean.parseBoolean(element.getText()));
selectGraphicsHandler.actionPerformed(new ActionEvent(arrowCheckBox, selectGraphicsHandler.actionPerformed(new ActionEvent(arrowCheckBox,

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2006, Swedish Institute of Computer Science. * Copyright (c) 2011, Swedish Institute of Computer Science.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -31,12 +31,29 @@
package se.sics.mrm; package se.sics.mrm;
import java.awt.geom.*; import java.awt.geom.GeneralPath;
import java.util.*; import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;
import java.util.Random;
import java.util.Vector;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.jdom.Element; import org.jdom.Element;
import se.sics.cooja.Simulation;
import se.sics.cooja.interfaces.DirectionalAntennaRadio;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.radiomediums.AbstractRadioMedium;
import statistics.GaussianWrapper; import statistics.GaussianWrapper;
/** /**
@ -53,9 +70,12 @@ import statistics.GaussianWrapper;
public class ChannelModel { public class ChannelModel {
private static Logger logger = Logger.getLogger(ChannelModel.class); private static Logger logger = Logger.getLogger(ChannelModel.class);
private static final double C = 299792458; /* m/s */
enum TransmissionData { SIGNAL_STRENGTH, SIGNAL_STRENGTH_VAR, SNR, SNR_VAR, PROB_OF_RECEPTION, DELAY_SPREAD, DELAY_SPREAD_RMS} enum TransmissionData { SIGNAL_STRENGTH, SIGNAL_STRENGTH_VAR, SNR, SNR_VAR, PROB_OF_RECEPTION, DELAY_SPREAD, DELAY_SPREAD_RMS}
private Properties parameters = new Properties(); private Hashtable<Parameter,Object> parametersDefaults = new Hashtable<Parameter,Object>();
private Hashtable<Parameter,Object> parameters = new Hashtable<Parameter,Object>();
private Properties parameterDescriptions = new Properties(); private Properties parameterDescriptions = new Properties();
// Parameters used for speeding up calculations // Parameters used for speeding up calculations
@ -71,6 +91,8 @@ public class ChannelModel {
private StringBuilder logInfo = null; private StringBuilder logInfo = null;
private ArrayList<Line2D> loggedRays = null; private ArrayList<Line2D> loggedRays = null;
private Simulation simulation;
// Ray tracing components temporary vector // Ray tracing components temporary vector
private Vector<Vector<Line2D>> calculatedVisibleSides = new Vector<Vector<Line2D>>(); private Vector<Vector<Line2D>> calculatedVisibleSides = new Vector<Vector<Line2D>>();
@ -89,105 +111,180 @@ public class ChannelModel {
} }
} }
private SettingsObservable settingsObservable = new SettingsObservable(); private SettingsObservable settingsObservable = new SettingsObservable();
public enum Parameter {
apply_random,
snr_threshold,
bg_noise_mean,
bg_noise_var,
system_gain_mean,
system_gain_var,
frequency,
tx_power,
tx_with_gain,
rx_sensitivity,
rx_with_gain,
rt_disallow_direct_path,
rt_ignore_non_direct,
rt_fspl_on_total_length,
rt_max_rays,
rt_max_refractions,
rt_max_reflections,
rt_max_diffractions,
rt_use_scattering,
rt_refrac_coefficient,
rt_reflec_coefficient,
rt_diffr_coefficient,
rt_scatt_coefficient,
obstacle_attenuation;
public ChannelModel() { public static Object getDefaultValue(Parameter p) {
// - Set initial parameter values - switch (p) {
case apply_random:
return new Boolean(false);
case snr_threshold:
return new Double(6);
case bg_noise_mean:
return new Double(AbstractRadioMedium.SS_NOTHING);
case bg_noise_var:
return new Double(1);
case system_gain_mean:
return new Double(0);
case system_gain_var:
return new Double(4);
case frequency: /* MHz */
return new Double(2400);
case tx_power:
return new Double(1.5);
case tx_with_gain:
return new Boolean(true);
case rx_sensitivity:
return new Double(-100);
case rx_with_gain:
return new Boolean(false);
case rt_disallow_direct_path:
return new Boolean(false);
case rt_ignore_non_direct:
return new Boolean(false);
case rt_fspl_on_total_length:
return new Boolean(true);
case rt_max_rays:
return new Integer(1);
case rt_max_refractions:
return new Integer(1);
case rt_max_reflections:
return new Integer(1);
case rt_max_diffractions:
return new Integer(0);
case rt_use_scattering:
return new Boolean(false);
case rt_refrac_coefficient:
return new Double(-3);
case rt_reflec_coefficient:
return new Double(-5);
case rt_diffr_coefficient:
return new Double(-10);
case rt_scatt_coefficient:
return new Double(-20);
case obstacle_attenuation:
return new Double(-3);
}
throw new RuntimeException("Unknown default value: " + p);
}
public static Parameter fromString(String name) {
/* Backwards compatability */
if (name.equals("apply_random")) {
return apply_random;
} else if (name.equals("snr_threshold")) {
return snr_threshold;
} else if (name.equals("bg_noise_mean")) {
return bg_noise_mean;
} else if (name.equals("bg_noise_var")) {
return bg_noise_var;
} else if (name.equals("system_gain_mean")) {
return system_gain_mean;
} else if (name.equals("system_gain_var")) {
return system_gain_var;
} else if (name.equals("tx_power")) {
return tx_power;
} else if (name.equals("rx_sensitivity")) {
return rx_sensitivity;
} else if (name.equals("rt_disallow_direct_path")) {
return rt_disallow_direct_path;
} else if (name.equals("rt_ignore_non_direct")) {
return rt_ignore_non_direct;
} else if (name.equals("rt_fspl_on_total_length")) {
return rt_fspl_on_total_length;
} else if (name.equals("rt_max_rays")) {
return rt_max_rays;
} else if (name.equals("rt_max_refractions")) {
return rt_max_refractions;
} else if (name.equals("rt_max_reflections")) {
return rt_max_reflections;
} else if (name.equals("rt_max_diffractions")) {
return rt_max_diffractions;
} else if (name.equals("rt_use_scattering")) {
return rt_use_scattering;
} else if (name.equals("rt_refrac_coefficient")) {
return rt_refrac_coefficient;
} else if (name.equals("rt_reflec_coefficient")) {
return rt_reflec_coefficient;
} else if (name.equals("rt_diffr_coefficient")) {
return rt_diffr_coefficient;
} else if (name.equals("rt_scatt_coefficient")) {
return rt_scatt_coefficient;
} else if (name.equals("obstacle_attenuation")) {
return obstacle_attenuation;
}
return null;
}
// Using random variables public static String getDescription(Parameter p) {
parameters.put("apply_random", new Boolean(false)); // TODO Should not use random variables as default switch (p) {
parameterDescriptions.put("apply_random", "Apply random values immediately"); case apply_random: return "(DEBUG) Apply random values";
case snr_threshold: return "SNR reception threshold (dB)";
case bg_noise_mean: return "Background noise mean (dBm)";
case bg_noise_var: return "Background noise variance (dB)";
case system_gain_mean: return "Extra system gain mean (dB)";
case system_gain_var: return "Extra system gain variance (dB)";
case frequency: return "Frequency (MHz)";
case tx_power: return "Default transmitter output power (dBm)";
case tx_with_gain: return "Directional antennas: with TX gain";
case rx_sensitivity: return "Receiver sensitivity (dBm)";
case rx_with_gain: return "Directional antennas: with RX gain";
case rt_disallow_direct_path: return "Disallow direct path";
case rt_ignore_non_direct: return "If existing: return only use direct path";
case rt_fspl_on_total_length: return "Use FSPL on total path lengths only";
case rt_max_rays: return "Max path rays";
case rt_max_refractions: return "Max refractions";
case rt_max_reflections: return "Max reflections";
case rt_max_diffractions: return "Max diffractions";
case rt_refrac_coefficient: return "Refraction coefficient (dB)";
case rt_reflec_coefficient: return "Reflection coefficient (dB)";
case rt_diffr_coefficient: return "Diffraction coefficient (dB)";
case obstacle_attenuation: return "Obstacle attenuation (dB/m)";
}
throw new RuntimeException("Unknown decrption: " + p);
}
}
public ChannelModel(Simulation simulation) {
this.simulation = simulation;
/* Default values */
for (Parameter p: Parameter.values()) {
parameters.put(p, Parameter.getDefaultValue(p));
}
// Signal to noise reception threshold parametersDefaults = (Hashtable<Parameter,Object>) parameters.clone();
parameters.put("snr_threshold", new Double(6));
parameterDescriptions.put("snr_threshold", "SNR reception threshold (dB)");
// Background noise mean
parameters.put("bg_noise_mean", new Double(-150));
parameterDescriptions.put("bg_noise_mean", "Background noise mean (dBm)");
// Background noise variance
parameters.put("bg_noise_var", new Double(1));
parameterDescriptions.put("bg_noise_var", "Background noise variance (dB)");
// Extra system gain mean
parameters.put("system_gain_mean", new Double(0));
parameterDescriptions.put("system_gain_mean", "Extra system gain mean (dB)");
// Extra system gain variance
parameters.put("system_gain_var", new Double(4)); // TODO Should probably be default 0 or 1
parameterDescriptions.put("system_gain_var", "Extra system gain variance (dB)");
// Transmission wavelength
parameters.put("wavelength", new Double(0.346)); // ~868 MHz (RFM TR1001)
parameterDescriptions.put("wavelength", "Wavelength w (m)");
// Transmitter output power
parameters.put("tx_power", new Double(1.5)); // dBm (deciBel milliwatts)
parameterDescriptions.put("tx_power", "Transmitter output power (dBm)");
// Transmitter antenna gain
parameters.put("tx_antenna_gain", new Double(0)); // TODO Should use angle
parameterDescriptions.put("tx_antenna_gain", "Transmitter antenna gain (dB)");
// Receiver sensitivity
parameters.put("rx_sensitivity", new Double(-100));
parameterDescriptions.put("rx_sensitivity", "Receiver sensitivity (dBm)");
// Receiver antenna gain
parameters.put("rx_antenna_gain", new Double(0)); // TODO Should use angle
parameterDescriptions.put("rx_antenna_gain", "Receiver antenna gain (dB)");
// Ray Tracer - Disallow direct path
parameters.put("rt_disallow_direct_path", new Boolean(false));
parameterDescriptions.put("rt_disallow_direct_path", "Disallow direct path");
// Ray Tracer - If direct path exists, ignore the non-direct (used for debugging)
parameters.put("rt_ignore_non_direct", new Boolean(false));
parameterDescriptions.put("rt_ignore_non_direct", "If existing, only use direct path");
// Ray Tracer - Use FSPL on total length only
parameters.put("rt_fspl_on_total_length", new Boolean(true));
parameterDescriptions.put("rt_fspl_on_total_length", "Use FSPL on total path lengths only");
// Ray Tracer - Max number of subrays
parameters.put("rt_max_rays", new Integer(1));
parameterDescriptions.put("rt_max_rays", "Max path rays");
// Ray Tracer - Max number of refractions
parameters.put("rt_max_refractions", new Integer(1));
parameterDescriptions.put("rt_max_refractions", "Max refractions");
// Ray Tracer - Max number of reflections
parameters.put("rt_max_reflections", new Integer(1));
parameterDescriptions.put("rt_max_reflections", "Max reflections");
// Ray Tracer - Max number of diffractions
parameters.put("rt_max_diffractions", new Integer(0));
parameterDescriptions.put("rt_max_diffractions", "Max diffractions");
// Ray Tracer - Use scattering // Ray Tracer - Use scattering
//parameters.put("rt_use_scattering", new Boolean(false)); // TODO Not used yet //parameters.put(Parameters.rt_use_scattering, Parameter.getDefaultValue(Parameters.rt_use_scattering)); // TODO Not used yet
//parameterDescriptions.put("rt_use_scattering", "Use simple scattering"); //parameterDescriptions.put(Parameters.rt_use_scattering, "Use simple scattering");
// Ray Tracer - Refraction coefficient
parameters.put("rt_refrac_coefficient", new Double(-3));
parameterDescriptions.put("rt_refrac_coefficient", "Refraction coefficient (dB)");
// Ray Tracer - Reflection coefficient
parameters.put("rt_reflec_coefficient", new Double(-5));
parameterDescriptions.put("rt_reflec_coefficient", "Reflection coefficient (dB)");
// Ray Tracer - Diffraction coefficient
parameters.put("rt_diffr_coefficient", new Double(-10));
parameterDescriptions.put("rt_diffr_coefficient", "Diffraction coefficient (dB)");
// Ray Tracer - Scattering coefficient // Ray Tracer - Scattering coefficient
//parameters.put("rt_scatt_coefficient", new Double(-20)); // TODO Not used yet //parameters.put(Parameters.rt_scatt_coefficient, Parameter.getDefaultValue(Parameters.rt_scatt_coefficient)); // TODO Not used yet
//parameterDescriptions.put("rt_scatt_coefficient", "!! Scattering coefficient (dB)"); //parameterDescriptions.put(Parameters.rt_scatt_coefficient, "!! Scattering coefficient (dB)");
// Shadowing - Obstacle Attenuation constant
parameters.put("obstacle_attenuation", new Double(-3));
parameterDescriptions.put("obstacle_attenuation", "Obstacle attenuation (dB/m)");
} }
/** /**
@ -272,7 +369,7 @@ public class ChannelModel {
* @param identifier Parameter identifier * @param identifier Parameter identifier
* @return Current parameter value * @return Current parameter value
*/ */
public Object getParameterValue(String id) { public Object getParameterValue(Parameter id) {
Object value = parameters.get(id); Object value = parameters.get(id);
if (value == null) { if (value == null) {
logger.fatal("No parameter with id:" + id + ", aborting"); logger.fatal("No parameter with id:" + id + ", aborting");
@ -287,7 +384,7 @@ public class ChannelModel {
* @param identifier Parameter identifier * @param identifier Parameter identifier
* @return Current parameter value * @return Current parameter value
*/ */
public double getParameterDoubleValue(String id) { public double getParameterDoubleValue(Parameter id) {
return ((Double) getParameterValue(id)).doubleValue(); return ((Double) getParameterValue(id)).doubleValue();
} }
@ -297,7 +394,7 @@ public class ChannelModel {
* @param identifier Parameter identifier * @param identifier Parameter identifier
* @return Current parameter value * @return Current parameter value
*/ */
public int getParameterIntegerValue(String id) { public int getParameterIntegerValue(Parameter id) {
return ((Integer) getParameterValue(id)).intValue(); return ((Integer) getParameterValue(id)).intValue();
} }
@ -307,7 +404,7 @@ public class ChannelModel {
* @param identifier Parameter identifier * @param identifier Parameter identifier
* @return Current parameter value * @return Current parameter value
*/ */
public boolean getParameterBooleanValue(String id) { public boolean getParameterBooleanValue(Parameter id) {
return ((Boolean) getParameterValue(id)).booleanValue(); return ((Boolean) getParameterValue(id)).booleanValue();
} }
@ -317,7 +414,7 @@ public class ChannelModel {
* @param id Parameter identifier * @param id Parameter identifier
* @param newValue New parameter value * @param newValue New parameter value
*/ */
public void setParameterValue(String id, Object newValue) { public void setParameterValue(Parameter id, Object newValue) {
if (!parameters.containsKey(id)) { if (!parameters.containsKey(id)) {
logger.fatal("No parameter with id:" + id + ", aborting"); logger.fatal("No parameter with id:" + id + ", aborting");
return; return;
@ -331,21 +428,6 @@ public class ChannelModel {
settingsObservable.notifySettingsChanged(); settingsObservable.notifySettingsChanged();
} }
/**
* Returns a parameter description
*
* @param identifier Parameter identifier
* @return Current parameter description
*/
public String getParameterDescription(String id) {
Object value = parameterDescriptions.get(id);
if (value == null) {
logger.fatal("No parameter description with id:" + id + ", aborting");
return null;
}
return ((String) value);
}
/** /**
* When this method is called all settings observers * When this method is called all settings observers
* will be notified. * will be notified.
@ -355,27 +437,20 @@ public class ChannelModel {
} }
/** /**
* Returns the Free Space Path Loss factor (in dB), by using * Path loss component from Friis' transmission equation.
* parts of the Friis equation. (FSPL <= 0) * Uses frequency and distance only.
* *
* @param distance Distance from transmitter to receiver * @param distance Transmitter-receiver distance
* @return FSPL factor * @return Path loss (dB)
*/ */
protected double getFSPL(double distance) { protected double getFSPL(double distance) {
// From Friis equation:
// Pr(d) = Pt * (Gt * Gr * w2) / ( (4*PI)2 * d2 * L)
// For FSPL, ignoring Pt, Gt, Gr, L:
// Pr(d) = 1 * (1 * 1 * w2) / ( (4*PI)2 * d2 * 1)
// Pr(d) = w2 / ( (4*PI)2 * d2)
// Pr_dB(d) = 20*log10(w) - 20*log10(4*PI) - 20*log10(d)
if (needToPrecalculateFSPL) { if (needToPrecalculateFSPL) {
double w = getParameterDoubleValue("wavelength"); double f = getParameterDoubleValue(Parameter.frequency);
paramFSPL = 20*Math.log10(w) - 20*Math.log10(4*Math.PI); paramFSPL = -32.44 -20*Math.log10(f /*mhz*/);
needToPrecalculateFSPL = false; needToPrecalculateFSPL = false;
} }
return Math.min(0.0, paramFSPL - 20*Math.log10(distance)); return Math.min(0.0, paramFSPL - 20*Math.log10(distance/1000.0 /*km*/));
} }
@ -674,7 +749,7 @@ public class ChannelModel {
// Check if direct path exists // Check if direct path exists
justBeforeDestination = sourcePoint; justBeforeDestination = sourcePoint;
if (!getParameterBooleanValue("rt_disallow_direct_path")) { if (!getParameterBooleanValue(Parameter.rt_disallow_direct_path)) {
directPathExists = isDirectPath(justBeforeDestination, dest); directPathExists = isDirectPath(justBeforeDestination, dest);
} else { } else {
directPathExists = false; directPathExists = false;
@ -797,7 +872,7 @@ public class ChannelModel {
allPaths.add(currentPath); allPaths.add(currentPath);
// Stop here if no other paths should be considered // Stop here if no other paths should be considered
if (type == RayData.RayType.ORIGIN && getParameterBooleanValue("rt_ignore_non_direct")) { if (type == RayData.RayType.ORIGIN && getParameterBooleanValue(Parameter.rt_ignore_non_direct)) {
return allPaths; return allPaths;
} }
@ -1326,17 +1401,15 @@ public class ChannelModel {
* @return Received signal strength (dBm) random variable. The first value is * @return Received signal strength (dBm) random variable. The first value is
* the random variable mean, and the second is the variance. * the random variable mean, and the second is the variance.
*/ */
public double[] getReceivedSignalStrength(double sourceX, double sourceY, double destX, double destY) { public double[] getReceivedSignalStrength(TxPair txPair) {
return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH, null); return getTransmissionData(txPair, TransmissionData.SIGNAL_STRENGTH);
}
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 // TODO Fix better data type support
private double[] getTransmissionData(double sourceX, double sourceY, double destX, double destY, TransmissionData dataType, Double txPower) { private double[] getTransmissionData(TxPair txPair, TransmissionData dataType) {
Point2D source = new Point2D.Double(sourceX, sourceY); Point2D source = txPair.getFrom();
Point2D dest = new Point2D.Double(destX, destY); Point2D dest = txPair.getTo();
double accumulatedVariance = 0; double accumulatedVariance = 0;
// - Get all ray paths from source to destination - // - Get all ray paths from source to destination -
@ -1344,28 +1417,24 @@ public class ChannelModel {
RayData.RayType.ORIGIN, RayData.RayType.ORIGIN,
source, source,
null, null,
getParameterIntegerValue("rt_max_rays"), getParameterIntegerValue(Parameter.rt_max_rays),
getParameterIntegerValue("rt_max_refractions"), getParameterIntegerValue(Parameter.rt_max_refractions),
getParameterIntegerValue("rt_max_reflections"), getParameterIntegerValue(Parameter.rt_max_reflections),
getParameterIntegerValue("rt_max_diffractions") getParameterIntegerValue(Parameter.rt_max_diffractions)
); );
// TODO Current (changing) signal strength should be built into 'build visible lines' to speed up things!
// Check if origin tree is already calculated and saved // Check if origin tree is already calculated and saved
DefaultMutableTreeNode visibleLinesTree = null; DefaultMutableTreeNode visibleLinesTree = buildVisibleLinesTree(originRayData);
visibleLinesTree =
buildVisibleLinesTree(originRayData);
// Calculate all paths from source to destination, using above calculated tree // Calculate all paths from source to destination, using above calculated tree
Vector<RayPath> allPaths = getConnectingPaths(source, dest, visibleLinesTree); Vector<RayPath> allPaths = getConnectingPaths(source, dest, visibleLinesTree);
if (logMode) { if (logMode) {
logInfo.append("Signal components:\n"); logInfo.append("Signal components:\n");
Enumeration<RayPath> pathsEnum = allPaths.elements(); Enumeration<RayPath> pathsEnum = allPaths.elements();
while (pathsEnum.hasMoreElements()) { while (pathsEnum.hasMoreElements()) {
RayPath currentPath = pathsEnum.nextElement(); RayPath currentPath = pathsEnum.nextElement();
logInfo.append("* " + currentPath + "\n"); logInfo.append("* " + currentPath + "\n");
for (int i=0; i < currentPath.getSubPathCount(); i++) { for (int i=0; i < currentPath.getSubPathCount(); i++) {
loggedRays.add(currentPath.getSubPath(i)); loggedRays.add(currentPath.getSubPath(i));
} }
@ -1389,20 +1458,20 @@ public class ChannelModel {
// Type specific losses // Type specific losses
// TODO Type specific losses depends on angles as well! // TODO Type specific losses depends on angles as well!
if (subPathStartType == RayData.RayType.REFRACTION) { if (subPathStartType == RayData.RayType.REFRACTION) {
pathGain[i] += getParameterDoubleValue("rt_refrac_coefficient"); pathGain[i] += getParameterDoubleValue(Parameter.rt_refrac_coefficient);
} else if (subPathStartType == RayData.RayType.REFLECTION) { } else if (subPathStartType == RayData.RayType.REFLECTION) {
pathGain[i] += getParameterDoubleValue("rt_reflec_coefficient"); pathGain[i] += getParameterDoubleValue(Parameter.rt_reflec_coefficient);
// Add FSPL from last subpaths (if FSPL on individual rays) // Add FSPL from last subpaths (if FSPL on individual rays)
if (!getParameterBooleanValue("rt_fspl_on_total_length") && accumulatedStraightLength > 0) { if (!getParameterBooleanValue(Parameter.rt_fspl_on_total_length) && accumulatedStraightLength > 0) {
pathGain[i] += getFSPL(accumulatedStraightLength); pathGain[i] += getFSPL(accumulatedStraightLength);
} }
accumulatedStraightLength = 0; // Reset straight length accumulatedStraightLength = 0; // Reset straight length
} else if (subPathStartType == RayData.RayType.DIFFRACTION) { } else if (subPathStartType == RayData.RayType.DIFFRACTION) {
pathGain[i] += getParameterDoubleValue("rt_diffr_coefficient"); pathGain[i] += getParameterDoubleValue(Parameter.rt_diffr_coefficient);
// Add FSPL from last subpaths (if FSPL on individual rays) // Add FSPL from last subpaths (if FSPL on individual rays)
if (!getParameterBooleanValue("rt_fspl_on_total_length") && accumulatedStraightLength > 0) { if (!getParameterBooleanValue(Parameter.rt_fspl_on_total_length) && accumulatedStraightLength > 0) {
pathGain[i] += getFSPL(accumulatedStraightLength); pathGain[i] += getFSPL(accumulatedStraightLength);
} }
accumulatedStraightLength = 0; // Reset straight length accumulatedStraightLength = 0; // Reset straight length
@ -1414,7 +1483,7 @@ public class ChannelModel {
// Ray passes through a wall, calculate distance through that wall // Ray passes through a wall, calculate distance through that wall
// Fetch attenuation constant // Fetch attenuation constant
double attenuationConstant = getParameterDoubleValue("obstacle_attenuation"); double attenuationConstant = getParameterDoubleValue(Parameter.obstacle_attenuation);
Vector<Rectangle2D> allPossibleObstacles = myObstacleWorld.getAllObstaclesNear(subPath.getP1()); Vector<Rectangle2D> allPossibleObstacles = myObstacleWorld.getAllObstaclesNear(subPath.getP1());
@ -1434,9 +1503,7 @@ public class ChannelModel {
pathGain[i] += attenuationConstant * line.getP1().distance(line.getP2()); pathGain[i] += attenuationConstant * line.getP1().distance(line.getP2());
break; break;
} }
} }
} }
// Add to total path length // Add to total path length
@ -1444,12 +1511,12 @@ public class ChannelModel {
} }
// Add FSPL from last rays (if FSPL on individual rays) // Add FSPL from last rays (if FSPL on individual rays)
if (!getParameterBooleanValue("rt_fspl_on_total_length") && accumulatedStraightLength > 0) { if (!getParameterBooleanValue(Parameter.rt_fspl_on_total_length) && accumulatedStraightLength > 0) {
pathGain[i] += getFSPL(accumulatedStraightLength); pathGain[i] += getFSPL(accumulatedStraightLength);
} }
// Free space path loss on total path length? // Free space path loss on total path length?
if (getParameterBooleanValue("rt_fspl_on_total_length")) { if (getParameterBooleanValue(Parameter.rt_fspl_on_total_length)) {
pathGain[i] += getFSPL(pathLengths[i]); pathGain[i] += getFSPL(pathLengths[i]);
} }
@ -1463,7 +1530,8 @@ public class ChannelModel {
double[] pathModdedLengths = new double[allPaths.size()]; double[] pathModdedLengths = new double[allPaths.size()];
double delaySpread = 0; double delaySpread = 0;
double delaySpreadRMS = 0; double delaySpreadRMS = 0;
double wavelength = getParameterDoubleValue("wavelength"); double freq = getParameterDoubleValue(Parameter.frequency);
double wavelength = C/(freq*1000000d);
double totalPathGain = 0; double totalPathGain = 0;
double delaySpreadTotalWeight = 0; double delaySpreadTotalWeight = 0;
double speedOfLight = 300; // Approximate value (m/us) double speedOfLight = 300; // Approximate value (m/us)
@ -1490,12 +1558,12 @@ public class ChannelModel {
// Using Rician fading approach, TODO Only one best signal considered - combine these? (need two limits) // Using Rician fading approach, TODO Only one best signal considered - combine these? (need two limits)
totalPathGain += Math.pow(10, pathGain[i]/10.0)*Math.cos(2*Math.PI * pathModdedLengths[i]/wavelength); totalPathGain += Math.pow(10, pathGain[i]/10.0)*Math.cos(2*Math.PI * pathModdedLengths[i]/wavelength);
if (logMode) { if (logMode) {
logInfo.append("Signal component: " + String.format("%2.3f", pathGain[i]) + " dB, phase " + String.format("%2.3f", (2*/*Math.PI* */ pathModdedLengths[i]/wavelength)) + " pi\n"); logInfo.append("Signal component: " + String.format("%2.3f", pathGain[i]) + " dB, phase " + String.format("%2.3f", (2*/*Math.PI* */ pathModdedLengths[i]/wavelength)) + " pi\n");
} }
} else if (logMode) { } else if (logMode) {
/* TODO Log mode affects result? */ /* TODO Log mode affects result? */
pathModdedLengths[i] = (pathLengths[i] - pathLengths[bestSignalNr]) % wavelength; pathModdedLengths[i] = (pathLengths[i] - pathLengths[bestSignalNr]) % wavelength;
logInfo.append("(IGNORED) Signal component: " + String.format("%2.3f", pathGain[i]) + " dB, phase " + String.format("%2.3f", (2*/*Math.PI* */ pathModdedLengths[i]/wavelength)) + " pi\n"); logInfo.append("(IGNORED) Signal component: " + String.format("%2.3f", pathGain[i]) + " dB, phase " + String.format("%2.3f", (2*/*Math.PI* */ pathModdedLengths[i]/wavelength)) + " pi\n");
} }
} }
@ -1509,33 +1577,32 @@ public class ChannelModel {
totalPathGain = 10*Math.log10(Math.abs(totalPathGain)); totalPathGain = 10*Math.log10(Math.abs(totalPathGain));
if (logMode) { if (logMode) {
logInfo.append("\nTotal path gain: " + String.format("%2.3f", totalPathGain) + " dB\n"); logInfo.append("\nTotal path gain: " + String.format("%2.3f", totalPathGain) + " dB\n");
logInfo.append("Delay spread: " + String.format("%2.3f", delaySpread) + "\n"); logInfo.append("Delay spread: " + String.format("%2.3f", delaySpread) + "\n");
logInfo.append("RMS delay spread: " + String.format("%2.3f", delaySpreadRMS) + "\n"); logInfo.append("RMS delay spread: " + String.format("%2.3f", delaySpreadRMS) + "\n");
} }
// - Calculate received power - // - Calculate received power -
// Using formula (dB) // Using formula (dB)
// Received power = Output power + System gain + Transmitter gain + Path Loss + Receiver gain // Received power = Output power + System gain + Transmitter gain + Path Loss + Receiver gain
// TODO Update formulas // TODO Update formulas
double outputPower; double outputPower = txPair.getTxPower();
if (txPower == null) { double systemGain = getParameterDoubleValue(Parameter.system_gain_mean);
outputPower = getParameterDoubleValue("tx_power"); if (getParameterBooleanValue(Parameter.apply_random)) {
} else {
outputPower = txPower;
}
double systemGain = getParameterDoubleValue("system_gain_mean");
if (getParameterBooleanValue("apply_random")) {
Random random = new Random(); /* TODO Use main random generator? */ Random random = new Random(); /* TODO Use main random generator? */
systemGain += Math.sqrt(getParameterDoubleValue("system_gain_var")) * random.nextGaussian(); systemGain += Math.sqrt(getParameterDoubleValue(Parameter.system_gain_var)) * random.nextGaussian();
} else { } else {
accumulatedVariance += getParameterDoubleValue("system_gain_var"); accumulatedVariance += getParameterDoubleValue(Parameter.system_gain_var);
}
double transmitterGain = 0;
if (getParameterBooleanValue(Parameter.tx_with_gain)) {
transmitterGain = txPair.getTxGain();
} }
double transmitterGain = getParameterDoubleValue("tx_antenna_gain"); // TODO Should depend on angle
double receivedPower = outputPower + systemGain + transmitterGain + totalPathGain; double receivedPower = outputPower + systemGain + transmitterGain + totalPathGain;
if (logMode) { if (logMode) {
logInfo.append("\nReceived signal strength: " + String.format("%2.3f", receivedPower) + " dB (variance " + accumulatedVariance + ")\n"); logInfo.append("\nReceived signal strength: " + String.format("%2.3f", receivedPower) + " dB (variance " + accumulatedVariance + ")\n");
} }
if (dataType == TransmissionData.DELAY_SPREAD || dataType == TransmissionData.DELAY_SPREAD_RMS) { if (dataType == TransmissionData.DELAY_SPREAD || dataType == TransmissionData.DELAY_SPREAD_RMS) {
@ -1546,8 +1613,8 @@ public class ChannelModel {
} }
public class TrackedSignalComponents { public class TrackedSignalComponents {
ArrayList<Line2D> components; ArrayList<Line2D> components;
String log; String log;
} }
/** /**
@ -1561,7 +1628,7 @@ public class ChannelModel {
* @param destY Destination position Y * @param destY Destination position Y
* @return Signal components and printable description * @return Signal components and printable description
*/ */
public TrackedSignalComponents getRaysOfTransmission(double sourceX, double sourceY, double destX, double destY) { public TrackedSignalComponents getRaysOfTransmission(TxPair txPair) {
TrackedSignalComponents tsc = new TrackedSignalComponents(); TrackedSignalComponents tsc = new TrackedSignalComponents();
logInfo = new StringBuilder(); logInfo = new StringBuilder();
@ -1569,7 +1636,7 @@ public class ChannelModel {
/* TODO Include background noise? */ /* TODO Include background noise? */
logMode = true; logMode = true;
getProbability(sourceX, sourceY, destX, destY, -Double.MAX_VALUE); getProbability(txPair, -Double.MAX_VALUE);
logMode = false; logMode = false;
tsc.log = logInfo.toString(); tsc.log = logInfo.toString();
@ -1596,27 +1663,26 @@ public class ChannelModel {
* The second is the variance. * The second is the variance.
* The third value is the received signal strength which may be used in comparison with interference etc. * 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) { public double[] getSINR(TxPair txPair, double interference) {
/* TODO Cache values: called repeatedly with noise sources. */ /* TODO Cache values: called repeatedly with noise sources. */
// Calculate received signal strength // Calculate received signal strength
double[] signalStrength = getReceivedSignalStrength(sourceX, sourceY, destX, destY); double[] signalStrength = getReceivedSignalStrength(txPair);
double[] snrData = new double[] { signalStrength[0], signalStrength[1], signalStrength[0] };
double[] snrData = // Add antenna gain
new double[] { signalStrength[0], signalStrength[1], signalStrength[0] }; if (getParameterBooleanValue(Parameter.rx_with_gain)) {
snrData[0] += txPair.getRxGain();
}
// Add antenna gain TODO Should depend on angle double noiseVariance = getParameterDoubleValue(Parameter.bg_noise_var);
snrData[0] += getParameterDoubleValue("rx_antenna_gain"); double noiseMean = getParameterDoubleValue(Parameter.bg_noise_mean);
double noiseVariance = getParameterDoubleValue("bg_noise_var");
double noiseMean = getParameterDoubleValue("bg_noise_mean");
if (interference > noiseMean) { if (interference > noiseMean) {
noiseMean = interference; noiseMean = interference;
} }
if (getParameterBooleanValue("apply_random")) { if (getParameterBooleanValue(Parameter.apply_random)) {
Random random = new Random(); /* TODO Use main random generator? */ Random random = new Random(); /* TODO Use main random generator? */
noiseMean += Math.sqrt(noiseVariance) * random.nextGaussian(); noiseMean += Math.sqrt(noiseVariance) * random.nextGaussian();
noiseVariance = 0; noiseVariance = 0;
@ -1627,7 +1693,7 @@ public class ChannelModel {
snrData[1] += noiseVariance; snrData[1] += noiseVariance;
if (logMode) { if (logMode) {
logInfo.append("\nReceived SNR: " + String.format("%2.3f", snrData[0]) + " dB (variance " + snrData[1] + ")\n"); logInfo.append("\nReceived SNR: " + String.format("%2.3f", snrData[0]) + " dB (variance " + snrData[1] + ")\n");
} }
return snrData; return snrData;
} }
@ -1649,19 +1715,19 @@ public class ChannelModel {
* @param interference Current interference at destination (dBm) * @param interference Current interference at destination (dBm)
* @return [Probability of reception, signal strength at destination] * @return [Probability of reception, signal strength at destination]
*/ */
public double[] getProbability(double sourceX, double sourceY, double destX, double destY, double interference) { public double[] getProbability(TxPair txPair, double interference) {
double[] snrData = getSINR(sourceX, sourceY, destX, destY, interference); double[] snrData = getSINR(txPair, interference);
double snrMean = snrData[0]; double snrMean = snrData[0];
double snrVariance = snrData[1]; double snrVariance = snrData[1];
double signalStrength = snrData[2]; double signalStrength = snrData[2];
double threshold = getParameterDoubleValue("snr_threshold"); double threshold = getParameterDoubleValue(Parameter.snr_threshold);
double rxSensitivity = getParameterDoubleValue("rx_sensitivity"); double rxSensitivity = getParameterDoubleValue(Parameter.rx_sensitivity);
// Check signal strength against receiver sensitivity and interference // Check signal strength against receiver sensitivity and interference
if (rxSensitivity > signalStrength - snrMean && if (rxSensitivity > signalStrength - snrMean &&
threshold < rxSensitivity + snrMean - signalStrength) { threshold < rxSensitivity + snrMean - signalStrength) {
if (logMode) { if (logMode) {
logInfo.append("Weak signal: increasing threshold\n"); logInfo.append("Weak signal: increasing threshold\n");
} }
// Keeping snr variance but increasing theshold to sensitivity // Keeping snr variance but increasing theshold to sensitivity
@ -1707,8 +1773,8 @@ public class ChannelModel {
* Destination position Y * Destination position Y
* @return RMS delay spread * @return RMS delay spread
*/ */
public double getRMSDelaySpread(double sourceX, double sourceY, double destX, double destY) { public double getRMSDelaySpread(TxPair tx) {
return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.DELAY_SPREAD, null)[1]; return getTransmissionData(tx, TransmissionData.DELAY_SPREAD)[1];
} }
/** /**
@ -1718,14 +1784,18 @@ public class ChannelModel {
* @return XML element collection * @return XML element collection
*/ */
public Collection<Element> getConfigXML() { public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>(); ArrayList<Element> config = new ArrayList<Element>();
Element element; Element element;
Enumeration paramEnum = parameters.keys(); Enumeration<Parameter> paramEnum = parameters.keys();
while (paramEnum.hasMoreElements()) { while (paramEnum.hasMoreElements()) {
String name = (String) paramEnum.nextElement(); Parameter p = (Parameter) paramEnum.nextElement();
element = new Element(name); element = new Element(p.toString());
element.setText(parameters.get(name).toString()); if (parametersDefaults.get(p).equals(parameters.get(p))) {
/* Default value */
continue;
}
element.setAttribute("value", parameters.get(p).toString());
config.add(element); config.add(element);
} }
@ -1749,19 +1819,47 @@ public class ChannelModel {
if (element.getName().equals("obstacles")) { if (element.getName().equals("obstacles")) {
myObstacleWorld = new ObstacleWorld(); myObstacleWorld = new ObstacleWorld();
myObstacleWorld.setConfigXML(element.getChildren()); myObstacleWorld.setConfigXML(element.getChildren());
} else { } else /* Parameter values */ {
// Assuming parameter value String name = element.getName();
String value;
Parameter param = null;
if (name.equals("wavelength")) {
/* Backwards compatability: ignored parameters */
value = element.getAttributeValue("value");
if (value == null) {
value = element.getText();
}
// private static final double C = 299792458; /* m/s */
double frequency = C/Double.parseDouble(value);
frequency /= 1000000.0; /* mhz */
parameters.put(Parameter.frequency, frequency); /* mhz */
// Fetch current class before applying saved value logger.warn("MRM parameter converted from wavelength to frequency: " + String.format("%1.1f MHz", frequency));
Object obj = parameters.get(element.getName()); continue;
Class paramClass = obj.getClass(); } else if (name.equals("tx_antenna_gain") || name.equals("rx_antenna_gain")) {
logger.warn("MRM parameter \"" + name + "\" was removed");
continue;
} else if (Parameter.fromString(name) != null) {
/* Backwards compatability: renamed parameters */
param = Parameter.fromString(name);
} else {
param = Parameter.valueOf(name);
}
value = element.getAttributeValue("value");
if (value == null || value.isEmpty()) {
/* Backwards compatability: renamed parameters */
value = element.getText();
}
Class<?> paramClass = parameters.get(param).getClass();
if (paramClass == Double.class) { if (paramClass == Double.class) {
parameters.put(element.getName(), new Double(Double.parseDouble(element.getText()))); parameters.put(param, new Double(Double.parseDouble(value)));
} else if (paramClass == Boolean.class) { } else if (paramClass == Boolean.class) {
parameters.put(element.getName(), Boolean.parseBoolean(element.getText())); parameters.put(param, Boolean.parseBoolean(value));
} else if (paramClass == Integer.class) { } else if (paramClass == Integer.class) {
parameters.put(element.getName(), Integer.parseInt(element.getText())); parameters.put(param, Integer.parseInt(value));
} else { } else {
logger.fatal("Unsupported class type: " + paramClass); logger.fatal("Unsupported class type: " + paramClass);
} }
@ -1772,4 +1870,85 @@ public class ChannelModel {
settingsObservable.notifySettingsChanged(); settingsObservable.notifySettingsChanged();
return true; return true;
} }
public static abstract class TxPair {
public abstract double getFromX();
public abstract double getFromY();
public abstract double getToX();
public abstract double getToY();
public abstract double getTxPower();
public double getDistance() {
double w = getFromX() - getToX();
double h = getFromY() - getToY();
return Math.sqrt(w*w+h*h);
}
/**
* @return Radians
*/
public double getAngle() {
return Math.atan2(getToY()-getFromY(), getToX()-getFromX());
}
public Point2D getFrom() {
return new Point2D.Double(getFromX(), getFromY());
}
public Point2D getTo() {
return new Point2D.Double(getToX(), getToY());
}
/**
* @return Relative transmitter gain (zero for omnidirectional radios)
*/
public abstract double getTxGain();
/**
* @return Relative receiver gain (zero for omnidirectional radios)
*/
public abstract double getRxGain();
}
public static abstract class RadioPair extends TxPair {
public abstract Radio getFromRadio();
public abstract Radio getToRadio();
public double getDistance() {
double w = getFromX() - getToX();
double h = getFromY() - getToY();
return Math.sqrt(w*w+h*h);
}
public double getFromX() {
return getFromRadio().getPosition().getXCoordinate();
}
public double getFromY() {
return getFromRadio().getPosition().getYCoordinate();
}
public double getToX() {
return getToRadio().getPosition().getXCoordinate();
}
public double getToY() {
return getToRadio().getPosition().getYCoordinate();
}
public double getTxPower() {
return getFromRadio().getCurrentOutputPower();
}
public double getTxGain() {
if (!(getFromRadio() instanceof DirectionalAntennaRadio)) {
return 0;
}
DirectionalAntennaRadio r = (DirectionalAntennaRadio)getFromRadio();
double txGain = r.getRelativeGain(r.getDirection() + getAngle(), getAngle());
//logger.debug("tx gain: " + txGain + " (angle " + String.format("%1.1f", Math.toDegrees(r.getDirection() + getAngle())) + ")");
return txGain;
}
public double getRxGain() {
if (!(getToRadio() instanceof DirectionalAntennaRadio)) {
return 0;
}
DirectionalAntennaRadio r = (DirectionalAntennaRadio)getFromRadio();
double txGain = r.getRelativeGain(r.getDirection() + getAngle() + Math.PI, getDistance());
//logger.debug("rx gain: " + txGain + " (angle " + String.format("%1.1f", Math.toDegrees(r.getDirection() + getAngle() + Math.PI)) + ")");
return txGain;
}
}
} }

View file

@ -42,29 +42,30 @@ import org.apache.log4j.Logger;
import org.jdom.Element; import org.jdom.Element;
import se.sics.cooja.*; import se.sics.cooja.*;
import se.sics.mrm.ChannelModel.Parameter;
/** /**
* This plugin allows a user to reconfigure current radio channel parameters. * This plugin allows a user to reconfigure current radio channel parameters.
* *
* @author Fredrik Osterlind * @author Fredrik Osterlind
*/ */
@ClassDescription("MRM - Formula Viewer") @ClassDescription("MRM Settings")
@PluginType(PluginType.SIM_PLUGIN) @PluginType(PluginType.SIM_PLUGIN)
public class FormulaViewer extends se.sics.cooja.VisPlugin { public class FormulaViewer extends se.sics.cooja.VisPlugin {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(FormulaViewer.class); private static Logger logger = Logger.getLogger(FormulaViewer.class);
private Simulation currentSimulation; private Simulation simulation;
private MRM currentRadioMedium; private MRM radioMedium;
private ChannelModel currentChannelModel; private ChannelModel channelModel;
private static Dimension labelDimension = new Dimension(240, 20); private static Dimension labelDimension = new Dimension(240, 20);
private static NumberFormat doubleFormat = NumberFormat.getNumberInstance(); private static NumberFormat doubleFormat = NumberFormat.getNumberInstance();
private static NumberFormat integerFormat = NumberFormat.getIntegerInstance(); private static NumberFormat integerFormat = NumberFormat.getIntegerInstance();
private Vector<JFormattedTextField> allIntegerParameters = new Vector<JFormattedTextField>(); private ArrayList<JFormattedTextField> allIntegerParameters = new ArrayList<JFormattedTextField>();
private Vector<JFormattedTextField> allDoubleParameters = new Vector<JFormattedTextField>(); private ArrayList<JFormattedTextField> allDoubleParameters = new ArrayList<JFormattedTextField>();
private Vector<JCheckBox> allBooleanParameters = new Vector<JCheckBox>(); private ArrayList<JCheckBox> allBooleanParameters = new ArrayList<JCheckBox>();
private JPanel areaGeneral; private JPanel areaGeneral;
private JPanel areaTransmitter; private JPanel areaTransmitter;
@ -78,11 +79,11 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
* @param simulationToVisualize Simulation which holds the MRM channel model. * @param simulationToVisualize Simulation which holds the MRM channel model.
*/ */
public FormulaViewer(Simulation simulationToVisualize, GUI gui) { public FormulaViewer(Simulation simulationToVisualize, GUI gui) {
super("MRM - Formula Viewer", gui); super("MRM Settings", gui);
currentSimulation = simulationToVisualize; simulation = simulationToVisualize;
currentRadioMedium = (MRM) currentSimulation.getRadioMedium(); radioMedium = (MRM) simulation.getRadioMedium();
currentChannelModel = currentRadioMedium.getChannelModel(); channelModel = radioMedium.getChannelModel();
// -- Create and add GUI components -- // -- Create and add GUI components --
JPanel allComponents = new JPanel(); JPanel allComponents = new JPanel();
@ -102,52 +103,52 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
areaGeneral = collapsableArea; areaGeneral = collapsableArea;
addBooleanParameter( addBooleanParameter(
"apply_random", Parameter.apply_random,
currentChannelModel.getParameterDescription("apply_random"), Parameter.getDescription(Parameter.apply_random),
collapsableArea, collapsableArea,
currentChannelModel.getParameterBooleanValue("apply_random") channelModel.getParameterBooleanValue(Parameter.apply_random)
); );
addDoubleParameter( addDoubleParameter(
"snr_threshold", Parameter.snr_threshold,
currentChannelModel.getParameterDescription("snr_threshold"), Parameter.getDescription(Parameter.snr_threshold),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("snr_threshold") channelModel.getParameterDoubleValue(Parameter.snr_threshold)
); );
addDoubleParameter( addDoubleParameter(
"bg_noise_mean", Parameter.bg_noise_mean,
currentChannelModel.getParameterDescription("bg_noise_mean"), Parameter.getDescription(Parameter.bg_noise_mean),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("bg_noise_mean") channelModel.getParameterDoubleValue(Parameter.bg_noise_mean)
); );
addDoubleParameter( addDoubleParameter(
"bg_noise_var", Parameter.bg_noise_var,
currentChannelModel.getParameterDescription("bg_noise_var"), Parameter.getDescription(Parameter.bg_noise_var),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("bg_noise_var") channelModel.getParameterDoubleValue(Parameter.bg_noise_var)
); );
addDoubleParameter( addDoubleParameter(
"system_gain_mean", Parameter.system_gain_mean,
currentChannelModel.getParameterDescription("system_gain_mean"), Parameter.getDescription(Parameter.system_gain_mean),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("system_gain_mean") channelModel.getParameterDoubleValue(Parameter.system_gain_mean)
); );
addDoubleParameter( addDoubleParameter(
"system_gain_var", Parameter.system_gain_var,
currentChannelModel.getParameterDescription("system_gain_var"), Parameter.getDescription(Parameter.system_gain_var),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("system_gain_var") channelModel.getParameterDoubleValue(Parameter.system_gain_var)
); );
addDoubleParameter( addDoubleParameter(
"wavelength", Parameter.frequency,
currentChannelModel.getParameterDescription("wavelength"), Parameter.getDescription(Parameter.frequency),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("wavelength") channelModel.getParameterDoubleValue(Parameter.frequency)
); );
// Transmitter parameters // Transmitter parameters
@ -155,17 +156,17 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
areaTransmitter = collapsableArea; areaTransmitter = collapsableArea;
addDoubleParameter( addDoubleParameter(
"tx_power", Parameter.tx_power,
currentChannelModel.getParameterDescription("tx_power"), Parameter.getDescription(Parameter.tx_power),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("tx_power") channelModel.getParameterDoubleValue(Parameter.tx_power)
); );
addDoubleParameter( addBooleanParameter(
"tx_antenna_gain", Parameter.tx_with_gain,
currentChannelModel.getParameterDescription("tx_antenna_gain"), Parameter.getDescription(Parameter.tx_with_gain),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("tx_antenna_gain") channelModel.getParameterBooleanValue(Parameter.tx_with_gain)
); );
// Receiver parameters // Receiver parameters
@ -173,17 +174,17 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
areaReceiver = collapsableArea; areaReceiver = collapsableArea;
addDoubleParameter( addDoubleParameter(
"rx_sensitivity", Parameter.rx_sensitivity,
currentChannelModel.getParameterDescription("rx_sensitivity"), Parameter.getDescription(Parameter.rx_sensitivity),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rx_sensitivity") channelModel.getParameterDoubleValue(Parameter.rx_sensitivity)
); );
addDoubleParameter( addBooleanParameter(
"rx_antenna_gain", Parameter.rx_with_gain,
currentChannelModel.getParameterDescription("rx_antenna_gain"), Parameter.getDescription(Parameter.rx_with_gain),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rx_antenna_gain") channelModel.getParameterBooleanValue(Parameter.rx_with_gain)
); );
// Ray Tracer parameters // Ray Tracer parameters
@ -191,87 +192,87 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
areaRayTracer = collapsableArea; areaRayTracer = collapsableArea;
addBooleanParameter( addBooleanParameter(
"rt_disallow_direct_path", Parameter.rt_disallow_direct_path,
currentChannelModel.getParameterDescription("rt_disallow_direct_path"), Parameter.getDescription(Parameter.rt_disallow_direct_path),
collapsableArea, collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_disallow_direct_path") channelModel.getParameterBooleanValue(Parameter.rt_disallow_direct_path)
); );
addBooleanParameter( addBooleanParameter(
"rt_ignore_non_direct", Parameter.rt_ignore_non_direct,
currentChannelModel.getParameterDescription("rt_ignore_non_direct"), Parameter.getDescription(Parameter.rt_ignore_non_direct),
collapsableArea, collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_ignore_non_direct") channelModel.getParameterBooleanValue(Parameter.rt_ignore_non_direct)
); );
addBooleanParameter( addBooleanParameter(
"rt_fspl_on_total_length", Parameter.rt_fspl_on_total_length,
currentChannelModel.getParameterDescription("rt_fspl_on_total_length"), Parameter.getDescription(Parameter.rt_fspl_on_total_length),
collapsableArea, collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_fspl_on_total_length") channelModel.getParameterBooleanValue(Parameter.rt_fspl_on_total_length)
); );
addIntegerParameter( addIntegerParameter(
"rt_max_rays", Parameter.rt_max_rays,
currentChannelModel.getParameterDescription("rt_max_rays"), Parameter.getDescription(Parameter.rt_max_rays),
collapsableArea, collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_rays") channelModel.getParameterIntegerValue(Parameter.rt_max_rays)
); );
addIntegerParameter( addIntegerParameter(
"rt_max_refractions", Parameter.rt_max_refractions,
currentChannelModel.getParameterDescription("rt_max_refractions"), Parameter.getDescription(Parameter.rt_max_refractions),
collapsableArea, collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_refractions") channelModel.getParameterIntegerValue(Parameter.rt_max_refractions)
); );
addIntegerParameter( addIntegerParameter(
"rt_max_reflections", Parameter.rt_max_reflections,
currentChannelModel.getParameterDescription("rt_max_reflections"), Parameter.getDescription(Parameter.rt_max_reflections),
collapsableArea, collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_reflections") channelModel.getParameterIntegerValue(Parameter.rt_max_reflections)
); );
addIntegerParameter( addIntegerParameter(
"rt_max_diffractions", Parameter.rt_max_diffractions,
currentChannelModel.getParameterDescription("rt_max_diffractions"), Parameter.getDescription(Parameter.rt_max_diffractions),
collapsableArea, collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_diffractions") channelModel.getParameterIntegerValue(Parameter.rt_max_diffractions)
); );
/* addBooleanParameter( /* addBooleanParameter(
"rt_use_scattering", Parameters.rt_use_scattering,
currentChannelModel.getParameterDescription("rt_use_scattering"), Parameter.getDescription(Parameters.rt_use_scattering),
collapsableArea, collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_use_scattering") currentChannelModel.getParameterBooleanValue(Parameters.rt_use_scattering)
); );
*/ */
addDoubleParameter( addDoubleParameter(
"rt_refrac_coefficient", Parameter.rt_refrac_coefficient,
currentChannelModel.getParameterDescription("rt_refrac_coefficient"), Parameter.getDescription(Parameter.rt_refrac_coefficient),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_refrac_coefficient") channelModel.getParameterDoubleValue(Parameter.rt_refrac_coefficient)
); );
addDoubleParameter( addDoubleParameter(
"rt_reflec_coefficient", Parameter.rt_reflec_coefficient,
currentChannelModel.getParameterDescription("rt_reflec_coefficient"), Parameter.getDescription(Parameter.rt_reflec_coefficient),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_reflec_coefficient") channelModel.getParameterDoubleValue(Parameter.rt_reflec_coefficient)
); );
addDoubleParameter( addDoubleParameter(
"rt_diffr_coefficient", Parameter.rt_diffr_coefficient,
currentChannelModel.getParameterDescription("rt_diffr_coefficient"), Parameter.getDescription(Parameter.rt_diffr_coefficient),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_diffr_coefficient") channelModel.getParameterDoubleValue(Parameter.rt_diffr_coefficient)
); );
/* addDoubleParameter( /* addDoubleParameter(
"rt_scatt_coefficient", Parameters.rt_scatt_coefficient,
currentChannelModel.getParameterDescription("rt_scatt_coefficient"), Parameter.getDescription(Parameters.rt_scatt_coefficient),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_scatt_coefficient") currentChannelModel.getParameterDoubleValue(Parameters.rt_scatt_coefficient)
); );
*/ */
// Shadowing parameters // Shadowing parameters
@ -279,16 +280,14 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
areaShadowing = collapsableArea; areaShadowing = collapsableArea;
addDoubleParameter( addDoubleParameter(
"obstacle_attenuation", Parameter.obstacle_attenuation,
currentChannelModel.getParameterDescription("obstacle_attenuation"), Parameter.getDescription(Parameter.obstacle_attenuation),
collapsableArea, collapsableArea,
currentChannelModel.getParameterDoubleValue("obstacle_attenuation") channelModel.getParameterDoubleValue(Parameter.obstacle_attenuation)
); );
// Add channel model observer responsible to keep all GUI components synched // Add channel model observer responsible to keep all GUI components synched
currentChannelModel.addSettingsObserver(channelModelSettingsObserver); channelModel.addSettingsObserver(channelModelSettingsObserver);
// Set initial size etc. // Set initial size etc.
pack(); pack();
@ -312,6 +311,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
private JPanel createCollapsableArea(String title, Container contentPane) { private JPanel createCollapsableArea(String title, Container contentPane) {
// Create panels // Create panels
JPanel holdingPanel = new JPanel() { JPanel holdingPanel = new JPanel() {
private static final long serialVersionUID = -7925426641856424500L;
public Dimension getMaximumSize() { public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height); return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
} }
@ -319,6 +319,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
holdingPanel.setLayout(new BoxLayout(holdingPanel, BoxLayout.Y_AXIS)); holdingPanel.setLayout(new BoxLayout(holdingPanel, BoxLayout.Y_AXIS));
final JPanel collapsableArea = new JPanel() { final JPanel collapsableArea = new JPanel() {
private static final long serialVersionUID = -1261182973911973773L;
public Dimension getMaximumSize() { public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height); return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
} }
@ -327,6 +328,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
collapsableArea.setVisible(false); collapsableArea.setVisible(false);
JPanel titlePanel = new JPanel(new BorderLayout()) { JPanel titlePanel = new JPanel(new BorderLayout()) {
private static final long serialVersionUID = -9121775806029887815L;
public Dimension getMaximumSize() { public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height); return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
} }
@ -369,7 +371,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
* @param initialValue Initial value * @param initialValue Initial value
* @return Text field in created panel * @return Text field in created panel
*/ */
private JFormattedTextField addDoubleParameter(String id, String description, Container contentPane, double initialValue) { private JFormattedTextField addDoubleParameter(Parameter id, String description, Container contentPane, double initialValue) {
JPanel panel = new JPanel(); JPanel panel = new JPanel();
JLabel label; JLabel label;
JFormattedTextField textField; JFormattedTextField textField;
@ -386,19 +388,34 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
textField.putClientProperty("id", id); textField.putClientProperty("id", id);
textField.addPropertyChangeListener("value", new PropertyChangeListener() { textField.addPropertyChangeListener("value", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) { public void propertyChange(PropertyChangeEvent e) {
Object sourceObject = e.getSource(); JFormattedTextField textField = (JFormattedTextField) e.getSource();
Double newValue = ((Number) e.getNewValue()).doubleValue(); Parameter id = (Parameter) textField.getClientProperty("id");
String id = (String) ((JFormattedTextField) sourceObject).getClientProperty("id"); Double val = ((Number) e.getNewValue()).doubleValue();
currentChannelModel.setParameterValue(id, newValue); channelModel.setParameterValue(id, val);
if (!Parameter.getDefaultValue(id).equals(val)) {
textField.setBackground(Color.LIGHT_GRAY);
textField.setToolTipText("Default value: " + Parameter.getDefaultValue(id));
} else {
textField.setBackground(null);
textField.setToolTipText(null);
}
} }
}); });
if (!Parameter.getDefaultValue(id).equals(initialValue)) {
textField.setBackground(Color.LIGHT_GRAY);
textField.setToolTipText("Default value: " + Parameter.getDefaultValue(id));
} else {
textField.setBackground(null);
textField.setToolTipText(null);
}
allDoubleParameters.add(textField); allDoubleParameters.add(textField);
contentPane.add(panel); contentPane.add(panel);
return textField; return textField;
} }
/** /**
* Creates and adds a panel with a label and a * Creates and adds a panel with a label and a
* text field which accepts integers. * text field which accepts integers.
@ -409,7 +426,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
* @param initialValue Initial value * @param initialValue Initial value
* @return Text field in created panel * @return Text field in created panel
*/ */
private JFormattedTextField addIntegerParameter(String id, String description, Container contentPane, int initialValue) { private JFormattedTextField addIntegerParameter(Parameter id, String description, Container contentPane, int initialValue) {
JPanel panel = new JPanel(); JPanel panel = new JPanel();
JLabel label; JLabel label;
JFormattedTextField textField; JFormattedTextField textField;
@ -426,12 +443,26 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
textField.putClientProperty("id", id); textField.putClientProperty("id", id);
textField.addPropertyChangeListener("value", new PropertyChangeListener() { textField.addPropertyChangeListener("value", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) { public void propertyChange(PropertyChangeEvent e) {
Object sourceObject = e.getSource(); JFormattedTextField textField = (JFormattedTextField) e.getSource();
Integer newValue = ((Number) e.getNewValue()).intValue(); Parameter id = (Parameter) textField.getClientProperty("id");
String id = (String) ((JFormattedTextField) sourceObject).getClientProperty("id"); Integer val = ((Number) e.getNewValue()).intValue();
currentChannelModel.setParameterValue(id, newValue); channelModel.setParameterValue(id, val);
if (!Parameter.getDefaultValue(id).equals(val)) {
textField.setBackground(Color.LIGHT_GRAY);
textField.setToolTipText("Default value: " + Parameter.getDefaultValue(id));
} else {
textField.setBackground(null);
textField.setToolTipText(null);
}
} }
}); });
if (!Parameter.getDefaultValue(id).equals(initialValue)) {
textField.setBackground(Color.LIGHT_GRAY);
textField.setToolTipText("Default value: " + Parameter.getDefaultValue(id));
} else {
textField.setBackground(null);
textField.setToolTipText(null);
}
allIntegerParameters.add(textField); allIntegerParameters.add(textField);
@ -450,7 +481,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
* @param initialValue Initial value * @param initialValue Initial value
* @return Checkbox in created panel * @return Checkbox in created panel
*/ */
private JCheckBox addBooleanParameter(String id, String description, Container contentPane, boolean initialValue) { private JCheckBox addBooleanParameter(Parameter id, String description, Container contentPane, boolean initialValue) {
JPanel panel = new JPanel(); JPanel panel = new JPanel();
JLabel label; JLabel label;
JCheckBox checkBox; JCheckBox checkBox;
@ -466,14 +497,23 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
checkBox.putClientProperty("id", id); checkBox.putClientProperty("id", id);
checkBox.addActionListener(new ActionListener() { checkBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JCheckBox source = (JCheckBox) e.getSource(); JCheckBox checkBox = (JCheckBox) e.getSource();
currentChannelModel.setParameterValue( Parameter id = (Parameter) checkBox.getClientProperty("id");
(String) source.getClientProperty("id"), Object val = new Boolean(checkBox.isSelected());
new Boolean(source.isSelected()) channelModel.setParameterValue(id, val);
); if (!Parameter.getDefaultValue(id).equals(val)) {
checkBox.setText("<");
} else {
checkBox.setText("");
}
} }
}); });
if (!Parameter.getDefaultValue(id).equals(initialValue)) {
checkBox.setText("<");
} else {
checkBox.setText("");
}
allBooleanParameters.add(checkBox); allBooleanParameters.add(checkBox);
contentPane.add(panel); contentPane.add(panel);
@ -481,29 +521,6 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
return checkBox; return checkBox;
} }
/**
* Creates and adds a panel with a description label.
*
* @param description Description of new parameter
* @param contentPane Where to add created panel
* @return Created label
*/
private JLabel addLabelParameter(String description, Container contentPane) {
JPanel panel = new JPanel();
JLabel label;
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setAlignmentY(Component.TOP_ALIGNMENT);
panel.add(Box.createHorizontalStrut(10));
panel.add(label = new JLabel(description));
label.setPreferredSize(labelDimension);
panel.add(Box.createHorizontalGlue());
contentPane.add(panel);
return label;
}
/** /**
* Listens to settings changes in the channel model. * Listens to settings changes in the channel model.
* If it changes, all GUI parameters are updated accordingly. * If it changes, all GUI parameters are updated accordingly.
@ -513,22 +530,22 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
// Update all integers // Update all integers
for (int i=0; i < allIntegerParameters.size(); i++) { for (int i=0; i < allIntegerParameters.size(); i++) {
JFormattedTextField textField = allIntegerParameters.get(i); JFormattedTextField textField = allIntegerParameters.get(i);
String id = (String) textField.getClientProperty("id"); Parameter id = (Parameter) textField.getClientProperty("id");
textField.setValue(currentChannelModel.getParameterValue(id)); textField.setValue(channelModel.getParameterValue(id));
} }
// Update all doubles // Update all doubles
for (int i=0; i < allDoubleParameters.size(); i++) { for (int i=0; i < allDoubleParameters.size(); i++) {
JFormattedTextField textField = allDoubleParameters.get(i); JFormattedTextField textField = allDoubleParameters.get(i);
String id = (String) textField.getClientProperty("id"); Parameter id = (Parameter) textField.getClientProperty("id");
textField.setValue(currentChannelModel.getParameterValue(id)); textField.setValue(channelModel.getParameterValue(id));
} }
// Update all booleans // Update all booleans
for (int i=0; i < allBooleanParameters.size(); i++) { for (int i=0; i < allBooleanParameters.size(); i++) {
JCheckBox checkBox = allBooleanParameters.get(i); JCheckBox checkBox = allBooleanParameters.get(i);
String id = (String) checkBox.getClientProperty("id"); Parameter id = (Parameter) checkBox.getClientProperty("id");
checkBox.setSelected(currentChannelModel.getParameterBooleanValue(id)); checkBox.setSelected(channelModel.getParameterBooleanValue(id));
} }
repaint(); repaint();
@ -536,12 +553,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
}; };
public void closePlugin() { public void closePlugin() {
// Remove the channel model observer channelModel.deleteSettingsObserver(channelModelSettingsObserver);
if (currentChannelModel != null && channelModelSettingsObserver != null) {
currentChannelModel.deleteSettingsObserver(channelModelSettingsObserver);
} else {
logger.fatal("Can't remove channel model observer: " + channelModelSettingsObserver);
}
} }
/** /**
@ -551,7 +563,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin {
* @return XML element collection * @return XML element collection
*/ */
public Collection<Element> getConfigXML() { public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>(); ArrayList<Element> config = new ArrayList<Element>();
Element element; Element element;
element = new Element("show_general"); element = new Element("show_general");

View file

@ -43,11 +43,16 @@ import org.jdom.Element;
import se.sics.cooja.ClassDescription; import se.sics.cooja.ClassDescription;
import se.sics.cooja.RadioConnection; import se.sics.cooja.RadioConnection;
import se.sics.cooja.Simulation; import se.sics.cooja.Simulation;
import se.sics.cooja.interfaces.DirectionalAntennaRadio;
import se.sics.cooja.interfaces.NoiseSourceRadio; import se.sics.cooja.interfaces.NoiseSourceRadio;
import se.sics.cooja.interfaces.NoiseSourceRadio.NoiseLevelListener; import se.sics.cooja.interfaces.NoiseSourceRadio.NoiseLevelListener;
import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Position;
import se.sics.cooja.interfaces.Radio; import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.plugins.Visualizer;
import se.sics.cooja.radiomediums.AbstractRadioMedium; import se.sics.cooja.radiomediums.AbstractRadioMedium;
import se.sics.mrm.ChannelModel.Parameter;
import se.sics.mrm.ChannelModel.RadioPair;
import se.sics.mrm.ChannelModel.TxPair;
/** /**
* Multi-path Ray-tracing radio medium (MRM). * Multi-path Ray-tracing radio medium (MRM).
@ -63,8 +68,9 @@ import se.sics.cooja.radiomediums.AbstractRadioMedium;
* *
* Future work includes adding support for diffraction and scattering. * Future work includes adding support for diffraction and scattering.
* *
* MRM supports noise source radios. * MRM supports both noise source radios and directional antenna radios.
* *
* @see DirectionalAntennaRadio
* @see NoiseSourceRadio * @see NoiseSourceRadio
* @author Fredrik Osterlind * @author Fredrik Osterlind
*/ */
@ -72,7 +78,8 @@ import se.sics.cooja.radiomediums.AbstractRadioMedium;
public class MRM extends AbstractRadioMedium { public class MRM extends AbstractRadioMedium {
private static Logger logger = Logger.getLogger(MRM.class); private static Logger logger = Logger.getLogger(MRM.class);
public final static boolean WITH_NOISE = true; /* NoiseSourceRadio:s */ public final static boolean WITH_NOISE = true; /* NoiseSourceRadio */
public final static boolean WITH_DIRECTIONAL = true; /* DirectionalAntennaRadio */
public final static boolean WITH_CAPTURE_EFFECT = true; public final static boolean WITH_CAPTURE_EFFECT = true;
private Simulation sim; private Simulation sim;
@ -92,44 +99,46 @@ public class MRM extends AbstractRadioMedium {
sim = simulation; sim = simulation;
random = simulation.getRandomGenerator(); random = simulation.getRandomGenerator();
currentChannelModel = new ChannelModel(); currentChannelModel = new ChannelModel(sim);
/* Register plugins */ /* Register plugins */
sim.getGUI().registerPlugin(AreaViewer.class); sim.getGUI().registerPlugin(AreaViewer.class);
sim.getGUI().registerPlugin(FormulaViewer.class); sim.getGUI().registerPlugin(FormulaViewer.class);
Visualizer.registerVisualizerSkin(MRMVisualizerSkin.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() { public void removed() {
super.removed(); super.removed();
/* Unregister plugins */ /* Unregister plugins */
sim.getGUI().unregisterPlugin(AreaViewer.class); sim.getGUI().unregisterPlugin(AreaViewer.class);
sim.getGUI().unregisterPlugin(FormulaViewer.class); sim.getGUI().unregisterPlugin(FormulaViewer.class);
Visualizer.unregisterVisualizerSkin(MRMVisualizerSkin.class);
} }
public MRMRadioConnection createConnections(Radio sender) { 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 (WITH_NOISE && radio instanceof NoiseSourceRadio) {
((NoiseSourceRadio)radio).addNoiseLevelListener(noiseListener);
}
}
public void unregisterRadioInterface(Radio radio, Simulation sim) {
super.unregisterRadioInterface(radio, sim);
if (WITH_NOISE && radio instanceof NoiseSourceRadio) {
((NoiseSourceRadio)radio).removeNoiseLevelListener(noiseListener);
}
}
public MRMRadioConnection createConnections(final Radio sender) {
MRMRadioConnection newConnection = new MRMRadioConnection(sender); MRMRadioConnection newConnection = new MRMRadioConnection(sender);
Position senderPos = sender.getPosition(); final Position senderPos = sender.getPosition();
/* TODO Cache potential destination in DGRM */ /* TODO Cache potential destination in DGRM */
/* Loop through all potential destinations */ /* Loop through all potential destinations */
@ -144,21 +153,26 @@ public class MRM extends AbstractRadioMedium {
sender.getChannel() != recv.getChannel()) { sender.getChannel() != recv.getChannel()) {
continue; continue;
} }
Position recvPos = recv.getPosition(); final Radio recvFinal = recv;
/* Calculate receive probability */ /* Calculate receive probability */
TxPair txPair = new RadioPair() {
public Radio getFromRadio() {
return sender;
}
public Radio getToRadio() {
return recvFinal;
}
};
double[] probData = currentChannelModel.getProbability( double[] probData = currentChannelModel.getProbability(
senderPos.getXCoordinate(), txPair,
senderPos.getYCoordinate(),
recvPos.getXCoordinate(),
recvPos.getYCoordinate(),
-Double.MAX_VALUE /* TODO Include interference */ -Double.MAX_VALUE /* TODO Include interference */
); );
double recvProb = probData[0]; double recvProb = probData[0];
double recvSignalStrength = probData[1]; double recvSignalStrength = probData[1];
if (recvProb == 1.0 || random.nextDouble() < recvProb) { if (recvProb == 1.0 || random.nextDouble() < recvProb) {
/* Yes, the receiver *may* receive this packet (it's strong enough) */ /* Yes, the receiver *may* receive this packet (it's strong enough) */
if (!recv.isReceiverOn()) { if (!recv.isReceiverOn()) {
newConnection.addInterfered(recv); newConnection.addInterfered(recv);
recv.interfereAnyReception(); recv.interfereAnyReception();
@ -170,15 +184,15 @@ public class MRM extends AbstractRadioMedium {
} else if (recv.isReceiving()) { } else if (recv.isReceiving()) {
/* Was already receiving: start interfering. /* Was already receiving: start interfering.
* Assuming no continuous preambles checking */ * Assuming no continuous preambles checking */
double currSignal = recv.getCurrentSignalStrength(); double currSignal = recv.getCurrentSignalStrength();
/* Capture effect: recv-radio is already receiving. /* Capture effect: recv-radio is already receiving.
* Are we strong enough to interfere? */ * Are we strong enough to interfere? */
if (WITH_CAPTURE_EFFECT && if (WITH_CAPTURE_EFFECT &&
recvSignalStrength < currSignal - 3 /* config */) { recvSignalStrength < currSignal - 3 /* config */) {
/* No, we are too weak */ /* No, we are too weak */
} else { } else {
newConnection.addInterfered(recv, recvSignalStrength); newConnection.addInterfered(recv, recvSignalStrength);
recv.interfereAnyReception(); recv.interfereAnyReception();
/* Interfere receiver in all other active radio connections */ /* Interfere receiver in all other active radio connections */
@ -187,22 +201,22 @@ public class MRM extends AbstractRadioMedium {
conn.addInterfered(recv); conn.addInterfered(recv);
} }
} }
} }
} else { } else {
/* Success: radio starts receiving */ /* Success: radio starts receiving */
newConnection.addDestination(recv, recvSignalStrength); newConnection.addDestination(recv, recvSignalStrength);
} }
} else if (recvSignalStrength > currentChannelModel.getParameterDoubleValue("bg_noise_mean")) { } else if (recvSignalStrength > currentChannelModel.getParameterDoubleValue(Parameter.bg_noise_mean)) {
/* The incoming signal is strong, but strong enough to interfere? */ /* The incoming signal is strong, but strong enough to interfere? */
if (!WITH_CAPTURE_EFFECT) { if (!WITH_CAPTURE_EFFECT) {
newConnection.addInterfered(recv, recvSignalStrength); newConnection.addInterfered(recv, recvSignalStrength);
recv.interfereAnyReception(); recv.interfereAnyReception();
} else { } else {
/* TODO Implement new type: newConnection.addNoise()? /* TODO Implement new type: newConnection.addNoise()?
* Currently, this connection will never disturb this radio... */ * Currently, this connection will never disturb this radio... */
} }
} }
} }
@ -213,8 +227,8 @@ public class MRM extends AbstractRadioMedium {
public void updateSignalStrengths() { public void updateSignalStrengths() {
/* Reset: Background noise */ /* Reset: Background noise */
double background = double background =
currentChannelModel.getParameterDoubleValue(("bg_noise_mean")); currentChannelModel.getParameterDoubleValue((Parameter.bg_noise_mean));
for (Radio radio : getRegisteredRadios()) { for (Radio radio : getRegisteredRadios()) {
radio.setCurrentSignalStrength(background); radio.setCurrentSignalStrength(background);
} }
@ -235,12 +249,12 @@ public class MRM extends AbstractRadioMedium {
for (Radio intfRadio : ((MRMRadioConnection) conn).getInterfered()) { for (Radio intfRadio : ((MRMRadioConnection) conn).getInterfered()) {
double signalStrength = ((MRMRadioConnection) conn).getInterferenceSignalStrength(intfRadio); double signalStrength = ((MRMRadioConnection) conn).getInterferenceSignalStrength(intfRadio);
if (intfRadio.getCurrentSignalStrength() < signalStrength) { if (intfRadio.getCurrentSignalStrength() < signalStrength) {
intfRadio.setCurrentSignalStrength(signalStrength); intfRadio.setCurrentSignalStrength(signalStrength);
} }
if (!intfRadio.isInterfered()) { if (!intfRadio.isInterfered()) {
/*logger.warn("Radio was not interfered: " + intfRadio);*/ /*logger.warn("Radio was not interfered: " + intfRadio);*/
intfRadio.interfereAnyReception(); intfRadio.interfereAnyReception();
} }
} }
} }
@ -248,53 +262,58 @@ public class MRM extends AbstractRadioMedium {
/* Check for noise sources */ /* Check for noise sources */
if (!WITH_NOISE) return; if (!WITH_NOISE) return;
for (Radio noiseRadio: getRegisteredRadios()) { for (Radio noiseRadio: getRegisteredRadios()) {
if (!(noiseRadio instanceof NoiseSourceRadio)) { if (!(noiseRadio instanceof NoiseSourceRadio)) {
continue; continue;
} }
NoiseSourceRadio radio = (NoiseSourceRadio) noiseRadio; final Radio fromRadio = noiseRadio;
int signalStrength = radio.getNoiseLevel(); NoiseSourceRadio radio = (NoiseSourceRadio) noiseRadio;
if (signalStrength == Integer.MIN_VALUE) { int signalStrength = radio.getNoiseLevel();
continue; if (signalStrength == Integer.MIN_VALUE) {
} continue;
}
/* Calculate how noise source affects surrounding radios */ /* Calculate how noise source affects surrounding radios */
for (Radio affectedRadio : getRegisteredRadios()) { for (Radio affectedRadio : getRegisteredRadios()) {
if (noiseRadio == affectedRadio) { if (noiseRadio == affectedRadio) {
continue; continue;
} }
/* Update noise levels */ /* Update noise levels */
double[] signalMeanVar = currentChannelModel.getReceivedSignalStrength( final Radio toRadio = affectedRadio;
noiseRadio.getPosition().getXCoordinate(), TxPair txPair = new RadioPair() {
noiseRadio.getPosition().getYCoordinate(), public Radio getFromRadio() {
affectedRadio.getPosition().getXCoordinate(), return fromRadio;
affectedRadio.getPosition().getYCoordinate(), }
(double) signalStrength); /* TODO Convert to dBm */ public Radio getToRadio() {
double signal = signalMeanVar[0]; return toRadio;
if (signal < background) { }
continue; };
} double[] signalMeanVar = currentChannelModel.getReceivedSignalStrength(txPair);
double signal = signalMeanVar[0];
if (signal < background) {
continue;
}
/* TODO Additive signals strengths? */ /* TODO Additive signals strengths? */
/* TODO XXX Consider radio channels */ /* TODO XXX Consider radio channels */
/* TODO XXX Potentially interfere even when signal is weaker (~3dB)... /* TODO XXX Potentially interfere even when signal is weaker (~3dB)...
* (we may alternatively just use the getSINR method...) */ * (we may alternatively just use the getSINR method...) */
if (affectedRadio.getCurrentSignalStrength() < signal) { if (affectedRadio.getCurrentSignalStrength() < signal) {
affectedRadio.setCurrentSignalStrength(signal); affectedRadio.setCurrentSignalStrength(signal);
/* TODO Interfere with radio connections? */ /* TODO Interfere with radio connections? */
if (affectedRadio.isReceiving() && !affectedRadio.isInterfered()) { if (affectedRadio.isReceiving() && !affectedRadio.isInterfered()) {
for (RadioConnection conn : conns) { for (RadioConnection conn : conns) {
if (conn.isDestination(affectedRadio)) { if (conn.isDestination(affectedRadio)) {
/* Intefere with current reception, mark radio as interfered */ /* Intefere with current reception, mark radio as interfered */
conn.addInterfered(affectedRadio); conn.addInterfered(affectedRadio);
if (!affectedRadio.isInterfered()) { if (!affectedRadio.isInterfered()) {
affectedRadio.interfereAnyReception(); affectedRadio.interfereAnyReception();
} }
} }
} }
} }
} }
} }
} }
} }
@ -334,7 +353,7 @@ public class MRM extends AbstractRadioMedium {
* @return Number of registered radios. * @return Number of registered radios.
*/ */
public int getRegisteredRadioCount() { public int getRegisteredRadioCount() {
/* TODO Expensive operation */ /* TODO Expensive operation */
return getRegisteredRadios().length; return getRegisteredRadios().length;
} }
@ -383,16 +402,16 @@ public class MRM extends AbstractRadioMedium {
} }
public double getDestinationSignalStrength(Radio radio) { public double getDestinationSignalStrength(Radio radio) {
if (signalStrengths.get(radio) == null) { if (signalStrengths.get(radio) == null) {
return Double.MIN_VALUE; return Double.MIN_VALUE;
} }
return signalStrengths.get(radio); return signalStrengths.get(radio);
} }
public double getInterferenceSignalStrength(Radio radio) { public double getInterferenceSignalStrength(Radio radio) {
if (signalStrengths.get(radio) == null) { if (signalStrengths.get(radio) == null) {
return Double.MIN_VALUE; return Double.MIN_VALUE;
} }
return signalStrengths.get(radio); return signalStrengths.get(radio);
} }
} }

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2011, 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 se.sics.mrm;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import org.apache.log4j.Logger;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.Mote;
import se.sics.cooja.Simulation;
import se.sics.cooja.interfaces.Position;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.plugins.Visualizer;
import se.sics.cooja.plugins.VisualizerSkin;
import se.sics.mrm.ChannelModel.RadioPair;
import se.sics.mrm.ChannelModel.TxPair;
@ClassDescription("Radio environment (MRM)")
public class MRMVisualizerSkin implements VisualizerSkin {
private static Logger logger = Logger.getLogger(MRMVisualizerSkin.class);
private Simulation simulation = null;
private Visualizer visualizer = null;
public void setActive(Simulation simulation, Visualizer vis) {
if (!(simulation.getRadioMedium() instanceof MRM)) {
logger.fatal("Cannot activate MRM skin for unknown radio medium: " + simulation.getRadioMedium());
return;
}
this.simulation = simulation;
this.visualizer = vis;
}
public void setInactive() {
if (simulation == null) {
/* Skin was never activated */
return;
}
}
public Color[] getColorOf(Mote mote) {
Mote selectedMote = visualizer.getSelectedMote();
if (mote == selectedMote) {
return new Color[] { Color.CYAN };
}
return null;
}
public void paintBeforeMotes(Graphics g) {
final Mote selectedMote = visualizer.getSelectedMote();
if (simulation == null
|| selectedMote == null
|| selectedMote.getInterfaces().getRadio() == null) {
return;
}
final Position sPos = selectedMote.getInterfaces().getPosition();
/* Paint transmission and interference range for selected mote */
Position motePos = selectedMote.getInterfaces().getPosition();
Point pixelCoord = visualizer.transformPositionToPixel(motePos);
int x = pixelCoord.x;
int y = pixelCoord.y;
FontMetrics fm = g.getFontMetrics();
g.setColor(Color.BLACK);
MRM radioMedium = (MRM) simulation.getRadioMedium();
/* Print transmission success probabilities */
Mote[] dests = simulation.getMotes();
if (dests == null || dests.length == 0) {
String msg = "No edges";
int msgWidth = fm.stringWidth(msg);
g.setColor(Color.BLACK);
g.drawString(msg, x - msgWidth/2, y + 2*Visualizer.MOTE_RADIUS + 3);
return;
}
g.setColor(Color.BLACK);
int edges = 0;
for (Mote d: dests) {
if (d == selectedMote) {
continue;
}
final Radio dRadio = d.getInterfaces().getRadio();
TxPair txPair = new RadioPair() {
public Radio getFromRadio() {
return selectedMote.getInterfaces().getRadio();
}
public Radio getToRadio() {
return dRadio;
}
};
double probArr[] = radioMedium.getChannelModel().getProbability(
txPair,
Double.NEGATIVE_INFINITY
);
double prob = probArr[0];
double ss = probArr[1];
if (prob == 0.0d) {
continue;
}
edges++;
String msg = String.format("%1.1f%%, %1.2fdB", 100.0*prob, ss);
Point pixel = visualizer.transformPositionToPixel(d.getInterfaces().getPosition());
int msgWidth = fm.stringWidth(msg);
g.setColor(new Color(1-(float)prob, (float)prob, 0.0f));
g.drawLine(x, y, pixel.x, pixel.y);
g.setColor(Color.BLACK);
g.drawString(msg, pixel.x - msgWidth/2, pixel.y + 2*Visualizer.MOTE_RADIUS + 3);
}
String msg = dests.length + " edges";
int msgWidth = fm.stringWidth(msg);
g.setColor(Color.BLACK);
g.drawString(msg, x - msgWidth/2, y + 2*Visualizer.MOTE_RADIUS + 3);
}
public void paintAfterMotes(Graphics g) {
}
public Visualizer getVisualizer() {
return visualizer;
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2011, 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 se.sics.cooja.interfaces;
/**
* Directional antenna.
*
* @see MRM
* @author Fredrik Osterlind
*/
public interface DirectionalAntennaRadio {
/**
* @return Current direction (radians). Typically direction 0 has the maximum
* relative gain.
*
* @see #getRelativeGain(double)
*/
public double getDirection();
/**
* Relative gain (dB) as compared to an omnidirectional antenna.
* Note that the given angle is relative to the current direction!
*
* @see #getDirection()
* @param radians Angle relative to current direction
* @param distance Distance from antenna
* @return
*/
public double getRelativeGain(double radians, double distance);
public void addDirectionChangeListener(DirectionChangeListener l);
public void removeDirectionChangeListener(DirectionChangeListener l);
public interface DirectionChangeListener {
public void newDirection(DirectionalAntennaRadio radio, double direction);
}
}