+2011-09-26 Sampo Niskanen
+
+ * [BUG] Thrust was computed from dropped stages
+
2011-09-18 Sampo Niskanen
* Remember window/dialog sizes and/or positions
SimuRunDlg.msg.unknownerror2 = The program may be unstable, you should save all your designs and restart OpenRocket now!
+RK4SimulationStepper.error.valuesTooLarge = Simulation values exceeded limits. Try selecting a shorter time step.
+
! SimulationExportPanel
SimExpPan.desc = Comma Separated Files (*.csv)
simplotpanel.LEFT_NAME = Left
simplotpanel.RIGHT_NAME = Right
simplotpanel.CUSTOM = Custom
+SimulationPlotPanel.error.noPlotSelected = Please add one or more variables to plot on the Y-axis.
+SimulationPlotPanel.error.noPlotSelected.title = Nothing to plot
+
! Component add buttons
compaddbuttons.Bodycompandfinsets = Body components and fin sets
--- /dev/null
+package net.sf.openrocket.file.configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class XmlContainerElement extends XmlElement {
+
+ private ArrayList<XmlElement> subelements = new ArrayList<XmlElement>();
+
+ public XmlContainerElement(String name) {
+ super(name);
+ }
+
+
+ public void addElement(XmlElement element) {
+ subelements.add(element);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<XmlElement> getElements() {
+ return (List<XmlElement>) subelements.clone();
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.file.configuration;
+
+/**
+ * A simple XML element that contains textual content.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class XmlContentElement extends XmlElement {
+
+ private String content = "";
+
+ public XmlContentElement(String name) {
+ super(name);
+ }
+
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ if (content == null) {
+ throw new IllegalArgumentException("XML content cannot be null");
+ }
+ this.content = content;
+ }
+
+}
--- /dev/null
+package net.sf.openrocket.file.configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A base simple XML element. A simple XML element can contain either other XML elements
+ * (XmlContainerElement) or textual content (XmlContentElement), but not both.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public abstract class XmlElement {
+
+ private final String name;
+ private final HashMap<String, String> attributes = new HashMap<String, String>();
+
+
+
+ public XmlElement(String name) {
+ this.name = name;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+ public void setAttribute(String key, String value) {
+ attributes.put(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ attributes.remove(key);
+ }
+
+ public String getAttribute(String key) {
+ return attributes.get(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> getAttributes() {
+ return (Map<String, String>) attributes.clone();
+ }
+
+}
data.add(branch.get(types[i]));
}
List<Double> timeData = branch.get(FlightDataType.TYPE_TIME);
- if (timeData == null) {
- // TODO: MEDIUM: External data may not have time data
- throw new IllegalArgumentException("Data did not contain time data");
- }
// Build the <databranch> tag
StringBuilder sb = new StringBuilder();
}
for (int i = 1; i < length - 1; i++) {
- if (Math.abs(timeData.get(i) - previousTime - timeSkip) < Math.abs(timeData.get(i + 1) - previousTime - timeSkip)) {
+ if (timeData != null) {
+ if (Math.abs(timeData.get(i) - previousTime - timeSkip) < Math.abs(timeData.get(i + 1) - previousTime - timeSkip)) {
+ writeDataPointString(data, i, sb);
+ previousTime = timeData.get(i);
+ }
+ } else {
+ // If time data is not available, write all points
writeDataPointString(data, i, sb);
- previousTime = timeData.get(i);
}
}
List<Double> timeData = branch.get(FlightDataType.TYPE_TIME);
if (timeData == null) {
- // TODO: MEDIUM: External data may not have time data
- throw new IllegalArgumentException("Data did not contain time data");
+ // If time data not available, store all points
+ return branch.getLength();
}
// Write the data
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
-import net.sf.openrocket.l10n.Translator;
-import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.GUIUtil;
public class DetailDialog {
- private static final Translator trans = Application.getTranslator();
public static void showDetailedMessageDialog(Component parentComponent, Object message,
String details, String title, int messageType) {
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
-import java.io.CharArrayWriter;
-import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
return; // Ignore cancellations
}
- // Retrieve the stack trace in a textual form
- CharArrayWriter arrayWriter = new CharArrayWriter();
- arrayWriter.append(t.toString() + "\n" + "\n");
- t.printStackTrace(new PrintWriter(arrayWriter));
- String stackTrace = arrayWriter.toString();
-
// Analyze the exception type
if (t instanceof SimulationLaunchException) {
trans.get("SimuRunDlg.msg.errorOccurred"),
t.getMessage()
},
- stackTrace, simulation.getName(), JOptionPane.ERROR_MESSAGE);
+ null, simulation.getName(), JOptionPane.ERROR_MESSAGE);
} else {
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons;
+import net.sf.openrocket.util.Utils;
/**
* Panel that displays the simulation plot options to the user.
FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
types = branch.getTypes();
- // TODO: LOW: Revert to custom if data type is not available.
- configuration = defaultConfiguration.clone();
+ setConfiguration(defaultConfiguration);
-
//// Configuration selector
// Setup the combo box
configurationSelector.setSelectedItem(config);
}
}
+
+ // FIXME: Bugs when expected branch is not present
+
configurationSelector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (conf == CUSTOM_CONFIGURATION)
return;
modifying++;
- configuration = conf.clone().resetUnits();
+ setConfiguration(conf.clone().resetUnits());
updatePlots();
modifying--;
}
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ if (configuration.getTypeCount() == 0) {
+ JOptionPane.showMessageDialog(SimulationPlotPanel.this,
+ trans.get("error.noPlotSelected"),
+ trans.get("error.noPlotSelected.title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
defaultConfiguration = configuration.clone();
SimulationPlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
simulation, configuration);
}
+ private void setConfiguration(PlotConfiguration conf) {
+
+ boolean modified = false;
+
+ configuration = conf.clone();
+ if (!Utils.contains(types, configuration.getDomainAxisType())) {
+ configuration.setDomainAxisType(types[0]);
+ modified = true;
+ }
+
+ for (int i = 0; i < configuration.getTypeCount(); i++) {
+ if (!Utils.contains(types, configuration.getType(i))) {
+ configuration.removePlotDataType(i);
+ i--;
+ modified = true;
+ }
+ }
+
+ if (modified) {
+ configuration.setName(CUSTOM);
+ }
+
+ }
+
+
private void setToCustom() {
modifying++;
configuration.setName(CUSTOM);
modID++;
}
+ /**
+ * Return a list of all motor IDs in this configuration (not only ones in active stages).
+ */
public List<MotorId> getMotorIDs() {
return unmodifiableIds;
}
return isStageActive(0);
}
- public boolean isStageActive(RocketComponent stage) {
- if (!(stage instanceof Stage)) {
- throw new IllegalArgumentException("called with component " + stage);
- }
- return stages.get(stage.getParent().getChildPosition(stage));
- }
-
+
+ /**
+ * Check whether the stage specified by the index is active.
+ */
public boolean isStageActive(int stage) {
if (stage >= rocket.getStageCount())
return false;
}
+ /**
+ * Return whether a component is in the currently active stages.
+ */
+ public boolean isComponentActive(final RocketComponent c) {
+ int stage = c.getStageNumber();
+ return isStageActive(stage);
+ }
+
+
/**
* Return the bounds of the current configuration. The bounds are cached.
*
List<Iterator<RocketComponent>> list = new ArrayList<Iterator<RocketComponent>>();
for (RocketComponent stage : rocket.getChildren()) {
- if (isStageActive(stage)) {
+ if (isComponentActive(stage)) {
list.add(stage.iterator(false));
}
}
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
+import net.sf.openrocket.rocketcomponent.Configuration;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
import net.sf.openrocket.util.BugException;
return thrust;
}
+ Configuration configuration = status.getConfiguration();
+
// Iterate over the motors and calculate combined thrust
- MotorInstanceConfiguration configuration = status.getMotorConfiguration();
+ MotorInstanceConfiguration mic = status.getMotorConfiguration();
if (!stepMotors) {
- configuration = configuration.clone();
+ mic = mic.clone();
}
- configuration.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
+ mic.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
thrust = 0;
- for (MotorId id : configuration.getMotorIDs()) {
- MotorInstance motor = configuration.getMotorInstance(id);
- thrust += motor.getThrust();
+ for (MotorId id : mic.getMotorIDs()) {
+ if (configuration.isComponentActive((RocketComponent) mic.getMotorMount(id))) {
+ MotorInstance motor = mic.getMotorInstance(id);
+ thrust += motor.getThrust();
+ }
}
// Post-listeners
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.WorldCoordinate;
public class BasicLandingStepper extends AbstractSimulationStepper {
private static final double RECOVERY_TIME_STEP = 0.5;
- // FIXME: Add lat/lon code here as well
-
@Override
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
return status;
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
+ // Add coriolis acceleration
+ Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
+ status.getRocketWorldPosition(), status.getRocketVelocity());
+ linearAcceleration = linearAcceleration.add(coriolisAcceleration);
+
+
+
// Select time step
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
status.setSimulationTime(status.getSimulationTime() + timeStep);
+ // Update the world coordinate
+ WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
+ w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
+ status.setRocketWorldPosition(w);
+
+
// Store data
FlightDataBranch data = status.getFlightData();
boolean extra = status.getSimulationConditions().isCalculateExtras();
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
}
+
+ data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
+ data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
+ if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
+ data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
+ }
+
+
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.simulation.exception.SimulationCalculationException;
public class RK4SimulationStepper extends AbstractSimulationStepper {
private static final LogHelper log = Application.getLogger();
+ private static final Translator trans = Application.getTranslator();
+
/** Random value with which to XOR the random seed value */
private static final int SEED_RANDOMIZATION = 0x23E3A01F;
if (status.getRocketVelocity().length2() > 1e18 ||
status.getRocketPosition().length2() > 1e18 ||
status.getRocketRotationVelocity().length2() > 1e18) {
-
- // FIXME: Make error message better, recommend shortening time step
-
- throw new SimulationCalculationException("Simulation values exceeded limits");
+ throw new SimulationCalculationException(trans.get("error.valuesTooLarge"));
}
}
public class SimulationCalculationException extends SimulationException {
public SimulationCalculationException() {
- // TODO Auto-generated constructor stub
}
public SimulationCalculationException(String message) {
super(message);
- // TODO Auto-generated constructor stub
}
public SimulationCalculationException(Throwable cause) {
super(cause);
- // TODO Auto-generated constructor stub
}
public SimulationCalculationException(String message, Throwable cause) {
super(message, cause);
- // TODO Auto-generated constructor stub
}
}
}
}
+
+ /**
+ * Check whether an array contains a specified object.
+ *
+ * @param array the array to search
+ * @param search the object to search for
+ * @return whether the object was in the array
+ */
+ public static boolean contains(Object[] array, Object search) {
+ for (Object o : array) {
+ if (equals(o, search)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}