added new more advanced ray tracing based radio medium: Advanced Radio Medium (ARM)

* still not entirely integrated in the rest of the system
* needs testing
This commit is contained in:
fros4943 2006-12-04 15:46:27 +00:00
parent 4e166903a4
commit a1759d5bed
20 changed files with 6854 additions and 0 deletions

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<project name="COOJA ARM" default="compile" basedir=".">
<property name="java" location="java"/>
<property name="build" location="build"/>
<property name="lib" location="lib"/>
<property name="cooja_jar" value="../../dist/cooja.jar"/>
<target name="init">
<tstamp/>
</target>
<target name="compile" depends="init">
<mkdir dir="${build}"/>
<javac srcdir="${java}" destdir="${build}">
<classpath>
<pathelement path="."/>
<pathelement location="${cooja_jar}"/>
<pathelement location="${lib}/jfreechart-1.0.1.jar"/>
<pathelement location="${lib}/jcommon-1.0.0.jar"/>
</classpath>
</javac>
</target>
<target name="clean" depends="init">
<delete dir="${build}"/>
</target>
<target name="jar" depends="clean, init, compile">
<jar destfile="${lib}/arm.jar" basedir="${build}">
<fileset dir="${build}"/>
<fileset dir="images"/>
<manifest>
<attribute name="Class-Path" value="."/>
</manifest>
</jar>
</target>
</project>

View file

@ -0,0 +1,2 @@
se.sics.cooja.GUI.RADIOMEDIUMS = + se.sics.arm.AdvancedRadioMedium
se.sics.cooja.GUI.JARFILES = + arm.jar jfreechart-1.0.1.jar jcommon-1.0.0.jar

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,658 @@
package se.sics.arm;
import java.util.*;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.*;
import se.sics.cooja.interfaces.*;
/**
* This is the main class of the COOJA Advanced Radio Medium (ARM) package.
*
* ARM is meant to be a replacement for the simpler radio mediums available in
* COOJA. It is packet based and uses a 2D ray-tracing approach to approximate
* signal strength attenuations between simulated radios. Currently the
* ray-tracing only supports reflections and refractions through homogeneous
* obstacles.
*
* ARM provides a number of plugins for example a plugin for visualizing radio
* environments, and a plugin for configuring the radio medium.
*
* When a radio transmits data the area of interference around it will be
* occupied for a time depending on the length of the packet sent. If the entire
* transmission is completed without any interference the packet will be
* delivered, otherwise nothing will be delivered.
*
* Future work includes adding diffractions and scattering support.
*
* @author Fredrik Osterlind
*/
@ClassDescription("Advanced Radio Medium (ARM)")
public class AdvancedRadioMedium extends RadioMedium {
private static Logger logger = Logger.getLogger(AdvancedRadioMedium.class);
private ChannelModel currentChannelModel = null;
private Observer simulationObserver = null;
private Simulation mySimulation = null;
// Registered members of radio medium
private Vector<ARMMember> registeredMembers = new Vector<ARMMember>();
private Vector<RadioTransmission> allTransmissions = new Vector<RadioTransmission>();
private Vector<RadioTransfer> allTransfers = new Vector<RadioTransfer>();
private Vector<RadioInterference> allInterferences = new Vector<RadioInterference>();
/**
* Notifies observers when this radio medium is starting or has finished a packet delivery.
*/
private TransmissionsObservable radioActivityObservable = new TransmissionsObservable();
/**
* Notifies observers when this radio medium has changed settings.
*/
private SettingsObservable settingsObservable = new SettingsObservable();
/**
* Listens to all registered radios, and fetches any new incoming radio packets.
*/
private Observer radioObserver = new Observer() {
public void update(Observable radio, Object obj) {
Radio sendingRadio = (Radio) radio;
if (sendingRadio.getLastEvent() != Radio.RadioEvent.TRANSMISSION_STARTED)
return;
// Locate corresponding member
ARMMember sendingMember = null;
for (ARMMember member: registeredMembers) {
if (member.radio == radio) {
sendingMember = member;
break;
}
}
if (sendingMember == null) {
logger.fatal("ARM: Could not locate radio member - is radio registered? " + radio);
return;
}
// Check that member is not receiving data
if (sendingMember.isListeningOnTransmission()) {
logger.fatal("ARM: Radio is trying to send data but is currently receiving! This must be fixed in Contiki!");
return;
}
// Check that member is not already sending data
for (RadioTransmission transmission: allTransmissions) {
if (transmission.source == sendingMember) {
logger.fatal("ARM: Radio is trying to send data but is already sending! This must be fixed in Contiki!");
return;
}
}
int transmissionEndTime = sendingRadio.getTransmissionEndTime();
// Create transmission
byte[] packetToSend = sendingRadio.getLastPacketTransmitted();
RadioTransmission transmission = new RadioTransmission(sendingMember, transmissionEndTime, packetToSend);
allTransmissions.add(transmission);
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
double sendingX = sendingMember.position.getXCoordinate();
double sendingY = sendingMember.position.getYCoordinate();
Random random = new Random();
// Calculate how the other radios will be affected by this packet
for (ARMMember member: registeredMembers) {
// Ignore this sending radio
if (member != sendingMember) {
double receivingX = member.position.getXCoordinate();
double receivingY = member.position.getYCoordinate();
double[] probData = currentChannelModel.getProbability(sendingX, sendingY, receivingX, receivingY, -Double.MAX_VALUE);
//logger.info("Probability of reception is " + probData[0]);
//logger.info("Signal strength at destination is " + probData[1]);
if (random.nextFloat() < probData[0]) {
// Connection successful (if not interfered later)
//logger.info("OK, creating connection and starting to transmit");
tryCreateTransmission(transmission, member, probData[1]);
} else if (probData[1] > 100) { // TODO Impossible value, what should it be?!
// Transmission is only interference at destination
tryCreateInterference(transmission, member, probData[1]);
} else {
//logger.info("Signal to low to be considered interference");
}
}
}
}
};
/**
* Creates a new Advanced Radio Medium (ARM).
*/
public AdvancedRadioMedium() {
// Create the channel model
currentChannelModel = new ChannelModel();
// Register temporary plugins
logger.info("Registering ARM plugins");
GUI.currentGUI.registerTemporaryPlugin(AreaViewer.class);
GUI.currentGUI.registerTemporaryPlugin(FormulaViewer.class);
}
// -- Radio Medium standard methods --
public void registerMote(Mote mote, Simulation sim) {
registerRadioInterface(mote.getInterfaces().getRadio(), mote.getInterfaces().getPosition(), sim);
}
public void unregisterMote(Mote mote, Simulation sim) {
unregisterRadioInterface(mote.getInterfaces().getRadio(), sim);
}
public void registerRadioInterface(Radio radio, Position position, Simulation sim) {
if (radio == null || position == null) {
logger.fatal("Could not register radio: " + radio + " @ " + position);
return;
}
// If we are not already tick observering simulation, we should be
if (simulationObserver == null) {
mySimulation = sim;
simulationObserver = new Observer() {
public void update(Observable obs, Object obj) {
// Check if any transmission is active in the radio medium
if (allTransmissions.isEmpty())
return;
Vector<RadioTransmission> uncompletedTransmissions = new Vector<RadioTransmission>();
Vector<RadioTransmission> completedTransmissions = new Vector<RadioTransmission>();
// Check if any transmission has completed
for (RadioTransmission transmission: allTransmissions) {
if (transmission.isCompleted()) {
completedTransmissions.add(transmission);
} else {
uncompletedTransmissions.add(transmission);
}
}
if (completedTransmissions.isEmpty())
// Nothing to do
return;
// At least one transmission has completed - deliver data for associated transfers
for (RadioTransmission transmission: completedTransmissions) {
// Unregister interferences of this transmission source
Vector<RadioInterference> intfToUnregister = new Vector<RadioInterference>();
for (RadioInterference interference: allInterferences) {
if (interference.mySource == transmission) {
intfToUnregister.add(interference);
}
}
for (RadioInterference interference: intfToUnregister)
unregisterInterference(interference);
// Deliver data and unregister transmission
Vector<RadioTransfer> transToUnregister = new Vector<RadioTransfer>();
for (RadioTransfer transfer: allTransfers) {
if (transfer.mySource == transmission) {
if (!transfer.interferenceDestroyedConnection()) {
// Don't interfer connection
} else {
transfer.myDestination.radio.interferReception(0);
}
transToUnregister.add(transfer);
}
}
for (RadioTransfer transfer: transToUnregister)
unregisterTransmission(transfer);
}
allTransmissions = uncompletedTransmissions;
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
// Update all radio signal strengths
for (ARMMember member: registeredMembers) {
member.updateHeardSignalStrength();
}
}
};
sim.addTickObserver(simulationObserver);
}
// Save both radio and its position, and also register us as an observer to the radio
ARMMember member = new ARMMember(radio, position);
registeredMembers.add(member);
radio.addObserver(radioObserver);
radio.setCurrentSignalStrength(currentChannelModel.getParameterDoubleValue(("bg_noise_mean")));
// Settings have changed - notify observers
settingsObservable.notifySettingsChanged();
}
public void unregisterRadioInterface(Radio radioToRemove, Simulation sim) {
// Find corresponding radio member and remove it
ARMMember memberToRemove = null;
for (ARMMember member: registeredMembers) {
if (member.radio == radioToRemove) {
memberToRemove = member;
break;
}
}
if (memberToRemove != null) {
registeredMembers.remove(memberToRemove);
} else
logger.warn("ARM: Could not unregister radio: " + radioToRemove);
// Settings have changed - notify observers
settingsObservable.notifySettingsChanged();
}
public void addRadioMediumObserver(Observer observer) {
// Add radio traffic observer to this radio medium
radioActivityObservable.addObserver(observer);
}
public void deleteRadioMediumObserver(Observer observer) {
// Remove observer from this radio medium
radioActivityObservable.deleteObserver(observer);
}
public RadioConnection[] getLastTickConnections() {
logger.fatal("ARM: getLastTickConnections() not implemented");
return null;
}
public void setConnectionLogger(ConnectionLogger connection) {
logger.fatal("ARM: setConnectionLogger() not implemented");
}
public Collection<Element> getConfigXML() {
// Just forwarding to current channel model
return currentChannelModel.getConfigXML();
}
public boolean setConfigXML(Collection<Element> configXML) {
// Just forwarding to current channel model
return currentChannelModel.setConfigXML(configXML);
}
// -- Advanced Radio Medium specific methods --
/**
* Adds an observer which is notified when this radio medium has
* changed settings, such as added or removed radios.
*
* @param obs New observer
*/
public void addSettingsObserver(Observer obs) {
settingsObservable.addObserver(obs);
}
/**
* Deletes an earlier registered setting observer.
*
* @param osb
* Earlier registered observer
*/
public void deleteSettingsObserver(Observer obs) {
settingsObservable.deleteObserver(obs);
}
/**
* Returns position of given radio.
*
* @param radio Registered radio
* @return Position of given radio
*/
public Position getRadioPosition(Radio radio) {
// Find radio, and return its position
for (ARMMember member: registeredMembers) {
if (member.radio == radio) {
return member.position;
}
}
logger.fatal("ARM: Given radio is not registered!");
return null;
}
/**
* @return Number of registered radios.
*/
public int getRegisteredRadioCount() {
return registeredMembers.size();
}
/**
* Returns radio at given index.
*
* @param index Index of registered radio.
* @return Radio at given index
*/
public Radio getRegisteredRadio(int index) {
return registeredMembers.get(index).radio;
}
/**
* Returns the current channel model object, responsible for
* all probability and transmission calculations.
*
* @return Current channel model
*/
public ChannelModel getChannelModel() {
return currentChannelModel;
}
/**
* Tries to create a new transmission between given transmission and
* destination. The given signal strength should be the incoming signal
* strength at the destination. This value will be used after the transmission
* is completed in order to compare the connection with any interference.
*
* @param source
* @param destination
* @param signalStrength
* @return
*/
public void tryCreateTransmission(RadioTransmission source, ARMMember destination, double signalStrength) {
// Check if destination is already listening to a connection
if (destination.isListeningOnTransmission()) {
RadioInterference newInterference = new RadioInterference(source, destination, signalStrength);
destination.heardInterferences.add(newInterference);
registerInterference(newInterference);
return;
}
// Create new transmission
RadioTransfer newTransmission = new RadioTransfer(source, destination, signalStrength);
destination.heardTransmission = newTransmission;
registerTransmission(newTransmission);
}
public void tryCreateInterference(RadioTransmission source, ARMMember destination, double signalStrength) {
RadioInterference newInterference = new RadioInterference(source, destination, signalStrength);
destination.heardInterferences.add(newInterference);
registerInterference(newInterference);
}
/**
* ARM radio transmission.
*/
class RadioTransmission {
ARMMember source = null;
int endTime = 0;
byte[] dataToTransfer = null;
public RadioTransmission(ARMMember source, int endTime, byte[] dataToTransfer) {
this.source = source;
this.endTime = endTime;
this.dataToTransfer = dataToTransfer;
}
/**
* @return True if no longer transmitting.
*/
public boolean isCompleted() {
return mySimulation.getSimulationTime() >= endTime;
}
}
/**
* ARM radio interference
*/
class RadioInterference {
RadioTransmission mySource;
ARMMember myDestination;
double interferenceSignalStrength;
public RadioInterference(RadioTransmission transmission, ARMMember destination, double signalStrength) {
this.mySource = transmission;
this.myDestination = destination;
this.interferenceSignalStrength = signalStrength;
}
/**
* @return True if interference is no more.
*/
public boolean isOld() {
return mySource.isCompleted();
}
}
/**
* ARM radio transfers
*/
class RadioTransfer {
RadioTransmission mySource;
ARMMember myDestination;
double transmissionSignalStrength;
double maxInterferenceSignalStrength;
public RadioTransfer(RadioTransmission source, ARMMember destination, double signalStrength) {
this.mySource = source;
this.myDestination = destination;
this.transmissionSignalStrength = signalStrength;
maxInterferenceSignalStrength = -Double.MAX_VALUE;
destination.radio.receivePacket(source.dataToTransfer, source.endTime);
destination.radio.setCurrentSignalStrength(signalStrength);
}
public void addInterference(double signalStrength) {
if (signalStrength > maxInterferenceSignalStrength) {
maxInterferenceSignalStrength = signalStrength;
}
myDestination.radio.setCurrentSignalStrength(Math.max(
maxInterferenceSignalStrength, transmissionSignalStrength));
}
/**
* @return True if transmission is completed.
*/
public boolean isOld() {
return mySource.isCompleted();
}
/**
* @return True if interference destroyed transmission
*/
public boolean interferenceDestroyedConnection() {
if (maxInterferenceSignalStrength + 30 > transmissionSignalStrength) {
// Recalculating probability of delivery
double[] probData = currentChannelModel.getProbability(
mySource.source.position.getXCoordinate(),
mySource.source.position.getYCoordinate(),
myDestination.position.getXCoordinate(),
myDestination.position.getYCoordinate(),
maxInterferenceSignalStrength);
// logger.info("Transfer was interfered, recalculating probability of success: " + probData[0]);
if (new Random().nextFloat() >= probData[0]) {
return true;
}
}
return false;
}
}
/**
* Inner class used for keeping track transceivers.
*/
class ARMMember {
Radio radio = null;
Position position = null;
private RadioTransfer heardTransmission;
private Vector<RadioInterference> heardInterferences = new Vector<RadioInterference>();
double currentSignalStrength = -Double.MAX_VALUE;
public ARMMember(Radio radio, Position position) {
this.radio = radio;
this.position = position;
}
public boolean isListeningOnTransmission() {
if (heardTransmission == null)
return false;
if (heardTransmission.isOld()) {
heardTransmission = null;
}
return heardTransmission != null;
}
/**
* Calculates current incoming signal strength at this radio.
* Observe, does not alter any transmissions!
*/
public void updateHeardSignalStrength() {
double maxSignalStrength = -Double.MAX_VALUE;
// Get maximum interference and also update interference list
Vector<RadioInterference> newInterferences = new Vector<RadioInterference>();
for (RadioInterference interference: heardInterferences) {
if (!interference.isOld()) {
newInterferences.add(interference);
maxSignalStrength = Math.max(maxSignalStrength,
interference.interferenceSignalStrength);
}
}
heardInterferences = newInterferences;
if (heardTransmission != null && !heardTransmission.isOld()) {
maxSignalStrength = Math.max(maxSignalStrength,
heardTransmission.transmissionSignalStrength);
} else
heardTransmission = null;
// Noise level
maxSignalStrength = Math.max(maxSignalStrength, currentChannelModel
.getParameterDoubleValue("bg_noise_mean"));
currentSignalStrength = maxSignalStrength;
radio.setCurrentSignalStrength(currentSignalStrength);
}
}
public void registerInterference(RadioInterference interference) {
allInterferences.add(interference);
updateInterferences();
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
}
public void registerTransmission(RadioTransfer transmission) {
allTransfers.add(transmission);
updateInterferences();
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
}
public void unregisterInterference(RadioInterference interference) {
updateInterferences();
allInterferences.remove(interference);
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
}
public void unregisterTransmission(RadioTransfer transmission) {
updateInterferences();
allTransfers.remove(transmission);
radioActivityObservable.notifyRadioActivityChanged(); // Need to notify observers
}
private void updateInterferences() {
// We need to check impact of interferences on transmissions
for (RadioTransfer transmission: allTransfers) {
for (RadioInterference interference: allInterferences) {
if (interference.myDestination == transmission.myDestination) {
transmission.addInterference(interference.interferenceSignalStrength);
}
}
}
}
class TransmissionsObservable extends Observable {
private void notifyRadioActivityChanged() {
setChanged();
notifyObservers();
}
}
class SettingsObservable extends Observable {
private void notifySettingsChanged() {
setChanged();
notifyObservers();
}
}
/**
* @return Current active transmissions
*/
public Vector<RadioTransmission> getCurrentTransmissions() {
return allTransmissions;
}
/**
* @return Current active transmissions
*/
public RadioTransmission[] getCurrentTransmissionsArray() {
return allTransmissions.toArray(new RadioTransmission[0]);
}
/**
* @return Current active interferences
*/
public Vector<RadioInterference> getCurrentInterferences() {
return allInterferences;
}
/**
* @return Current active interferences
*/
public RadioInterference[] getCurrentInterferencesArray() {
return allInterferences.toArray(new RadioInterference[0]);
}
/**
* @return Current active transfers
*/
public Vector<RadioTransfer> getCurrentTransfers() {
return allTransfers;
}
/**
* @return Current active transfers
*/
public RadioTransfer[] getCurrentTransfersArray() {
return allTransfers.toArray(new RadioTransfer[0]);
}
}

View file

@ -0,0 +1,385 @@
package se.sics.arm;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Vector;
import org.apache.log4j.Logger;
/**
* This class represents an angle interval.
*
* @author Fredrik Osterlind
*/
class AngleInterval {
private static Logger logger = Logger.getLogger(AngleInterval.class);
// Sub intervals all between 0 and 2*PI
Vector<Interval> subIntervals;
/**
* Creates a new angle interval.
*
* @param startAngle Start angle (rad)
* @param endAngle End angle (rad) (> start angle)
*/
public AngleInterval(double startAngle, double endAngle) {
subIntervals = new Vector<Interval>();
if (endAngle < startAngle) {
} else if (endAngle - startAngle >= 2*Math.PI) {
subIntervals.add(new Interval(0, 2*Math.PI));
} else {
while (startAngle < 0)
startAngle += 2*Math.PI;
while (endAngle < 0)
endAngle += 2*Math.PI;
startAngle %= 2*Math.PI;
endAngle %= 2*Math.PI;
Interval tempInterval;
if (startAngle < endAngle) {
tempInterval = new Interval(startAngle, endAngle);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
} else {
tempInterval = new Interval(startAngle, 2*Math.PI);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
tempInterval = new Interval(0, endAngle);
if (!tempInterval.isEmpty())
subIntervals.add(tempInterval);
}
}
}
/**
* Returns new intervals consisting of this interval with the given interval removed.
* These can either be null (if entire interval was removed),
* one interval (if upper or lower part, or nothing was removed) or two intervals
* (if middle part of interval was removed).
*
* @param intervalToSubtract Other interval
* @return New intervals
*/
public Vector<AngleInterval> subtract(AngleInterval intervalToSubtract) {
Vector<Interval> afterSubtractionIntervals = new Vector<Interval>();
// Before subtraction
afterSubtractionIntervals.addAll(subIntervals);
if (intervalToSubtract == null) {
Vector<AngleInterval> ret = new Vector<AngleInterval>();
ret.add(this);
return ret;
}
// Subtract every subinterval each
for (int i=0; i < intervalToSubtract.subIntervals.size(); i++) {
Interval subIntervalToSubtract = intervalToSubtract.subIntervals.get(i);
Vector<Interval> newAfterSubtractionIntervals = new Vector<Interval>();
for (int j=0; j < afterSubtractionIntervals.size(); j++) {
Vector<Interval> tempIntervals = afterSubtractionIntervals.get(j).subtract(subIntervalToSubtract);
if (tempIntervals != null)
newAfterSubtractionIntervals.addAll(tempIntervals);
}
afterSubtractionIntervals = newAfterSubtractionIntervals;
}
Vector<AngleInterval> newAngleIntervals = new Vector<AngleInterval>();
for (int i=0; i < afterSubtractionIntervals.size(); i++) {
if (afterSubtractionIntervals.get(i) != null && !afterSubtractionIntervals.get(i).isEmpty())
newAngleIntervals.add(
new AngleInterval(afterSubtractionIntervals.get(i).getLow(), afterSubtractionIntervals.get(i).getHigh())
);
}
return newAngleIntervals;
}
/**
* Returns the intersection of this interval with
* the given.
*
* @param interval Other interval
* @return Intersection
*/
public AngleInterval intersectWith(AngleInterval interval) {
Vector<Interval> afterIntersectionIntervals = new Vector<Interval>();
// Intersect all subintervals, keep all results
for (int i=0; i < interval.subIntervals.size(); i++) {
for (int j=0; j < subIntervals.size(); j++) {
Interval temp = interval.subIntervals.get(i).intersectWith(subIntervals.get(j));
if (temp != null && !temp.isEmpty())
afterIntersectionIntervals.add(temp);
}
}
if (afterIntersectionIntervals.size() > 2) {
logger.fatal("AngleInterval.intersectWith() error!");
} else if (afterIntersectionIntervals.size() == 2) {
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = afterIntersectionIntervals.get(0);
Interval interval2 = afterIntersectionIntervals.get(1);
if (interval1.getLow() == 0)
return new AngleInterval(
interval2.getLow(), interval1.getHigh() + 2*Math.PI
);
else
return new AngleInterval(
interval1.getLow(), interval2.getHigh() + 2*Math.PI
);
} else if (afterIntersectionIntervals.size() == 1) {
return new AngleInterval(
afterIntersectionIntervals.firstElement().getLow(),
afterIntersectionIntervals.firstElement().getHigh()
);
}
return null;
}
/**
* Returns start angle of this interval.
* This angle is always > 0 and < the end angle.
*
* @return Start angle
*/
public double getStartAngle() {
if (subIntervals == null || subIntervals.isEmpty()) {
logger.warn("Getting start angle of null angle interval!");
return 0;
}
if (subIntervals.size() > 2) {
logger.warn("Getting start angle of malformed angle interval!");
return 0;
}
if (subIntervals.size() == 1) {
return subIntervals.firstElement().getLow();
}
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = subIntervals.get(0);
Interval interval2 = subIntervals.get(1);
if (interval1.getLow() == 0)
return interval2.getLow();
else
return interval1.getLow();
}
/**
* Returns end angle of this interval.
* This angle is always > start angle, and may be > 2*PI.
*
* @return End angle
*/
public double getEndAngle() {
if (subIntervals == null || subIntervals.isEmpty()) {
logger.warn("Getting start angle of null angle interval!");
return 0;
}
if (subIntervals.size() > 2) {
logger.warn("Getting start angle of malformed angle interval!");
return 0;
}
if (subIntervals.size() == 1) {
return subIntervals.firstElement().getHigh();
}
// The interval (y-x) is divided into:
// y -> 2*PI
// 0 -> x
Interval interval1 = subIntervals.get(0);
Interval interval2 = subIntervals.get(1);
if (interval1.getLow() == 0)
return interval1.getHigh() + 2*Math.PI;
else
return interval2.getHigh() + 2*Math.PI;
}
/**
* @return Size of interval (rad)
*/
public double getSize() {
double size = 0;
for (int i=0; i < subIntervals.size(); i++)
size += subIntervals.get(i).getSize();
return size;
}
/**
* Checks if the given interval is a subset of this interval.
*
* @param interval Other interval
* @return True if this interval contains given interval
*/
public boolean contains(AngleInterval interval) {
// Check that all parts of argument is contained by any part of this
for (int i=0; i < interval.subIntervals.size(); i++) {
boolean contained = false;
for (int j=0; j < subIntervals.size(); j++) {
if (subIntervals.get(j).contains(interval.subIntervals.get(i))) {
contained = true;
break;
}
}
if (!contained)
return false;
}
return true;
}
/**
* Checks if the two intervals intersect.
*
* @param interval Other interval
* @return True if this interval intersects given interval
*/
public boolean intersects(AngleInterval interval) {
return (intersectWith(interval) != null);
}
/**
* @return True if interval defined is of no size.
*/
public boolean isEmpty() {
if (subIntervals.isEmpty())
return true;
if (getSize() <= 0.001)
return true;
return false;
}
public String toString() {
String retString = "";
for (int i=0; i < subIntervals.size(); i++) {
if (!retString.equals(""))
retString = retString.concat(" && ");
retString = retString.concat("(");
retString = retString.concat(Math.toDegrees(subIntervals.get(i).getLow()) + " -> " + Math.toDegrees(subIntervals.get(i).getHigh()));
retString = retString.concat(")");
}
return retString;
}
/**
* Returns a line starting at given start point and
* in the given direction.
* This line may be used when calculating intersection points.
*
* @param startPoint Start point
* @param angle Line direction (rad)
* @return Line
*/
public static Line2D getDirectedLine(Point2D startPoint, double angle, double length) {
return new Line2D.Double(
startPoint.getX(),
startPoint.getY(),
startPoint.getX() + length*Math.cos(angle),
startPoint.getY() + length*Math.sin(angle)
);
}
/**
* Returns an angle interval of the given line seen from
* the given reference point.
*
* @param refPoint Reference point
* @param line Line to measure angle against
* @return Angle interval (-pi <-> pi)
*/
public static AngleInterval getAngleIntervalOfLine(Point2D refPoint, Line2D line) {
// Create angle interval of this line
double x1 = line.getX1() - refPoint.getX();
double y1 = line.getY1() - refPoint.getY();
double x2 = line.getX2() - refPoint.getX();
double y2 = line.getY2() - refPoint.getY();
double angle1 = Math.atan2(y1, x1);
double angle2 = Math.atan2(y2, x2);
// If interval is bigger than PI, line angles must wrap
if (Math.abs(angle1 - angle2) > Math.PI) {
if (angle1 < 0)
angle1 += 2*Math.PI;
else
angle2 += 2*Math.PI;
}
return new AngleInterval(
Math.min(angle1, angle2), Math.max(angle1, angle2)
);
}
public boolean equals(Object object) {
if (object == null)
return false;
AngleInterval interval = (AngleInterval) object;
return (interval.getStartAngle() == this.getStartAngle() && interval.getEndAngle() == this.getEndAngle());
}
/**
* Subtracts given interval from all intervals in given vector.
* This method never returns null (but empty vectors).
*
* @param initialIntervals Initial intervals
* @param interval Interval to subtract
* @return New intervals
*/
public static Vector<AngleInterval> subtract(Vector<AngleInterval> initialIntervals, AngleInterval interval) {
Vector<AngleInterval> newIntervals = new Vector<AngleInterval>();
for (int i=0; i < initialIntervals.size(); i++) {
Vector<AngleInterval> tempIntervals = initialIntervals.get(i).subtract(interval);
if (tempIntervals != null) {
newIntervals.addAll(tempIntervals);
}
}
return newIntervals;
}
/**
* Intersects given interval with all intervals in given vector.
* This method never returns null (but empty vectors).
*
* @param initialIntervals Initial intervals
* @param interval Interval to intersect
* @return New intervals
*/
public static Vector<AngleInterval> intersect(Vector<AngleInterval> initialIntervals, AngleInterval interval) {
Vector<AngleInterval> newIntervals = new Vector<AngleInterval>();
for (int i=0; i < initialIntervals.size(); i++) {
AngleInterval tempInterval = initialIntervals.get(i).intersectWith(interval);
if (tempInterval != null) {
newIntervals.add(tempInterval);
}
}
return newIntervals;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,583 @@
package se.sics.arm;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.*;
/**
* This plugin allows a user to reconfigure current radio channel parameters.
*
* @author Fredrik Osterlind
*/
@ClassDescription("ARM - Formula Viewer")
@VisPluginType(VisPluginType.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 AdvancedRadioMedium currentRadioMedium;
private ChannelModel currentChannelModel;
private static Dimension labelDimension = new Dimension(240, 20);
private static NumberFormat doubleFormat = NumberFormat.getNumberInstance();
private static NumberFormat integerFormat = NumberFormat.getIntegerInstance();
private Vector<JFormattedTextField> allIntegerParameters = new Vector<JFormattedTextField>();
private Vector<JFormattedTextField> allDoubleParameters = new Vector<JFormattedTextField>();
private Vector<JCheckBox> allBooleanParameters = new Vector<JCheckBox>();
private JPanel areaGeneral;
private JPanel areaTransmitter;
private JPanel areaReceiver;
private JPanel areaRayTracer;
private JPanel areaShadowing;
/**
* Creates a new formula viewer.
*
* @param simulationToVisualize Simulation which holds the ARM channel model.
*/
public FormulaViewer(Simulation simulationToVisualize) {
super("Advanced Radio Medium - Formula Viewer");
currentSimulation = simulationToVisualize;
currentRadioMedium = (AdvancedRadioMedium) currentSimulation.getRadioMedium();
currentChannelModel = currentRadioMedium.getChannelModel();
// -- Create and add GUI components --
JPanel allComponents = new JPanel();
allComponents.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
allComponents.setLayout(new BoxLayout(allComponents, BoxLayout.Y_AXIS));
JScrollPane scrollPane = new JScrollPane(allComponents);
scrollPane.setPreferredSize(new Dimension(500,400));
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
setContentPane(scrollPane);
JPanel collapsableArea;
// General parameters
collapsableArea = createCollapsableArea("General parameters", allComponents);
areaGeneral = collapsableArea;
addBooleanParameter(
"apply_random",
currentChannelModel.getParameterDescription("apply_random"),
collapsableArea,
currentChannelModel.getParameterBooleanValue("apply_random")
);
addDoubleParameter(
"snr_threshold",
currentChannelModel.getParameterDescription("snr_threshold"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("snr_threshold")
);
addDoubleParameter(
"bg_noise_mean",
currentChannelModel.getParameterDescription("bg_noise_mean"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("bg_noise_mean")
);
addDoubleParameter(
"bg_noise_var",
currentChannelModel.getParameterDescription("bg_noise_var"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("bg_noise_var")
);
addDoubleParameter(
"system_gain_mean",
currentChannelModel.getParameterDescription("system_gain_mean"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("system_gain_mean")
);
addDoubleParameter(
"system_gain_var",
currentChannelModel.getParameterDescription("system_gain_var"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("system_gain_var")
);
addDoubleParameter(
"wavelength",
currentChannelModel.getParameterDescription("wavelength"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("wavelength")
);
// Transmitter parameters
collapsableArea = createCollapsableArea("Transmitter parameters", allComponents);
areaTransmitter = collapsableArea;
addDoubleParameter(
"tx_power",
currentChannelModel.getParameterDescription("tx_power"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("tx_power")
);
addDoubleParameter(
"tx_antenna_gain",
currentChannelModel.getParameterDescription("tx_antenna_gain"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("tx_antenna_gain")
);
// Receiver parameters
collapsableArea = createCollapsableArea("Receiver parameters", allComponents);
areaReceiver = collapsableArea;
addDoubleParameter(
"rx_sensitivity",
currentChannelModel.getParameterDescription("rx_sensitivity"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rx_sensitivity")
);
addDoubleParameter(
"rx_antenna_gain",
currentChannelModel.getParameterDescription("rx_antenna_gain"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rx_antenna_gain")
);
// Ray Tracer parameters
collapsableArea = createCollapsableArea("Ray Tracer parameters", allComponents);
areaRayTracer = collapsableArea;
addBooleanParameter(
"rt_disallow_direct_path",
currentChannelModel.getParameterDescription("rt_disallow_direct_path"),
collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_disallow_direct_path")
);
addBooleanParameter(
"rt_ignore_non_direct",
currentChannelModel.getParameterDescription("rt_ignore_non_direct"),
collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_ignore_non_direct")
);
addBooleanParameter(
"rt_fspl_on_total_length",
currentChannelModel.getParameterDescription("rt_fspl_on_total_length"),
collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_fspl_on_total_length")
);
addIntegerParameter(
"rt_max_rays",
currentChannelModel.getParameterDescription("rt_max_rays"),
collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_rays")
);
addIntegerParameter(
"rt_max_refractions",
currentChannelModel.getParameterDescription("rt_max_refractions"),
collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_refractions")
);
addIntegerParameter(
"rt_max_reflections",
currentChannelModel.getParameterDescription("rt_max_reflections"),
collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_reflections")
);
addIntegerParameter(
"rt_max_diffractions",
currentChannelModel.getParameterDescription("rt_max_diffractions"),
collapsableArea,
currentChannelModel.getParameterIntegerValue("rt_max_diffractions")
);
/* addBooleanParameter(
"rt_use_scattering",
currentChannelModel.getParameterDescription("rt_use_scattering"),
collapsableArea,
currentChannelModel.getParameterBooleanValue("rt_use_scattering")
);
*/
addDoubleParameter(
"rt_refrac_coefficient",
currentChannelModel.getParameterDescription("rt_refrac_coefficient"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_refrac_coefficient")
);
addDoubleParameter(
"rt_reflec_coefficient",
currentChannelModel.getParameterDescription("rt_reflec_coefficient"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_reflec_coefficient")
);
addDoubleParameter(
"rt_diffr_coefficient",
currentChannelModel.getParameterDescription("rt_diffr_coefficient"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_diffr_coefficient")
);
/* addDoubleParameter(
"rt_scatt_coefficient",
currentChannelModel.getParameterDescription("rt_scatt_coefficient"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("rt_scatt_coefficient")
);
*/
// Shadowing parameters
collapsableArea = createCollapsableArea("Shadowing parameters", allComponents);
areaShadowing = collapsableArea;
addDoubleParameter(
"obstacle_attenuation",
currentChannelModel.getParameterDescription("obstacle_attenuation"),
collapsableArea,
currentChannelModel.getParameterDoubleValue("obstacle_attenuation")
);
// Add channel model observer responsible to keep all GUI components synched
currentChannelModel.addSettingsObserver(channelModelSettingsObserver);
// Set initial size etc.
pack();
setVisible(true);
// Tries to select this plugin
try {
setSelected(true);
} catch (java.beans.PropertyVetoException e) {
// Could not select
}
}
/**
* Creates a new collapsable area which may be used for holding model parameters.
* @param title Title of area
* @param contentPane Where this area should be added
* @return New empty collapsable area
*/
private JPanel createCollapsableArea(String title, Container contentPane) {
// Create panels
JPanel holdingPanel = new JPanel() {
public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
}
};
holdingPanel.setLayout(new BoxLayout(holdingPanel, BoxLayout.Y_AXIS));
final JPanel collapsableArea = new JPanel() {
public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
}
};
collapsableArea.setLayout(new BoxLayout(collapsableArea, BoxLayout.Y_AXIS));
collapsableArea.setVisible(false);
JPanel titlePanel = new JPanel(new BorderLayout()) {
public Dimension getMaximumSize() {
return new Dimension(super.getMaximumSize().width, getPreferredSize().height);
}
};
titlePanel.add(BorderLayout.WEST, new JLabel(title));
JCheckBox collapseCheckBox = new JCheckBox("show settings", false);
collapseCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (((JCheckBox) e.getSource()).isSelected())
collapsableArea.setVisible(true);
else
collapsableArea.setVisible(false);
}
});
collapsableArea.putClientProperty("my_checkbox", collapseCheckBox);
titlePanel.add(BorderLayout.EAST, collapseCheckBox);
collapsableArea.setBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY)
);
collapsableArea.setAlignmentY(Component.TOP_ALIGNMENT);
holdingPanel.add(titlePanel);
holdingPanel.add(collapsableArea);
contentPane.add(holdingPanel);
return collapsableArea;
}
/**
* Creates and adds a panel with a label and a
* text field which accepts doubles.
*
* @param id Identifier of new parameter
* @param description Description of new parameter
* @param contentPane Where to add created panel
* @param initialValue Initial value
* @return Text field in created panel
*/
private JFormattedTextField addDoubleParameter(String id, String description, Container contentPane, double initialValue) {
JPanel panel = new JPanel();
JLabel label;
JFormattedTextField textField;
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());
panel.add(textField = new JFormattedTextField(doubleFormat));
textField.setValue(new Double(initialValue));
textField.setColumns(4);
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);
}
});
allDoubleParameters.add(textField);
contentPane.add(panel);
return textField;
}
/**
* Creates and adds a panel with a label and a
* text field which accepts integers.
*
* @param id Identifier of new parameter
* @param description Description of new parameter
* @param contentPane Where to add created panel
* @param initialValue Initial value
* @return Text field in created panel
*/
private JFormattedTextField addIntegerParameter(String id, String description, Container contentPane, int initialValue) {
JPanel panel = new JPanel();
JLabel label;
JFormattedTextField textField;
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());
panel.add(textField = new JFormattedTextField(integerFormat));
textField.setValue(new Double(initialValue));
textField.setColumns(4);
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);
}
});
allIntegerParameters.add(textField);
contentPane.add(panel);
return textField;
}
/**
* Creates and adds a panel with a label and a
* boolean checkbox.
*
* @param id Identifier of new parameter
* @param description Description of new parameter
* @param contentPane Where to add created panel
* @param initialValue Initial value
* @return Checkbox in created panel
*/
private JCheckBox addBooleanParameter(String id, String description, Container contentPane, boolean initialValue) {
JPanel panel = new JPanel();
JLabel label;
JCheckBox checkBox;
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());
panel.add(checkBox = new JCheckBox());
checkBox.setSelected(initialValue);
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())
);
}
});
allBooleanParameters.add(checkBox);
contentPane.add(panel);
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.
*/
private Observer channelModelSettingsObserver = new Observer() {
public void update(Observable obs, Object obj) {
// Update all integers
for (int i=0; i < allIntegerParameters.size(); i++) {
JFormattedTextField textField = (JFormattedTextField) allIntegerParameters.get(i);
String id = (String) textField.getClientProperty("id");
textField.setValue(currentChannelModel.getParameterValue(id));
}
// Update all doubles
for (int i=0; i < allDoubleParameters.size(); i++) {
JFormattedTextField textField = (JFormattedTextField) allDoubleParameters.get(i);
String id = (String) textField.getClientProperty("id");
textField.setValue(currentChannelModel.getParameterValue(id));
}
// Update all booleans
for (int i=0; i < allBooleanParameters.size(); i++) {
JCheckBox checkBox = (JCheckBox) allBooleanParameters.get(i);
String id = (String) checkBox.getClientProperty("id");
checkBox.setSelected(currentChannelModel.getParameterBooleanValue(id));
}
repaint();
}
};
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);
}
}
/**
* Returns XML elements representing the current configuration.
*
* @see #setConfigXML(Collection)
* @return XML element collection
*/
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
Element element;
element = new Element("show_general");
element.setText(Boolean.toString(areaGeneral.isVisible()));
config.add(element);
element = new Element("show_transmitter");
element.setText(Boolean.toString(areaTransmitter.isVisible()));
config.add(element);
element = new Element("show_receiver");
element.setText(Boolean.toString(areaReceiver.isVisible()));
config.add(element);
element = new Element("show_raytracer");
element.setText(Boolean.toString(areaRayTracer.isVisible()));
config.add(element);
element = new Element("show_shadowing");
element.setText(Boolean.toString(areaShadowing.isVisible()));
config.add(element);
return config;
}
/**
* Sets the configuration depending on the given XML elements.
*
* @see #getConfigXML()
* @param configXML
* Config XML elements
* @return True if config was set successfully, false otherwise
*/
public boolean setConfigXML(Collection<Element> configXML) {
for (Element element : configXML) {
if (element.getName().equals("show_general")) {
JCheckBox checkBox = (JCheckBox) areaGeneral.getClientProperty("my_checkbox");
checkBox.setSelected(Boolean.parseBoolean(element.getText()));
checkBox.getActionListeners()[0].actionPerformed(new ActionEvent(checkBox,
ActionEvent.ACTION_PERFORMED, ""));
} else if (element.getName().equals("show_transmitter")) {
JCheckBox checkBox = (JCheckBox) areaTransmitter.getClientProperty("my_checkbox");
checkBox.setSelected(Boolean.parseBoolean(element.getText()));
checkBox.getActionListeners()[0].actionPerformed(new ActionEvent(checkBox,
ActionEvent.ACTION_PERFORMED, ""));
} else if (element.getName().equals("show_receiver")) {
JCheckBox checkBox = (JCheckBox) areaReceiver.getClientProperty("my_checkbox");
checkBox.setSelected(Boolean.parseBoolean(element.getText()));
checkBox.getActionListeners()[0].actionPerformed(new ActionEvent(checkBox,
ActionEvent.ACTION_PERFORMED, ""));
} else if (element.getName().equals("show_raytracer")) {
JCheckBox checkBox = (JCheckBox) areaRayTracer.getClientProperty("my_checkbox");
checkBox.setSelected(Boolean.parseBoolean(element.getText()));
checkBox.getActionListeners()[0].actionPerformed(new ActionEvent(checkBox,
ActionEvent.ACTION_PERFORMED, ""));
} else if (element.getName().equals("show_shadowing")) {
JCheckBox checkBox = (JCheckBox) areaShadowing.getClientProperty("my_checkbox");
checkBox.setSelected(Boolean.parseBoolean(element.getText()));
checkBox.getActionListeners()[0].actionPerformed(new ActionEvent(checkBox,
ActionEvent.ACTION_PERFORMED, ""));
}
}
return true;
}
}

View file

@ -0,0 +1,205 @@
package se.sics.arm;
import java.util.Vector;
import org.apache.log4j.Logger;
/**
* This class represents a interval. Some operations on these intervals exist,
* such as intersecting a interval with another and subtracting an interval from
* another.
*
* @author Fredrik Osterlind
*/
class Interval {
private static Logger logger = Logger.getLogger(Interval.class);
private double lowValue;
private double highValue;
/**
* Creates a new double interval.
* The given low value must be lower than the given high value.
*
* @param lowValue Low interval border (< End interval border)
* @param highValue High interval border
*/
public Interval(double lowValue, double highValue) {
this.lowValue = Math.min(lowValue, highValue);
this.highValue = highValue;
}
/**
* Set new values of interval
*
* @param newLow New low value
* @param newHigh New high value
*/
public void setInterval(double newLow, double newHigh) {
lowValue = newLow;
highValue = newHigh;
}
/**
* @return Low border value
*/
public double getLow() {
return lowValue;
}
/**
* @return High border value
*/
public double getHigh() {
return highValue;
}
/**
* @return Size of interval
*/
public double getSize() {
return highValue - lowValue;
}
/**
* Returns the intersection between this interval and the given
* interval or null if no intersection exists.
*
* @param interval Other interval
* @return Intersection interval
*/
public Interval intersectWith(Interval interval) {
// Given interval higher than this interval
if (highValue <= interval.getLow())
return null;
// Given interval lower than this interval
if (lowValue >= interval.getHigh())
return null;
// Given interval covers this interval
if (lowValue >= interval.getLow() &&
highValue <= interval.getHigh())
return this;
// Given interval inside this interval
if (lowValue <= interval.getLow() &&
highValue >= interval.getHigh())
return interval;
// Given interval overlaps lower part of this interval
if (lowValue >= interval.getLow() &&
highValue >= interval.getHigh())
return new Interval(lowValue, interval.getHigh());
// Given interval overlaps upper part of this interval
if (lowValue <= interval.getLow() &&
highValue <= interval.getHigh())
return new Interval(interval.getLow(), highValue);
logger.fatal("DoubleInterval.intersectWithInterval(), error!");
return null;
}
/**
* Checks if the given interval is a subset of this interval.
*
* @param interval Other interval
* @return True if this interval contains the given interval
*/
public boolean contains(Interval interval) {
return getLow() <= interval.getLow() && getHigh() >= interval.getHigh();
}
/**
* Returns new intervals consisting of this interval with the given interval removed.
* These can either be null (if entire interval was removed),
* one interval (if upper or lower part, or nothing was removed) or two intervals
* (if middle part of interval was removed).
*
* @param interval Other interval
* @return New intervals
*/
public Vector<Interval> subtract(Interval interval) {
Vector<Interval> returnIntervals = new Vector<Interval>();
// Given interval higher than this interval
if (highValue <= interval.getLow()) {
returnIntervals.add(this);
return returnIntervals;
}
// Given interval lower than this interval
if (lowValue >= interval.getHigh()) {
returnIntervals.add(this);
return returnIntervals;
}
// Given interval covers this interval
if (lowValue >= interval.getLow() &&
highValue <= interval.getHigh()) {
return null;
}
// Given interval inside this interval
if (lowValue <= interval.getLow() &&
highValue >= interval.getHigh()) {
returnIntervals.add(new Interval(lowValue, interval.getLow()));
returnIntervals.add(new Interval(interval.getHigh(), highValue));
return returnIntervals;
}
// Given interval overlaps lower part of this interval
if (lowValue >= interval.getLow() &&
highValue >= interval.getHigh()) {
returnIntervals.add(new Interval(interval.getHigh(), highValue));
return returnIntervals;
}
// Given interval overlaps upper part of this interval
if (lowValue <= interval.getLow() &&
highValue <= interval.getHigh()) {
returnIntervals.add(new Interval(lowValue, interval.getLow()));
return returnIntervals;
}
logger.fatal("DoubleInterval.subtractInterval(), error!");
return null;
}
/**
* Subtracts given interval from all intervals in given vector.
* This method never returns null (but empty vectors).
*
* @param initialIntervals Initial intervals
* @param interval Interval to subtract
* @return New intervals
*/
static public Vector<Interval> subtract(Vector<Interval> initialIntervals, Interval interval) {
Vector<Interval> newIntervals = new Vector<Interval>();
for (int i=0; i < initialIntervals.size(); i++) {
Vector<Interval> tempIntervals = initialIntervals.get(i).subtract(interval);
if (tempIntervals != null)
newIntervals.addAll(tempIntervals);
}
return newIntervals;
}
/**
* @return True if interval does not have a length.
*/
public boolean isEmpty() {
if (highValue <= lowValue)
return true;
return false;
}
public String toString() {
if (isEmpty())
return "Double interval: (null)";
else
return "Double interval: " +
getLow() +
" -> " +
getHigh();
}
}

View file

@ -0,0 +1,621 @@
package se.sics.arm;
import java.awt.Point;
import java.awt.geom.*;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.jdom.Element;
/**
* This class represents an area with obstacles.
* Obstacles may only be of rectangular shape.
*
* @author Fredrik Osterlind
*/
class ObstacleWorld {
private static Logger logger = Logger.getLogger(ObstacleWorld.class);
// All registered obstacles
private Vector<Rectangle2D> allObstacles = null;
// All registered obstacles, with spatial information
private int spatialResolution = 10;
private Vector<Rectangle2D>[][] allObstaclesSpatial = new Vector[spatialResolution][spatialResolution];
private boolean obstaclesOrganized = false;
// Outer bounds of all obstacles
private Rectangle2D outerBounds = null;
/**
* Creates a new obstacle world without any obstacles.
*/
public ObstacleWorld() {
// No obstacles present so far
allObstacles = new Vector<Rectangle2D>();
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++)
allObstaclesSpatial[x][y] = new Vector<Rectangle2D>();
outerBounds = new Rectangle2D.Double(0,0,0,0);
}
/**
* @return The total number of registered obstacles
*/
public int getNrObstacles() {
return allObstacles.size();
}
/**
* This method can be used to find extreme coordinates of all obstacles.
*
* @return Outer bounds of all registered obstacles
*/
public Rectangle2D getOuterBounds() {
return outerBounds;
}
/**
* Returns obstacle registered at given position.
* The coordinates of an obstacles should never
* be changed directly on an object returned by this method.
*
* @param i Obstacle position
* @return Obstacle at given position
*/
public Rectangle2D getObstacle(int i) {
return allObstacles.get(i);
}
/**
* @return All registered obstacles
*/
public Vector<Rectangle2D> getAllObstacles() {
return allObstacles;
}
/**
* Returns at least all registered obstacles that contains given point.
* Note that obstacles close to but not containing the point may also
* be returned.
*
* @param center Center point
* @return All obstacles containing or near center
*/
public Vector<Rectangle2D> getAllObstaclesNear(Point2D center) {
double boxWidth = outerBounds.getWidth() / (double) spatialResolution;
double boxHeight = outerBounds.getHeight() / (double) spatialResolution;
double areaStartX = outerBounds.getMinX();
double areaStartY = outerBounds.getMinY();
double centerX = (center.getX() - areaStartX)/boxWidth;
double centerY = (center.getY() - areaStartY)/boxHeight;
Vector<Rectangle2D> allNearObstacles = new Vector<Rectangle2D>();
Point pointToAdd = new Point((int) centerX, (int) centerY);
if (pointToAdd.x >= 0 &&
pointToAdd.x < allObstaclesSpatial.length &&
pointToAdd.y >= 0 &&
pointToAdd.y < allObstaclesSpatial[0].length)
allNearObstacles.addAll(allObstaclesSpatial[pointToAdd.x][pointToAdd.y]);
// Add borders if needed
boolean addedXBorder = false;
boolean addedYBorder = false;
if (Math.floor(centerX) == centerX) {
pointToAdd = new Point((int) centerX-1, (int) centerY);
if (pointToAdd.x >= 0 &&
pointToAdd.x < allObstaclesSpatial.length &&
pointToAdd.y >= 0 &&
pointToAdd.y < allObstaclesSpatial[0].length) {
allNearObstacles.addAll(allObstaclesSpatial[pointToAdd.x][pointToAdd.y]);
addedXBorder = true;
}
}
if (Math.floor(centerY) == centerY) {
pointToAdd = new Point((int) centerX, (int) centerY-1);
if (pointToAdd.x >= 0 &&
pointToAdd.x < allObstaclesSpatial.length &&
pointToAdd.y >= 0 &&
pointToAdd.y < allObstaclesSpatial[0].length) {
allNearObstacles.addAll(allObstaclesSpatial[pointToAdd.x][pointToAdd.y]);
addedYBorder = true;
}
}
if (addedXBorder && addedYBorder) {
pointToAdd = new Point((int) centerX-1, (int) centerY-1);
allNearObstacles.addAll(allObstaclesSpatial[pointToAdd.x][pointToAdd.y]);
}
return allNearObstacles;
}
/**
* Returns at least all registered obstacles inside the given angle
* interval when at the given center point. Note that obstacles partly or
* completely outside the interval may also be returned.
* All obstacles are preferably returned in order of distance from given
* center point, although this is not guaranteed.
*
* @param center Center point
* @param angleInterval Angle interval
* @return All obstacles in given angle interval
*/
public Vector<Rectangle2D> getAllObstaclesInAngleInterval(Point2D center, AngleInterval angleInterval) {
Vector<Rectangle2D> obstaclesToReturn = new Vector<Rectangle2D>();
if (!obstaclesOrganized) {
reorganizeSpatialObstacles();
}
double boxWidth = outerBounds.getWidth() / (double) spatialResolution;
double boxHeight = outerBounds.getHeight() / (double) spatialResolution;
double areaStartX = outerBounds.getMinX();
double areaStartY = outerBounds.getMinY();
// Calculate which boxes to check (and in which order)
Point centerInArray = new Point(
(int) ((center.getX() - areaStartX)/boxWidth),
(int) ((center.getY() - areaStartY)/boxHeight)
);
Vector<Point> pointsToCheck = new Vector<Point>();
int currentDistance = 0;
while (currentDistance < 2*spatialResolution) {
if (currentDistance > 0) {
int currentX = centerInArray.x - currentDistance;
int currentY = centerInArray.y - currentDistance;
// Step right
while (currentX < centerInArray.x + currentDistance) {
if (currentX >= 0 &&
currentX < allObstaclesSpatial.length &&
currentY >= 0 &&
currentY < allObstaclesSpatial[0].length)
pointsToCheck.add(new Point(currentX, currentY));
currentX++;
}
// Step right
while (currentY < centerInArray.y + currentDistance) {
if (currentX >= 0 &&
currentX < allObstaclesSpatial.length &&
currentY >= 0 &&
currentY < allObstaclesSpatial[0].length)
pointsToCheck.add(new Point(currentX, currentY));
currentY++;
}
// Step left
while (currentX > centerInArray.x - currentDistance) {
if (currentX >= 0 &&
currentX < allObstaclesSpatial.length &&
currentY >= 0 &&
currentY < allObstaclesSpatial[0].length)
pointsToCheck.add(new Point(currentX, currentY));
currentX--;
}
// Step up
while (currentY > centerInArray.y - currentDistance) {
if (currentX >= 0 &&
currentX < allObstaclesSpatial.length &&
currentY >= 0 &&
currentY < allObstaclesSpatial[0].length)
pointsToCheck.add(new Point(currentX, currentY));
currentY--;
}
} else {
if (centerInArray.x >= 0 &&
centerInArray.x < allObstaclesSpatial.length &&
centerInArray.y >= 0 &&
centerInArray.y < allObstaclesSpatial[0].length) {
pointsToCheck.add(new Point(centerInArray.x, centerInArray.y));
}
}
currentDistance++;
}
for (int pointNr=0; pointNr < pointsToCheck.size(); pointNr++) {
// Check which obstacles should be in this box
boolean hit = false;
int x = pointsToCheck.get(pointNr).x;
int y = pointsToCheck.get(pointNr).y;
// Test if we are inside test box
if (!hit) {
if (new Rectangle2D.Double(
areaStartX + x*boxWidth,
areaStartY + y*boxHeight,
boxWidth,
boxHeight).contains(center)) {
hit = true;
for (int i=0; i < allObstaclesSpatial[x][y].size(); i++) {
if (!obstaclesToReturn.contains(allObstaclesSpatial[x][y].get(i)))
obstaclesToReturn.add(allObstaclesSpatial[x][y].get(i));
}
}
}
// Test first diagonal
if (!hit) {
AngleInterval testInterval = AngleInterval.getAngleIntervalOfLine(
center,
new Line2D.Double(
areaStartX + x*boxWidth,
areaStartY + y*boxHeight,
areaStartX + (x+1)*boxWidth,
areaStartY + (y+1)*boxHeight)
);
if (testInterval.intersects(angleInterval)) {
hit = true;
for (int i=0; i < allObstaclesSpatial[x][y].size(); i++) {
if (!obstaclesToReturn.contains(allObstaclesSpatial[x][y].get(i)))
obstaclesToReturn.add(allObstaclesSpatial[x][y].get(i));
}
}
}
// Test second diagonal
if (!hit) {
AngleInterval testInterval = AngleInterval.getAngleIntervalOfLine(
center,
new Line2D.Double(
areaStartX + x*boxWidth,
areaStartY + (y+1)*boxHeight,
areaStartX + (x+1)*boxWidth,
areaStartY + y*boxHeight)
);
if (testInterval.intersects(angleInterval)) {
hit = true;
for (int i=0; i < allObstaclesSpatial[x][y].size(); i++) {
if (!obstaclesToReturn.contains(allObstaclesSpatial[x][y].get(i)))
obstaclesToReturn.add(allObstaclesSpatial[x][y].get(i));
}
}
}
}
return obstaclesToReturn;
}
/**
* Removes all registered obstacles.
*/
public void removeAll() {
allObstacles.removeAllElements();
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++)
allObstaclesSpatial[x][y].removeAllElements();
outerBounds = new Rectangle2D.Double(0,0,0,0);
}
/**
* Returns true of given point is on a corner of
* any of the structures build from the obstacles.
* Internally this method checks how many of four point
* close to and located around given point (diagonally) are
* inside any obstacle.
* This method returns true if exactly one point is inside an obstacle.
*
* @param point Point to check
* @return True of point is on a corner, false otherwise
*/
public boolean pointIsNearCorner(Point2D point) {
double boxWidth = outerBounds.getWidth() / (double) spatialResolution;
double boxHeight = outerBounds.getHeight() / (double) spatialResolution;
double areaStartX = outerBounds.getMinX();
double areaStartY = outerBounds.getMinY();
// Which obstacles should be checked
Point centerInArray = new Point(
(int) ((point.getX() - areaStartX)/boxWidth),
(int) ((point.getY() - areaStartY)/boxHeight)
);
Vector<Rectangle2D> allObstaclesToCheck = null;
if (centerInArray.x < 0)
centerInArray.x = 0;
if (centerInArray.x >= spatialResolution)
centerInArray.x = spatialResolution-1;
if (centerInArray.y < 0)
centerInArray.y = 0;
if (centerInArray.y >= spatialResolution)
centerInArray.y = spatialResolution-1;
allObstaclesToCheck = allObstaclesSpatial[centerInArray.x][centerInArray.y];
if (allObstaclesToCheck.size() == 0) {
return false;
}
// Create the four point to check
double deltaDistance = 0.01; // 1 cm TODO Change this?
Point2D point1 = new Point2D.Double(point.getX() - deltaDistance, point.getY() - deltaDistance);
Point2D point2 = new Point2D.Double(point.getX() - deltaDistance, point.getY() + deltaDistance);
Point2D point3 = new Point2D.Double(point.getX() + deltaDistance, point.getY() - deltaDistance);
Point2D point4 = new Point2D.Double(point.getX() + deltaDistance, point.getY() + deltaDistance);
int containedPoints = 0;
Enumeration<Rectangle2D> allObstaclesToCheckEnum = allObstaclesToCheck.elements();
while (allObstaclesToCheckEnum.hasMoreElements()) {
Rectangle2D obstacleToCheck = allObstaclesToCheckEnum.nextElement();
if (obstacleToCheck.contains(point1))
containedPoints++;
if (obstacleToCheck.contains(point2))
containedPoints++;
if (obstacleToCheck.contains(point3))
containedPoints++;
if (obstacleToCheck.contains(point4))
containedPoints++;
// Abort if already to many contained points
if (containedPoints > 1) {
return false;
}
}
return (containedPoints == 1);
}
/**
* Checks if specified obstacle can be merged with any existing obstacle
* in order to reduce the total number of obstacles. And in that case a merge
* is performed and this method returns the new obstacle object.
* The checking is performed by looping through all existing obstacles and
* for each one comparing the union area of it and the given obstacle to the
* area sum of the two. And since obstacles are not allowed to overlap, if the
* union area is equal to the area sum, they can be merged.
* If a merge is performed, another may be made possible so this method
* should be looped until returning null.
*
* This method does not notify observers of changes made!
*
* @return New object of a merge was performed, null otherwise
*/
private Rectangle2D mergeObstacle(Rectangle2D mergeObstacle) {
double mergeObstacleArea = mergeObstacle.getWidth() * mergeObstacle.getHeight();
double mergeObstacleTolerance = mergeObstacleArea * 0.01; // 1%
// Loop through all existing obstacles (but ignore itself)
for (int i=0; i < getNrObstacles(); i++) {
Rectangle2D existingObstacle = getObstacle(i);
if (!existingObstacle.equals(mergeObstacle)) {
double existingObstacleArea = existingObstacle.getWidth() * existingObstacle.getHeight();
Rectangle2D unionObstacle = existingObstacle.createUnion(mergeObstacle);
double unionArea = unionObstacle.getWidth() * unionObstacle.getHeight();
// Fault-tolerance
double faultTolerance = Math.min(mergeObstacleTolerance, existingObstacleArea*0.01);
// Compare areas
if (unionArea - faultTolerance <= existingObstacleArea + mergeObstacleArea) {
// Remove both old obstacles, add union
removeObstacle(mergeObstacle);
removeObstacle(existingObstacle);
addObstacle(unionObstacle, false);
obstaclesOrganized = false;
return unionObstacle;
}
}
}
return null;
}
/**
* Register new obstacle with given attributes.
* This method will try to merge this obstacle with other already existing obstacles.
*
* @param startX Start X coordinate
* @param startY Start Y coordinate
* @param width Width
* @param height Height
*/
public void addObstacle(double startX, double startY, double width, double height) {
addObstacle(startX, startY, width, height, true);
}
/**
* Register new obstacle with given attributes.
* This method will, depending on given argument, try to merge
* this obstacle with other already existing obstacles.
*
* @param startX Start X coordinate
* @param startY Start Y coordinate
* @param width Width
* @param height Height
* @param merge Should this obstacle, if possible, be merged with existing obstacles
*/
public void addObstacle(double startX, double startY, double width, double height, boolean merge) {
Rectangle2D newRect = new Rectangle2D.Double(startX, startY, width, height);
addObstacle(newRect, merge);
}
/**
* Registers a given obstacle.
* This method will try to merge this obstacle with other already existing obstacles.
*
* @param obstacle New obstacle
*/
public void addObstacle(Rectangle2D obstacle) {
addObstacle(obstacle, true);
}
/**
* Registers a given obstacle.
* This method will, depending on the given argument, try to
* merge this obstacle with other already existing obstacles.
*
* @param obstacle New obstacle
*/
public void addObstacle(Rectangle2D obstacle, boolean merge) {
// TODO Should we keep the rounding?
obstacle.setRect(
Math.round(obstacle.getMinX()*1000.0) / 1000.0,
Math.round(obstacle.getMinY()*1000.0) / 1000.0,
Math.round(obstacle.getWidth()*1000.0) / 1000.0,
Math.round(obstacle.getHeight()*1000.0) / 1000.0
);
allObstacles.add(obstacle);
outerBounds = outerBounds.createUnion(obstacle);
if (merge) {
// Check if obstacle can be merged with another obstacle
Rectangle2D mergedObstacle = mergeObstacle(obstacle);
// Keep merging...
while (mergedObstacle != null)
mergedObstacle = mergeObstacle(mergedObstacle);
}
obstaclesOrganized = false;
}
/**
* Remove the given obstacle, if it exists.
*
* @param obstacle Obstacle to remove
*/
public void removeObstacle(Rectangle2D obstacle) {
allObstacles.remove(obstacle);
recreateOuterBounds();
obstaclesOrganized = false;
}
/**
* This method recreates the outer bounds of
* this obstacle area by checking all registered
* obstacles.
* This method should never have to be called directly
* by a user.
*/
public void recreateOuterBounds() {
outerBounds = new Rectangle2D.Double(0,0,0,0);
for (int i=0; i < allObstacles.size(); i++) {
outerBounds = outerBounds.createUnion(allObstacles.get(i));
}
obstaclesOrganized = false;
}
/**
* Reorganizes all registered obstacles in order to speed up
* searches for obstacles in spatial areas.
* This method is run automatically
*/
public void reorganizeSpatialObstacles() {
// Remove all spatial obstacles
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++)
allObstaclesSpatial[x][y].removeAllElements();
double boxWidth = outerBounds.getWidth() / (double) spatialResolution;
double boxHeight = outerBounds.getHeight() / (double) spatialResolution;
double currentBoxMinX = outerBounds.getMinX();
double currentBoxMinY = outerBounds.getMinY();
// For each box, add obstacles that belong there
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++) {
// Check which obstacles should be in this box
Rectangle2D boxToCheck = new Rectangle2D.Double(currentBoxMinX + x*boxWidth, currentBoxMinY + y*boxHeight, boxWidth, boxHeight);
for (int i=0; i < allObstacles.size(); i++) {
if (allObstacles.get(i).intersects(boxToCheck)) {
allObstaclesSpatial[x][y].add(allObstacles.get(i));
}
}
}
obstaclesOrganized = true;
//printObstacleGridToConsole();
}
/**
* Prints a description of all obstacles to the console
*/
public void printObstacleGridToConsole() {
logger.info("<<<<<<< printObstacleGridToConsole >>>>>>>");
logger.info(". Number of obstacles:\t" + getNrObstacles());
logger.info(". Outer boundary min:\t" + getOuterBounds().getMinX() + ", " + getOuterBounds().getMinY());
logger.info(". Outer boundary max:\t" + getOuterBounds().getMaxX() + ", " + getOuterBounds().getMaxY());
Vector<Rectangle2D> uniqueSpatialObstacles = new Vector<Rectangle2D>();
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++)
for (int i=0; i < allObstaclesSpatial[x][y].size(); i++)
if (!uniqueSpatialObstacles.contains(allObstaclesSpatial[x][y].get(i)))
uniqueSpatialObstacles.add(allObstaclesSpatial[x][y].get(i));
logger.info(". Unique spatial obstacles:\t" + uniqueSpatialObstacles.size());
int allSpatialObstacles = 0;
for (int x=0; x < spatialResolution; x++)
for (int y=0; y < spatialResolution; y++)
for (int i=0; i < allObstaclesSpatial[x][y].size(); i++)
allSpatialObstacles++;
logger.debug(". All spatial obstacles:\t" + allSpatialObstacles);
logger.info(". Spatial map counts:");
for (int y=0; y < spatialResolution; y++) {
for (int x=0; x < spatialResolution; x++) {
System.out.print(allObstaclesSpatial[x][y].size() + " ");
}
System.out.println("");
}
}
/**
* Returns XML elements representing the current obstacles.
*
* @see #setConfigXML(Collection)
* @return XML elements representing the obstacles
*/
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
Element element;
for (Rectangle2D rect: allObstacles) {
element = new Element("obst");
element.setText(rect.getMinX() + ";" + rect.getMinY() + ";" + rect.getWidth() + ";" + rect.getHeight());
config.add(element);
}
return config;
}
/**
* Sets the current obstacles depending on the given XML elements.
*
* @see #getConfigXML()
* @param configXML
* Config XML elements
* @return True if config was set successfully, false otherwise
*/
public boolean setConfigXML(Collection<Element> configXML) {
for (Element element : configXML) {
if (element.getName().equals("obst")) {
String rectValues[] = element.getText().split(";");
Rectangle2D newObst = new Rectangle2D.Double(
Double.parseDouble(rectValues[0]),
Double.parseDouble(rectValues[1]),
Double.parseDouble(rectValues[2]),
Double.parseDouble(rectValues[3]));
this.addObstacle(newObst, false);
}
}
return true;
}
}

View file

@ -0,0 +1,89 @@
package se.sics.arm;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
/**
* @author Fredrik Osterlind
*/
class RayData {
enum RayType { ORIGIN, REFRACTION, REFLECTION, DIFFRACTION, DESTINATION }
RayType type;
private Point2D sourcePoint = null;
private Line2D line = null;
private int limitOverall = 0;
private int limitRefracted = 0;
private int limitReflected = 0;
private int limitDiffracted = 0;
/**
* Creates a new ray data instance.
* A ray can be of the following types:
*
* ORIGIN - starting at source point, line should be null
* REFRACTED - ray was refracted at an obstacle,
* source is intersection point with obstacle, line should be null
* REFLECTED - an interval of rays is reflected at given line,
* the source point should be a "psuedo-source" is located behind it
* (as if one was looking from given source point through given line)
* DIFFRACTED - a ray is diffracted at given source point,
* line should be null
* DESTINATION
* @param type Type of ray (one of the above)
* @param sourcePoint See above
* @param line See above (may be null)
* @param limitOverall Maximum numbers of sub rays this ray may produce
* @param limitRefracted Maximum numbers of refracted sub rays this ray may produce
* @param limitReflected Maximum numbers of reflected sub rays this ray may produce
* @param limitDiffracted Maximum numbers of diffracted sub rays this ray may produce
*/
public RayData(
RayType type,
Point2D sourcePoint,
Line2D line,
int limitOverall,
int limitRefracted,
int limitReflected,
int limitDiffracted
) {
this.type = type;
this.sourcePoint = sourcePoint;
this.line = line;
this.limitOverall = limitOverall;
this.limitRefracted = limitRefracted;
this.limitReflected = limitReflected;
this.limitDiffracted = limitDiffracted;
}
public RayType getType() {
return type;
}
public Point2D getSourcePoint() {
return sourcePoint;
}
public Line2D getLine() {
return line;
}
public int getSubRaysLimit() {
return Math.min(limitOverall, limitRefracted + limitReflected + limitDiffracted);
}
public int getRefractedSubRaysLimit() {
return Math.min(limitOverall, limitRefracted);
}
public int getReflectedSubRaysLimit() {
return Math.min(limitOverall, limitReflected);
}
public int getDiffractedSubRaysLimit() {
return Math.min(limitOverall, limitDiffracted);
}
}

View file

@ -0,0 +1,71 @@
package se.sics.arm;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Vector;
/**
* @author Fredrik Osterlind
*/
public class RayPath {
private Vector<Point2D> points = new Vector<Point2D>();
private Vector<RayData.RayType> types = new Vector<RayData.RayType>();
public void addPoint(Point2D point, RayData.RayType type) {
points.insertElementAt(point, 0);
types.insertElementAt(type, 0);
}
public int getSubPathCount() {
return points.size() - 1;
}
public Line2D getSubPath(int pos) {
return new Line2D.Double(points.get(pos), points.get(pos + 1));
}
public Point2D getPoint(int i) {
return points.get(i);
}
public RayData.RayType getType(int i) {
return types.get(i);
}
public String toString() {
if (points.size() != types.size())
return "Malformed ray path (differing sizes)";
if (points.size() == 0)
return "Empty ray path";
if (types.firstElement() != RayData.RayType.ORIGIN && types.lastElement() != RayData.RayType.ORIGIN)
return "Malformed ray path (not closed)";
if (types.firstElement() != RayData.RayType.DESTINATION && types.lastElement() != RayData.RayType.DESTINATION)
return "Malformed ray path (not closed)";
if (types.firstElement() == types.lastElement())
return "Malformed ray path (last == first element)";
String retVal = "";
for (int i=0; i < types.size(); i++) {
RayData.RayType currentType = types.get(i);
if (currentType == RayData.RayType.DESTINATION)
retVal = retVal + " DEST ";
else if (currentType == RayData.RayType.DIFFRACTION)
retVal = retVal + " DIFF ";
else if (currentType == RayData.RayType.ORIGIN)
retVal = retVal + " ORIG ";
else if (currentType == RayData.RayType.REFLECTION)
retVal = retVal + " REFL ";
else if (currentType == RayData.RayType.REFRACTION)
retVal = retVal + " REFR ";
else
retVal = retVal + " ???? ";
}
return retVal;
}
}

View file

@ -0,0 +1,216 @@
package statistics;
//Gaussian CDF Taylor approximation
//Code borrowed from http://www1.fpl.fs.fed.us/distributions.html 19/9 2006
/**
*
*This class contains routines to calculate the
*normal cumulative distribution function (CDF) and
*its inverse.
*
*@version .5 --- June 7, 1996
*@version .6 --- January 10, 2001 (normcdf added)
*
*/
public class CDF_Normal extends Object {
/**
*
*This method calculates the normal cdf inverse function.
*<p>
*Let PHI(x) be the normal cdf. Suppose that Q calculates
*1.0 - PHI(x), and that QINV calculates QINV(p) for p in (0.0,.5].
*Then for p .le. .5, x = PHIINV(p) = -QINV(p).
*For p .gt. .5, x = PHIINV(p) = QINV(1.0 - p).
*The formula for approximating QINV is taken from Abramowitz and Stegun,
*Handbook of Mathematical Functions, Dover, 9th printing,
*formula 26.2.3, page 933. The error in x is claimed to
*be less than 4.5e-4 in absolute value.
*
*@param p p must lie between 0 and 1. xnormi returns
* the normal cdf inverse evaluated at p.
*
*@author Steve Verrill
*@version .5 --- June 7, 1996
*
*/
// FIX: Eventually I should build in a check that p lies in (0,1)
public static double xnormi(double p) {
double arg,t,t2,t3,xnum,xden,qinvp,x,pc;
final double c[] = {2.515517,
.802853,
.010328};
final double d[] = {1.432788,
.189269,
.001308};
if (p <= .5) {
arg = -2.0*Math.log(p);
t = Math.sqrt(arg);
t2 = t*t;
t3 = t2*t;
xnum = c[0] + c[1]*t + c[2]*t2;
xden = 1.0 + d[0]*t + d[1]*t2 + d[2]*t3;
qinvp = t - xnum/xden;
x = -qinvp;
return x;
}
else {
pc = 1.0 - p;
arg = -2.0*Math.log(pc);
t = Math.sqrt(arg);
t2 = t*t;
t3 = t2*t;
xnum = c[0] + c[1]*t + c[2]*t2;
xden = 1.0 + d[0]*t + d[1]*t2 + d[2]*t3;
x = t - xnum/xden;
return x;
}
}
/**
*
*This method calculates the normal cumulative distribution function.
*<p>
*It is based upon algorithm 5666 for the error function, from:<p>
*<pre>
* Hart, J.F. et al, 'Computer Approximations', Wiley 1968
*</pre>
*<p>
*The FORTRAN programmer was Alan Miller. The documentation
*in the FORTRAN code claims that the function is "accurate
*to 1.e-15."<p>
*Steve Verrill
*translated the FORTRAN code (the March 30, 1986 version)
*into Java. This translation was performed on January 10, 2001.
*
*@param z The method returns the value of the normal
* cumulative distribution function at z.
*
*@version .5 --- January 10, 2001
*
*/
/*
Here is a copy of the documentation in the FORTRAN code:
SUBROUTINE NORMP(Z, P, Q, PDF)
C
C Normal distribution probabilities accurate to 1.e-15.
C Z = no. of standard deviations from the mean.
C P, Q = probabilities to the left & right of Z. P + Q = 1.
C PDF = the probability density.
C
C Based upon algorithm 5666 for the error function, from:
C Hart, J.F. et al, 'Computer Approximations', Wiley 1968
C
C Programmer: Alan Miller
C
C Latest revision - 30 March 1986
C
*/
public static double normp(double z) {
double zabs;
double p;
double expntl,pdf;
final double p0 = 220.2068679123761;
final double p1 = 221.2135961699311;
final double p2 = 112.0792914978709;
final double p3 = 33.91286607838300;
final double p4 = 6.373962203531650;
final double p5 = .7003830644436881;
final double p6 = .3526249659989109E-01;
final double q0 = 440.4137358247522;
final double q1 = 793.8265125199484;
final double q2 = 637.3336333788311;
final double q3 = 296.5642487796737;
final double q4 = 86.78073220294608;
final double q5 = 16.06417757920695;
final double q6 = 1.755667163182642;
final double q7 = .8838834764831844E-1;
final double cutoff = 7.071;
final double root2pi = 2.506628274631001;
zabs = Math.abs(z);
// |z| > 37
if (z > 37.0) {
p = 1.0;
return p;
}
if (z < -37.0) {
p = 0.0;
return p;
}
// |z| <= 37.
expntl = Math.exp(-.5*zabs*zabs);
pdf = expntl/root2pi;
// |z| < cutoff = 10/sqrt(2).
if (zabs < cutoff) {
p = expntl*((((((p6*zabs + p5)*zabs + p4)*zabs + p3)*zabs +
p2)*zabs + p1)*zabs + p0)/(((((((q7*zabs + q6)*zabs +
q5)*zabs + q4)*zabs + q3)*zabs + q2)*zabs + q1)*zabs +
q0);
} else {
p = pdf/(zabs + 1.0/(zabs + 2.0/(zabs + 3.0/(zabs + 4.0/
(zabs + 0.65)))));
}
if (z < 0.0) {
return p;
} else {
p = 1.0 - p;
return p;
}
}
}

View file

@ -0,0 +1,63 @@
package statistics;
// Gaussian CDF Taylor approximation
// Code borrowed from http://www.cs.princeton.edu/introcs/21function/Gaussian.java.html 19/9 2006
/*************************************************************************
* Compilation: javac Gaussian.java
* Execution: java Gaussian x mu sigma
*
* Function to compute the Gaussian pdf (probability density function)
* and the Gaussian cdf (cumulative density function)
*
* % java Gaussian 820 1019 209
* 0.17050966869132111
*
* % java Gaussian 1500 1019 209
* 0.9893164837383883
*
* % java Gaussian 1500 1025 231
* 0.9801220907365489
*
*************************************************************************/
public class Gaussian {
// return phi(x) = standard Gaussian pdf
public static double phi(double x) {
return Math.exp(-x*x / 2) / Math.sqrt(2 * Math.PI);
}
// return phi(x) = Gaussian pdf with mean mu and stddev sigma
public static double phi(double x, double mu, double sigma) {
return phi((x - mu) / sigma) / sigma;
}
// return Phi(z) = standard Gaussian cdf using Taylor approximation
public static double Phi(double z) {
if (z < -8.0) return 0.0;
if (z > 8.0) return 1.0;
double sum = 0.0, term = z;
for (int i = 3; sum + term != sum; i += 2) {
sum = sum + term;
term = term * z * z / i;
}
return 0.5 + sum * phi(z);
}
// return Phi(z, mu, sigma) = Gaussian cdf with mean mu and stddev sigma
public static double Phi(double z, double mu, double sigma) {
return Phi((z - mu) / sigma);
}
public static void main(String[] args) {
double z = Double.parseDouble(args[0]);
double mu = Double.parseDouble(args[1]);
double sigma = Double.parseDouble(args[2]);
System.out.println(Phi(z, mu, sigma));
}
}

View file

@ -0,0 +1,50 @@
package statistics;
public class GaussianWrapper {
/**
* Returns standard Gaussian cdf approximation based on algortihm for error function.
*
* @param value Value
* @return Probability
*/
public static double cdfErrorAlgo(double value) {
return CDF_Normal.normp(value);
}
/**
* Returns Gaussian cdf approximation based on algorithm for error function.
*
* @param value Value
* @param mean Mean value
* @param stdDev Standard deviance
* @return Probability
*/
public static double cdfErrorAlgo(double value, double mean, double stdDev) {
return CDF_Normal.normp((value - mean) / stdDev);
}
/**
* Returns standard Gaussian cdf using Taylor approximation.
*
* @param value Value
* @return Probability
*/
public static double cdfTaylor(double value) {
return Gaussian.Phi(value);
}
/**
* Returns Gaussian cdf using Taylor approximation .
*
* @param value Value
* @param mean Mean value
* @param stdDev Standard deviance
* @return Probability
*/
public static double cdfTaylor(double value, double mean, double stdDev) {
return Gaussian.Phi(value, mean, stdDev);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -20,3 +20,4 @@ COMPILER_ARGS =
CONTIKI_STANDARD_PROCESSES = sensors_process;etimer_process;tcpip_process;uip_fw_process;cfs_cooja_process
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
DEFAULT_USERPLATFORMS = ../apps/arm

View file

@ -20,3 +20,4 @@ COMPILER_ARGS = -mno-cygwin -I'C:/Program Files/Java/jdk1.5.0_06/include' -I'C:/
CONTIKI_STANDARD_PROCESSES = sensors_process;etimer_process;tcpip_process;uip_fw_process;cfs_cooja_process
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
DEFAULT_USERPLATFORMS = ../apps/arm