From eb84fbeb79a7d9561b77a7fd430924d18c70c4ad Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Tue, 1 Nov 2011 11:37:19 +0100 Subject: [PATCH] * 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 --- .../apps/mrm/java/se/sics/mrm/AreaViewer.java | 388 ++++++----- .../mrm/java/se/sics/mrm/ChannelModel.java | 617 +++++++++++------- .../mrm/java/se/sics/mrm/FormulaViewer.java | 296 +++++---- .../cooja/apps/mrm/java/se/sics/mrm/MRM.java | 225 ++++--- .../java/se/sics/mrm/MRMVisualizerSkin.java | 157 +++++ .../interfaces/DirectionalAntennaRadio.java | 65 ++ 6 files changed, 1126 insertions(+), 622 deletions(-) create mode 100644 tools/cooja/apps/mrm/java/se/sics/mrm/MRMVisualizerSkin.java create mode 100644 tools/cooja/java/se/sics/cooja/interfaces/DirectionalAntennaRadio.java diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java b/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java index b90d6de0e..0b47d6429 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/AreaViewer.java @@ -63,14 +63,16 @@ import java.io.File; import java.net.URL; import java.text.DecimalFormat; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Observable; import java.util.Observer; import java.util.Random; -import java.util.Vector; +import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -99,11 +101,12 @@ import org.jdom.Element; import se.sics.cooja.ClassDescription; import se.sics.cooja.GUI; import se.sics.cooja.PluginType; -import se.sics.cooja.RadioConnection; import se.sics.cooja.Simulation; import se.sics.cooja.VisPlugin; +import se.sics.cooja.interfaces.DirectionalAntennaRadio; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; +import se.sics.mrm.ChannelModel.TxPair; /** * The class AreaViewer belongs to the MRM package. @@ -118,14 +121,13 @@ import se.sics.cooja.interfaces.Radio; * @see MRM * @author Fredrik Osterlind */ -@ClassDescription("MRM - Area Viewer") +@ClassDescription("MRM Radio environment") @PluginType(PluginType.SIM_PLUGIN) public class AreaViewer extends VisPlugin { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(AreaViewer.class); private final JPanel canvas; - private final VisPlugin thisPlugin; ChannelModel.TransmissionData dataTypeToVisualize = ChannelModel.TransmissionData.SIGNAL_STRENGTH; ButtonGroup visTypeSelectionGroup; @@ -144,7 +146,7 @@ public class AreaViewer extends VisPlugin { private boolean drawCalculatedObstacles = true; private boolean drawChannelProbabilities = true; private boolean drawRadios = true; - private boolean drawRadioActivity = true; + //private boolean drawRadioActivity = true; private boolean drawScaleArrow = true; // Background drawing parameters (meters) @@ -171,7 +173,7 @@ public class AreaViewer extends VisPlugin { private Image channelImage = null; private JSlider resolutionSlider; - private JPanel controlPanel; + private Box controlPanel; private JScrollPane scrollControlPanel; private Simulation currentSimulation; @@ -200,20 +202,25 @@ public class AreaViewer extends VisPlugin { private JCheckBox obstaclesCheckBox; private JCheckBox channelCheckBox; private JCheckBox radiosCheckBox; - private JCheckBox radioActivityCheckBox; +// private JCheckBox radioActivityCheckBox; private JCheckBox arrowCheckBox; private JRadioButton noneButton = null; + private JRadioButton selectModeButton; + private JRadioButton panModeButton; + private JRadioButton zoomModeButton; private JRadioButton trackModeButton; + private Action paintEnvironmentAction; + /** * Initializes an AreaViewer. * * @param simulationToVisualize Simulation using MRM */ public AreaViewer(Simulation simulationToVisualize, GUI gui) { - super("MRM - Area Viewer", gui); + super("MRM Radio environment", gui); currentSimulation = simulationToVisualize; currentRadioMedium = (MRM) currentSimulation.getRadioMedium(); @@ -227,7 +234,6 @@ public class AreaViewer extends VisPlugin { // Set initial size etc. setSize(500, 500); setVisible(true); - thisPlugin = this; // Canvas mode radio buttons + show settings checkbox showSettingsBox = new JCheckBox ("settings", true); @@ -236,20 +242,20 @@ public class AreaViewer extends VisPlugin { showSettingsBox.setActionCommand("toggle show settings"); showSettingsBox.addActionListener(canvasModeHandler); - JRadioButton selectModeButton = new JRadioButton ("select"); + selectModeButton = new JRadioButton ("select"); selectModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); selectModeButton.setContentAreaFilled(false); selectModeButton.setActionCommand("set select mode"); selectModeButton.addActionListener(canvasModeHandler); selectModeButton.setSelected(true); - JRadioButton panModeButton = new JRadioButton ("pan"); + panModeButton = new JRadioButton ("pan"); panModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); panModeButton.setContentAreaFilled(false); panModeButton.setActionCommand("set pan mode"); panModeButton.addActionListener(canvasModeHandler); - JRadioButton zoomModeButton = new JRadioButton ("zoom"); + zoomModeButton = new JRadioButton ("zoom"); zoomModeButton.setAlignmentY(Component.BOTTOM_ALIGNMENT); zoomModeButton.setContentAreaFilled(false); zoomModeButton.setActionCommand("set zoom mode"); @@ -322,10 +328,10 @@ public class AreaViewer extends VisPlugin { radiosCheckBox.addActionListener(selectGraphicsHandler); graphicsComponentsPanel.add(radiosCheckBox); - radioActivityCheckBox = new JCheckBox("Radio Activity", true); - radioActivityCheckBox.setActionCommand("toggle radio activity"); - radioActivityCheckBox.addActionListener(selectGraphicsHandler); - graphicsComponentsPanel.add(radioActivityCheckBox); +// radioActivityCheckBox = new JCheckBox("Radio Activity", true); +// radioActivityCheckBox.setActionCommand("toggle radio activity"); +// radioActivityCheckBox.addActionListener(selectGraphicsHandler); +// graphicsComponentsPanel.add(radioActivityCheckBox); arrowCheckBox = new JCheckBox("Scale arrow", true); arrowCheckBox.setActionCommand("toggle arrow"); @@ -366,8 +372,7 @@ public class AreaViewer extends VisPlugin { graphicsComponentsPanel.add(customButton); // Create visualize channel output panel - JPanel visualizeChannelPanel = new JPanel(); - visualizeChannelPanel.setLayout(new BoxLayout(visualizeChannelPanel, BoxLayout.Y_AXIS)); + Box visualizeChannelPanel = Box.createVerticalBox(); visualizeChannelPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -400,6 +405,8 @@ public class AreaViewer extends VisPlugin { visualizeChannelPanel.add(fixedVsRelative); coloringIntervalPanel = new JPanel() { + private static final long serialVersionUID = 8247374386307237940L; + public void paintComponent(Graphics g) { super.paintComponent(g); @@ -572,20 +579,22 @@ public class AreaViewer extends VisPlugin { visualizeChannelPanel.add(Box.createRigidArea(new Dimension(0,20))); JButton recalculateVisibleButton = new JButton("Paint radio channel"); - recalculateVisibleButton.setActionCommand("recalculate visible area"); - recalculateVisibleButton.addActionListener(formulaHandler); + paintEnvironmentAction = new AbstractAction("Paint radio channel") { + private static final long serialVersionUID = 1L; + public void actionPerformed(ActionEvent e) { + repaintRadioEnvironment(); + } + }; + paintEnvironmentAction.setEnabled(false); + recalculateVisibleButton.setAction(paintEnvironmentAction); visualizeChannelPanel.add(recalculateVisibleButton); // Create control panel - controlPanel = new JPanel(); - controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); + controlPanel = Box.createVerticalBox(); graphicsComponentsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); controlPanel.add(graphicsComponentsPanel); controlPanel.add(new JSeparator()); - controlPanel.add(Box.createRigidArea(new Dimension(0, 5))); - visualizeChannelPanel.setAlignmentX(Component.LEFT_ALIGNMENT); controlPanel.add(visualizeChannelPanel); - controlPanel.setPreferredSize(new Dimension(250,700)); controlPanel.setAlignmentX(Component.LEFT_ALIGNMENT); scrollControlPanel = new JScrollPane( controlPanel, @@ -627,15 +636,47 @@ public class AreaViewer extends VisPlugin { */ private MouseAdapter canvasMouseHandler = new MouseAdapter() { private Popup popUpToolTip = null; + private boolean temporaryZoom = false; + private boolean temporaryPan = false; + private boolean trackedPreviously = false; 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) { popUpToolTip.hide(); 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) { popUpToolTip.hide(); popUpToolTip = null; @@ -646,32 +687,40 @@ public class AreaViewer extends VisPlugin { zoomCenterX = e.getX() / currentZoomX - currentPanX; zoomCenterY = e.getY() / currentZoomY - currentPanY; zoomCenterPoint = e.getPoint(); + if (temporaryZoom || temporaryPan) { + e.consume(); + return; + } /* Select */ if (inSelectMode) { - Vector hitRadios = trackClickedRadio(e.getPoint()); + ArrayList hitRadios = trackClickedRadio(e.getPoint()); if (hitRadios == null || hitRadios.size() == 0) { if (e.getButton() != MouseEvent.BUTTON1) { selectedRadio = null; channelImage = null; trackModeButton.setEnabled(false); + paintEnvironmentAction.setEnabled(false); canvas.repaint(); } return; } - if (hitRadios.size() == 1 && hitRadios.firstElement() == selectedRadio) { + if (hitRadios.size() == 1 && hitRadios.get(0) == selectedRadio) { return; } if (selectedRadio == null || !hitRadios.contains(selectedRadio)) { - selectedRadio = hitRadios.firstElement(); + selectedRadio = hitRadios.get(0); trackModeButton.setEnabled(true); + paintEnvironmentAction.setEnabled(true); } else { selectedRadio = hitRadios.get( (hitRadios.indexOf(selectedRadio)+1) % hitRadios.size() ); + trackModeButton.setEnabled(true); + paintEnvironmentAction.setEnabled(true); } channelImage = null; @@ -681,30 +730,42 @@ public class AreaViewer extends VisPlugin { /* Track */ if (inTrackMode && selectedRadio != null) { - double realClickedX = e.getX() / currentZoomX - currentPanX; - double realClickedY = e.getY() / currentZoomY - currentPanY; - - Position radioPosition = selectedRadio.getPosition(); - final double radioX = radioPosition.getXCoordinate(); - final double radioY = radioPosition.getYCoordinate(); - - trackedComponents = currentChannelModel.getRaysOfTransmission(radioX, radioY, realClickedX, realClickedY); + TxPair txPair = new TxPair() { + public double getFromX() { return selectedRadio.getPosition().getXCoordinate(); } + public double getFromY() { return selectedRadio.getPosition().getYCoordinate(); } + public double getToX() { return e.getX() / currentZoomX - currentPanX; } + public double getToY() { return e.getY() / currentZoomY - currentPanY; } + 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; + } + }; + trackedComponents = currentChannelModel.getRaysOfTransmission(txPair); canvas.repaint(); /* Show popup */ JToolTip t = AreaViewer.this.createToolTip(); String logHtml = - "" + - trackedComponents.log.replace("\n", "
").replace(" pi", " π") + - ""; + "" + + trackedComponents.log.replace("\n", "
").replace(" pi", " π") + + ""; t.setTipText(logHtml); if (t.getTipText() == null || t.getTipText().equals("")) { return; } popUpToolTip = PopupFactory.getSharedInstance().getPopup( - AreaViewer.this, t, e.getXOnScreen(), e.getYOnScreen()); + AreaViewer.this, t, e.getXOnScreen(), e.getYOnScreen()); popUpToolTip.show(); } } @@ -806,9 +867,8 @@ public class AreaViewer extends VisPlugin { } else { scrollControlPanel.setVisible(false); } - thisPlugin.invalidate(); - thisPlugin.revalidate(); - + AreaViewer.this.invalidate(); + AreaViewer.this.revalidate(); } } }; @@ -826,8 +886,8 @@ public class AreaViewer extends VisPlugin { drawChannelProbabilities = ((JCheckBox) e.getSource()).isSelected(); } else if (e.getActionCommand().equals("toggle radios")) { drawRadios = ((JCheckBox) e.getSource()).isSelected(); - } else if (e.getActionCommand().equals("toggle radio activity")) { - drawRadioActivity = ((JCheckBox) e.getSource()).isSelected(); +// } else if (e.getActionCommand().equals("toggle radio activity")) { +// drawRadioActivity = ((JCheckBox) e.getSource()).isSelected(); } else if (e.getActionCommand().equals("toggle arrow")) { drawScaleArrow = ((JCheckBox) e.getSource()).isSelected(); } @@ -871,6 +931,7 @@ public class AreaViewer extends VisPlugin { } class ImageSettingsDialog extends JDialog { + private static final long serialVersionUID = 3026474554976919518L; private double virtualStartX = 0.0, @@ -1205,7 +1266,7 @@ public class AreaViewer extends VisPlugin { } currentChannelModel.notifySettingsChanged(); - thisPlugin.repaint(); + AreaViewer.this.repaint(); } catch (Exception ex) { if (pm.isCanceled()) { @@ -1230,6 +1291,8 @@ public class AreaViewer extends VisPlugin { }; class ObstacleFinderDialog extends JDialog { + private static final long serialVersionUID = -8963997923536967296L; + private NumberFormat intFormat = NumberFormat.getIntegerInstance(); private BufferedImage imageToAnalyze = null; private BufferedImage obstacleImage = null; @@ -1449,6 +1512,7 @@ public class AreaViewer extends VisPlugin { // Preview image tempPanel = new JPanel() { + private static final long serialVersionUID = 1L; public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(imageToAnalyze, 0, 0, getWidth(), getHeight(), this); @@ -1609,6 +1673,7 @@ public class AreaViewer extends VisPlugin { selectedRadio = null; channelImage = null; trackModeButton.setEnabled(false); + paintEnvironmentAction.setEnabled(false); canvas.repaint(); } }; @@ -1675,13 +1740,7 @@ public class AreaViewer extends VisPlugin { return (alpha << 24) | (red << 16) | (green << 8) | blue; } - /** - * 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")) { - + private void repaintRadioEnvironment() { // Get resolution of new image final Dimension resolution = new Dimension( resolutionSlider.getValue(), @@ -1734,15 +1793,36 @@ public class AreaViewer extends VisPlugin { double[][] imageValues = new double[resolution.width][resolution.height]; for (int x=0; x < resolution.width; x++) { 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) { // Attenuate - double[] signalStrength = currentChannelModel.getReceivedSignalStrength( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height - ); + double[] signalStrength = currentChannelModel.getReceivedSignalStrength(txPair); // Collecting signal strengths if (signalStrength[0] < lowestImageValue) { @@ -1756,12 +1836,7 @@ public class AreaViewer extends VisPlugin { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SIGNAL_STRENGTH_VAR) { // Attenuate - double[] signalStrength = currentChannelModel.getReceivedSignalStrength( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height - ); + double[] signalStrength = currentChannelModel.getReceivedSignalStrength(txPair); // Collecting variances if (signalStrength[1] < lowestImageValue) { @@ -1776,10 +1851,7 @@ public class AreaViewer extends VisPlugin { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR) { // Get signal to noise ratio double[] snr = currentChannelModel.getSINR( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height, + txPair, -Double.MAX_VALUE ); @@ -1796,10 +1868,7 @@ public class AreaViewer extends VisPlugin { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.SNR_VAR) { // Get signal to noise ratio double[] snr = currentChannelModel.getSINR( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height, + txPair, -Double.MAX_VALUE ); @@ -1815,11 +1884,7 @@ public class AreaViewer extends VisPlugin { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.PROB_OF_RECEPTION) { // Get probability of receiving a packet TODO What size? Does it matter? double probability = currentChannelModel.getProbability( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height, - -Double.MAX_VALUE + txPair, -Double.MAX_VALUE )[0]; // Collecting variances @@ -1834,10 +1899,7 @@ public class AreaViewer extends VisPlugin { } else if (dataTypeToVisualize == ChannelModel.TransmissionData.DELAY_SPREAD_RMS) { // Get RMS delay spread of receiving a packet double rmsDelaySpread = currentChannelModel.getRMSDelaySpread( - radioX, - radioY, - startX + width * x/resolution.width, - startY + height * y/resolution.height + txPair ); // Collecting variances @@ -1909,7 +1971,7 @@ public class AreaViewer extends VisPlugin { channelHeight = height; channelImage = tempChannelImage; - thisPlugin.repaint(); + AreaViewer.this.repaint(); coloringIntervalPanel.repaint(); } catch (Exception ex) { @@ -1927,9 +1989,7 @@ public class AreaViewer extends VisPlugin { // Start thread attenuatorThread = new Thread(runnable); attenuatorThread.start(); - } - } - }; + } /** * Repaint the canvas @@ -2112,66 +2172,66 @@ public class AreaViewer extends VisPlugin { } // -- Draw radio activity -- - if (drawRadioActivity) { - for (RadioConnection connection: currentRadioMedium.getActiveConnections()) { - Position sourcePosition = connection.getSource().getPosition(); - - // Paint scaled (otherwise bad rounding to integers may occur) - g2d.setTransform(realWorldTransformScaled); - g2d.setStroke(new BasicStroke((float) 0.0)); - - for (Radio receivingRadio: connection.getDestinations()) { - g2d.setColor(Color.GREEN); - - // Get source and destination coordinates - Position destinationPosition = receivingRadio.getPosition(); - - g2d.draw(new Line2D.Double( - sourcePosition.getXCoordinate()*100.0, - sourcePosition.getYCoordinate()*100.0, - destinationPosition.getXCoordinate()*100.0, - destinationPosition.getYCoordinate()*100.0 - )); - } - - for (Radio interferedRadio: connection.getInterfered()) { - g2d.setColor(Color.RED); - - // Get source and destination coordinates - Position destinationPosition = interferedRadio.getPosition(); - - g2d.draw(new Line2D.Double( - sourcePosition.getXCoordinate()*100.0, - sourcePosition.getYCoordinate()*100.0, - destinationPosition.getXCoordinate()*100.0, - destinationPosition.getYCoordinate()*100.0 - )); - } - - g2d.setColor(Color.BLUE); - g2d.setTransform(realWorldTransform); - - g2d.translate( - sourcePosition.getXCoordinate(), - sourcePosition.getYCoordinate() - ); - - // Fetch current translation - double xPos = g2d.getTransform().getTranslateX(); - double yPos = g2d.getTransform().getTranslateY(); - - // Jump to identity transform and paint without scaling - g2d.setTransform(new AffineTransform()); - - g2d.fillOval( - (int) xPos, - (int) yPos, - 5, - 5 - ); - - } - } +// if (drawRadioActivity) { +// for (RadioConnection connection: currentRadioMedium.getActiveConnections()) { +// Position sourcePosition = connection.getSource().getPosition(); +// +// // Paint scaled (otherwise bad rounding to integers may occur) +// g2d.setTransform(realWorldTransformScaled); +// g2d.setStroke(new BasicStroke((float) 0.0)); +// +// for (Radio receivingRadio: connection.getDestinations()) { +// g2d.setColor(Color.GREEN); +// +// // Get source and destination coordinates +// Position destinationPosition = receivingRadio.getPosition(); +// +// g2d.draw(new Line2D.Double( +// sourcePosition.getXCoordinate()*100.0, +// sourcePosition.getYCoordinate()*100.0, +// destinationPosition.getXCoordinate()*100.0, +// destinationPosition.getYCoordinate()*100.0 +// )); +// } +// +// for (Radio interferedRadio: connection.getInterfered()) { +// g2d.setColor(Color.RED); +// +// // Get source and destination coordinates +// Position destinationPosition = interferedRadio.getPosition(); +// +// g2d.draw(new Line2D.Double( +// sourcePosition.getXCoordinate()*100.0, +// sourcePosition.getYCoordinate()*100.0, +// destinationPosition.getXCoordinate()*100.0, +// destinationPosition.getYCoordinate()*100.0 +// )); +// } +// +// g2d.setColor(Color.BLUE); +// g2d.setTransform(realWorldTransform); +// +// g2d.translate( +// sourcePosition.getXCoordinate(), +// sourcePosition.getYCoordinate() +// ); +// +// // Fetch current translation +// double xPos = g2d.getTransform().getTranslateX(); +// double yPos = g2d.getTransform().getTranslateY(); +// +// // Jump to identity transform and paint without scaling +// g2d.setTransform(new AffineTransform()); +// +// g2d.fillOval( +// (int) xPos, +// (int) yPos, +// 5, +// 5 +// ); +// +// } +// } // -- Draw scale arrow -- if (drawScaleArrow) { @@ -2210,7 +2270,7 @@ public class AreaViewer extends VisPlugin { } // -- Draw tracked components (if any) -- - if (inTrackMode && trackedComponents != null) { + if (!currentSimulation.isRunning() && inTrackMode && trackedComponents != null) { g2d.setTransform(realWorldTransformScaled); g2d.setStroke(new BasicStroke((float) 0.0)); @@ -2238,8 +2298,8 @@ public class AreaViewer extends VisPlugin { * @param clickedPoint On-screen position * @return All hit radios */ - protected Vector trackClickedRadio(Point clickedPoint) { - Vector hitRadios = new Vector(); + protected ArrayList trackClickedRadio(Point clickedPoint) { + ArrayList hitRadios = new ArrayList(); if (currentRadioMedium.getRegisteredRadioCount() == 0) { return null; } @@ -2297,9 +2357,16 @@ public class AreaViewer extends VisPlugin { * @return XML element collection */ public Collection getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); Element element; + /* Selected mote */ + if (selectedRadio != null) { + element = new Element("selected"); + element.setAttribute("mote", "" + selectedRadio.getMote().getID()); + config.add(element); + } + // Controls visible element = new Element("controls_visible"); element.setText(Boolean.toString(showSettingsBox.isSelected())); @@ -2332,9 +2399,9 @@ public class AreaViewer extends VisPlugin { element = new Element("show_radios"); element.setText(Boolean.toString(drawRadios)); config.add(element); - element = new Element("show_activity"); - element.setText(Boolean.toString(drawRadioActivity)); - config.add(element); +// element = new Element("show_activity"); +// element.setText(Boolean.toString(drawRadioActivity)); +// config.add(element); element = new Element("show_arrow"); element.setText(Boolean.toString(drawScaleArrow)); config.add(element); @@ -2382,7 +2449,12 @@ public class AreaViewer extends VisPlugin { */ public boolean setConfigXML(Collection configXML, boolean visAvailable) { 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())); canvasModeHandler.actionPerformed(new ActionEvent(showSettingsBox, ActionEvent.ACTION_PERFORMED, showSettingsBox.getActionCommand())); @@ -2411,9 +2483,9 @@ public class AreaViewer extends VisPlugin { selectGraphicsHandler.actionPerformed(new ActionEvent(radiosCheckBox, ActionEvent.ACTION_PERFORMED, radiosCheckBox.getActionCommand())); } else if (element.getName().equals("show_activity")) { - radioActivityCheckBox.setSelected(Boolean.parseBoolean(element.getText())); - selectGraphicsHandler.actionPerformed(new ActionEvent(radioActivityCheckBox, - ActionEvent.ACTION_PERFORMED, radioActivityCheckBox.getActionCommand())); +// radioActivityCheckBox.setSelected(Boolean.parseBoolean(element.getText())); +// selectGraphicsHandler.actionPerformed(new ActionEvent(radioActivityCheckBox, +// ActionEvent.ACTION_PERFORMED, radioActivityCheckBox.getActionCommand())); } else if (element.getName().equals("show_arrow")) { arrowCheckBox.setSelected(Boolean.parseBoolean(element.getText())); selectGraphicsHandler.actionPerformed(new ActionEvent(arrowCheckBox, diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java b/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java index 0babcf6e2..17ae7f8d4 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/ChannelModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Swedish Institute of Computer Science. + * Copyright (c) 2011, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,12 +31,29 @@ package se.sics.mrm; -import java.awt.geom.*; -import java.util.*; +import java.awt.geom.GeneralPath; +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 org.apache.log4j.Logger; 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; /** @@ -53,9 +70,12 @@ import statistics.GaussianWrapper; public class ChannelModel { 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} - private Properties parameters = new Properties(); + private Hashtable parametersDefaults = new Hashtable(); + private Hashtable parameters = new Hashtable(); private Properties parameterDescriptions = new Properties(); // Parameters used for speeding up calculations @@ -71,6 +91,8 @@ public class ChannelModel { private StringBuilder logInfo = null; private ArrayList loggedRays = null; + private Simulation simulation; + // Ray tracing components temporary vector private Vector> calculatedVisibleSides = new Vector>(); @@ -89,105 +111,180 @@ public class ChannelModel { } } 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() { - // - Set initial parameter values - + public static Object getDefaultValue(Parameter p) { + 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 - parameters.put("apply_random", new Boolean(false)); // TODO Should not use random variables as default - parameterDescriptions.put("apply_random", "Apply random values immediately"); + public static String getDescription(Parameter p) { + switch (p) { + 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 - 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"); + parametersDefaults = (Hashtable) parameters.clone(); // Ray Tracer - Use scattering - //parameters.put("rt_use_scattering", new Boolean(false)); // TODO Not used yet - //parameterDescriptions.put("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)"); + //parameters.put(Parameters.rt_use_scattering, Parameter.getDefaultValue(Parameters.rt_use_scattering)); // TODO Not used yet + //parameterDescriptions.put(Parameters.rt_use_scattering, "Use simple scattering"); // Ray Tracer - Scattering coefficient - //parameters.put("rt_scatt_coefficient", new Double(-20)); // TODO Not used yet - //parameterDescriptions.put("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)"); + //parameters.put(Parameters.rt_scatt_coefficient, Parameter.getDefaultValue(Parameters.rt_scatt_coefficient)); // TODO Not used yet + //parameterDescriptions.put(Parameters.rt_scatt_coefficient, "!! Scattering coefficient (dB)"); } /** @@ -272,7 +369,7 @@ public class ChannelModel { * @param identifier Parameter identifier * @return Current parameter value */ - public Object getParameterValue(String id) { + public Object getParameterValue(Parameter id) { Object value = parameters.get(id); if (value == null) { logger.fatal("No parameter with id:" + id + ", aborting"); @@ -287,7 +384,7 @@ public class ChannelModel { * @param identifier Parameter identifier * @return Current parameter value */ - public double getParameterDoubleValue(String id) { + public double getParameterDoubleValue(Parameter id) { return ((Double) getParameterValue(id)).doubleValue(); } @@ -297,7 +394,7 @@ public class ChannelModel { * @param identifier Parameter identifier * @return Current parameter value */ - public int getParameterIntegerValue(String id) { + public int getParameterIntegerValue(Parameter id) { return ((Integer) getParameterValue(id)).intValue(); } @@ -307,7 +404,7 @@ public class ChannelModel { * @param identifier Parameter identifier * @return Current parameter value */ - public boolean getParameterBooleanValue(String id) { + public boolean getParameterBooleanValue(Parameter id) { return ((Boolean) getParameterValue(id)).booleanValue(); } @@ -317,7 +414,7 @@ public class ChannelModel { * @param id Parameter identifier * @param newValue New parameter value */ - public void setParameterValue(String id, Object newValue) { + public void setParameterValue(Parameter id, Object newValue) { if (!parameters.containsKey(id)) { logger.fatal("No parameter with id:" + id + ", aborting"); return; @@ -331,21 +428,6 @@ public class ChannelModel { 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 * will be notified. @@ -355,27 +437,20 @@ public class ChannelModel { } /** - * Returns the Free Space Path Loss factor (in dB), by using - * parts of the Friis equation. (FSPL <= 0) + * Path loss component from Friis' transmission equation. + * Uses frequency and distance only. * - * @param distance Distance from transmitter to receiver - * @return FSPL factor + * @param distance Transmitter-receiver distance + * @return Path loss (dB) */ 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) { - double w = getParameterDoubleValue("wavelength"); - paramFSPL = 20*Math.log10(w) - 20*Math.log10(4*Math.PI); + double f = getParameterDoubleValue(Parameter.frequency); + paramFSPL = -32.44 -20*Math.log10(f /*mhz*/); 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 justBeforeDestination = sourcePoint; - if (!getParameterBooleanValue("rt_disallow_direct_path")) { + if (!getParameterBooleanValue(Parameter.rt_disallow_direct_path)) { directPathExists = isDirectPath(justBeforeDestination, dest); } else { directPathExists = false; @@ -797,7 +872,7 @@ public class ChannelModel { allPaths.add(currentPath); // 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; } @@ -1326,17 +1401,15 @@ public class ChannelModel { * @return Received signal strength (dBm) random variable. The first value is * the random variable mean, and the second is the variance. */ - public double[] getReceivedSignalStrength(double sourceX, double sourceY, double destX, double destY) { - return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH, null); - } - public double[] getReceivedSignalStrength(double sourceX, double sourceY, double destX, double destY, Double txPower) { - return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.SIGNAL_STRENGTH, txPower); + public double[] getReceivedSignalStrength(TxPair txPair) { + return getTransmissionData(txPair, TransmissionData.SIGNAL_STRENGTH); } + // TODO Fix better data type support - private double[] getTransmissionData(double sourceX, double sourceY, double destX, double destY, TransmissionData dataType, Double txPower) { - Point2D source = new Point2D.Double(sourceX, sourceY); - Point2D dest = new Point2D.Double(destX, destY); + private double[] getTransmissionData(TxPair txPair, TransmissionData dataType) { + Point2D source = txPair.getFrom(); + Point2D dest = txPair.getTo(); double accumulatedVariance = 0; // - Get all ray paths from source to destination - @@ -1344,28 +1417,24 @@ public class ChannelModel { RayData.RayType.ORIGIN, source, null, - getParameterIntegerValue("rt_max_rays"), - getParameterIntegerValue("rt_max_refractions"), - getParameterIntegerValue("rt_max_reflections"), - getParameterIntegerValue("rt_max_diffractions") + getParameterIntegerValue(Parameter.rt_max_rays), + getParameterIntegerValue(Parameter.rt_max_refractions), + getParameterIntegerValue(Parameter.rt_max_reflections), + 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 - DefaultMutableTreeNode visibleLinesTree = null; - visibleLinesTree = - buildVisibleLinesTree(originRayData); + DefaultMutableTreeNode visibleLinesTree = buildVisibleLinesTree(originRayData); // Calculate all paths from source to destination, using above calculated tree Vector allPaths = getConnectingPaths(source, dest, visibleLinesTree); if (logMode) { - logInfo.append("Signal components:\n"); + logInfo.append("Signal components:\n"); Enumeration pathsEnum = allPaths.elements(); while (pathsEnum.hasMoreElements()) { RayPath currentPath = pathsEnum.nextElement(); - logInfo.append("* " + currentPath + "\n"); + logInfo.append("* " + currentPath + "\n"); for (int i=0; i < currentPath.getSubPathCount(); i++) { loggedRays.add(currentPath.getSubPath(i)); } @@ -1389,20 +1458,20 @@ public class ChannelModel { // Type specific losses // TODO Type specific losses depends on angles as well! if (subPathStartType == RayData.RayType.REFRACTION) { - pathGain[i] += getParameterDoubleValue("rt_refrac_coefficient"); + pathGain[i] += getParameterDoubleValue(Parameter.rt_refrac_coefficient); } 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) - if (!getParameterBooleanValue("rt_fspl_on_total_length") && accumulatedStraightLength > 0) { + if (!getParameterBooleanValue(Parameter.rt_fspl_on_total_length) && accumulatedStraightLength > 0) { pathGain[i] += getFSPL(accumulatedStraightLength); } accumulatedStraightLength = 0; // Reset straight length } 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) - if (!getParameterBooleanValue("rt_fspl_on_total_length") && accumulatedStraightLength > 0) { + if (!getParameterBooleanValue(Parameter.rt_fspl_on_total_length) && accumulatedStraightLength > 0) { pathGain[i] += getFSPL(accumulatedStraightLength); } accumulatedStraightLength = 0; // Reset straight length @@ -1414,7 +1483,7 @@ public class ChannelModel { // Ray passes through a wall, calculate distance through that wall // Fetch attenuation constant - double attenuationConstant = getParameterDoubleValue("obstacle_attenuation"); + double attenuationConstant = getParameterDoubleValue(Parameter.obstacle_attenuation); Vector allPossibleObstacles = myObstacleWorld.getAllObstaclesNear(subPath.getP1()); @@ -1434,9 +1503,7 @@ public class ChannelModel { pathGain[i] += attenuationConstant * line.getP1().distance(line.getP2()); break; } - } - } // Add to total path length @@ -1444,12 +1511,12 @@ public class ChannelModel { } // 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); } // 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]); } @@ -1463,7 +1530,8 @@ public class ChannelModel { double[] pathModdedLengths = new double[allPaths.size()]; double delaySpread = 0; double delaySpreadRMS = 0; - double wavelength = getParameterDoubleValue("wavelength"); + double freq = getParameterDoubleValue(Parameter.frequency); + double wavelength = C/(freq*1000000d); double totalPathGain = 0; double delaySpreadTotalWeight = 0; 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) totalPathGain += Math.pow(10, pathGain[i]/10.0)*Math.cos(2*Math.PI * pathModdedLengths[i]/wavelength); 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) { - /* TODO Log mode affects result? */ + /* TODO Log mode affects result? */ 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)); if (logMode) { - logInfo.append("\nTotal path gain: " + String.format("%2.3f", totalPathGain) + " dB\n"); - logInfo.append("Delay spread: " + String.format("%2.3f", delaySpread) + "\n"); - logInfo.append("RMS delay spread: " + String.format("%2.3f", delaySpreadRMS) + "\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("RMS delay spread: " + String.format("%2.3f", delaySpreadRMS) + "\n"); } // - Calculate received power - // Using formula (dB) // Received power = Output power + System gain + Transmitter gain + Path Loss + Receiver gain // TODO Update formulas - double outputPower; - if (txPower == null) { - outputPower = getParameterDoubleValue("tx_power"); - } else { - outputPower = txPower; - } - double systemGain = getParameterDoubleValue("system_gain_mean"); - if (getParameterBooleanValue("apply_random")) { + double outputPower = txPair.getTxPower(); + double systemGain = getParameterDoubleValue(Parameter.system_gain_mean); + if (getParameterBooleanValue(Parameter.apply_random)) { 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 { - 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; 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) { @@ -1546,8 +1613,8 @@ public class ChannelModel { } public class TrackedSignalComponents { - ArrayList components; - String log; + ArrayList components; + String log; } /** @@ -1561,7 +1628,7 @@ public class ChannelModel { * @param destY Destination position Y * @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(); logInfo = new StringBuilder(); @@ -1569,7 +1636,7 @@ public class ChannelModel { /* TODO Include background noise? */ logMode = true; - getProbability(sourceX, sourceY, destX, destY, -Double.MAX_VALUE); + getProbability(txPair, -Double.MAX_VALUE); logMode = false; tsc.log = logInfo.toString(); @@ -1596,27 +1663,26 @@ public class ChannelModel { * The second is the variance. * The third value is the received signal strength which may be used in comparison with interference etc. */ - public double[] getSINR(double sourceX, double sourceY, double destX, double destY, double interference) { - /* TODO Cache values: called repeatedly with noise sources. */ - - + public double[] getSINR(TxPair txPair, double interference) { + /* TODO Cache values: called repeatedly with noise sources. */ + // 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 = - new double[] { signalStrength[0], signalStrength[1], signalStrength[0] }; + // Add antenna gain + if (getParameterBooleanValue(Parameter.rx_with_gain)) { + snrData[0] += txPair.getRxGain(); + } - // Add antenna gain TODO Should depend on angle - snrData[0] += getParameterDoubleValue("rx_antenna_gain"); - - double noiseVariance = getParameterDoubleValue("bg_noise_var"); - double noiseMean = getParameterDoubleValue("bg_noise_mean"); + double noiseVariance = getParameterDoubleValue(Parameter.bg_noise_var); + double noiseMean = getParameterDoubleValue(Parameter.bg_noise_mean); if (interference > noiseMean) { noiseMean = interference; } - if (getParameterBooleanValue("apply_random")) { + if (getParameterBooleanValue(Parameter.apply_random)) { Random random = new Random(); /* TODO Use main random generator? */ noiseMean += Math.sqrt(noiseVariance) * random.nextGaussian(); noiseVariance = 0; @@ -1627,7 +1693,7 @@ public class ChannelModel { snrData[1] += noiseVariance; 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; } @@ -1649,19 +1715,19 @@ public class ChannelModel { * @param interference Current interference at destination (dBm) * @return [Probability of reception, signal strength at destination] */ - public double[] getProbability(double sourceX, double sourceY, double destX, double destY, double interference) { - double[] snrData = getSINR(sourceX, sourceY, destX, destY, interference); + public double[] getProbability(TxPair txPair, double interference) { + double[] snrData = getSINR(txPair, interference); double snrMean = snrData[0]; double snrVariance = snrData[1]; double signalStrength = snrData[2]; - double threshold = getParameterDoubleValue("snr_threshold"); - double rxSensitivity = getParameterDoubleValue("rx_sensitivity"); + double threshold = getParameterDoubleValue(Parameter.snr_threshold); + double rxSensitivity = getParameterDoubleValue(Parameter.rx_sensitivity); // Check signal strength against receiver sensitivity and interference if (rxSensitivity > signalStrength - snrMean && - threshold < rxSensitivity + snrMean - signalStrength) { + threshold < rxSensitivity + snrMean - signalStrength) { if (logMode) { - logInfo.append("Weak signal: increasing threshold\n"); + logInfo.append("Weak signal: increasing threshold\n"); } // Keeping snr variance but increasing theshold to sensitivity @@ -1707,8 +1773,8 @@ public class ChannelModel { * Destination position Y * @return RMS delay spread */ - public double getRMSDelaySpread(double sourceX, double sourceY, double destX, double destY) { - return getTransmissionData(sourceX, sourceY, destX, destY, TransmissionData.DELAY_SPREAD, null)[1]; + public double getRMSDelaySpread(TxPair tx) { + return getTransmissionData(tx, TransmissionData.DELAY_SPREAD)[1]; } /** @@ -1718,14 +1784,18 @@ public class ChannelModel { * @return XML element collection */ public Collection getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); Element element; - Enumeration paramEnum = parameters.keys(); + Enumeration paramEnum = parameters.keys(); while (paramEnum.hasMoreElements()) { - String name = (String) paramEnum.nextElement(); - element = new Element(name); - element.setText(parameters.get(name).toString()); + Parameter p = (Parameter) paramEnum.nextElement(); + element = new Element(p.toString()); + if (parametersDefaults.get(p).equals(parameters.get(p))) { + /* Default value */ + continue; + } + element.setAttribute("value", parameters.get(p).toString()); config.add(element); } @@ -1749,19 +1819,47 @@ public class ChannelModel { if (element.getName().equals("obstacles")) { myObstacleWorld = new ObstacleWorld(); myObstacleWorld.setConfigXML(element.getChildren()); - } else { - // Assuming parameter value + } else /* Parameter values */ { + 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 - Object obj = parameters.get(element.getName()); - Class paramClass = obj.getClass(); + logger.warn("MRM parameter converted from wavelength to frequency: " + String.format("%1.1f MHz", frequency)); + continue; + } 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) { - parameters.put(element.getName(), new Double(Double.parseDouble(element.getText()))); + parameters.put(param, new Double(Double.parseDouble(value))); } else if (paramClass == Boolean.class) { - parameters.put(element.getName(), Boolean.parseBoolean(element.getText())); + parameters.put(param, Boolean.parseBoolean(value)); } else if (paramClass == Integer.class) { - parameters.put(element.getName(), Integer.parseInt(element.getText())); + parameters.put(param, Integer.parseInt(value)); } else { logger.fatal("Unsupported class type: " + paramClass); } @@ -1772,4 +1870,85 @@ public class ChannelModel { settingsObservable.notifySettingsChanged(); 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; + } + } + } diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/FormulaViewer.java b/tools/cooja/apps/mrm/java/se/sics/mrm/FormulaViewer.java index cc2e1fe79..96f34388d 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/FormulaViewer.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/FormulaViewer.java @@ -42,29 +42,30 @@ import org.apache.log4j.Logger; import org.jdom.Element; import se.sics.cooja.*; +import se.sics.mrm.ChannelModel.Parameter; /** * This plugin allows a user to reconfigure current radio channel parameters. * * @author Fredrik Osterlind */ -@ClassDescription("MRM - Formula Viewer") +@ClassDescription("MRM Settings") @PluginType(PluginType.SIM_PLUGIN) public class FormulaViewer extends se.sics.cooja.VisPlugin { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(FormulaViewer.class); - private Simulation currentSimulation; - private MRM currentRadioMedium; - private ChannelModel currentChannelModel; + private Simulation simulation; + private MRM radioMedium; + private ChannelModel channelModel; private static Dimension labelDimension = new Dimension(240, 20); private static NumberFormat doubleFormat = NumberFormat.getNumberInstance(); private static NumberFormat integerFormat = NumberFormat.getIntegerInstance(); - private Vector allIntegerParameters = new Vector(); - private Vector allDoubleParameters = new Vector(); - private Vector allBooleanParameters = new Vector(); + private ArrayList allIntegerParameters = new ArrayList(); + private ArrayList allDoubleParameters = new ArrayList(); + private ArrayList allBooleanParameters = new ArrayList(); private JPanel areaGeneral; private JPanel areaTransmitter; @@ -78,11 +79,11 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { * @param simulationToVisualize Simulation which holds the MRM channel model. */ public FormulaViewer(Simulation simulationToVisualize, GUI gui) { - super("MRM - Formula Viewer", gui); + super("MRM Settings", gui); - currentSimulation = simulationToVisualize; - currentRadioMedium = (MRM) currentSimulation.getRadioMedium(); - currentChannelModel = currentRadioMedium.getChannelModel(); + simulation = simulationToVisualize; + radioMedium = (MRM) simulation.getRadioMedium(); + channelModel = radioMedium.getChannelModel(); // -- Create and add GUI components -- JPanel allComponents = new JPanel(); @@ -102,52 +103,52 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { areaGeneral = collapsableArea; addBooleanParameter( - "apply_random", - currentChannelModel.getParameterDescription("apply_random"), + Parameter.apply_random, + Parameter.getDescription(Parameter.apply_random), collapsableArea, - currentChannelModel.getParameterBooleanValue("apply_random") + channelModel.getParameterBooleanValue(Parameter.apply_random) ); addDoubleParameter( - "snr_threshold", - currentChannelModel.getParameterDescription("snr_threshold"), + Parameter.snr_threshold, + Parameter.getDescription(Parameter.snr_threshold), collapsableArea, - currentChannelModel.getParameterDoubleValue("snr_threshold") + channelModel.getParameterDoubleValue(Parameter.snr_threshold) ); addDoubleParameter( - "bg_noise_mean", - currentChannelModel.getParameterDescription("bg_noise_mean"), + Parameter.bg_noise_mean, + Parameter.getDescription(Parameter.bg_noise_mean), collapsableArea, - currentChannelModel.getParameterDoubleValue("bg_noise_mean") + channelModel.getParameterDoubleValue(Parameter.bg_noise_mean) ); addDoubleParameter( - "bg_noise_var", - currentChannelModel.getParameterDescription("bg_noise_var"), + Parameter.bg_noise_var, + Parameter.getDescription(Parameter.bg_noise_var), collapsableArea, - currentChannelModel.getParameterDoubleValue("bg_noise_var") + channelModel.getParameterDoubleValue(Parameter.bg_noise_var) ); addDoubleParameter( - "system_gain_mean", - currentChannelModel.getParameterDescription("system_gain_mean"), + Parameter.system_gain_mean, + Parameter.getDescription(Parameter.system_gain_mean), collapsableArea, - currentChannelModel.getParameterDoubleValue("system_gain_mean") + channelModel.getParameterDoubleValue(Parameter.system_gain_mean) ); addDoubleParameter( - "system_gain_var", - currentChannelModel.getParameterDescription("system_gain_var"), + Parameter.system_gain_var, + Parameter.getDescription(Parameter.system_gain_var), collapsableArea, - currentChannelModel.getParameterDoubleValue("system_gain_var") + channelModel.getParameterDoubleValue(Parameter.system_gain_var) ); addDoubleParameter( - "wavelength", - currentChannelModel.getParameterDescription("wavelength"), + Parameter.frequency, + Parameter.getDescription(Parameter.frequency), collapsableArea, - currentChannelModel.getParameterDoubleValue("wavelength") + channelModel.getParameterDoubleValue(Parameter.frequency) ); // Transmitter parameters @@ -155,17 +156,17 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { areaTransmitter = collapsableArea; addDoubleParameter( - "tx_power", - currentChannelModel.getParameterDescription("tx_power"), + Parameter.tx_power, + Parameter.getDescription(Parameter.tx_power), collapsableArea, - currentChannelModel.getParameterDoubleValue("tx_power") + channelModel.getParameterDoubleValue(Parameter.tx_power) ); - addDoubleParameter( - "tx_antenna_gain", - currentChannelModel.getParameterDescription("tx_antenna_gain"), + addBooleanParameter( + Parameter.tx_with_gain, + Parameter.getDescription(Parameter.tx_with_gain), collapsableArea, - currentChannelModel.getParameterDoubleValue("tx_antenna_gain") + channelModel.getParameterBooleanValue(Parameter.tx_with_gain) ); // Receiver parameters @@ -173,17 +174,17 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { areaReceiver = collapsableArea; addDoubleParameter( - "rx_sensitivity", - currentChannelModel.getParameterDescription("rx_sensitivity"), + Parameter.rx_sensitivity, + Parameter.getDescription(Parameter.rx_sensitivity), collapsableArea, - currentChannelModel.getParameterDoubleValue("rx_sensitivity") + channelModel.getParameterDoubleValue(Parameter.rx_sensitivity) ); - addDoubleParameter( - "rx_antenna_gain", - currentChannelModel.getParameterDescription("rx_antenna_gain"), + addBooleanParameter( + Parameter.rx_with_gain, + Parameter.getDescription(Parameter.rx_with_gain), collapsableArea, - currentChannelModel.getParameterDoubleValue("rx_antenna_gain") + channelModel.getParameterBooleanValue(Parameter.rx_with_gain) ); // Ray Tracer parameters @@ -191,87 +192,87 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { areaRayTracer = collapsableArea; addBooleanParameter( - "rt_disallow_direct_path", - currentChannelModel.getParameterDescription("rt_disallow_direct_path"), + Parameter.rt_disallow_direct_path, + Parameter.getDescription(Parameter.rt_disallow_direct_path), collapsableArea, - currentChannelModel.getParameterBooleanValue("rt_disallow_direct_path") + channelModel.getParameterBooleanValue(Parameter.rt_disallow_direct_path) ); addBooleanParameter( - "rt_ignore_non_direct", - currentChannelModel.getParameterDescription("rt_ignore_non_direct"), + Parameter.rt_ignore_non_direct, + Parameter.getDescription(Parameter.rt_ignore_non_direct), collapsableArea, - currentChannelModel.getParameterBooleanValue("rt_ignore_non_direct") + channelModel.getParameterBooleanValue(Parameter.rt_ignore_non_direct) ); addBooleanParameter( - "rt_fspl_on_total_length", - currentChannelModel.getParameterDescription("rt_fspl_on_total_length"), + Parameter.rt_fspl_on_total_length, + Parameter.getDescription(Parameter.rt_fspl_on_total_length), collapsableArea, - currentChannelModel.getParameterBooleanValue("rt_fspl_on_total_length") + channelModel.getParameterBooleanValue(Parameter.rt_fspl_on_total_length) ); addIntegerParameter( - "rt_max_rays", - currentChannelModel.getParameterDescription("rt_max_rays"), + Parameter.rt_max_rays, + Parameter.getDescription(Parameter.rt_max_rays), collapsableArea, - currentChannelModel.getParameterIntegerValue("rt_max_rays") + channelModel.getParameterIntegerValue(Parameter.rt_max_rays) ); addIntegerParameter( - "rt_max_refractions", - currentChannelModel.getParameterDescription("rt_max_refractions"), + Parameter.rt_max_refractions, + Parameter.getDescription(Parameter.rt_max_refractions), collapsableArea, - currentChannelModel.getParameterIntegerValue("rt_max_refractions") + channelModel.getParameterIntegerValue(Parameter.rt_max_refractions) ); addIntegerParameter( - "rt_max_reflections", - currentChannelModel.getParameterDescription("rt_max_reflections"), + Parameter.rt_max_reflections, + Parameter.getDescription(Parameter.rt_max_reflections), collapsableArea, - currentChannelModel.getParameterIntegerValue("rt_max_reflections") + channelModel.getParameterIntegerValue(Parameter.rt_max_reflections) ); addIntegerParameter( - "rt_max_diffractions", - currentChannelModel.getParameterDescription("rt_max_diffractions"), + Parameter.rt_max_diffractions, + Parameter.getDescription(Parameter.rt_max_diffractions), collapsableArea, - currentChannelModel.getParameterIntegerValue("rt_max_diffractions") + channelModel.getParameterIntegerValue(Parameter.rt_max_diffractions) ); /* addBooleanParameter( - "rt_use_scattering", - currentChannelModel.getParameterDescription("rt_use_scattering"), + Parameters.rt_use_scattering, + Parameter.getDescription(Parameters.rt_use_scattering), collapsableArea, - currentChannelModel.getParameterBooleanValue("rt_use_scattering") + currentChannelModel.getParameterBooleanValue(Parameters.rt_use_scattering) ); */ addDoubleParameter( - "rt_refrac_coefficient", - currentChannelModel.getParameterDescription("rt_refrac_coefficient"), + Parameter.rt_refrac_coefficient, + Parameter.getDescription(Parameter.rt_refrac_coefficient), collapsableArea, - currentChannelModel.getParameterDoubleValue("rt_refrac_coefficient") + channelModel.getParameterDoubleValue(Parameter.rt_refrac_coefficient) ); addDoubleParameter( - "rt_reflec_coefficient", - currentChannelModel.getParameterDescription("rt_reflec_coefficient"), + Parameter.rt_reflec_coefficient, + Parameter.getDescription(Parameter.rt_reflec_coefficient), collapsableArea, - currentChannelModel.getParameterDoubleValue("rt_reflec_coefficient") + channelModel.getParameterDoubleValue(Parameter.rt_reflec_coefficient) ); addDoubleParameter( - "rt_diffr_coefficient", - currentChannelModel.getParameterDescription("rt_diffr_coefficient"), + Parameter.rt_diffr_coefficient, + Parameter.getDescription(Parameter.rt_diffr_coefficient), collapsableArea, - currentChannelModel.getParameterDoubleValue("rt_diffr_coefficient") + channelModel.getParameterDoubleValue(Parameter.rt_diffr_coefficient) ); /* addDoubleParameter( - "rt_scatt_coefficient", - currentChannelModel.getParameterDescription("rt_scatt_coefficient"), + Parameters.rt_scatt_coefficient, + Parameter.getDescription(Parameters.rt_scatt_coefficient), collapsableArea, - currentChannelModel.getParameterDoubleValue("rt_scatt_coefficient") + currentChannelModel.getParameterDoubleValue(Parameters.rt_scatt_coefficient) ); */ // Shadowing parameters @@ -279,16 +280,14 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { areaShadowing = collapsableArea; addDoubleParameter( - "obstacle_attenuation", - currentChannelModel.getParameterDescription("obstacle_attenuation"), + Parameter.obstacle_attenuation, + Parameter.getDescription(Parameter.obstacle_attenuation), collapsableArea, - currentChannelModel.getParameterDoubleValue("obstacle_attenuation") + channelModel.getParameterDoubleValue(Parameter.obstacle_attenuation) ); - - // Add channel model observer responsible to keep all GUI components synched - currentChannelModel.addSettingsObserver(channelModelSettingsObserver); + channelModel.addSettingsObserver(channelModelSettingsObserver); // Set initial size etc. pack(); @@ -312,6 +311,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { private JPanel createCollapsableArea(String title, Container contentPane) { // Create panels JPanel holdingPanel = new JPanel() { + private static final long serialVersionUID = -7925426641856424500L; public Dimension getMaximumSize() { 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)); final JPanel collapsableArea = new JPanel() { + private static final long serialVersionUID = -1261182973911973773L; public Dimension getMaximumSize() { return new Dimension(super.getMaximumSize().width, getPreferredSize().height); } @@ -327,6 +328,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { collapsableArea.setVisible(false); JPanel titlePanel = new JPanel(new BorderLayout()) { + private static final long serialVersionUID = -9121775806029887815L; public Dimension getMaximumSize() { return new Dimension(super.getMaximumSize().width, getPreferredSize().height); } @@ -369,7 +371,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { * @param initialValue Initial value * @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(); JLabel label; JFormattedTextField textField; @@ -386,19 +388,34 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { textField.putClientProperty("id", id); textField.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { - Object sourceObject = e.getSource(); - Double newValue = ((Number) e.getNewValue()).doubleValue(); - String id = (String) ((JFormattedTextField) sourceObject).getClientProperty("id"); - currentChannelModel.setParameterValue(id, newValue); + JFormattedTextField textField = (JFormattedTextField) e.getSource(); + Parameter id = (Parameter) textField.getClientProperty("id"); + Double val = ((Number) e.getNewValue()).doubleValue(); + 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); contentPane.add(panel); return textField; } - + /** * Creates and adds a panel with a label and a * text field which accepts integers. @@ -409,7 +426,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { * @param initialValue Initial value * @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(); JLabel label; JFormattedTextField textField; @@ -426,12 +443,26 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { textField.putClientProperty("id", id); textField.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { - Object sourceObject = e.getSource(); - Integer newValue = ((Number) e.getNewValue()).intValue(); - String id = (String) ((JFormattedTextField) sourceObject).getClientProperty("id"); - currentChannelModel.setParameterValue(id, newValue); + JFormattedTextField textField = (JFormattedTextField) e.getSource(); + Parameter id = (Parameter) textField.getClientProperty("id"); + Integer val = ((Number) e.getNewValue()).intValue(); + 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); @@ -450,7 +481,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { * @param initialValue Initial value * @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(); JLabel label; JCheckBox checkBox; @@ -466,14 +497,23 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { checkBox.putClientProperty("id", id); checkBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - JCheckBox source = (JCheckBox) e.getSource(); - currentChannelModel.setParameterValue( - (String) source.getClientProperty("id"), - new Boolean(source.isSelected()) - ); + JCheckBox checkBox = (JCheckBox) e.getSource(); + Parameter id = (Parameter) checkBox.getClientProperty("id"); + Object val = new Boolean(checkBox.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); contentPane.add(panel); @@ -481,29 +521,6 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { 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. * If it changes, all GUI parameters are updated accordingly. @@ -513,22 +530,22 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { // Update all integers for (int i=0; i < allIntegerParameters.size(); i++) { JFormattedTextField textField = allIntegerParameters.get(i); - String id = (String) textField.getClientProperty("id"); - textField.setValue(currentChannelModel.getParameterValue(id)); + Parameter id = (Parameter) textField.getClientProperty("id"); + textField.setValue(channelModel.getParameterValue(id)); } // Update all doubles for (int i=0; i < allDoubleParameters.size(); i++) { JFormattedTextField textField = allDoubleParameters.get(i); - String id = (String) textField.getClientProperty("id"); - textField.setValue(currentChannelModel.getParameterValue(id)); + Parameter id = (Parameter) textField.getClientProperty("id"); + textField.setValue(channelModel.getParameterValue(id)); } // Update all booleans for (int i=0; i < allBooleanParameters.size(); i++) { JCheckBox checkBox = allBooleanParameters.get(i); - String id = (String) checkBox.getClientProperty("id"); - checkBox.setSelected(currentChannelModel.getParameterBooleanValue(id)); + Parameter id = (Parameter) checkBox.getClientProperty("id"); + checkBox.setSelected(channelModel.getParameterBooleanValue(id)); } repaint(); @@ -536,12 +553,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { }; public void closePlugin() { - // Remove the channel model observer - if (currentChannelModel != null && channelModelSettingsObserver != null) { - currentChannelModel.deleteSettingsObserver(channelModelSettingsObserver); - } else { - logger.fatal("Can't remove channel model observer: " + channelModelSettingsObserver); - } + channelModel.deleteSettingsObserver(channelModelSettingsObserver); } /** @@ -551,7 +563,7 @@ public class FormulaViewer extends se.sics.cooja.VisPlugin { * @return XML element collection */ public Collection getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); Element element; element = new Element("show_general"); diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java b/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java index ba9e45d0a..f97520265 100644 --- a/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/MRM.java @@ -43,11 +43,16 @@ import org.jdom.Element; import se.sics.cooja.ClassDescription; import se.sics.cooja.RadioConnection; import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.DirectionalAntennaRadio; import se.sics.cooja.interfaces.NoiseSourceRadio; import se.sics.cooja.interfaces.NoiseSourceRadio.NoiseLevelListener; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; +import se.sics.cooja.plugins.Visualizer; 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). @@ -63,8 +68,9 @@ import se.sics.cooja.radiomediums.AbstractRadioMedium; * * 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 * @author Fredrik Osterlind */ @@ -72,7 +78,8 @@ import se.sics.cooja.radiomediums.AbstractRadioMedium; public class MRM extends AbstractRadioMedium { 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; private Simulation sim; @@ -92,44 +99,46 @@ public class MRM extends AbstractRadioMedium { sim = simulation; random = simulation.getRandomGenerator(); - currentChannelModel = new ChannelModel(); + currentChannelModel = new ChannelModel(sim); - /* Register plugins */ + /* Register plugins */ sim.getGUI().registerPlugin(AreaViewer.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() { - super.removed(); + super.removed(); - /* Unregister plugins */ + /* Unregister plugins */ sim.getGUI().unregisterPlugin(AreaViewer.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); - Position senderPos = sender.getPosition(); + final Position senderPos = sender.getPosition(); /* TODO Cache potential destination in DGRM */ /* Loop through all potential destinations */ @@ -144,21 +153,26 @@ public class MRM extends AbstractRadioMedium { sender.getChannel() != recv.getChannel()) { continue; } - Position recvPos = recv.getPosition(); + final Radio recvFinal = recv; /* Calculate receive probability */ + TxPair txPair = new RadioPair() { + public Radio getFromRadio() { + return sender; + } + public Radio getToRadio() { + return recvFinal; + } + }; double[] probData = currentChannelModel.getProbability( - senderPos.getXCoordinate(), - senderPos.getYCoordinate(), - recvPos.getXCoordinate(), - recvPos.getYCoordinate(), + txPair, -Double.MAX_VALUE /* TODO Include interference */ ); double recvProb = probData[0]; double recvSignalStrength = probData[1]; if (recvProb == 1.0 || random.nextDouble() < recvProb) { - /* Yes, the receiver *may* receive this packet (it's strong enough) */ + /* Yes, the receiver *may* receive this packet (it's strong enough) */ if (!recv.isReceiverOn()) { newConnection.addInterfered(recv); recv.interfereAnyReception(); @@ -170,15 +184,15 @@ public class MRM extends AbstractRadioMedium { } else if (recv.isReceiving()) { /* Was already receiving: start interfering. * Assuming no continuous preambles checking */ - - double currSignal = recv.getCurrentSignalStrength(); - /* Capture effect: recv-radio is already receiving. - * Are we strong enough to interfere? */ - if (WITH_CAPTURE_EFFECT && - recvSignalStrength < currSignal - 3 /* config */) { - /* No, we are too weak */ - } else { - newConnection.addInterfered(recv, recvSignalStrength); + + double currSignal = recv.getCurrentSignalStrength(); + /* Capture effect: recv-radio is already receiving. + * Are we strong enough to interfere? */ + if (WITH_CAPTURE_EFFECT && + recvSignalStrength < currSignal - 3 /* config */) { + /* No, we are too weak */ + } else { + newConnection.addInterfered(recv, recvSignalStrength); recv.interfereAnyReception(); /* Interfere receiver in all other active radio connections */ @@ -187,22 +201,22 @@ public class MRM extends AbstractRadioMedium { conn.addInterfered(recv); } } - } + } } else { /* Success: radio starts receiving */ newConnection.addDestination(recv, recvSignalStrength); } - } else if (recvSignalStrength > currentChannelModel.getParameterDoubleValue("bg_noise_mean")) { - /* The incoming signal is strong, but strong enough to interfere? */ + } else if (recvSignalStrength > currentChannelModel.getParameterDoubleValue(Parameter.bg_noise_mean)) { + /* The incoming signal is strong, but strong enough to interfere? */ - if (!WITH_CAPTURE_EFFECT) { - newConnection.addInterfered(recv, recvSignalStrength); - recv.interfereAnyReception(); - } else { - /* TODO Implement new type: newConnection.addNoise()? - * Currently, this connection will never disturb this radio... */ - } + if (!WITH_CAPTURE_EFFECT) { + newConnection.addInterfered(recv, recvSignalStrength); + recv.interfereAnyReception(); + } else { + /* TODO Implement new type: newConnection.addNoise()? + * Currently, this connection will never disturb this radio... */ + } } } @@ -213,8 +227,8 @@ public class MRM extends AbstractRadioMedium { public void updateSignalStrengths() { /* Reset: Background noise */ - double background = - currentChannelModel.getParameterDoubleValue(("bg_noise_mean")); + double background = + currentChannelModel.getParameterDoubleValue((Parameter.bg_noise_mean)); for (Radio radio : getRegisteredRadios()) { radio.setCurrentSignalStrength(background); } @@ -235,12 +249,12 @@ public class MRM extends AbstractRadioMedium { for (Radio intfRadio : ((MRMRadioConnection) conn).getInterfered()) { double signalStrength = ((MRMRadioConnection) conn).getInterferenceSignalStrength(intfRadio); if (intfRadio.getCurrentSignalStrength() < signalStrength) { - intfRadio.setCurrentSignalStrength(signalStrength); + intfRadio.setCurrentSignalStrength(signalStrength); } if (!intfRadio.isInterfered()) { /*logger.warn("Radio was not interfered: " + intfRadio);*/ - intfRadio.interfereAnyReception(); + intfRadio.interfereAnyReception(); } } } @@ -248,53 +262,58 @@ public class MRM extends AbstractRadioMedium { /* Check for noise sources */ if (!WITH_NOISE) return; for (Radio noiseRadio: getRegisteredRadios()) { - if (!(noiseRadio instanceof NoiseSourceRadio)) { - continue; - } - NoiseSourceRadio radio = (NoiseSourceRadio) noiseRadio; - int signalStrength = radio.getNoiseLevel(); - if (signalStrength == Integer.MIN_VALUE) { - continue; - } + if (!(noiseRadio instanceof NoiseSourceRadio)) { + continue; + } + final Radio fromRadio = noiseRadio; + NoiseSourceRadio radio = (NoiseSourceRadio) noiseRadio; + int signalStrength = radio.getNoiseLevel(); + if (signalStrength == Integer.MIN_VALUE) { + continue; + } - /* Calculate how noise source affects surrounding radios */ + /* Calculate how noise source affects surrounding radios */ for (Radio affectedRadio : getRegisteredRadios()) { - if (noiseRadio == affectedRadio) { - continue; - } + if (noiseRadio == affectedRadio) { + continue; + } - /* Update noise levels */ - double[] signalMeanVar = currentChannelModel.getReceivedSignalStrength( - noiseRadio.getPosition().getXCoordinate(), - noiseRadio.getPosition().getYCoordinate(), - affectedRadio.getPosition().getXCoordinate(), - affectedRadio.getPosition().getYCoordinate(), - (double) signalStrength); /* TODO Convert to dBm */ - double signal = signalMeanVar[0]; - if (signal < background) { - continue; - } + /* Update noise levels */ + final Radio toRadio = affectedRadio; + TxPair txPair = new RadioPair() { + public Radio getFromRadio() { + return fromRadio; + } + public Radio getToRadio() { + return toRadio; + } + }; + double[] signalMeanVar = currentChannelModel.getReceivedSignalStrength(txPair); + double signal = signalMeanVar[0]; + if (signal < background) { + continue; + } - /* TODO Additive signals strengths? */ - /* TODO XXX Consider radio channels */ - /* TODO XXX Potentially interfere even when signal is weaker (~3dB)... - * (we may alternatively just use the getSINR method...) */ - if (affectedRadio.getCurrentSignalStrength() < signal) { - affectedRadio.setCurrentSignalStrength(signal); + /* TODO Additive signals strengths? */ + /* TODO XXX Consider radio channels */ + /* TODO XXX Potentially interfere even when signal is weaker (~3dB)... + * (we may alternatively just use the getSINR method...) */ + if (affectedRadio.getCurrentSignalStrength() < signal) { + affectedRadio.setCurrentSignalStrength(signal); - /* TODO Interfere with radio connections? */ - if (affectedRadio.isReceiving() && !affectedRadio.isInterfered()) { - for (RadioConnection conn : conns) { - if (conn.isDestination(affectedRadio)) { - /* Intefere with current reception, mark radio as interfered */ + /* TODO Interfere with radio connections? */ + if (affectedRadio.isReceiving() && !affectedRadio.isInterfered()) { + for (RadioConnection conn : conns) { + if (conn.isDestination(affectedRadio)) { + /* Intefere with current reception, mark radio as interfered */ conn.addInterfered(affectedRadio); if (!affectedRadio.isInterfered()) { - affectedRadio.interfereAnyReception(); + affectedRadio.interfereAnyReception(); } - } - } - } - } + } + } + } + } } } } @@ -334,7 +353,7 @@ public class MRM extends AbstractRadioMedium { * @return Number of registered radios. */ public int getRegisteredRadioCount() { - /* TODO Expensive operation */ + /* TODO Expensive operation */ return getRegisteredRadios().length; } @@ -383,16 +402,16 @@ public class MRM extends AbstractRadioMedium { } public double getDestinationSignalStrength(Radio radio) { - if (signalStrengths.get(radio) == null) { - return Double.MIN_VALUE; - } + if (signalStrengths.get(radio) == null) { + return Double.MIN_VALUE; + } return signalStrengths.get(radio); } public double getInterferenceSignalStrength(Radio radio) { - if (signalStrengths.get(radio) == null) { - return Double.MIN_VALUE; - } + if (signalStrengths.get(radio) == null) { + return Double.MIN_VALUE; + } return signalStrengths.get(radio); } } diff --git a/tools/cooja/apps/mrm/java/se/sics/mrm/MRMVisualizerSkin.java b/tools/cooja/apps/mrm/java/se/sics/mrm/MRMVisualizerSkin.java new file mode 100644 index 000000000..8008c78ce --- /dev/null +++ b/tools/cooja/apps/mrm/java/se/sics/mrm/MRMVisualizerSkin.java @@ -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; + } +} diff --git a/tools/cooja/java/se/sics/cooja/interfaces/DirectionalAntennaRadio.java b/tools/cooja/java/se/sics/cooja/interfaces/DirectionalAntennaRadio.java new file mode 100644 index 000000000..66bbc2772 --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/interfaces/DirectionalAntennaRadio.java @@ -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); + } + +}