bug fixes
This commit is contained in:
parent
d2cf2aa180
commit
85b6d7358b
@ -1,3 +1,7 @@
|
||||
2011-09-26 Sampo Niskanen
|
||||
|
||||
* [BUG] Thrust was computed from dropped stages
|
||||
|
||||
2011-09-18 Sampo Niskanen
|
||||
|
||||
* Remember window/dialog sizes and/or positions
|
||||
|
@ -401,6 +401,8 @@ SimuRunDlg.msg.unknownerror1 = An unknown error was encountered during the simul
|
||||
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)
|
||||
@ -469,6 +471,9 @@ simplotpanel.AUTO_NAME = Auto
|
||||
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
|
||||
|
@ -0,0 +1,24 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
45
src/net/sf/openrocket/file/configuration/XmlElement.java
Normal file
45
src/net/sf/openrocket/file/configuration/XmlElement.java
Normal file
@ -0,0 +1,45 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -409,10 +409,6 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
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();
|
||||
@ -442,9 +438,14 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,8 +476,8 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
|
||||
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
|
||||
|
@ -6,12 +6,9 @@ import javax.swing.JOptionPane;
|
||||
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) {
|
||||
|
@ -8,8 +8,6 @@ import java.awt.event.ActionEvent;
|
||||
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;
|
||||
@ -382,12 +380,6 @@ public class SimulationRunDialog extends JDialog {
|
||||
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) {
|
||||
|
||||
@ -407,7 +399,7 @@ public class SimulationRunDialog extends JDialog {
|
||||
trans.get("SimuRunDlg.msg.errorOccurred"),
|
||||
t.getMessage()
|
||||
},
|
||||
stackTrace, simulation.getName(), JOptionPane.ERROR_MESSAGE);
|
||||
null, simulation.getName(), JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -32,6 +32,7 @@ import net.sf.openrocket.startup.Application;
|
||||
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.
|
||||
@ -105,10 +106,8 @@ public class SimulationPlotPanel extends JPanel {
|
||||
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
|
||||
@ -118,6 +117,9 @@ public class SimulationPlotPanel extends JPanel {
|
||||
configurationSelector.setSelectedItem(config);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Bugs when expected branch is not present
|
||||
|
||||
configurationSelector.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
@ -127,7 +129,7 @@ public class SimulationPlotPanel extends JPanel {
|
||||
if (conf == CUSTOM_CONFIGURATION)
|
||||
return;
|
||||
modifying++;
|
||||
configuration = conf.clone().resetUnits();
|
||||
setConfiguration(conf.clone().resetUnits());
|
||||
updatePlots();
|
||||
modifying--;
|
||||
}
|
||||
@ -291,6 +293,13 @@ public class SimulationPlotPanel extends JPanel {
|
||||
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);
|
||||
@ -303,6 +312,31 @@ public class SimulationPlotPanel extends JPanel {
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -51,6 +51,9 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
|
||||
modID++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all motor IDs in this configuration (not only ones in active stages).
|
||||
*/
|
||||
public List<MotorId> getMotorIDs() {
|
||||
return unmodifiableIds;
|
||||
}
|
||||
|
@ -98,14 +98,11 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
|
||||
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;
|
||||
@ -255,6 +252,15 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -366,7 +372,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
|
||||
List<Iterator<RocketComponent>> list = new ArrayList<Iterator<RocketComponent>>();
|
||||
|
||||
for (RocketComponent stage : rocket.getChildren()) {
|
||||
if (isStageActive(stage)) {
|
||||
if (isComponentActive(stage)) {
|
||||
list.add(stage.iterator(false));
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
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;
|
||||
@ -168,16 +170,20 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
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
|
||||
|
@ -4,14 +4,14 @@ import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
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;
|
||||
@ -55,6 +55,13 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
|
||||
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);
|
||||
|
||||
@ -65,6 +72,12 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
|
||||
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();
|
||||
@ -93,6 +106,14 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
|
||||
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);
|
||||
|
||||
|
@ -6,6 +6,7 @@ import java.util.Random;
|
||||
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;
|
||||
@ -22,7 +23,9 @@ import net.sf.openrocket.util.WorldCoordinate;
|
||||
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;
|
||||
|
||||
@ -266,10 +269,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,22 +9,18 @@ package net.sf.openrocket.simulation.exception;
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,4 +17,21 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user