import java.util.ArrayList;
import java.util.List;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-import net.sf.openrocket.aerodynamics.AtmosphericModel;
-import net.sf.openrocket.aerodynamics.ExtendedISAModel;
+import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
+import net.sf.openrocket.masscalc.MassCalculator;
+import net.sf.openrocket.models.atmosphere.AtmosphericModel;
+import net.sf.openrocket.models.gravity.GravityModel;
+import net.sf.openrocket.models.wind.WindModel;
import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.util.ChangeSource;
-import net.sf.openrocket.util.MathUtil;
-
-
-public class SimulationConditions implements ChangeSource, Cloneable {
-
- public static final double MAX_LAUNCH_ROD_ANGLE = Math.PI/3;
-
- /**
- * The ISA standard atmosphere.
- */
- private static final AtmosphericModel ISA_ATMOSPHERIC_MODEL = new ExtendedISAModel();
-
-
- private final Rocket rocket;
+import net.sf.openrocket.simulation.listeners.SimulationListener;
+import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.GeodeticComputationStrategy;
+import net.sf.openrocket.util.Monitorable;
+import net.sf.openrocket.util.WorldCoordinate;
+
+/**
+ * A holder class for the simulation conditions. These include conditions that do not change
+ * during the flight of a rocket, for example launch rod parameters, atmospheric models,
+ * aerodynamic calculators etc.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SimulationConditions implements Monitorable, Cloneable {
+
+ private Rocket rocket;
private String motorID = null;
-
- /*
- * NOTE: When adding/modifying parameters, they must also be added to the
- * equals and copyFrom methods!!
- */
- // TODO: HIGH: Fetch default values from Prefs!
-
private double launchRodLength = 1;
- /** Launch rod angle > 0, radians from vertical */
+ /** Launch rod angle >= 0, radians from vertical */
private double launchRodAngle = 0;
/** Launch rod direction, 0 = upwind, PI = downwind. */
private double launchRodDirection = 0;
+ // TODO: Depreciate these and use worldCoordinate only.
+ //private double launchAltitude = 0;
+ //private double launchLatitude = 45;
+ //private double launchLongitude = 0;
+ private WorldCoordinate launchSite = new WorldCoordinate(0, 0, 0);
+ private GeodeticComputationStrategy geodeticComputation = GeodeticComputationStrategy.SPHERICAL;
+
- private double windAverage = 2.0;
- private double windTurbulence = 0.1;
+ private WindModel windModel;
+ private AtmosphericModel atmosphericModel;
+ private GravityModel gravityModel;
- private double launchAltitude = 0;
- private double launchLatitude = 45;
+ private AerodynamicCalculator aerodynamicCalculator;
+ private MassCalculator massCalculator;
- private boolean useISA = true;
- private double launchTemperature = ExtendedISAModel.STANDARD_TEMPERATURE;
- private double launchPressure = ExtendedISAModel.STANDARD_PRESSURE;
- private AtmosphericModel atmosphericModel = null;
+
+ private double timeStep = RK4SimulationStepper.RECOMMENDED_TIME_STEP;
+ private double maximumAngleStep = RK4SimulationStepper.RECOMMENDED_ANGLE_STEP;
+ /* Whether to calculate additional data or only primary simulation figures */
+ private boolean calculateExtras = true;
- private double timeStep = RK4Simulator.RECOMMENDED_TIME_STEP;
- private double maximumAngle = RK4Simulator.RECOMMENDED_ANGLE_STEP;
+
+ private List<SimulationListener> simulationListeners = new ArrayList<SimulationListener>();
- private boolean calculateExtras = true;
+
+ private int randomSeed = 0;
+
+ private int modID = 0;
+ private int modIDadd = 0;
+
+
- private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
+ public AerodynamicCalculator getAerodynamicCalculator() {
+ return aerodynamicCalculator;
+ }
+ public void setAerodynamicCalculator(AerodynamicCalculator aerodynamicCalculator) {
+ if (this.aerodynamicCalculator != null)
+ this.modIDadd += this.aerodynamicCalculator.getModID();
+ this.modID++;
+ this.aerodynamicCalculator = aerodynamicCalculator;
+ }
- public SimulationConditions(Rocket rocket) {
- this.rocket = rocket;
+ public MassCalculator getMassCalculator() {
+ return massCalculator;
}
+ public void setMassCalculator(MassCalculator massCalculator) {
+ if (this.massCalculator != null)
+ this.modIDadd += this.massCalculator.getModID();
+ this.modID++;
+ this.massCalculator = massCalculator;
+ }
+
public Rocket getRocket() {
return rocket;
}
-
+
+
+ public void setRocket(Rocket rocket) {
+ if (this.rocket != null)
+ this.modIDadd += this.rocket.getModID();
+ this.modID++;
+ this.rocket = rocket;
+ }
+
public String getMotorConfigurationID() {
return motorID;
}
- public void setMotorConfigurationID(String id) {
- if (id != null)
- id = id.intern();
- if (id == motorID)
- return;
- motorID = id;
- fireChangeEvent();
+
+ public void setMotorConfigurationID(String motorID) {
+ this.motorID = motorID;
+ this.modID++;
}
-
+
public double getLaunchRodLength() {
return launchRodLength;
}
-
+
+
public void setLaunchRodLength(double launchRodLength) {
- if (MathUtil.equals(this.launchRodLength, launchRodLength))
- return;
this.launchRodLength = launchRodLength;
- fireChangeEvent();
+ this.modID++;
}
-
+
public double getLaunchRodAngle() {
return launchRodAngle;
}
-
+
+
public void setLaunchRodAngle(double launchRodAngle) {
- launchRodAngle = MathUtil.clamp(launchRodAngle, 0, MAX_LAUNCH_ROD_ANGLE);
- if (MathUtil.equals(this.launchRodAngle, launchRodAngle))
- return;
this.launchRodAngle = launchRodAngle;
- fireChangeEvent();
+ this.modID++;
}
-
+
public double getLaunchRodDirection() {
return launchRodDirection;
}
-
+
+
public void setLaunchRodDirection(double launchRodDirection) {
- launchRodDirection = MathUtil.reduce180(launchRodDirection);
- if (MathUtil.equals(this.launchRodDirection, launchRodDirection))
- return;
this.launchRodDirection = launchRodDirection;
- fireChangeEvent();
+ this.modID++;
}
-
- public double getWindSpeedAverage() {
- return windAverage;
+ public WorldCoordinate getLaunchSite() {
+ return this.launchSite;
}
-
- public void setWindSpeedAverage(double windAverage) {
- if (MathUtil.equals(this.windAverage, windAverage))
+
+ public void setLaunchSite(WorldCoordinate site) {
+ if (this.launchSite.equals(site))
return;
- this.windAverage = MathUtil.max(windAverage, 0);
- fireChangeEvent();
+ this.launchSite = site;
+ this.modID++;
}
-
- public double getWindSpeedDeviation() {
- return windAverage * windTurbulence;
+
+ public GeodeticComputationStrategy getGeodeticComputation() {
+ return geodeticComputation;
}
-
- public void setWindSpeedDeviation(double windDeviation) {
- if (windAverage < 0.1) {
- windAverage = 0.1;
+
+ public void setGeodeticComputation(GeodeticComputationStrategy geodeticComputation) {
+ if (this.geodeticComputation == geodeticComputation)
+ return;
+ if (geodeticComputation == null) {
+ throw new IllegalArgumentException("strategy cannot be null");
}
- setWindTurbulenceIntensity(windDeviation / windAverage);
+ this.geodeticComputation = geodeticComputation;
+ this.modID++;
}
-
- /**
- * Return the wind turbulence intensity (standard deviation / average).
- *
- * @return the turbulence intensity
- */
- public double getWindTurbulenceIntensity() {
- return windTurbulence;
- }
-
- /**
- * Set the wind standard deviation to match the given turbulence intensity.
- *
- * @param intensity the turbulence intensity
- */
- public void setWindTurbulenceIntensity(double intensity) {
- // Does not check equality so that setWindSpeedDeviation can be sure of event firing
- this.windTurbulence = intensity;
- fireChangeEvent();
- }
-
-
+ public WindModel getWindModel() {
+ return windModel;
+ }
- public double getLaunchAltitude() {
- return launchAltitude;
- }
-
- public void setLaunchAltitude(double altitude) {
- if (MathUtil.equals(this.launchAltitude, altitude))
- return;
- this.launchAltitude = altitude;
- fireChangeEvent();
+ public void setWindModel(WindModel windModel) {
+ if (this.windModel != null)
+ this.modIDadd += this.windModel.getModID();
+ this.modID++;
+ this.windModel = windModel;
}
- public double getLaunchLatitude() {
- return launchLatitude;
- }
-
- public void setLaunchLatitude(double launchLatitude) {
- launchLatitude = MathUtil.clamp(launchLatitude, -90, 90);
- if (MathUtil.equals(this.launchLatitude, launchLatitude))
- return;
- this.launchLatitude = launchLatitude;
- fireChangeEvent();
+ public AtmosphericModel getAtmosphericModel() {
+ return atmosphericModel;
}
-
-
-
- public boolean isISAAtmosphere() {
- return useISA;
+ public void setAtmosphericModel(AtmosphericModel atmosphericModel) {
+ if (this.atmosphericModel != null)
+ this.modIDadd += this.atmosphericModel.getModID();
+ this.modID++;
+ this.atmosphericModel = atmosphericModel;
}
- public void setISAAtmosphere(boolean isa) {
- if (isa == useISA)
- return;
- useISA = isa;
- fireChangeEvent();
- }
-
- public double getLaunchTemperature() {
- return launchTemperature;
- }
-
-
-
- public void setLaunchTemperature(double launchTemperature) {
- if (MathUtil.equals(this.launchTemperature, launchTemperature))
- return;
- this.launchTemperature = launchTemperature;
- this.atmosphericModel = null;
- fireChangeEvent();
- }
-
-
-
- public double getLaunchPressure() {
- return launchPressure;
+ public GravityModel getGravityModel() {
+ return gravityModel;
}
-
-
-
- public void setLaunchPressure(double launchPressure) {
- if (MathUtil.equals(this.launchPressure, launchPressure))
- return;
- this.launchPressure = launchPressure;
- this.atmosphericModel = null;
- fireChangeEvent();
- }
-
- /**
- * Returns an atmospheric model corresponding to the launch conditions. The
- * atmospheric models may be shared between different calls.
- *
- * @return an AtmosphericModel object.
- */
- public AtmosphericModel getAtmosphericModel() {
- if (useISA) {
- return ISA_ATMOSPHERIC_MODEL;
- }
- if (atmosphericModel == null) {
- atmosphericModel = new ExtendedISAModel(launchAltitude,
- launchTemperature, launchPressure);
- }
- return atmosphericModel;
+
+ public void setGravityModel(GravityModel gravityModel) {
+ //if (this.gravityModel != null)
+ // this.modIDadd += this.gravityModel.getModID();
+ this.modID++;
+ this.gravityModel = gravityModel;
}
public double getTimeStep() {
return timeStep;
}
-
+
+
public void setTimeStep(double timeStep) {
- if (MathUtil.equals(this.timeStep, timeStep))
- return;
this.timeStep = timeStep;
- fireChangeEvent();
+ this.modID++;
}
-
- public double getMaximumStepAngle() {
- return maximumAngle;
+
+
+ public double getMaximumAngleStep() {
+ return maximumAngleStep;
}
-
- public void setMaximumStepAngle(double maximumAngle) {
- maximumAngle = MathUtil.clamp(maximumAngle, 1*Math.PI/180, 20*Math.PI/180);
- if (MathUtil.equals(this.maximumAngle, maximumAngle))
- return;
- this.maximumAngle = maximumAngle;
- fireChangeEvent();
+
+
+ public void setMaximumAngleStep(double maximumAngle) {
+ this.maximumAngleStep = maximumAngle;
+ this.modID++;
}
-
-
-
- public boolean getCalculateExtras() {
+
+
+ public boolean isCalculateExtras() {
return calculateExtras;
}
-
-
-
+
+
public void setCalculateExtras(boolean calculateExtras) {
- if (this.calculateExtras == calculateExtras)
- return;
this.calculateExtras = calculateExtras;
- fireChangeEvent();
+ this.modID++;
}
-
-
- @Override
- public SimulationConditions clone() {
- try {
- SimulationConditions copy = (SimulationConditions)super.clone();
- copy.listeners = new ArrayList<ChangeListener>();
- return copy;
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException(e);
- }
+
+
+ public int getRandomSeed() {
+ return randomSeed;
}
- public void copyFrom(SimulationConditions src) {
- if (this.rocket != src.rocket) {
- throw new IllegalArgumentException("Unable to copy simulation conditions of "+
- "a difference rocket");
- }
- if (this.equals(src))
- return;
-
- this.motorID = src.motorID;
- this.launchAltitude = src.launchAltitude;
- this.launchLatitude = src.launchLatitude;
- this.launchPressure = src.launchPressure;
- this.launchRodAngle = src.launchRodAngle;
- this.launchRodDirection = src.launchRodDirection;
- this.launchRodLength = src.launchRodLength;
- this.launchTemperature = src.launchTemperature;
- this.maximumAngle = src.maximumAngle;
- this.timeStep = src.timeStep;
- this.windAverage = src.windAverage;
- this.windTurbulence = src.windTurbulence;
- this.calculateExtras = src.calculateExtras;
-
- fireChangeEvent();
- }
-
-
-
- /**
- * Compares whether the two simulation conditions are equal. The two are considered
- * equal if the rocket, motor id and all variables are equal.
- */
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof SimulationConditions))
- return false;
- SimulationConditions o = (SimulationConditions)other;
- return ((this.rocket == o.rocket) &&
- this.motorID == o.motorID &&
- MathUtil.equals(this.launchAltitude, o.launchAltitude) &&
- MathUtil.equals(this.launchLatitude, o.launchLatitude) &&
- MathUtil.equals(this.launchPressure, o.launchPressure) &&
- MathUtil.equals(this.launchRodAngle, o.launchRodAngle) &&
- MathUtil.equals(this.launchRodDirection, o.launchRodDirection) &&
- MathUtil.equals(this.launchRodLength, o.launchRodLength) &&
- MathUtil.equals(this.launchTemperature, o.launchTemperature) &&
- MathUtil.equals(this.maximumAngle, o.maximumAngle) &&
- MathUtil.equals(this.timeStep, o.timeStep) &&
- MathUtil.equals(this.windAverage, o.windAverage) &&
- MathUtil.equals(this.windTurbulence, o.windTurbulence) &&
- this.calculateExtras == o.calculateExtras);
- }
-
- /**
- * Hashcode method compatible with {@link #equals(Object)}.
- */
- @Override
- public int hashCode() {
- if (motorID == null)
- return rocket.hashCode();
- return rocket.hashCode() + motorID.hashCode();
+ public void setRandomSeed(int randomSeed) {
+ this.randomSeed = randomSeed;
+ this.modID++;
}
+
+
- @Override
- public void addChangeListener(ChangeListener listener) {
- listeners.add(listener);
- }
+ // TODO: HIGH: Make cleaner
+ public List<SimulationListener> getSimulationListenerList() {
+ return simulationListeners;
+ }
+
+
@Override
- public void removeChangeListener(ChangeListener listener) {
- listeners.remove(listener);
+ public int getModID() {
+ //return (modID + modIDadd + rocket.getModID() + windModel.getModID() + atmosphericModel.getModID() +
+ // gravityModel.getModID() + aerodynamicCalculator.getModID() + massCalculator.getModID());
+ return (modID + modIDadd + rocket.getModID() + windModel.getModID() + atmosphericModel.getModID() +
+ aerodynamicCalculator.getModID() + massCalculator.getModID());
}
- private final ChangeEvent event = new ChangeEvent(this);
- private void fireChangeEvent() {
- ChangeListener[] array = listeners.toArray(new ChangeListener[0]);
-
- for (int i=array.length-1; i >=0; i--) {
- array[i].stateChanged(event);
+
+ @Override
+ public SimulationConditions clone() {
+ try {
+ // TODO: HIGH: Deep clone models
+ return (SimulationConditions) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new BugException(e);
}
}