// $Id: ObstructRadioModelPlugin.java,v 1.7.4.4 2003/09/10 03:41:52 mdwelsh Exp $ /** tab:2 * * * * Authors: Jeff Rupp * Date: November 30 03 * Desc: Obstruction enhanced Radio Plugin * Implements features for manipulating a lossy model, with obstructions * * Based on the RadioModelPlugin written by Nelson Lee, * adds in the capacity to specify obstructions to the signal, e.g. walls * Initially was going to use the dxf file format for the vector 'wall' * layout, but that was more work than time permited, so I created a * new file format, ascii with a vector per line: * startX,startY,endX,endY,attenuationInDb */ /** * @author Jeff Rupp */ package net.tinyos.sim.plugins; import java.lang.*; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.text.DecimalFormat; import net.tinyos.message.*; import net.tinyos.sim.*; import net.tinyos.sim.event.*; public class ObstructRadioModelPlugin extends Plugin implements SimConst { private static final boolean DEBUG = false; private Hashtable models = new Hashtable(); private PropagationModel curModel; private JTextField scalingFactorTextField; private JCheckBox cbOutEdges; private boolean outEdges = true; private Hashtable connectivityGraph; private DecimalFormat df = new DecimalFormat(); private Vector m_obstructions = new Vector(10); private JButton m_loadObstructionsButton = new JButton("Load Obstructions..."); private JFileChooser m_obstructionsFileChooser = new JFileChooser(System.getProperty("user.dir")); public void handleEvent(SimEvent event) { if (event instanceof SimObjectEvent) { SimObjectEvent simObjectEvent = (SimObjectEvent)event; switch (simObjectEvent.getType()) { case SimObjectEvent.OBJECT_ADDED: updateModel(false); break; case SimObjectEvent.OBJECT_REMOVED: updateModel(false); break; } motePanel.refresh(); } else if (event instanceof AttributeEvent) { AttributeEvent attributeEvent = (AttributeEvent)event; switch (attributeEvent.getType()) { case ATTRIBUTE_CHANGED: if (attributeEvent.getAttribute() instanceof MoteCoordinateAttribute) updateModel(false); motePanel.refresh(); break; } } else if (event instanceof SimObjectDraggedEvent) { updateModel(true); motePanel.refresh(); } else if (event instanceof OptionSetEvent) { OptionSetEvent ose = (OptionSetEvent)event; if (ose.name.equals("radiomodel")) { PropagationModel pm = (PropagationModel)models.get(ose.value); if (pm != null) { if (DEBUG) System.err.println("RADIOMODEL: Setting model to "+pm); curModel = pm; updateModel(true); motePanel.refresh(); } } else if (ose.name.equals("radioscaling")) { scalingFactorTextField.setText(ose.value); } } else if (event instanceof TossimInitEvent) { tv.pause(); updateModel(true); publishModel(); tv.resume(); } } public void updateModel(boolean force) { // Don't update if AutoRun is active if (!force && tv.getAutoRun() != null) return; Iterator it1 = state.getMoteSimObjects().iterator(); while (it1.hasNext()) { MoteSimObject moteSender = (MoteSimObject)it1.next(); MoteCoordinateAttribute moteSenderCoord = moteSender.getCoordinate(); Iterator it2 = state.getMoteSimObjects().iterator(); while (it2.hasNext()) { MoteSimObject moteReceiver = (MoteSimObject)it2.next(); if (moteReceiver.getID() == moteSender.getID()) continue; MoteCoordinateAttribute moteReceiverCoord = moteReceiver.getCoordinate(); double dx = (double)(moteSenderCoord.getX() - moteReceiverCoord.getX()); double dy = (double)(moteSenderCoord.getY() - moteReceiverCoord.getY()); double distance = Math.sqrt((dx * dx) + (dy * dy)); double prob = curModel.getPacketLossRate(distance, Double.parseDouble(scalingFactorTextField.getText())); if (DEBUG) System.err.println("RADIOMODEL: "+moteSender+"->"+moteReceiver+" dist "+distance+" scale "+scalingFactorTextField.getText()+" prob "+prob); long scaledBitLossRate = (long)(curModel.getBitLossRate(prob)*10000); //??? have to modify the loss rate depending on if we hit any 'walls' scaledBitLossRate = adjustForObstructions(scaledBitLossRate, moteSenderCoord, moteReceiverCoord); prob = ((double)scaledBitLossRate / 5000.0); // 5000 is the empirical max if (DEBUG) System.out.println("ObstructRadioModelPlugin: sampleLossRate [moteSender "+moteSender+"] [moteReceiver "+moteReceiver+"] [packetLossRate "+prob+"] [scaledBitLossRate "+scaledBitLossRate+"]"); connectivityGraph.put(""+moteSender.getID()+", "+moteReceiver.getID(), new Double(prob)); } } } public void publishModel() { if (DEBUG) System.err.println("RADIOMODEL: Publishing model, current is "+curModel); Iterator it1 = state.getMoteSimObjects().iterator(); try { while (it1.hasNext()) { MoteSimObject moteSender = (MoteSimObject)it1.next(); MoteCoordinateAttribute moteSenderCoord = moteSender.getCoordinate(); Iterator it2 = state.getMoteSimObjects().iterator(); while (it2.hasNext()) { MoteSimObject moteReceiver = (MoteSimObject)it2.next(); if (moteReceiver.getID() == moteSender.getID()) continue; // Sender -> Receiver String s = ""+moteSender.getID()+", "+moteReceiver.getID(); double prob = ((Double)connectivityGraph.get(s)).doubleValue(); long scaledBitLossRate = (long)(curModel.getBitLossRate(prob)*10000); //??? have to modify the loss rate depending on if we hit any 'walls' scaledBitLossRate = adjustForObstructions(scaledBitLossRate, moteSenderCoord, moteReceiver.getCoordinate()); SetLinkProbCommand cmd = new SetLinkProbCommand((short)moteSender.getID(), 0L, (short)moteReceiver.getID(), scaledBitLossRate); simComm.sendCommand(cmd); // Receiver -> Sender s = ""+moteReceiver.getID()+", "+moteSender.getID(); prob = ((Double)connectivityGraph.get(s)).doubleValue(); if (DEBUG) System.err.println("RADIOMODEL: "+s+" "+prob); scaledBitLossRate = (long)(curModel.getBitLossRate(prob)*10000); //??? have to modify the loss rate depending on if we hit any 'walls' scaledBitLossRate = adjustForObstructions(scaledBitLossRate, moteSenderCoord, moteReceiver.getCoordinate()); cmd = new SetLinkProbCommand((short)moteReceiver.getID(), 0L, (short)moteSender.getID(), scaledBitLossRate); simComm.sendCommand(cmd); } } } catch (java.io.IOException ioe) { System.err.println("Cannot send command: "+ioe); } } protected long adjustForObstructions(long scaledBitLossRate, MoteCoordinateAttribute moteSenderCoord, MoteCoordinateAttribute moteReceiverCoord) { if(DEBUG) System.out.println("adjustForObstructions provided: "+scaledBitLossRate); double totalAtten = 0.0; Iterator iter = m_obstructions.iterator(); while(iter.hasNext()) { java.lang.Object ob = iter.next(); if(ob instanceof ObstructionIF) { ObstructionIF obstacle = (ObstructionIF)ob; if(obstacle.Intersects(moteSenderCoord.getX(), moteSenderCoord.getY(), moteReceiverCoord.getX(), moteReceiverCoord.getY())) { totalAtten += obstacle.GetAttenuation(); } } } if(totalAtten > 0) { // assumes >= 60 dB should be 0 signal propagation // if we say signal atten = 20*log10(distance) // then reverse is 10^(atten/20), but since the scaled loss // runs 0..10000, we'll * by 10 again to get a subtractor // scaled correctly double probAttenAdjust = Math.pow(10, (totalAtten/20)) * 10; scaledBitLossRate += (long)probAttenAdjust; } else if(totalAtten < 0) { // negative == amplification, curious case, but we'll allow it double probAttenAdjust = Math.pow(10, (-totalAtten/20)) * 10; scaledBitLossRate -= (long)probAttenAdjust; } if(DEBUG) System.out.println("adjustForObstructions returning: "+scaledBitLossRate); return scaledBitLossRate; } public void register() { df.applyPattern("#.###"); connectivityGraph = new Hashtable(); JPanel parameterPane = new JPanel(); parameterPane.setLayout(new GridLayout(2,2)); // Create the out edge checkbox cbOutEdges = new JCheckBox("Out Edges", outEdges); cbOutEdges.addItemListener(new cbListener()); cbOutEdges.setFont(tv.labelFont); // Create radius constant text field and label JLabel scalingFactorLabel = new JLabel("Distance scaling factor"); scalingFactorLabel.setFont(tv.defaultFont); scalingFactorTextField = new JTextField("1", 5); scalingFactorTextField.setFont(tv.smallFont); scalingFactorTextField.setEditable(true); parameterPane.add(scalingFactorLabel); parameterPane.add(scalingFactorTextField); // Create button to update radio model JButton updateButton = new JButton("Update"); updateButton.addActionListener(new UpdateListener()); updateButton.setFont(tv.defaultFont); // Create combo box for different Propagation models JComboBox cb = new JComboBox(); cb.addActionListener(new ComboBoxListener()); EmpiricalModel empiricalModel = new EmpiricalModel(); models.put("empirical", empiricalModel); cb.addItem(empiricalModel); cb.setSelectedItem(empiricalModel); curModel = empiricalModel; // User can use scaling factor to adjust DiscModel dm; dm = new DiscModel(10.0); models.put("disc10", dm); cb.addItem(dm); dm = new DiscModel(100.0); models.put("disc100", dm); cb.addItem(dm); dm = new DiscModel(1000.0); models.put("disc1000", dm); cb.addItem(dm); // add original controls JPanel origControlsPnl = new JPanel(); origControlsPnl.add(parameterPane); origControlsPnl.add(updateButton); origControlsPnl.add(cbOutEdges); origControlsPnl.add(cb); // add new controls m_loadObstructionsButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { m_loadObstructionsButton_actionPerformed(e); } }); JPanel newControlsPnl = new JPanel(); newControlsPnl.add(m_loadObstructionsButton); pluginPanel.setLayout(new BorderLayout()); pluginPanel.add(origControlsPnl, BorderLayout.NORTH); pluginPanel.add(newControlsPnl, BorderLayout.SOUTH); pluginPanel.revalidate(); updateModel(false); } public void draw(Graphics graphics) { Iterator selectedMotes = state.getSelectedMoteSimObjects().iterator(); while (selectedMotes.hasNext()) { MoteSimObject selMote = (MoteSimObject)selectedMotes.next(); Iterator motes = state.getMoteSimObjects().iterator(); while (motes.hasNext()) { MoteSimObject mote = (MoteSimObject)motes.next(); if (selMote != mote) { MoteCoordinateAttribute selMoteCoord = selMote.getCoordinate(); MoteCoordinateAttribute moteCoord = mote.getCoordinate(); int x1 = (int)cT.simXToGUIX(selMoteCoord.getX()); int y1 = (int)cT.simYToGUIY(selMoteCoord.getY()); int x2 = (int)cT.simXToGUIX(moteCoord.getX()); int y2 = (int)cT.simYToGUIY(moteCoord.getY()); double prob; if (outEdges) { String s = ""+selMote.getID()+", "+mote.getID(); prob = ((Double)connectivityGraph.get((Object)s)).doubleValue(); if (prob < 1.0) { graphics.setColor(getColor(1-prob)); Arrow.drawArrow(graphics, x2, y2, x1, y1, Arrow.SIDE_TRAIL); } } else { String s = ""+mote.getID()+", "+selMote.getID(); prob = ((Double)connectivityGraph.get((Object)s)).doubleValue(); if (prob < 1.0) { graphics.setColor(getColor(1-prob)); Arrow.drawArrow(graphics, x1, y1, x2, y2, Arrow.SIDE_TRAIL); } } if (prob < 1.0) { int xMidPoint = x1 + (x2-x1)/2; int yMidPoint = y1 + (y2-y1)/2; Font origFnt = graphics.getFont(); Font newFnt = origFnt.deriveFont((float)12.0); graphics.setFont(newFnt); Color origClr = graphics.getColor(); graphics.setColor(Color.black); graphics.drawString(new String(df.format((1-prob)*100)), xMidPoint, yMidPoint); graphics.setColor(origClr); graphics.setFont(origFnt); } } } } // draw the obstructions Iterator wallsIter = m_obstructions.iterator(); while(wallsIter.hasNext()) { java.lang.Object ob = wallsIter.next(); if(ob instanceof ObstructionIF) { ((ObstructionIF)ob).draw(graphics); } } } public static Color getColor(double value) { if (value < 0.0) return Color.gray; if (value > 1.0) value = 1.0; int red = Math.min(255,(int)(512.0 - (value * 512.0))); int green = Math.min(255,(int)(value * 512.0)); int blue = 0; return new Color(red, green, blue); } public void deregister() {} public String toString() { return "Obstructed Radio model"; } class UpdateListener implements ActionListener { public void actionPerformed(ActionEvent e) { updateModel(true); publishModel(); motePanel.refresh(); } } class cbListener implements ItemListener { public void itemStateChanged(ItemEvent e) { outEdges = (e.getStateChange() == e.SELECTED); motePanel.refresh(); } } class ComboBoxListener implements ActionListener { public void actionPerformed(ActionEvent e) { JComboBox cb = (JComboBox)e.getSource(); PropagationModel pm = (PropagationModel)cb.getSelectedItem(); curModel = pm; updateModel(true); motePanel.refresh(); } } /** * loads the m_obstructions vector with the appropriate type of obstructions * based on the file sent in * File format is: classname, */ private void m_loadObstructionsButton_actionPerformed(ActionEvent e) { if(m_obstructionsFileChooser.showDialog(tv.getMainFrame(), "Open") == JFileChooser.APPROVE_OPTION) { try { java.io.FileReader fis = new java.io.FileReader(m_obstructionsFileChooser.getSelectedFile()); java.io.BufferedReader bufRead = new java.io.BufferedReader(fis); String line = bufRead.readLine(); while(line != null) { if((line.length() > 0) && (line.startsWith("#"))) { line = bufRead.readLine(); continue; // is a comment } // use reflection based on text up to first char if(line.indexOf(',') != -1) { String className = line.substring(0, line.indexOf(',')); try { Class obstacle = Class.forName(className); Object[] ctorParams = new Object[]{line, tv.getCoordTransformer()}; java.lang.reflect.Constructor strCtor = obstacle.getConstructor( new Class[]{String.class, CoordinateTransformer.class}); Object obsInstance = strCtor.newInstance(ctorParams); m_obstructions.addElement(obsInstance); } catch(Exception refEx) { refEx.printStackTrace(); } } line = bufRead.readLine(); } tv.getMainFrame().repaint(); } catch(Exception ex) { ex.printStackTrace(); } } } }