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:
parent
4e166903a4
commit
a1759d5bed
39
tools/cooja/apps/arm/build.xml
Normal file
39
tools/cooja/apps/arm/build.xml
Normal 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>
|
2
tools/cooja/apps/arm/cooja.config
Normal file
2
tools/cooja/apps/arm/cooja.config
Normal 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
|
BIN
tools/cooja/apps/arm/images/antenna.png
Normal file
BIN
tools/cooja/apps/arm/images/antenna.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
658
tools/cooja/apps/arm/java/se/sics/arm/AdvancedRadioMedium.java
Normal file
658
tools/cooja/apps/arm/java/se/sics/arm/AdvancedRadioMedium.java
Normal 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
385
tools/cooja/apps/arm/java/se/sics/arm/AngleInterval.java
Normal file
385
tools/cooja/apps/arm/java/se/sics/arm/AngleInterval.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
2177
tools/cooja/apps/arm/java/se/sics/arm/AreaViewer.java
Normal file
2177
tools/cooja/apps/arm/java/se/sics/arm/AreaViewer.java
Normal file
File diff suppressed because it is too large
Load diff
1693
tools/cooja/apps/arm/java/se/sics/arm/ChannelModel.java
Normal file
1693
tools/cooja/apps/arm/java/se/sics/arm/ChannelModel.java
Normal file
File diff suppressed because it is too large
Load diff
583
tools/cooja/apps/arm/java/se/sics/arm/FormulaViewer.java
Normal file
583
tools/cooja/apps/arm/java/se/sics/arm/FormulaViewer.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
205
tools/cooja/apps/arm/java/se/sics/arm/Interval.java
Normal file
205
tools/cooja/apps/arm/java/se/sics/arm/Interval.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
621
tools/cooja/apps/arm/java/se/sics/arm/ObstacleWorld.java
Normal file
621
tools/cooja/apps/arm/java/se/sics/arm/ObstacleWorld.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
89
tools/cooja/apps/arm/java/se/sics/arm/RayData.java
Normal file
89
tools/cooja/apps/arm/java/se/sics/arm/RayData.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
tools/cooja/apps/arm/java/se/sics/arm/RayPath.java
Normal file
71
tools/cooja/apps/arm/java/se/sics/arm/RayPath.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
216
tools/cooja/apps/arm/java/statistics/CDF_Normal.java
Normal file
216
tools/cooja/apps/arm/java/statistics/CDF_Normal.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
tools/cooja/apps/arm/java/statistics/Gaussian.java
Normal file
63
tools/cooja/apps/arm/java/statistics/Gaussian.java
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
tools/cooja/apps/arm/java/statistics/GaussianWrapper.java
Normal file
50
tools/cooja/apps/arm/java/statistics/GaussianWrapper.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
BIN
tools/cooja/apps/arm/lib/arm.jar
Normal file
BIN
tools/cooja/apps/arm/lib/arm.jar
Normal file
Binary file not shown.
BIN
tools/cooja/apps/arm/lib/jcommon-1.0.0.jar
Normal file
BIN
tools/cooja/apps/arm/lib/jcommon-1.0.0.jar
Normal file
Binary file not shown.
BIN
tools/cooja/apps/arm/lib/jfreechart-1.0.1.jar
Normal file
BIN
tools/cooja/apps/arm/lib/jfreechart-1.0.1.jar
Normal file
Binary file not shown.
|
@ -20,3 +20,4 @@ COMPILER_ARGS =
|
||||||
CONTIKI_STANDARD_PROCESSES = sensors_process;etimer_process;tcpip_process;uip_fw_process;cfs_cooja_process
|
CONTIKI_STANDARD_PROCESSES = sensors_process;etimer_process;tcpip_process;uip_fw_process;cfs_cooja_process
|
||||||
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
|
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
|
||||||
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
|
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
|
||||||
|
DEFAULT_USERPLATFORMS = ../apps/arm
|
||||||
|
|
|
@ -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_STANDARD_PROCESSES = sensors_process;etimer_process;tcpip_process;uip_fw_process;cfs_cooja_process
|
||||||
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
|
CONTIKI_MAIN_TEMPLATE_FILENAME = code_main_template
|
||||||
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
|
MANTIS_MAIN_TEMPLATE_FILENAME = mantis_template.c
|
||||||
|
DEFAULT_USERPLATFORMS = ../apps/arm
|
||||||
|
|
Loading…
Reference in a new issue